[quagga-dev 11350] [PATCH 5/6] ripngd: add ECMP support

Feng Lu lu.feng at 6wind.com
Wed Jun 25 09:43:19 BST 2014


* Each node in the routing table is changed into a list, holding
  the multiple equal-cost paths.

* RIPNG_MULTI_PATH_LIMIT is introduced to control the maximum number
  of nexthops added into zebra. it equals to MULTIPATH_NUM.

* All equal-cost route entries are saved in the list, while only
  the first RIPNG_MULTI_PATH_LIMIT ones exist in fib. This helps to
  switch to a candidate equal-cost path, when a path gets less
  preferred. The candidate entries in the ECMP list are shown as
  "R(-)" instead of "R(n)".

* In the multiple entries case, one of them will be directly deleted
  if it gets less-preferred or INFINITY. The garbage-collection timer
  is started only when the last entry in the list gets INFINITY.

* Some new functions are used to maintain the ECMP list. And hence
  ripng_route_process(), ripng_redistribute_add() and ripng_timeout()
  are significantly simplified.

* A command "ripng equal-cost N" is introduced to control the allowed
  maximum number of equal-cost paths. It can not exceed MULTIPATH_NUM.

* The flag RIPNG_RTF_FIB on the learnt route entries is now maintained
  in ripng_zebra_ipv6_add() and ripng_zebra_ipv6_delete().

Signed-off-by: Feng Lu <lu.feng at 6wind.com>
Reviewed-by: Alain Ritoux <alain.ritoux at 6wind.com>
---
 ripngd/Makefile.am       |    1 +
 ripngd/ripng_interface.c |   37 +--
 ripngd/ripng_route.c     |   45 +++-
 ripngd/ripng_route.h     |    3 +
 ripngd/ripng_zebra.c     |   97 +++++-
 ripngd/ripngd.c          |  730 ++++++++++++++++++++++++++++------------------
 ripngd/ripngd.h          |   21 +-
 7 files changed, 594 insertions(+), 340 deletions(-)

diff --git a/ripngd/Makefile.am b/ripngd/Makefile.am
index de5beba..183a3fb 100644
--- a/ripngd/Makefile.am
+++ b/ripngd/Makefile.am
@@ -2,6 +2,7 @@
 
 INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
 DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+DEFS += -DRIPNG_MULTI_PATH_LIMIT=@MULTIPATH_NUM@
 INSTALL_SDATA=@INSTALL@ -m 600
 
 AM_CFLAGS = $(PICFLAGS)
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
index 8717bfb..3588463 100644
--- a/ripngd/ripng_interface.c
+++ b/ripngd/ripng_interface.c
@@ -163,38 +163,15 @@ ripng_if_down (struct interface *ifp)
   struct route_node *rp;
   struct ripng_info *rinfo;
   struct ripng_interface *ri;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL, *nextnode = NULL;
 
   if (ripng)
-    {
-      for (rp = route_top (ripng->table); rp; rp = route_next (rp))
-	if ((rinfo = rp->info) != NULL)
-	  {
-	    /* Routes got through this interface. */
-	    if (rinfo->ifindex == ifp->ifindex
-		&& rinfo->type == ZEBRA_ROUTE_RIPNG
-		&& rinfo->sub_type == RIPNG_ROUTE_RTE)
-	      {
-		ripng_zebra_ipv6_delete ((struct prefix_ipv6 *) &rp->p,
-					 &rinfo->nexthop,
-					 rinfo->ifindex);
-
-		ripng_redistribute_delete (rinfo->type, rinfo->sub_type,
-					   (struct prefix_ipv6 *)&rp->p,
-					   rinfo->ifindex);
-	      }
-	    else
-	      {
-		/* All redistributed routes got through this interface,
-		 * but the static and system ones are kept. */
-		if ((rinfo->ifindex == ifp->ifindex) &&
-		    (rinfo->type != ZEBRA_ROUTE_STATIC) &&
-		    (rinfo->type != ZEBRA_ROUTE_SYSTEM))
-		  ripng_redistribute_delete (rinfo->type, rinfo->sub_type,
-					     (struct prefix_ipv6 *) &rp->p,
-					     rinfo->ifindex);
-	      }
-	  }
-    }
+    for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS (list, listnode, nextnode, rinfo))
+          if (rinfo->ifindex == ifp->ifindex)
+            ripng_ecmp_delete (rinfo);
 
   ri = ifp->info;
   
diff --git a/ripngd/ripng_route.c b/ripngd/ripng_route.c
index d4bf026..f26302e 100644
--- a/ripngd/ripng_route.c
+++ b/ripngd/ripng_route.c
@@ -40,7 +40,7 @@ ripng_aggregate_new ()
   return new;
 }
 
-static void
+void
 ripng_aggregate_free (struct ripng_aggregate *aggregate)
 {
   XFREE (MTYPE_RIPNG_AGGREGATE, aggregate);
@@ -76,6 +76,23 @@ ripng_aggregate_decrement (struct route_node *child, struct ripng_info *rinfo)
       }
 }
 
+/* Aggregate count decrement check for a list. */
+void
+ripng_aggregate_decrement_list (struct route_node *child, struct list *list)
+{
+  struct route_node *np;
+  struct ripng_aggregate *aggregate;
+  struct ripng_info *rinfo = NULL;
+  struct listnode *node = NULL;
+
+  for (np = child; np; np = np->parent)
+    if ((aggregate = np->aggregate) != NULL)
+      aggregate->count -= listcount (list);
+
+  for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+    rinfo->suppress--;
+}
+
 /* RIPng routes treatment. */
 int
 ripng_aggregate_add (struct prefix *p)
@@ -85,6 +102,8 @@ ripng_aggregate_add (struct prefix *p)
   struct ripng_info *rinfo;
   struct ripng_aggregate *aggregate;
   struct ripng_aggregate *sub;
+  struct list *list = NULL;
+  struct listnode *node = NULL;
 
   /* Get top node for aggregation. */
   top = route_node_get (ripng->table, p);
