]> git.puffer.fish Git - mirror/frr.git/commitdiff
This patch enables support for multipath for IPV6. The nexthop information
authorDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 20 May 2015 00:24:43 +0000 (17:24 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 20 May 2015 00:24:43 +0000 (17:24 -0700)
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.

zebra/rib.h
zebra/rt_netlink.c
zebra/zebra_rib.c
zebra/zserv.c

index d3a83c68a0445d1c6669d603aefde26dade1bc49..ad726c6cc9e95a50cb2d4e618ffb5397f17ba575 100644 (file)
@@ -398,6 +398,14 @@ 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);
+extern struct nexthop *nexthop_ipv6_ifname_add (struct rib *rib,
+                                               struct in6_addr *ipv6,
+                                               char *ifname);
+extern int
+rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
+                struct in6_addr *gate, unsigned int ifindex, int table);
 #endif /* HAVE_IPV6 */
 
 extern struct vrf *vrf_lookup (u_int32_t);
@@ -458,6 +466,10 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
                 const char *ifname, u_char flags, u_char distance,
                 u_int32_t vrf_id);
 
+extern int
+rib_add_ipv6_multipath (struct prefix_ipv6 *, struct rib *, safi_t,
+                        unsigned long);
+
 extern int
 static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
                    const char *ifname, u_char distance, u_int32_t vrf_id);
index 95a82fd2e46ca461c0cb001ce909c36633bc7a00..c242480f03c2fef419356ea013698c13040fa1f1 100644 (file)
@@ -1580,7 +1580,7 @@ _netlink_route_build_multipath(
     {
       rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
                      &nexthop->gate.ipv6, bytelen);
-
+      rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
       if (IS_ZEBRA_DEBUG_KERNEL)
         zlog_debug("netlink_route_multipath() (%s): "
                    "nexthop via %s if %u",
index 93edc23d7292fb6269f8c892c64a8a3d93be52c4..02116b3d295c720822b1b811fb2607b6f0d1544f 100644 (file)
@@ -341,7 +341,7 @@ nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6)
   return nexthop;
 }
 
-static struct nexthop *
+struct nexthop *
 nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
                         char *ifname)
 {
@@ -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)
 {
@@ -2598,7 +2598,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)
 {
@@ -2727,6 +2727,96 @@ 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;
+  int ret = 0;
+  int table_id = 0;
+
+  if (rib)
+    table_id = rib->table;
+  else
+    return 0;                  /* why are we getting called with NULL rib */
+
+  /* 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);
+  ret = 1;
+  /* 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 ((struct prefix *)p, same);
+    }
+    rib_delnode (rn, same);
+    ret = -1;
+  }
+
+  route_unlock_node (rn);
+  return ret;
+}
+
 /* XXX factor with rib_delete_ipv6 */
 int
 rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
index ca17c2c6de6252f49b60994dd6b70f58d04f4686..b245b7f7bc9b0fee90484fcb159546720cef076f 100644 (file)
@@ -940,34 +940,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);
 
@@ -975,30 +993,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;
 }