[quagga-dev 10828] [PATCH 1/4] zebra: ipv6-multipath.patch

Dinesh G Dutt ddutt at cumulusnetworks.com
Thu Oct 24 05:06:36 BST 2013


Zebra: Enable support for IPv6 multipath with Linux kernel

This patch enables support for multipath for IPV6. The nexthop information
from the protocols have ifindices and nexthop addresses in two different
structures. This patch combines them to ensure that the correct APIs can
be called. Also, given that IPV6 Linux implementation does not support the
rta_XXX APIs for multipath, the communication with the kernel is in terms
of a single nh/ifindex pair. The API to zebra is to provide an RTA_MULTIPATH
just like IPv4, but to convert the multipath into a series of single path
adds when communicating with the kernel.

Signed-off-by: Ayan Banerjee <ayabaner at gmail.com>
Signed-off-by: Dinesh G Dutt <ddutt at cumulusnetworks.com>
Reviewed-by: Scott Feldman <sfeldma at cumulusnetworks.com>
Reviewed-by: Shrijeet Mukherjee <shm at cumulusnetworks.com>
---
 zebra/rib.h        |   11 +++-
 zebra/rt_netlink.c |   52 +++++++++++++-----
 zebra/rt_socket.c  |    2 -
 zebra/zebra_rib.c  |  153 +++++++++++++++++++++++++++++++++++++++++++++-------
 zebra/zserv.c      |   99 ++++++++++++++++++++++++----------
 5 files changed, 254 insertions(+), 63 deletions(-)

diff --git a/zebra/rib.h b/zebra/rib.h
index e16ce68..50628bb 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -335,7 +335,7 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *,
                                                  unsigned int);
 extern void rib_lookup_and_dump (struct prefix_ipv4 *);
 extern void rib_lookup_and_pushup (struct prefix_ipv4 *);
-extern void rib_dump (const char *, const struct prefix_ipv4 *, const struct rib *);
+extern void rib_dump (const char *, const struct prefix *, const struct rib *);
 extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *);
 #define ZEBRA_RIB_LOOKUP_ERROR -1
 #define ZEBRA_RIB_FOUND_EXACT 0
@@ -345,6 +345,8 @@ extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *);
 
 #ifdef HAVE_IPV6
 extern struct nexthop *nexthop_ipv6_add (struct rib *, struct in6_addr *);
+extern struct nexthop *nexthop_ipv6_ifindex_add (struct rib *rib,
+		        struct in6_addr *ipv6, unsigned int ifindex);
 #endif /* HAVE_IPV6 */
 
 extern struct vrf *vrf_lookup (u_int32_t);
@@ -386,11 +388,18 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
 
 #ifdef HAVE_IPV6
 extern int
+rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
+                struct in6_addr *gate, unsigned int ifindex, int table);
+extern int
 rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
 	      struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id,
 	      u_int32_t metric, u_char distance, safi_t safi);
 
 extern int
+rib_add_ipv6_multipath (struct prefix_ipv6 *, struct rib *, safi_t,
+                        unsigned long);
+
+extern int
 rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
 		 struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi);
 
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index fa446a5..0f56166 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -1421,6 +1421,8 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
   struct nexthop *nexthop = NULL;
   int nexthop_num = 0;
   int discard;
+  int sent = 0;
+  int ret = 0;
 
   struct
   {
@@ -1429,6 +1431,10 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
     char buf[NL_PKT_BUF_SIZE];
   } req;
 
+  /* Destination netlink address. */
+  memset (&snl, 0, sizeof snl);
+  snl.nl_family = AF_NETLINK;
+
   memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE);
 
   bytelen = (family == AF_INET ? 4 : 16);
@@ -1643,6 +1649,11 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
       rta->rta_len = RTA_LENGTH (0);
       rtnh = RTA_DATA (rta);
 
+     /*
+      * Due to the limitation of the V6 netlink API, the RTA_MULTIPATH
+      * should not be used (rta_XXX APIs are not supported). Hence, send
+      * netlink messages for each nexthop explicitly for IPV6.
+      */
       nexthop_num = 0;
       for (nexthop = rib->nexthop;
            nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM);