@@ -99,11 +118,12 @@ ripng_aggregate_add (struct prefix *p)
   for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top))
     {
       /* Suppress normal route. */
-      if ((rinfo = rp->info) != NULL)
-	{
-	  aggregate->count++;
-	  rinfo->suppress++;
-	}
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+          {
+            aggregate->count++;
+            rinfo->suppress++;
+          }
       /* Suppress aggregate route.  This may not need. */
       if (rp != top && (sub = rp->aggregate) != NULL)
 	{
@@ -124,6 +144,8 @@ ripng_aggregate_delete (struct prefix *p)
   struct ripng_info *rinfo;
   struct ripng_aggregate *aggregate;
   struct ripng_aggregate *sub;
+  struct list *list = NULL;
+  struct listnode *node = NULL;
 
   /* Get top node for aggregation. */
   top = route_node_get (ripng->table, p);
@@ -135,11 +157,12 @@ ripng_aggregate_delete (struct prefix *p)
   for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top))
     {
       /* Suppress normal route. */
-      if ((rinfo = rp->info) != NULL)
-	{
-	  aggregate->count--;
-	  rinfo->suppress--;
-	}
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+          {
+            aggregate->count--;
+            rinfo->suppress--;
+          }
 
       if (rp != top && (sub = rp->aggregate) != NULL)
 	{
diff --git a/ripngd/ripng_route.h b/ripngd/ripng_route.h
index 2f5b757..fe65c88 100644
--- a/ripngd/ripng_route.h
+++ b/ripngd/ripng_route.h
@@ -48,7 +48,10 @@ extern void ripng_aggregate_increment (struct route_node *rp,
                                        struct ripng_info *rinfo);
 extern void ripng_aggregate_decrement (struct route_node *rp,
                                        struct ripng_info *rinfo);
+extern void ripng_aggregate_decrement_list (struct route_node *rp,
+                                       struct list *list);
 extern int ripng_aggregate_add (struct prefix *p);
 extern int ripng_aggregate_delete (struct prefix *p);
+extern void ripng_aggregate_free (struct ripng_aggregate *aggregate);
 
 #endif /* _ZEBRA_RIPNG_ROUTE_H */
diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c
index 68f37be..501f459 100644
--- a/ripngd/ripng_zebra.c
+++ b/ripngd/ripng_zebra.c
@@ -24,12 +24,14 @@
 
 #include "command.h"
 #include "prefix.h"
+#include "table.h"
 #include "stream.h"
 #include "routemap.h"
 #include "zclient.h"
 #include "log.h"
 
 #include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
 
 /* All information about zebra. */
 struct zclient *zclient = NULL;
@@ -42,11 +44,17 @@ int ripng_interface_delete (int, struct zclient *, zebra_size_t);
 int ripng_interface_address_add (int, struct zclient *, zebra_size_t);
 int ripng_interface_address_delete (int, struct zclient *, zebra_size_t);
 
+/* Add/update ECMP routes to zebra. */
 void
-ripng_zebra_ipv6_add (struct prefix_ipv6 *p, struct in6_addr *nexthop,
-		      unsigned int ifindex, u_char metric)
+ripng_zebra_ipv6_add (struct route_node *rp)
 {
   struct zapi_ipv6 api;
+  struct list *list = (struct list *)rp->info;
+  struct in6_addr *nexthops[RIPNG_MULTI_PATH_LIMIT];
+  unsigned int ifindexes[RIPNG_MULTI_PATH_LIMIT];
+  struct listnode *listnode = NULL;
+  struct ripng_info *rinfo = NULL;
+  u_char count = 0;
 
   if (zclient->redist[ZEBRA_ROUTE_RIPNG])
     {
@@ -54,24 +62,56 @@ ripng_zebra_ipv6_add (struct prefix_ipv6 *p, struct in6_addr *nexthop,
       api.flags = 0;
       api.message = 0;
       api.safi = SAFI_UNICAST;
+
       SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
-      api.nexthop_num = 1;
-      api.nexthop = &nexthop;
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        {
+          /* Only the first "multipath_limit" nexthops are added. */
+          if (count < ripng->ripng_multipath_limit)
+            {
+              nexthops[count] = &rinfo->nexthop;
+              ifindexes[count] = rinfo->ifindex;
+              count++;
+              SET_FLAG (rinfo->flags, RIPNG_RTF_FIB);
+            }
+          else
+            UNSET_FLAG (rinfo->flags, RIPNG_RTF_FIB);
+        }
+      if (!count)
+        return;
+
+      api.nexthop = nexthops;
+      api.nexthop_num = count;
+
       SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
-      api.ifindex_num = 1;
-      api.ifindex = &ifindex;
+      api.ifindex = ifindexes;
+      api.ifindex_num = count;
+
+      rinfo = listgetdata (listhead (list));
+
       SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
-      api.metric = metric;
-      
-      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, p, &api);
+      api.metric = rinfo->metric;
+
+      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient,
+                       (struct prefix_ipv6 *)&rp->p, &api);
+
+      if (IS_RIPNG_DEBUG_ZEBRA)
+        zlog_debug ("Install into zebra: %s/%d nexthops %d",
+                    inet6_ntoa (rp->p.u.prefix6), rp->p.prefixlen, count);
     }
 }
 
+/* Delete ECMP routes from zebra. */
 void
-ripng_zebra_ipv6_delete (struct prefix_ipv6 *p, struct in6_addr *nexthop,
-			 unsigned int ifindex)
+ripng_zebra_ipv6_delete (struct route_node *rp)
 {
   struct zapi_ipv6 api;
+  struct list *list = (struct list *)rp->info;
+  struct in6_addr *nexthops[RIPNG_MULTI_PATH_LIMIT];
+  unsigned int ifindexes[RIPNG_MULTI_PATH_LIMIT];
+  struct listnode *listnode = NULL;
+  struct ripng_info *rinfo = NULL;
+  u_char count = 0;
 
   if (zclient->redist[ZEBRA_ROUTE_RIPNG])
     {
@@ -79,14 +119,39 @@ ripng_zebra_ipv6_delete (struct prefix_ipv6 *p, struct in6_addr *nexthop,
       api.flags = 0;
       api.message = 0;
       api.safi = SAFI_UNICAST;
+
       SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);
-      api.nexthop_num = 1;
-      api.nexthop = &nexthop;
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        {
+          if (CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+            {
+              nexthops[count] = &rinfo->nexthop;
+              ifindexes[count] = rinfo->ifindex;
+              count++;
+              UNSET_FLAG (rinfo->flags, RIPNG_RTF_FIB);
+            }
+        }
+      if (!count)
+        return;
+
+      api.nexthop = nexthops;
+      api.nexthop_num = count;
+
       SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
-      api.ifindex_num = 1;
-      api.ifindex = &ifindex;
+      api.ifindex = ifindexes;
+      api.ifindex_num = count;
+
+      rinfo = listgetdata (listhead (list));
+
+      SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
+      api.metric = rinfo->metric;
+
+      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient,
+                       (struct prefix_ipv6 *)&rp->p, &api);
 
-      zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, p, &api);
+      if (IS_RIPNG_DEBUG_ZEBRA)
+        zlog_debug ("Delete from zebra: %s/%d nexthops %d",
+                    inet6_ntoa (rp->p.u.prefix6), rp->p.prefixlen, count);
     }
 }
 
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index 941c3a0..12f62be 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -421,8 +421,13 @@ ripng_garbage_collect (struct thread *t)
   rp = rinfo->rp;
 
   /* Unlock route_node. */
