]> git.puffer.fish Git - matthieu/frr.git/commitdiff
Zebra: Implement route replace semantics.
authorDonald Sharp <sharpd@cumulusnetworks.com>
Thu, 11 Jun 2015 16:11:12 +0000 (09:11 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Thu, 11 Jun 2015 16:11:12 +0000 (09:11 -0700)
Zebra currently performs a delete followed by add when a route needs to be
modified. Change this to use the replace semantics of netlink so that the
operation can possibly be atomic.

Note: Only implemented for IPv4 currently.

zebra/kernel_null.c
zebra/rt.h
zebra/rt_ioctl.c
zebra/rt_netlink.c
zebra/rt_socket.c
zebra/zebra_rib.c

index cdb6e23f0245c9d6e96959685747ef92286caee8..2e833febe6e8a8b6fa0791b9257b25a614a29ed2 100644 (file)
@@ -9,6 +9,7 @@
 #include "zebra/connected.h"
 
 int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; }
+int kernel_update_ipv4 (struct prefix *a, struct rib *b) { return 0; }
 #ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
 #pragma weak kernel_delete_ipv4 = kernel_add_ipv4
 #else
index 8bfe5a4290a26beb7418762c08d77726c768df1c..6d98f57ca33487d41d595ba2cfe3a17f07cf7ce4 100644 (file)
@@ -28,6 +28,7 @@
 #include "zebra/rib.h"
 
 extern int kernel_add_ipv4 (struct prefix *, struct rib *);
+extern int kernel_update_ipv4 (struct prefix *, struct rib *);
 extern int kernel_delete_ipv4 (struct prefix *, struct rib *);
 extern int kernel_add_route (struct prefix_ipv4 *, struct in_addr *, int, int);
 extern int kernel_address_add_ipv4 (struct interface *, struct connected *);
index e175d1e27b5102c9985fd3d01851d927a45ed41f..34e2a60febaefaec216af62d768980b8b97a4caa 100644 (file)
@@ -327,6 +327,13 @@ kernel_add_ipv4 (struct prefix *p, struct rib *rib)
   return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET);
 }
 
+int
+kernel_update_ipv4 (struct prefix *p, struct rib *rib)
+{
+  kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET);
+  return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET);
+}
+
 int
 kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
 {
index f3bf1917b47013a8798aca53607e3184abfdf598..ab49406cd72d354dfa036d15e2ba57bf28f90c7d 100644 (file)
@@ -1356,9 +1356,10 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl)
   n->nlmsg_flags |= NLM_F_ACK;
 
   if (IS_ZEBRA_DEBUG_KERNEL)
-    zlog_debug ("netlink_talk: %s type %s(%u), seq=%u", nl->name,
+    zlog_debug ("netlink_talk: %s type %s(%u), seq=%u flags 0x%x",
+               nl->name,
                lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type,
-               n->nlmsg_seq);
+               n->nlmsg_seq, n->nlmsg_flags);
 
   /* Send message to netlink interface. */
   if (zserv_privs.change (ZPRIVS_RAISE))
@@ -1683,9 +1684,10 @@ _netlink_route_debug(
 }
 
 /* Routing table change via netlink interface. */
+/* Update flag indicates whether this is a "replace" or not. */
 static int
 netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
-                         int family)
+                         int family, int update)
 {
   int bytelen;
   struct sockaddr_nl snl;
@@ -1710,6 +1712,8 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
 
   req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
   req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
+  if ((cmd == RTM_NEWROUTE) && update)
+    req.n.nlmsg_flags |= NLM_F_REPLACE;
   req.n.nlmsg_type = cmd;
   req.r.rtm_family = family;
   req.r.rtm_table = rib->table;
@@ -1912,13 +1916,19 @@ skip:
 int
 kernel_add_ipv4 (struct prefix *p, struct rib *rib)
 {
-  return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET);
+  return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET, 0);
+}
+
+int
+kernel_update_ipv4 (struct prefix *p, struct rib *rib)
+{
+  return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET, 1);
 }
 
 int
 kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
 {
-  return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET);
+  return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET, 0);
 }
 
 #ifdef HAVE_IPV6
@@ -1926,7 +1936,7 @@ int
 kernel_add_ipv6 (struct prefix *p, struct rib *rib)
 {
     {
-      return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET6);
+      return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET6, 0);
     }
 }
 
@@ -1934,7 +1944,7 @@ int
 kernel_delete_ipv6 (struct prefix *p, struct rib *rib)
 {
     {
-      return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET6);
+      return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET6, 0);
     }
 }
 
index 90ed73d0bd3a0a18c1acfe98d11178bf1ff407e9..c366775105e76024b27f749cf5544907924f3f68 100644 (file)
@@ -230,6 +230,13 @@ kernel_add_ipv4 (struct prefix *p, struct rib *rib)
   return route;
 }
 