@@ -1696,8 +1707,8 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
                       || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
                       || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
 		    {
-		      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-				     &nexthop->rgate.ipv6, bytelen);
+		      addattr_l (&req.n, sizeof req, RTA_GATEWAY,
+				 &nexthop->rgate.ipv6, bytelen);
 
 		      if (IS_ZEBRA_DEBUG_KERNEL)
 			zlog_debug("netlink_route_multipath() (recursive, "
@@ -1723,7 +1734,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
 		  else if (nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
                       || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
 		    {
-		      rtnh->rtnh_ifindex = nexthop->rifindex;
+		      addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->rifindex);
 
 		      if (IS_ZEBRA_DEBUG_KERNEL)
 			zlog_debug("netlink_route_multipath() (recursive, "
@@ -1770,8 +1781,8 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
                       || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
                       || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
 		    { 
-		      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-				     &nexthop->gate.ipv6, bytelen);
+		      addattr_l (&req.n, sizeof req, RTA_GATEWAY,
+				 &nexthop->gate.ipv6, bytelen);
 
 		      if (IS_ZEBRA_DEBUG_KERNEL)
 			zlog_debug("netlink_route_multipath() (multihop): "
@@ -1795,7 +1806,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
                   else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
                       || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
 		    {
-		      rtnh->rtnh_ifindex = nexthop->ifindex;
+		      addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex);
 
 		      if (IS_ZEBRA_DEBUG_KERNEL)
 			zlog_debug("netlink_route_multipath() (multihop): "
@@ -1806,7 +1817,26 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
 		      rtnh->rtnh_ifindex = 0;
 		    }
                 }
-              rtnh = RTNH_NEXT (rtnh);
+
+              if (family == AF_INET) {
+                rtnh = RTNH_NEXT (rtnh);
+              } else {
+#ifdef HAVE_IPV6
+                ret = netlink_talk (&req.n, &netlink_cmd);
+                if (ret < 0) {
+                  char buf[INET6_ADDRSTRLEN];
+
+                  inet_ntop(p->family, &p->u.prefix, buf, INET6_ADDRSTRLEN);
+                  zlog_warn ("%s: Netlink Talk failed for %s/%d via %s if %u\n",
+			      __func__, buf, p->prefixlen,
+                              (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) ?
+                              inet6_ntoa (nexthop->rgate.ipv6) : inet6_ntoa (nexthop->gate.ipv6),
+                              (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) ?
+                              nexthop->rifindex : nexthop->ifindex);
+                }
+                sent = 1;
+#endif
+              }
 
               if (cmd == RTM_NEWROUTE)
                 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
@@ -1815,7 +1845,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
       if (src)
         addattr_l (&req.n, sizeof req, RTA_PREFSRC, &src->ipv4, bytelen);
 
-      if (rta->rta_len > RTA_LENGTH (0))
+      if ((family == AF_INET) && (rta->rta_len > RTA_LENGTH (0)))
         addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA (rta),
                    RTA_PAYLOAD (rta));
     }
@@ -1830,12 +1860,8 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
 
 skip:
 
-  /* Destination netlink address. */
-  memset (&snl, 0, sizeof snl);
-  snl.nl_family = AF_NETLINK;
-
   /* Talk to netlink socket. */
-  return netlink_talk (&req.n, &netlink_cmd);
+  return ((sent == 0) ? netlink_talk (&req.n, &netlink_cmd) : ret);
 }
 
 int
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 1b8ded7..5907327 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -172,7 +172,7 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family)
              {
                zlog_debug ("%s: %s/%d: attention! gate not found for rib %p",
                  __func__, prefix_buf, p->prefixlen, rib);
-               rib_dump (__func__, (struct prefix_ipv4 *)p, rib);
+               rib_dump (__func__, p, rib);
              }
              else
                inet_ntop (AF_INET, &sin_gate.sin_addr, gate_buf, INET_ADDRSTRLEN);
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index d2227cb..94820a1 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -334,7 +334,7 @@ nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
   return nexthop;
 }
 