-  rp->info = NULL;
-  route_unlock_node (rp);
+  listnode_delete (rp->info, rinfo);
+  if (list_isempty ((struct list *)rp->info))
+    {
+      list_free (rp->info);
+      rp->info = NULL;
+      route_unlock_node (rp);
+    }
 
   /* Free RIPng routing information. */
   ripng_info_free (rinfo);
@@ -430,41 +435,161 @@ ripng_garbage_collect (struct thread *t)
   return 0;
 }
 
-/* Timeout RIPng routes. */
-static int
-ripng_timeout (struct thread *t)
+static void ripng_timeout_update (struct ripng_info *rinfo);
+
+/* Add new route to the ECMP list.
+ * RETURN: the new entry added in the list
+ */
+struct ripng_info *
+ripng_ecmp_add (struct ripng_info *rinfo_new)
 {
-  struct ripng_info *rinfo;
-  struct route_node *rp;
+  struct route_node *rp = rinfo_new->rp;
+  struct ripng_info *rinfo = NULL;
+  struct list *list = NULL;
 
-  rinfo = THREAD_ARG (t);
-  rinfo->t_timeout = NULL;
+  rinfo = ripng_info_new ();
+  memcpy (rinfo, rinfo_new, sizeof (struct ripng_info));
 
-  /* Get route_node pointer. */
-  rp = rinfo->rp;
+  if (rp->info == NULL)
+    rp->info = list_new ();
+  list = (struct list *)rp->info;
+  listnode_add (list, rinfo);
+
+  if (ripng_route_rte (rinfo))
+    {
+      ripng_timeout_update (rinfo);
+
+      if (listcount (list) <= ripng->ripng_multipath_limit)
+        ripng_zebra_ipv6_add (rp);
+    }
+
+  ripng_aggregate_increment (rp, rinfo);
+
+  /* Set the route change flag on the first entry. */
+  rinfo = listgetdata (listhead (list));
+  SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update. */
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+
+  return rinfo;
+}
+
+/* Replace the ECMP list with the new route.
+ * RETURN: the new entry added in the list
+ */
+struct ripng_info *
+ripng_ecmp_replace (struct ripng_info *rinfo_new)
+{
+  struct route_node *rp = rinfo_new->rp;
+  struct list *list = (struct list *)rp->info;
+  struct ripng_info *rinfo = NULL, *tmp_rinfo = NULL;
+  struct listnode *node = NULL, *nextnode = NULL;
+
+  if (list == NULL || listcount (list) == 0)
+    return ripng_ecmp_add (rinfo_new);
+
+  /* Get the first entry */
+  rinfo = listgetdata (listhead (list));
+
+  /* Learnt route replaced by a local one. Delete it from zebra. */
+  if (ripng_route_rte (rinfo) && !ripng_route_rte (rinfo_new))
+    if (CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+      ripng_zebra_ipv6_delete (rp);
+
+  if (rinfo->metric != RIPNG_METRIC_INFINITY)
+    ripng_aggregate_decrement_list (rp, list);
+
+  /* Re-use the first entry, and delete the others. */
+  for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo))
+    if (tmp_rinfo != rinfo)
+      {
+        RIPNG_TIMER_OFF (tmp_rinfo->t_timeout);
+        RIPNG_TIMER_OFF (tmp_rinfo->t_garbage_collect);
+        list_delete_node (list, node);
+        ripng_info_free (tmp_rinfo);
+      }
+
+  RIPNG_TIMER_OFF (rinfo->t_timeout);
+  RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+  memcpy (rinfo, rinfo_new, sizeof (struct ripng_info));
+
+  if (ripng_route_rte (rinfo))
+    {
+      ripng_timeout_update (rinfo);
+      /* The ADD message implies an update. */
+      ripng_zebra_ipv6_add (rp);
+    }
+
+  ripng_aggregate_increment (rp, rinfo);
 
-  /* - The garbage-collection timer is set for 120 seconds. */
-  RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect, 
-		  ripng->garbage_time);
+  /* Set the route change flag. */
+  SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
 
-  /* Delete this route from the kernel. */
-  ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, &rinfo->nexthop,
-				rinfo->ifindex);
-  /* - The metric for the route is set to 16 (infinity).  This causes
-     the route to be removed from service. */
-  rinfo->metric = RIPNG_METRIC_INFINITY;
-  rinfo->flags &= ~RIPNG_RTF_FIB;
+  /* Signal the output process to trigger an update. */
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
 
-  /* Aggregate count decrement. */
-  ripng_aggregate_decrement (rp, rinfo);
+  return rinfo;
+}
 
