[quagga-dev 11349] [PATCH 4/6] zebra: allow daemons to add IPv6 multipath routes

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


A new function rib_add_ipv6_multipath() is introduced to
process the message for adding IPv6 multipath routes.

zread_ipv6_add() is modified accordingly to be able to
call rib_add_ipv6_multipath(), instead of the original
rib_add_ipv6().

Signed-off-by: Feng Lu <lu.feng at 6wind.com>
Reviewed-by: Alain Ritoux <alain.ritoux at 6wind.com>
---
 zebra/rib.h       |    5 ++
 zebra/zebra_rib.c |   81 ++++++++++++++++++++++++++++-
 zebra/zserv.c     |  150 ++++++++++++++++++++++++++++++++++++++---------------
 3 files changed, 193 insertions(+), 43 deletions(-)

diff --git a/zebra/rib.h b/zebra/rib.h
index d3a83c6..9bc4025 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -398,6 +398,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 *,
+    struct in6_addr *, unsigned int);
 #endif /* HAVE_IPV6 */
 
 extern struct vrf *vrf_lookup (u_int32_t);
@@ -444,6 +446,9 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
 	      u_int32_t metric, u_char distance, safi_t safi);
 
 extern int
+rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi);
+
+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/zebra_rib.c b/zebra/zebra_rib.c
index dc7e1ca..57567e5 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -357,7 +357,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)
 {
@@ -2727,6 +2727,85 @@ 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)
+{
+  struct route_table *table;
+  struct route_node *rn;
+  struct rib *same;
+  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;
+    }
+
+  /* Filter bogus route.
+   * XXX there could be many ifindex (one for each nexthop)
+   */
+  if (rib_bogus_ipv6 (rib->type, p, &rib->nexthop->gate.ipv6,
+                      IFINDEX_INTERNAL, 0))
+    return 0;
+
+  /* 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 && same->table == rib->table &&
+          same->type != ZEBRA_ROUTE_CONNECT)
+        break;
+    }
+
+  /* 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);
+  if (IS_ZEBRA_DEBUG_RIB)
+    {
+      zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry",
+                  __func__, rn, rib);
+      rib_dump (p, 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 (p, same);
+        }
+      rib_delnode (rn, same);
+    }
+
+  route_unlock_node (rn);
+  return 0;
+}
+
 /* XXX factor with rib_delete_ipv6 */
 int
 rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
diff --git a/zebra/zserv.c b/zebra/zserv.c
index ca17c2c..5186c4b 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -939,66 +939,132 @@ static int
 zread_ipv6_add (struct zserv *client, u_short length)
 {
   int i;
-  struct stream *s;
-  struct zapi_ipv6 api;
-  struct in6_addr nexthop;
-  unsigned long ifindex;
+  struct rib *rib;
   struct prefix_ipv6 p;
-  
+  u_char message;
+  u_char nexthop_num;
+  u_char nexthop_type;
+  struct stream *s;
+  unsigned int ifindex;
+  safi_t safi;
+  char buf [BUFSIZ];
+
+  /* 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))
+  /* Nexthop parse. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP))
     {
-      u_char nexthop_type;
+      struct in6_addr *temp = NULL;
+      int err = 0;
+      /*
+       * In IPv6, we must have one ifindex for each gateway.
+       * So, the nexthop number is even.
+       * The stream is formed with nexthop1 nexthop2....ifindex1
+       * ifindex2... order (see /lib/zclient.c/zapi_ipv6_route)
+       * But nexthop1 goes with ifindex1, etc...
+       * That's why we use the 'temp' variable to save all gateway's info
+       * in case of multiple nexthop case.
+       */
+      nexthop_num = stream_getc (s);
+      if (((nexthop_num % 2) == 1) || (nexthop_num == 0))
+        {
+          zlog_err("%s/%d from protocol %d provides %d fields which is zero "
+                   "or not even",
+                   inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ),
+                   p.prefixlen, rib->type, nexthop_num);
+          /* XXX here we should flush or consume the stream */
+          return 1;
+        }
 
-      api.nexthop_num = stream_getc (s);
-      for (i = 0; i < api.nexthop_num; i++)
-	{
-	  nexthop_type = stream_getc (s);
+      temp = XCALLOC (MTYPE_NEXTHOP, (nexthop_num / 2) * sizeof (*temp));
 
-	  switch (nexthop_type)
-	    {
-	    case ZEBRA_NEXTHOP_IPV6:
-	      stream_get (&nexthop, s, 16);
-	      break;
-	    case ZEBRA_NEXTHOP_IFINDEX:
-	      ifindex = stream_getl (s);
-	      break;
-	    }
-	}
+      for (i = 0; i < (nexthop_num / 2); i++)
+        {
+          nexthop_type = stream_getc (s);
+
+          switch (nexthop_type)
+            {
+              case ZEBRA_NEXTHOP_IPV6:
+                stream_get (&temp[i], s, 16);
+                break;
+              default:
+                zlog_err("%s/%d from protocol %d does not provide a proper "
+                         "IPv6 nexthop",
+                         inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ),
+                         p.prefixlen, rib->type);
+                err = 1;
+                break;
+            }
+        }
+
+      for (i = 0; i < (nexthop_num / 2); i++)
+        {
+          nexthop_type = stream_getc (s);
+
+          switch (nexthop_type)
+            {
+              case ZEBRA_NEXTHOP_IFINDEX:
+                ifindex = stream_getl (s);
+
+                if (IN6_IS_ADDR_UNSPECIFIED (&temp[i]))
+                  nexthop_ifindex_add (rib, ifindex); /* add P/M via %if */
+                else if (ifindex)
+                  nexthop_ipv6_ifindex_add (rib, &temp[i], ifindex);
+                else if (!IN6_IS_ADDR_LINKLOCAL(&temp[i]))
+                  nexthop_ipv6_add (rib, &temp[i]);
+                else
+                  zlog_err ("protocol %d provides Link local nexthop without IF",
+                            rib->type);
+                break;
+              default:
+                zlog_err ("%s/%d from protocol %d does not provide a proper "
+                          "IPv6 ifindex",
+                          inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ),
+                          p.prefixlen, rib->type);
+                err = 1;
+                break;
+            }
+        }
+
+      XFREE (MTYPE_NEXTHOP, temp);
+
+      if (err)
+        return 1;
     }
 
-  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
-    api.distance = stream_getc (s);
+  /* Distance. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE))
+    rib->distance = stream_getc (s);
   else
-    api.distance = 0;
+    rib->distance = 0;
 
-  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
-    api.metric = stream_getl (s);
-  else
-    api.metric = 0;
-    
-  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);
+  /* Metric. */
+  if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC))
+    rib->metric = stream_getl (s);
   else
-    rib_add_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, zebrad.rtm_table_default, api.metric,
-		  api.distance, api.safi);
+    rib->metric = 0;
+
+  /* Table */
+  rib->table = zebrad.rtm_table_default;
+
+  rib_add_ipv6_multipath (&p, rib, safi);
   return 0;
 }
 
-- 
1.7.4.4





More information about the Quagga-dev mailing list