-static struct nexthop *
+struct nexthop *
 nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6,
 			  unsigned int ifindex)
 {
@@ -1420,11 +1420,11 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn)
 static void
 rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
 {
-  char buf[INET_ADDRSTRLEN];
+  char buf[INET6_ADDRSTRLEN];
   assert (zebra && rn);
   
   if (IS_ZEBRA_DEBUG_RIB_Q)
-    inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
+    inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
 
   /* Pointless to queue a route_node with no RIB entries to add or remove */
   if (!rnode_to_ribs (rn))
@@ -1784,12 +1784,12 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
  * question are passed as 1st and 2nd arguments.
  */
 
-void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib * rib)
+void rib_dump (const char * func, const struct prefix * p, const struct rib * rib)
 {
-  char straddr1[INET_ADDRSTRLEN], straddr2[INET_ADDRSTRLEN];
+  char straddr1[INET6_ADDRSTRLEN], straddr2[INET6_ADDRSTRLEN];
   struct nexthop *nexthop;
 
-  inet_ntop (AF_INET, &p->prefix, straddr1, INET_ADDRSTRLEN);
+  inet_ntop (p->family, &p->u.prefix, straddr1, INET6_ADDRSTRLEN);
   zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr1, p->prefixlen);
   zlog_debug
   (
@@ -1819,8 +1819,16 @@ void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib
   );
   for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
   {
-    inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, straddr1, INET_ADDRSTRLEN);
-    inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, straddr2, INET_ADDRSTRLEN);
+    if (p->family == AF_INET) {
+      inet_ntop (p->family, &nexthop->gate.ipv4.s_addr, straddr1, INET_ADDRSTRLEN);
+      inet_ntop (p->family, &nexthop->rgate.ipv4.s_addr, straddr2, INET_ADDRSTRLEN);
+    }
+#ifdef HAVE_IPV6
+    if (p->family == AF_INET6) {
+      inet_ntop (p->family, &nexthop->gate.ipv6.s6_addr, straddr1, INET6_ADDRSTRLEN);
+      inet_ntop (p->family, &nexthop->rgate.ipv6.s6_addr, straddr2, INET6_ADDRSTRLEN);
+    }
+#endif /* HAVE_IPV6 */
     zlog_debug
     (
       "%s: NH %s (%s) with flags %s%s%s",
@@ -1880,7 +1888,7 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p)
       (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"),
       (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected")
     );
-    rib_dump (__func__, p, rib);
+    rib_dump (__func__, (struct prefix *)p, rib);
   }
 }
 
@@ -1927,7 +1935,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p)
         char buf[INET_ADDRSTRLEN];
         inet_ntop (rn->p.family, &p->prefix, buf, INET_ADDRSTRLEN);
         zlog_debug ("%s: freeing way for connected prefix %s/%d", __func__, buf, p->prefixlen);
-        rib_dump (__func__, (struct prefix_ipv4 *)&rn->p, rib);
+        rib_dump (__func__, &rn->p, rib);
       }
       rib_uninstall (rn, rib);
     }
@@ -1989,7 +1997,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi)
   {
     zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry",
       __func__, rn, rib);
-    rib_dump (__func__, p, rib);
+    rib_dump (__func__, (struct prefix *)p, rib);
   }
 
   /* Free implicit route.*/
@@ -1999,7 +2007,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi)
     {
       zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
         __func__, rn, same);
-      rib_dump (__func__, p, same);
+      rib_dump (__func__, (struct prefix *)p, same);
     }
     rib_delnode (rn, same);
   }
@@ -2451,7 +2459,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
 
 
 #ifdef HAVE_IPV6
-static int
+int
 rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
 		struct in6_addr *gate, unsigned int ifindex, int table)
 {
@@ -2566,6 +2574,104 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
   return 0;
 }
 