-  /* - The route change flag is to indicate that this entry has been
-     changed. */
-  rinfo->flags |= RIPNG_RTF_CHANGED;
+/* Delete one route from the ECMP list.
+ * RETURN:
+ *  null - the entry is freed, and other entries exist in the list
+ *  the entry - the entry is the last one in the list; its metric is set
+ *              to INFINITY, and the garbage collector is started for it
+ */
+struct ripng_info *
+ripng_ecmp_delete (struct ripng_info *rinfo)
+{
+  struct route_node *rp = rinfo->rp;
+  struct list *list = (struct list *)rp->info;
+
+  RIPNG_TIMER_OFF (rinfo->t_timeout);
 
-  /* - The output process is signalled to trigger a response. */
+  if (rinfo->metric != RIPNG_METRIC_INFINITY)
+    ripng_aggregate_decrement (rp, rinfo);
+
+  if (listcount (list) > 1)
+    {
+      /* Some other ECMP entries still exist. Just delete this entry. */
+      RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+      listnode_delete (list, rinfo);
+      if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+        /* The ADD message implies the update. */
+        ripng_zebra_ipv6_add (rp);
+      ripng_info_free (rinfo);
+      rinfo = NULL;
+    }
+  else
+    {
+      assert (rinfo == listgetdata (listhead (list)));
+
+      /* This is the only entry left in the list. We must keep it in
+       * the list for garbage collection time, with INFINITY metric. */
+
+      rinfo->metric = RIPNG_METRIC_INFINITY;
+      RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+                      ripng_garbage_collect, ripng->garbage_time);
+
+      if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+        ripng_zebra_ipv6_delete (rp);
+    }
+
+  /* Set the route change flag on the first entry. */
+  rinfo = listgetdata (listhead (list));
+  SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+
+  /* Signal the output process to trigger an update. */
   ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
 
+  return rinfo;
+}
+
+/* Timeout RIPng routes. */
+static int
+ripng_timeout (struct thread *t)
+{
+  ripng_ecmp_delete ((struct ripng_info *)THREAD_ARG (t));
   return 0;
 }
 
@@ -628,11 +753,12 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
   int ret;
   struct prefix_ipv6 p;
   struct route_node *rp;
-  struct ripng_info *rinfo;
+  struct ripng_info *rinfo = NULL, newinfo;
   struct ripng_interface *ri;
   struct in6_addr *nexthop;
-  u_char oldmetric;
   int same = 0;
+  struct list *list = NULL;
+  struct listnode *node = NULL;
 
   /* Make prefix structure. */
   memset (&p, 0, sizeof (struct prefix_ipv6));
@@ -653,24 +779,23 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
   if (ret < 0)
     return;
 
+  memset (&newinfo, 0, sizeof (newinfo));
+  newinfo.type = ZEBRA_ROUTE_RIPNG;
+  newinfo.sub_type = RIPNG_ROUTE_RTE;
+  if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS)
+    newinfo.nexthop = ripng_nexthop->address;
+  else
+    newinfo.nexthop = from->sin6_addr;
+  newinfo.from = from->sin6_addr;
+  newinfo.ifindex = ifp->ifindex;
+  newinfo.metric = rte->metric;
+  newinfo.metric_out = rte->metric; /* XXX */
+  newinfo.tag = ntohs (rte->tag);   /* XXX */
+
   /* Modify entry. */
   if (ri->routemap[RIPNG_FILTER_IN])
     {
       int ret;
-      struct ripng_info newinfo;
-
-      memset (&newinfo, 0, sizeof (struct ripng_info));
-      newinfo.type = ZEBRA_ROUTE_RIPNG;
-      newinfo.sub_type = RIPNG_ROUTE_RTE;
-      if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS)
-        newinfo.nexthop = ripng_nexthop->address;
-      else
-        newinfo.nexthop = from->sin6_addr;
-      newinfo.from   = from->sin6_addr;
-      newinfo.ifindex = ifp->ifindex;
-      newinfo.metric = rte->metric;
-      newinfo.metric_out = rte->metric; /* XXX */
-      newinfo.tag    = ntohs(rte->tag); /* XXX */
 
       ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN], 
 			     (struct prefix *)&p, RMAP_RIPNG, &newinfo);
@@ -731,24 +856,66 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
   /* Lookup RIPng routing table. */
   rp = route_node_get (ripng->table, (struct prefix *) &p);
 
-  /* Sanity check */
-  rinfo = rp->info;
+  newinfo.rp = rp;
+  newinfo.nexthop = *nexthop;
+  newinfo.metric = rte->metric;
+  newinfo.tag = ntohs (rte->tag);
+
+  /* Check to see whether there is already RIPng route on the table. */
+  if ((list = rp->info) != NULL)
+    for (ALL_LIST_ELEMENTS_RO (list, node, rinfo))
+      {
+        /* Need to compare with redistributed entry or local entry */
+        if (!ripng_route_rte (rinfo))
+          break;
+
+        if (IPV6_ADDR_SAME (&rinfo->from, &from->sin6_addr) &&
+            IPV6_ADDR_SAME (&rinfo->nexthop, nexthop))
+          break;
+
+        if (!listnextnode (node))
+          {
+            /* Not found in the list */
+
+            if (rte->metric > rinfo->metric)
+              {
+                /* New route has a greater metric. Discard it. */
+                route_unlock_node (rp);
+                return;
+              }
+
+            if (rte->metric < rinfo->metric)
+              /* New route has a smaller metric. Replace the ECMP list
+               * with the new one in below. */
+              break;
+
+            /* Metrics are same. Keep "rinfo" null and the new route
+             * is added in the ECMP list in below. */
+          }
+      }
+
   if (rinfo)
     {
       /* Redistributed route check. */
       if (rinfo->type != ZEBRA_ROUTE_RIPNG
 	  && rinfo->metric != RIPNG_METRIC_INFINITY)
-	return;
+        {
+          route_unlock_node (rp);
+          return;
+        }
 
       /* Local static route. */
       if (rinfo->type == ZEBRA_ROUTE_RIPNG
 	  && ((rinfo->sub_type == RIPNG_ROUTE_STATIC) ||
 	      (rinfo->sub_type == RIPNG_ROUTE_DEFAULT))
 	  && rinfo->metric != RIPNG_METRIC_INFINITY)
-	return;
+        {
+          route_unlock_node (rp);
+          return;
+        }
     }
 
