From 6ae24471cb3ca237a8e479f9c39557601c2cc2f2 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 11 Jun 2015 09:11:12 -0700 Subject: [PATCH] Zebra: Implement route replace semantics. 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 | 1 + zebra/rt.h | 1 + zebra/rt_ioctl.c | 7 ++++ zebra/rt_netlink.c | 24 ++++++++++---- zebra/rt_socket.c | 7 ++++ zebra/zebra_rib.c | 81 +++++++++++++++++++++++++++++++++++++-------- 6 files changed, 101 insertions(+), 20 deletions(-) diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index cdb6e23f02..2e833febe6 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -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 diff --git a/zebra/rt.h b/zebra/rt.h index 8bfe5a4290..6d98f57ca3 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -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 *); diff --git a/zebra/rt_ioctl.c b/zebra/rt_ioctl.c index e175d1e27b..34e2a60feb 100644 --- a/zebra/rt_ioctl.c +++ b/zebra/rt_ioctl.c @@ -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) { diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index f3bf1917b4..ab49406cd7 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -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); } } diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 90ed73d0bd..c366775105 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -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) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6c61985155..7c07d9d245 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -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); } } -- 2.39.5