+int
+rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi,
+	                unsigned long ifindex)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *same = NULL;
+  struct nexthop *nexthop;
+
+  /* Lookup table.  */
+  table = vrf_table (AFI_IP6, safi, 0);
+  if (! table)
+    return 0;
+
+  /* Make sure mask is applied. */
+  apply_mask_ipv6 (p);
+
+  /* Set default distance by route type. */
+  if (rib->distance == 0)
+    {
+      rib->distance = route_info[rib->type].distance;
+
+      /* iBGP distance is 200. */
+      if (rib->type == ZEBRA_ROUTE_BGP
+	  && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP))
+	rib->distance = 200;
+    }
+
+  /* Lookup route node.*/
+  rn = route_node_get (table, (struct prefix *) p);
+
+  /* If same type of route are installed, treat it as a implicit
+     withdraw. */
+  RNODE_FOREACH_RIB (rn, same) {
+     if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) {
+       continue;
+     }
+     if (same->type != rib->type) {
+       continue;
+     }
+     if (same->table != rib->table) {
+       continue;
+     }
+     if (same->type != ZEBRA_ROUTE_CONNECT) {
+       break;
+     }
+     else if ((nexthop = same->nexthop) &&
+	       nexthop->type == NEXTHOP_TYPE_IFINDEX &&
+	       nexthop->ifindex == ifindex) {
+	    same->refcnt++;
+	    return 0;
+     }
+  }
+
+  /* If this route is kernel route, set FIB flag to the route. */
+  if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT) {
+    for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) {
+      SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+    }
+  }
+
+  /* Link new rib to node.*/
+  rib_addnode (rn, rib);
+
+  /* Free implicit route.*/
+  if (same)
+  {
+    if (IS_ZEBRA_DEBUG_RIB)
+    {
+      zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
+        __func__, rn, same);
+      rib_dump (__func__, (struct prefix *)p, same);
+    }
+    rib_delnode (rn, same);
+  }
+
+  route_unlock_node (rn);
+  return 0;
+}
+
+/*
+ * Return true if the gateway address is present in one of the
+ * nexthops of the rib.
+ */
+static int
+zebra_rib_check_ipv6_gwy (struct rib *rib, struct in6_addr *gate)
+{
+  struct nexthop *nexthop;
+
+  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) {
+	if (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate) ||
+		IPV6_ADDR_SAME (&nexthop->rgate.ipv6, gate)) {
+	  return 1;
+        }
+  }
+  return 0;
+}
+
 /* XXX factor with rib_delete_ipv6 */
 int
 rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
@@ -2636,14 +2742,19 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
 	  break;
 	}
       /* Make sure that the route found has the same gateway. */
-      else if (gate == NULL ||
-	       ((nexthop = rib->nexthop) &&
-	        (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate) ||
-		 IPV6_ADDR_SAME (&nexthop->rgate.ipv6, gate))))
-	{
-	  same = rib;
-	  break;
-	}
+      else
+        {
+          /* Since we have added multipath support here, we should
+           * ideally compare for an exact match and complain about
+           * a bug. This implies that the list of nexthops need to
+           * be passed. For now, we just look to see if the nexthop
+           * is in list of nexthops with the rib. */
+          if ((gate == NULL) || zebra_rib_check_ipv6_gwy (rib, gate))
+            {
+              same = rib;
+              break;
+            }
+        }
     }
 
   /* If same type of route can't be found and this message is from
diff --git a/zebra/zserv.c b/zebra/zserv.c
index fa289c5..5cc25be 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -931,34 +931,52 @@ zread_ipv6_add (struct zserv *client, u_short length)
 {
   int i;
   struct stream *s;
-  struct zapi_ipv6 api;
   struct in6_addr nexthop;
+  struct rib *rib;
+  u_char message;
+  u_char nexthop_num;
+  u_char nexthop_type;
   unsigned long ifindex;
   struct prefix_ipv6 p;
-  
+  u_char ifname_len;
+  safi_t safi;
+  static struct in6_addr nexthops[MULTIPATH_NUM];
+  static unsigned int ifindices[MULTIPATH_NUM];
+
+  /* Get input stream.  */
   s = client->ibuf;
+
   ifindex = 0;
   memset (&nexthop, 0, sizeof (struct in6_addr));
 