-  if (rp->info == NULL)
+  if (!rinfo)
     {
       /* Now, check to see whether there is already an explicit route
 	 for the destination prefix.  If there is no such route, add
@@ -756,53 +923,10 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
 	 infinity (there is no point in adding a route which
 	 unusable). */
       if (rte->metric != RIPNG_METRIC_INFINITY)
-	{
-	  rinfo = ripng_info_new ();
-	  
-	  /* - Setting the destination prefix and length to those in
-	     the RTE. */
-	  rp->info = rinfo;
-	  rinfo->rp = rp;
-
-	  /* - Setting the metric to the newly calculated metric (as
-	     described above). */
-	  rinfo->metric = rte->metric;
-	  rinfo->tag = ntohs (rte->tag);
-
-	  /* - Set the next hop address to be the address of the router
-	     from which the datagram came or the next hop address
-	     specified by a next hop RTE. */
-	  IPV6_ADDR_COPY (&rinfo->nexthop, nexthop);
-	  IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr);
-	  rinfo->ifindex = ifp->ifindex;
-
-	  /* - Initialize the timeout for the route.  If the
-	     garbage-collection timer is running for this route, stop it. */
-	  ripng_timeout_update (rinfo);
-
-	  /* - Set the route change flag. */
-	  rinfo->flags |= RIPNG_RTF_CHANGED;
-
-	  /* - Signal the output process to trigger an update (see section
-	     2.5). */
-	  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
-
-	  /* Finally, route goes into the kernel. */
-	  rinfo->type = ZEBRA_ROUTE_RIPNG;
-	  rinfo->sub_type = RIPNG_ROUTE_RTE;
-
-	  ripng_zebra_ipv6_add (&p, &rinfo->nexthop, rinfo->ifindex,
-				rinfo->metric);
-	  rinfo->flags |= RIPNG_RTF_FIB;
-
-	  /* Aggregate check. */
-	  ripng_aggregate_increment (rp, rinfo);
-	}
+        ripng_ecmp_add (&newinfo);
     }
   else
     {
-      rinfo = rp->info;
-	  
       /* If there is an existing route, compare the next hop address
 	 to the address of the router from which the datagram came.
 	 If this datagram is from the same router as the existing
@@ -810,9 +934,6 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
       same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr) 
 	      && (rinfo->ifindex == ifp->ifindex));
 
-      if (same)
-	ripng_timeout_update (rinfo);
-
       /* Next, compare the metrics.  If the datagram is from the same
 	 router as the existing route, and the new metric is different
 	 than the old one; or, if the new metric is lower than the old
@@ -820,96 +941,24 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
       if ((same && rinfo->metric != rte->metric) ||
 	  rte->metric < rinfo->metric)
 	{
-	  /* - Adopt the route from the datagram.  That is, put the
-	     new metric in, and adjust the next hop address (if
-	     necessary). */
-	  oldmetric = rinfo->metric;
-	  rinfo->metric = rte->metric;
-	  rinfo->tag = ntohs (rte->tag);
-	  IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr);
-	  rinfo->ifindex = ifp->ifindex;
-
-	  /* Should a new route to this network be established
-	     while the garbage-collection timer is running, the
-	     new route will replace the one that is about to be
-	     deleted.  In this case the garbage-collection timer
-	     must be cleared. */
-
-	  if (oldmetric == RIPNG_METRIC_INFINITY &&
-	      rinfo->metric < RIPNG_METRIC_INFINITY)
-	    {
-	      rinfo->type = ZEBRA_ROUTE_RIPNG;
-	      rinfo->sub_type = RIPNG_ROUTE_RTE;
-
-	      RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
-
-	      if (! IPV6_ADDR_SAME (&rinfo->nexthop, nexthop))
-		IPV6_ADDR_COPY (&rinfo->nexthop, nexthop);
-
-	      ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex, rinfo->metric);
-	      rinfo->flags |= RIPNG_RTF_FIB;
-
-	      /* The aggregation counter needs to be updated because
-		     the prefixes, which are into the gc, have been
-			 removed from the aggregator (see ripng_timout). */
-		  ripng_aggregate_increment (rp, rinfo);
-	    }
-
-	  /* Update nexthop and/or metric value.  */
-	  if (oldmetric != RIPNG_METRIC_INFINITY)
-	    {
-	      ripng_zebra_ipv6_delete (&p, &rinfo->nexthop, rinfo->ifindex);
-	      ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex, rinfo->metric);
-	      rinfo->flags |= RIPNG_RTF_FIB;
-
-	      if (! IPV6_ADDR_SAME (&rinfo->nexthop, nexthop))
-		IPV6_ADDR_COPY (&rinfo->nexthop, nexthop);
-	    }
-
-	  /* - Set the route change flag and signal the output process
-	     to trigger an update. */
-	  rinfo->flags |= RIPNG_RTF_CHANGED;
-	  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
-
-	  /* - If the new metric is infinity, start the deletion
-	     process (described above); */
-	  if (rinfo->metric == RIPNG_METRIC_INFINITY)
-	    {
-	      /* If the new metric is infinity, the deletion process
-		 begins for the route, which is no longer used for
-		 routing packets.  Note that the deletion process is
-		 started only when the metric is first set to
-		 infinity.  If the metric was already infinity, then a
-		 new deletion process is not started. */
-	      if (oldmetric != RIPNG_METRIC_INFINITY)
-		{
-		  /* - The garbage-collection timer is set for 120 seconds. */
-		  RIPNG_TIMER_ON (rinfo->t_garbage_collect, 
-				  ripng_garbage_collect, ripng->garbage_time);
-		  RIPNG_TIMER_OFF (rinfo->t_timeout);
-
-		  /* - The metric for the route is set to 16
-		     (infinity).  This causes the route to be removed
-		     from service.*/
-		  ripng_zebra_ipv6_delete (&p, &rinfo->nexthop, rinfo->ifindex);
-		  rinfo->flags &= ~RIPNG_RTF_FIB;
-
-		  /* Aggregate count decrement. */
-		  ripng_aggregate_decrement (rp, rinfo);
-
-		  /* - The route change flag is to indicate that this
-		     entry has been changed. */
-		  /* - The output process is signalled to trigger a
-                     response. */
-		  ;  /* Above processes are already done previously. */
-		}
-	    }
-	  else
-	    {
-	      /* otherwise, re-initialize the timeout. */
-	      ripng_timeout_update (rinfo);
-	    }
+          if (listcount (list) == 1)
+            {
+              if (newinfo.metric != RIPNG_METRIC_INFINITY)
+                ripng_ecmp_replace (&newinfo);
+              else
+                ripng_ecmp_delete (rinfo);
+            }
+          else
+            {
+              if (newinfo.metric < rinfo->metric)
+                ripng_ecmp_replace (&newinfo);
+              else /* newinfo.metric > rinfo->metric */
+                ripng_ecmp_delete (rinfo);
+            }
 	}