+int
+kernel_update_ipv4 (struct prefix *p, struct rib *rib)
+{
+  kernel_delete_ipv4 (p, rib);
+  return kernel_add_ipv4 (p, rib);
+}
+
 int
 kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
 {
index 6c6198515597b51b41c161dc01c49f1586e0155b..7c07d9d24549ea6703815f282caa9a80ee724221 100644 (file)
@@ -1352,8 +1352,11 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
 
 
 
+/* Update flag indicates whether this is a "replace" or not. Currently, this
+ * is only used for IPv4.
+ */
 static void
-rib_install_kernel (struct route_node *rn, struct rib *rib)
+rib_install_kernel (struct route_node *rn, struct rib *rib, int update)
 {
   int ret = 0;
   struct nexthop *nexthop, *tnexthop;
@@ -1367,7 +1370,10 @@ rib_install_kernel (struct route_node *rn, struct rib *rib)
   switch (PREFIX_FAMILY (&rn->p))
     {
     case AF_INET:
-      ret = kernel_add_ipv4 (&rn->p, rib);
+      if (update)
+        ret = kernel_update_ipv4 (&rn->p, rib);
+      else
+        ret = kernel_add_ipv4 (&rn->p, rib);
       break;
 #ifdef HAVE_IPV6
     case AF_INET6:
@@ -1509,6 +1515,7 @@ rib_process (struct route_node *rn)
   struct nexthop *nexthop = NULL, *tnexthop;
   int recursing;
   char buf[INET6_ADDRSTRLEN];
+  int update_ok = 0;
   
   assert (rn);
   
@@ -1624,8 +1631,16 @@ rib_process (struct route_node *rn)
          zfpm_trigger_update (rn, "updating existing route");
 
           redistribute_delete (&rn->p, select);
+
           if (! RIB_SYSTEM_ROUTE (select))
-            rib_uninstall_kernel (rn, select);
+            {
+              /* For v4, use the replace semantics of netlink. */
+              if (PREFIX_FAMILY (&rn->p) == AF_INET)
+                update_ok = 1;
+              else
+                rib_uninstall_kernel (rn, select);
+            }
+
 
           /* Set real nexthop. */
          /* Need to check if any NHs are active to clear the
@@ -1633,12 +1648,21 @@ rib_process (struct route_node *rn)
           */
           if (nexthop_active_update (rn, select, 1))
            {
+              /* Clear FIB flag for IPv4, install will set it */
+              if (update_ok)
+                {
+                  for (nexthop = select->nexthop; nexthop; nexthop = nexthop->next)
+                    UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+                }
              if (! RIB_SYSTEM_ROUTE (select))
-               rib_install_kernel (rn, select);
+               rib_install_kernel (rn, select, update_ok);
              redistribute_add (&rn->p, select);
            }
          else
            {
+              /* For IPv4, do the uninstall here. */
+              if (update_ok)
+                rib_uninstall_kernel (rn, select);
              UNSET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
            }
          UNSET_FLAG (select->flags, ZEBRA_FLAG_CHANGED);
@@ -1659,7 +1683,7 @@ rib_process (struct route_node *rn)
               break;
             }
           if (! installed) 
-            rib_install_kernel (rn, select);
+            rib_install_kernel (rn, select, 0);
         }
       goto end;
     }
@@ -1677,8 +1701,22 @@ rib_process (struct route_node *rn)
       zfpm_trigger_update (rn, "removing existing route");
 
       redistribute_delete (&rn->p, fib);
+
       if (! RIB_SYSTEM_ROUTE (fib))
-       rib_uninstall_kernel (rn, fib);
+        {
+          /* For v4, use the replace semantics of netlink -- only if there is
+           * another route to replace this with.
+           */
+          if (PREFIX_FAMILY (&rn->p) == AF_INET)
+            {
+              if (!select || RIB_SYSTEM_ROUTE(select))
+                rib_uninstall_kernel (rn, fib);
+              else
+                update_ok = 1;
+            }
+            else
+              rib_uninstall_kernel (rn, fib);
+        }
       UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED);
 
       /* Set real nexthop. */
@@ -1701,11 +1739,27 @@ rib_process (struct route_node *rn)
       /* Set real nexthop. */
       if (nexthop_active_update (rn, select, 1))
        {
+          /* Clear FIB flag for IPv4 for previous installed route. */
+          if (update_ok)
+            {
+              assert (fib);
+              for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next)
+                UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+            }
          if (! RIB_SYSTEM_ROUTE (select))
-           rib_install_kernel (rn, select);
+           rib_install_kernel (rn, select, update_ok);
          SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
          redistribute_add (&rn->p, select);
        }
+      else
+       {
+          /* For IPv4, uninstall prior route here, if any. */
+          if (update_ok)
+            {
+              assert (fib);
+              rib_uninstall_kernel (rn, fib);
+            }
+       }
       UNSET_FLAG(select->flags, ZEBRA_FLAG_CHANGED);
     }
 
@@ -2624,7 +2678,7 @@ rib_delete_ipv4 (int type, u_short instance, int flags, struct prefix_ipv4 *p,
             }
           /* This means someone else, other than Zebra, has deleted
            * a Zebra router from the kernel. We will add it back */
-           rib_install_kernel(rn, fib);
+           rib_install_kernel(rn, fib, 0);
         }
       else
        {
@@ -2824,13 +2878,14 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
         {
           redistribute_delete (&rn->p, rib);
-          rib_uninstall_kernel (rn, rib);
-          /* Are there other active nexthops? */
+          /* If there are other active nexthops, do an update. */
           if (rib->nexthop_active_num > 1)
             {
-              rib_install_kernel (rn, rib);
+              rib_install_kernel (rn, rib, 1);
               redistribute_add (&rn->p, rib);
             }
+          else
+            rib_uninstall_kernel (rn, rib);
         }
 
       /* Delete the nexthop and dereg from NHT */
@@ -3361,7 +3416,7 @@ rib_delete_ipv6 (int type, u_short instance, int flags, struct prefix_ipv6 *p,
             }
           /* This means someone else, other than Zebra, has deleted a Zebra
            * route from the kernel. We will add it back */
-          rib_install_kernel(rn, fib);
+          rib_install_kernel(rn, fib, 0);
         }
       else
        {
@@ -3569,7 +3624,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
           /* Are there other active nexthops? */
           if (rib->nexthop_active_num > 1)
             {
-              rib_install_kernel (rn, rib);
+              rib_install_kernel (rn, rib, 0);
               redistribute_add (&rn->p, rib);
             }
         }