+  /* Allocate new rib. */
+  rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+
   /* Type, flags, message. */
-  api.type = stream_getc (s);
-  api.flags = stream_getc (s);
-  api.message = stream_getc (s);
-  api.safi = stream_getw (s);
+  rib->type = stream_getc (s);
+  rib->flags = stream_getc (s);
+  message = stream_getc (s);
+  safi = stream_getw (s);
+  rib->uptime = time (NULL);
 
-  /* IPv4 prefix. */
+  /* IPv6 prefix. */
   memset (&p, 0, sizeof (struct prefix_ipv6));
   p.family = AF_INET6;
   p.prefixlen = stream_getc (s);
   stream_get (&p.prefix, s, PSIZE (p.prefixlen));
 
-  /* Nexthop, ifindex, distance, metric. */
-  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+  /* We need to give nh-addr, nh-ifindex with the same next-hop object
+   * to the rib to ensure that IPv6 multipathing works; need to coalesce
+   * these. Clients should send the same number of paired set of
+   * next-hop-addr/next-hop-ifindices. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP))
     {
-      u_char nexthop_type;
+      int nh_count = 0;
+      int if_count = 0;
+      int max_nh_if = 0;
 
-      api.nexthop_num = stream_getc (s);
-      for (i = 0; i < api.nexthop_num; i++)
+      nexthop_num = stream_getc (s);
+      for (i = 0; i < nexthop_num; i++)
 	{
 	  nexthop_type = stream_getc (s);
 
@@ -966,30 +984,57 @@ zread_ipv6_add (struct zserv *client, u_short length)
 	    {
 	    case ZEBRA_NEXTHOP_IPV6:
 	      stream_get (&nexthop, s, 16);
+              if (nh_count < MULTIPATH_NUM) {
+	        nexthops[nh_count++] = nexthop;
+              }
 	      break;
 	    case ZEBRA_NEXTHOP_IFINDEX:
-	      ifindex = stream_getl (s);
+              if (if_count < MULTIPATH_NUM) {
+	        ifindices[if_count++] = stream_getl (s);
+              }
 	      break;
 	    }
 	}
+
+      max_nh_if = (nh_count > if_count) ? nh_count : if_count;
+      for (i = 0; i < max_nh_if; i++)
+        {
+	  if ((i < nh_count) && !IN6_IS_ADDR_UNSPECIFIED (&nexthops[i])) {
+            if ((i < if_count) && ifindices[i]) {
+              if (rib_bogus_ipv6 (rib->type, &p, &nexthops[i], ifindices[i], 0)) {
+                continue;
+              }
+              nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]);
+            }
+            else {
+              if (rib_bogus_ipv6 (rib->type, &p, &nexthops[i], 0, 0)) {
+                continue;
+              }
+	      nexthop_ipv6_add (rib, &nexthops[i]);
+            }
+          }
+          else {
+            if ((i < if_count) && ifindices[i]) {
+              if (rib_bogus_ipv6 (rib->type, &p, NULL, ifindices[i], 0)) {
+                continue;
+              }
+	      nexthop_ifindex_add (rib, ifindices[i]);
+	    }
+          }
+	}
     }
 
-  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
-    api.distance = stream_getc (s);
-  else
-    api.distance = 0;
+  /* Distance. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+    rib->distance = stream_getc (s);
 
-  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
-    api.metric = stream_getl (s);
-  else
-    api.metric = 0;
+  /* Metric. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+    rib->metric = stream_getl (s);
     
-  if (IN6_IS_ADDR_UNSPECIFIED (&nexthop))
-    rib_add_ipv6 (api.type, api.flags, &p, NULL, ifindex, zebrad.rtm_table_default, api.metric,
-		  api.distance, api.safi);
-  else
-    rib_add_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, zebrad.rtm_table_default, api.metric,
-		  api.distance, api.safi);
+  /* Table */
+  rib->table=zebrad.rtm_table_default;
+  rib_add_ipv6_multipath (&p, rib, safi, ifindex);
   return 0;
 }
 





More information about the Quagga-dev mailing list