+      else /* same & no change */
+        ripng_timeout_update (rinfo);
+
       /* Unlock tempolary lock of the route. */
       route_unlock_node (rp);
     }
@@ -921,7 +970,8 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
 			unsigned int ifindex, struct in6_addr *nexthop)
 {
   struct route_node *rp;
-  struct ripng_info *rinfo;
+  struct ripng_info *rinfo = NULL, newinfo;
+  struct list *list = NULL;
 
   /* Redistribute route  */
   if (IN6_IS_ADDR_LINKLOCAL (&p->prefix))
@@ -939,10 +989,20 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
 #endif /* MUSICA or LINUX */
 
   rp = route_node_get (ripng->table, (struct prefix *) p);
-  rinfo = rp->info;
 
-  if (rinfo)
+  memset (&newinfo, 0, sizeof (struct ripng_info));
+  newinfo.type = type;
+  newinfo.sub_type = sub_type;
+  newinfo.ifindex = ifindex;
+  newinfo.metric = 1;
+  newinfo.rp = rp;
+  if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop))
+    newinfo.nexthop = *nexthop;
+
+  if ((list = rp->info) != NULL && listcount (list) != 0)
     {
+      rinfo = listgetdata (listhead (list));
+
       if (rinfo->type == ZEBRA_ROUTE_CONNECT
           && rinfo->sub_type == RIPNG_ROUTE_INTERFACE
 	  && rinfo->metric != RIPNG_METRIC_INFINITY) {
@@ -962,42 +1022,12 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
 	  return;
 	}
       }
-      
-      RIPNG_TIMER_OFF (rinfo->t_timeout);
-      RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
-
-      /* Tells the other daemons about the deletion of
-       * this RIPng route
-       **/
-      if (ripng_route_rte (rinfo))
-	ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, &rinfo->nexthop,
-			       rinfo->metric);
-
-      rp->info = NULL;
-      ripng_info_free (rinfo);
 
+      rinfo = ripng_ecmp_replace (&newinfo);
       route_unlock_node (rp);
-
     }
-
-  rinfo = ripng_info_new ();
-
-  rinfo->type = type;
-  rinfo->sub_type = sub_type;
-  rinfo->ifindex = ifindex;
-  rinfo->metric = 1;
-  rinfo->rp = rp;
-  
-  if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop))
-    rinfo->nexthop = *nexthop;
-  
-  rinfo->flags |= RIPNG_RTF_FIB;
-  rp->info = rinfo;
-
-  /* Aggregate check. */
-  ripng_aggregate_increment (rp, rinfo);
-
-  rinfo->flags |= RIPNG_RTF_CHANGED;
+  else
+    rinfo = ripng_ecmp_add (&newinfo);
 
   if (IS_RIPNG_DEBUG_EVENT) {
     if (!nexthop)
@@ -1039,31 +1069,37 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
 
   if (rp)
     {
-      rinfo = rp->info;
-
-      if (rinfo != NULL
-	  && rinfo->type == type 
-	  && rinfo->sub_type == sub_type 
-	  && rinfo->ifindex == ifindex)
-	{
-	  /* Perform poisoned reverse. */
-	  rinfo->metric = RIPNG_METRIC_INFINITY;
-	  RIPNG_TIMER_ON (rinfo->t_garbage_collect, 
-			ripng_garbage_collect, ripng->garbage_time);
-	  RIPNG_TIMER_OFF (rinfo->t_timeout);
-
-	  /* Aggregate count decrement. */
-	  ripng_aggregate_decrement (rp, rinfo);
-
-	  rinfo->flags |= RIPNG_RTF_CHANGED;
-	  
-          if (IS_RIPNG_DEBUG_EVENT)
-            zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]",
-                       inet6_ntoa(p->prefix), p->prefixlen,
-                       ifindex2ifname(ifindex));
-
-	  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
-	}
+      struct list *list = rp->info;
+
+      if (list != NULL && listcount (list) != 0)
+        {
+          rinfo = listgetdata (listhead (list));
+          if (rinfo != NULL
+              && rinfo->type == type
+              && rinfo->sub_type == sub_type
+              && rinfo->ifindex == ifindex)
+            {
+              /* Perform poisoned reverse. */
+              rinfo->metric = RIPNG_METRIC_INFINITY;
+              RIPNG_TIMER_ON (rinfo->t_garbage_collect,
+                              ripng_garbage_collect, ripng->garbage_time);
+              RIPNG_TIMER_OFF (rinfo->t_timeout);
+
+              /* Aggregate count decrement. */
+              ripng_aggregate_decrement (rp, rinfo);
+
+              rinfo->flags |= RIPNG_RTF_CHANGED;
+
+              if (IS_RIPNG_DEBUG_EVENT)
+                zlog_debug ("Poisone %s/%d on the interface %s with an "
+                            "infinity metric [delete]",
+                            inet6_ntoa (p->prefix), p->prefixlen,
+                            ifindex2ifname (ifindex));
+
+              ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
+            }
+        }
+      route_unlock_node (rp);
     }
 }
 
@@ -1072,14 +1108,16 @@ void
 ripng_redistribute_withdraw (int type)
 {
   struct route_node *rp;
-  struct ripng_info *rinfo;
+  struct ripng_info *rinfo = NULL;
+  struct list *list = NULL;
 
   if (!ripng)
     return;
   
   for (rp = route_top (ripng->table); rp; rp = route_next (rp))
-    if ((rinfo = rp->info) != NULL)
+    if ((list = rp->info) != NULL)
       {
+	rinfo = listgetdata (listhead (list));
 	if ((rinfo->type == type)
 	    && (rinfo->sub_type != RIPNG_ROUTE_INTERFACE))
 	  {
@@ -1314,7 +1352,7 @@ ripng_request_process (struct ripng_packet *packet,int size,
 
 	  if (rp)
 	    {
-	      rinfo = rp->info;
+	      rinfo = listgetdata (listhead ((struct list *)rp->info));
 	      rte->metric = rinfo->metric;
 	      route_unlock_node (rp);
 	    }
@@ -1422,12 +1460,18 @@ static void
 ripng_clear_changed_flag (void)
 {
   struct route_node *rp;
-  struct ripng_info *rinfo;
+  struct ripng_info *rinfo = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
 
   for (rp = route_top (ripng->table); rp; rp = route_next (rp))
-    if ((rinfo = rp->info) != NULL)
-      if (rinfo->flags & RIPNG_RTF_CHANGED)
-	rinfo->flags &= ~RIPNG_RTF_CHANGED;
+    if ((list = rp->info) != NULL)
+      for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+        {
+          UNSET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED);
+          /* This flag can be set only on the first entry. */
+          break;
+        }
 }
 
 /* Regular update of RIPng route.  Send all routing formation to RIPng
@@ -1605,6 +1649,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
   struct ripng_aggregate *aggregate;
   struct prefix_ipv6 *p;
   struct list * ripng_rte_list;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
 
   if (IS_RIPNG_DEBUG_EVENT) {
     if (to)
@@ -1621,7 +1667,9 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
  
   for (rp = route_top (ripng->table); rp; rp = route_next (rp))
     {
-      if ((rinfo = rp->info) != NULL && rinfo->suppress == 0)
+      if ((list = rp->info) != NULL &&
+          (rinfo = listgetdata (listhead (list))) != NULL &&
+          rinfo->suppress == 0)
 	{
 	  /* If no route-map are applied, the RTE will be these following
 	   * informations.
@@ -1653,8 +1701,17 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
 	  if (ri->split_horizon == RIPNG_SPLIT_HORIZON)
 	  {
 	    /* We perform split horizon for RIPng routes. */
-	    if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
-		rinfo->ifindex == ifp->ifindex)
+	    int suppress = 0;
+	    struct ripng_info *tmp_rinfo = NULL;
+
+	    for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo))
+	      if (tmp_rinfo->type == ZEBRA_ROUTE_RIPNG &&
+	          tmp_rinfo->ifindex == ifp->ifindex)
+	        {
+	          suppress = 1;
+	          break;
+	        }
+	    if (suppress)
 	      continue;
 	  }
 
@@ -1733,9 +1790,12 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
 	   * for RIPng routes.
 	   **/
 	  if (ri->split_horizon == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) {
-	    if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
-	         rinfo->ifindex == ifp->ifindex)
-	         rinfo->metric_out = RIPNG_METRIC_INFINITY;
+	    struct ripng_info *tmp_rinfo = NULL;
+
+	    for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo))
+	      if ((tmp_rinfo->type == ZEBRA_ROUTE_RIPNG) &&
+	           tmp_rinfo->ifindex == ifp->ifindex)
+	        rinfo->metric_out = RIPNG_METRIC_INFINITY;
 	  }
 
 	  /* Add RTE to the list */
@@ -1968,7 +2028,7 @@ ripng_route_subtype_print (struct ripng_info *rinfo)
   switch (rinfo->sub_type)
     {
        case RIPNG_ROUTE_RTE:
-         strcat(str, "n");
+         strcat(str, CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB) ? "n" : "-");
          break;
        case RIPNG_ROUTE_STATIC:
          strcat(str, "s");
@@ -2001,6 +2061,8 @@ DEFUN (show_ipv6_ripng,
   struct ripng_info *rinfo;
   struct ripng_aggregate *aggregate;
   struct prefix_ipv6 *p;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
   int len;
 
   if (! ripng)
@@ -2010,9 +2072,10 @@ DEFUN (show_ipv6_ripng,
   vty_out (vty, "Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP%s"
 	   "Sub-codes:%s"
 	   "      (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s"
-	   "      (i) - interface, (a/S) - aggregated/Suppressed%s%s"
+	   "      (i) - interface, (a/S) - aggregated/Suppressed%s"
+	   "      (-) - normal but not in fib%s%s"
 	   "   Network      Next Hop                      Via     Metric Tag Time%s",
-	   VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+	   VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
 	   VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
   
   for (rp = route_top (ripng->table); rp; rp = route_next (rp))
@@ -2038,7 +2101,8 @@ DEFUN (show_ipv6_ripng,
 		   VTY_NEWLINE);
 	}
 
-      if ((rinfo = rp->info) != NULL)
+      if ((list = rp->info) != NULL)
+        for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
 	{
 	  p = (struct prefix_ipv6 *) &rp->p;
 
@@ -2593,6 +2657,96 @@ DEFUN (no_ripng_default_information_originate,
   return CMD_SUCCESS;
 }
 
+/* Update ECMP routes to zebra according to the new multipath limit. */
+static void
+ripng_ecmp_adjust (void)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo = NULL;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
+
+  if (!ripng)
+    return;
+
+  for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+    if ((list = rp->info) != NULL)
+      {
+        rinfo = listgetdata (listhead (list));
+        if (ripng_route_rte (rinfo))
+          {
+            unsigned int fib = 0;
+            for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+              if (CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB))
+                fib++;
+
+            /* 1. Some nexthops must be removed because the ECMP limit
+             *    decreased; or
+             * 2. Some more nexthops can be installed now because the
+             *    ECMP limit increased. */
+            if (fib > ripng->ripng_multipath_limit ||
+                (fib != listcount (list) &&
+                 fib < ripng->ripng_multipath_limit))
+              /* Send an ADD message to update the routes in zebra */
+              ripng_zebra_ipv6_add (rp);
+          }
+      }
+}
+
+DEFUN (ripng_ecmp_limit,
+       ripng_ecmp_limit_val_cmd,
+       "ripng equal-cost <1-255>",
+       "RIPng specific commands\n"
+       "Equal Cost MultiPath\n"
+       "Path Limit Value\n")
+{
+  unsigned int limit = (unsigned int)atoi (argv[0]);
+  if (limit > RIPNG_MULTI_PATH_LIMIT)
+    {
+      vty_out (vty, "Can't be set: RIPng equal-cost limit is exceeding "
+                    "multi-path limit of %d%s",
+               RIPNG_MULTI_PATH_LIMIT, VTY_NEWLINE);
+      zlog_err ("Can't be set: RIPng equal-cost limit is exceeding "
+                "multi-path limit of %d", RIPNG_MULTI_PATH_LIMIT);
+      return CMD_WARNING;
+    }
+
+  if (limit != ripng->ripng_multipath_limit)
+    {
+      ripng->ripng_multipath_limit = limit;
+      if (ripng->ripng_multipath_limit > 1)
+        ripng->ecmp = 1;
+      else
+        ripng->ecmp = 0;
+      ripng_ecmp_adjust ();
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ripng_ecmp_limit,
+       no_ripng_ecmp_limit_val_cmd,
+       "no ripng equal-cost <1-255>",
+       NO_STR
+       "RIPng specific commands\n"
+       "Equal Cost MultiPath\n"
+       "Path Limit Value\n")
+{
+  if (ripng->ripng_multipath_limit != 1)
+    {
+      ripng->ecmp = 0;
+      ripng->ripng_multipath_limit = 1;
+      ripng_ecmp_adjust ();
+    }
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ripng_ecmp_limit,
+       no_ripng_ecmp_limit_cmd,
+       "no ripng equal-cost",
+       NO_STR
+       "RIPng settings\n"
+       "Equal Cost MultiPath\n")
+
 /* RIPng configuration write function. */
 static int
 ripng_config_write (struct vty *vty)
@@ -2662,6 +2816,11 @@ ripng_config_write (struct vty *vty)
 		 VTY_NEWLINE);
 #endif /* 0 */
 
+      /* RIPng ECMP configuration. */
+      if (ripng->ecmp)
+        vty_out (vty, " ripng equal-cost %d%s", ripng->ripng_multipath_limit,
+                 VTY_NEWLINE);
+
       write += config_write_distribute (vty);
 
       write += config_write_if_rmap (vty);
@@ -2775,24 +2934,37 @@ ripng_clean()
   int i;
   struct route_node *rp;
   struct ripng_info *rinfo;
+  struct ripng_aggregate *aggregate;
+  struct list *list = NULL;
+  struct listnode *listnode = NULL;
 
   if (ripng) {
     /* Clear RIPng routes */
-    for (rp = route_top (ripng->table); rp; rp = route_next (rp)) {
-      if ((rinfo = rp->info) != NULL) {
-        if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
-            (rinfo->sub_type == RIPNG_ROUTE_RTE))
-          ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p,
-                                   &rinfo->nexthop, rinfo->metric);
-
-        RIPNG_TIMER_OFF (rinfo->t_timeout);
-        RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
-
-        rp->info = NULL;
-        route_unlock_node (rp);
-
-        ripng_info_free(rinfo);
-      }
+    for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+      {
+        if ((list = rp->info) != NULL)
+          {
+            rinfo = listgetdata (listhead (list));
+            if (ripng_route_rte (rinfo))
+              ripng_zebra_ipv6_delete (rp);
+
+            for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo))
+              {
+                RIPNG_TIMER_OFF (rinfo->t_timeout);
+                RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+                ripng_info_free (rinfo);
+              }
+            list_delete (list);
+            rp->info = NULL;
+            route_unlock_node (rp);
+          }
+
+        if ((aggregate = rp->aggregate) != NULL)
+          {
+            ripng_aggregate_free (aggregate);
+            rp->aggregate = NULL;
+            route_unlock_node (rp);
+          }
     }
 
     /* Cancel the RIPng timers */
@@ -2984,6 +3156,10 @@ ripng_init ()
   install_element (RIPNG_NODE, &ripng_default_information_originate_cmd);
   install_element (RIPNG_NODE, &no_ripng_default_information_originate_cmd);
 
+  install_element (RIPNG_NODE, &ripng_ecmp_limit_val_cmd);
+  install_element (RIPNG_NODE, &no_ripng_ecmp_limit_val_cmd);
+  install_element (RIPNG_NODE, &no_ripng_ecmp_limit_cmd);
+
   ripng_if_init ();
   ripng_debug_init ();
 
diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h
index ab06d81..03f9866 100644
--- a/ripngd/ripngd.h
+++ b/ripngd/ripngd.h
@@ -88,6 +88,11 @@
 #define IFMINMTU    576
 #endif /* IFMINMTU */
 
+/* RIPng ECMP Multipath Limit */
+#ifndef RIPNG_MULTI_PATH_LIMIT
+#define RIPNG_MULTI_PATH_LIMIT      1
+#endif
+
 /* RIPng structure. */
 struct ripng 
 {
@@ -129,6 +134,10 @@ struct ripng
   struct thread *t_triggered_update;
   struct thread *t_triggered_interval;
 
+  /* RIPng ECMP limit */
+  unsigned int ecmp;
+  unsigned int ripng_multipath_limit;
+
   /* For redistribute route map. */
   struct
   {
@@ -389,12 +398,8 @@ extern void ripng_redistribute_withdraw (int type);
 extern void ripng_distribute_update_interface (struct interface *);
 extern void ripng_if_rmap_update_interface (struct interface *);
 
-extern void ripng_zebra_ipv6_add (struct prefix_ipv6 *p,
-                                  struct in6_addr *nexthop,
-                                  unsigned int ifindex, u_char metric);
-extern void ripng_zebra_ipv6_delete (struct prefix_ipv6 *p,
-                                     struct in6_addr *nexthop,
-                                     unsigned int ifindex);
+extern void ripng_zebra_ipv6_add (struct route_node *);
+extern void ripng_zebra_ipv6_delete (struct route_node *);
 
 extern void ripng_redistribute_clean (void);
 extern int ripng_redistribute_check (int);
@@ -418,4 +423,8 @@ extern int ripng_interface_address_delete (int command, struct zclient *, zebra_
 
 extern int ripng_network_write (struct vty *, int);
 
+extern struct ripng_info *ripng_ecmp_add (struct ripng_info *);
+extern struct ripng_info *ripng_ecmp_replace (struct ripng_info *);
+extern struct ripng_info *ripng_ecmp_delete (struct ripng_info *);
+
 #endif /* _ZEBRA_RIPNG_RIPNGD_H */
-- 
1.7.4.4





More information about the Quagga-dev mailing list