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.
This commit is contained in:
Donald Sharp 2015-06-11 09:11:12 -07:00
parent 94ad353dfd
commit 6ae24471cb
6 changed files with 101 additions and 20 deletions

View File

@ -9,6 +9,7 @@
#include "zebra/connected.h" #include "zebra/connected.h"
int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; } 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 #ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
#pragma weak kernel_delete_ipv4 = kernel_add_ipv4 #pragma weak kernel_delete_ipv4 = kernel_add_ipv4
#else #else

View File

@ -28,6 +28,7 @@
#include "zebra/rib.h" #include "zebra/rib.h"
extern int kernel_add_ipv4 (struct prefix *, struct rib *); 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_delete_ipv4 (struct prefix *, struct rib *);
extern int kernel_add_route (struct prefix_ipv4 *, struct in_addr *, int, int); extern int kernel_add_route (struct prefix_ipv4 *, struct in_addr *, int, int);
extern int kernel_address_add_ipv4 (struct interface *, struct connected *); extern int kernel_address_add_ipv4 (struct interface *, struct connected *);

View File

@ -327,6 +327,13 @@ kernel_add_ipv4 (struct prefix *p, struct rib *rib)
return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET); 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 int
kernel_delete_ipv4 (struct prefix *p, struct rib *rib) kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
{ {

View File

@ -1356,9 +1356,10 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl)
n->nlmsg_flags |= NLM_F_ACK; n->nlmsg_flags |= NLM_F_ACK;
if (IS_ZEBRA_DEBUG_KERNEL) 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, lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type,
n->nlmsg_seq); n->nlmsg_seq, n->nlmsg_flags);
/* Send message to netlink interface. */ /* Send message to netlink interface. */
if (zserv_privs.change (ZPRIVS_RAISE)) if (zserv_privs.change (ZPRIVS_RAISE))
@ -1683,9 +1684,10 @@ _netlink_route_debug(
} }
/* Routing table change via netlink interface. */ /* Routing table change via netlink interface. */
/* Update flag indicates whether this is a "replace" or not. */
static int static int
netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
int family) int family, int update)
{ {
int bytelen; int bytelen;
struct sockaddr_nl snl; 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_len = NLMSG_LENGTH (sizeof (struct rtmsg));
req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; 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.n.nlmsg_type = cmd;
req.r.rtm_family = family; req.r.rtm_family = family;
req.r.rtm_table = rib->table; req.r.rtm_table = rib->table;
@ -1912,13 +1916,19 @@ skip:
int int
kernel_add_ipv4 (struct prefix *p, struct rib *rib) 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 int
kernel_delete_ipv4 (struct prefix *p, struct rib *rib) 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 #ifdef HAVE_IPV6
@ -1926,7 +1936,7 @@ int
kernel_add_ipv6 (struct prefix *p, struct rib *rib) 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) 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);
} }
} }

View File

@ -230,6 +230,13 @@ kernel_add_ipv4 (struct prefix *p, struct rib *rib)
return route; return route;
} }
int
kernel_update_ipv4 (struct prefix *p, struct rib *rib)
{
kernel_delete_ipv4 (p, rib);
return kernel_add_ipv4 (p, rib);
}
int int
kernel_delete_ipv4 (struct prefix *p, struct rib *rib) kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
{ {

View 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 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; int ret = 0;
struct nexthop *nexthop, *tnexthop; struct nexthop *nexthop, *tnexthop;
@ -1367,7 +1370,10 @@ rib_install_kernel (struct route_node *rn, struct rib *rib)
switch (PREFIX_FAMILY (&rn->p)) switch (PREFIX_FAMILY (&rn->p))
{ {
case AF_INET: 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; break;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
case AF_INET6: case AF_INET6:
@ -1509,6 +1515,7 @@ rib_process (struct route_node *rn)
struct nexthop *nexthop = NULL, *tnexthop; struct nexthop *nexthop = NULL, *tnexthop;
int recursing; int recursing;
char buf[INET6_ADDRSTRLEN]; char buf[INET6_ADDRSTRLEN];
int update_ok = 0;
assert (rn); assert (rn);
@ -1624,8 +1631,16 @@ rib_process (struct route_node *rn)
zfpm_trigger_update (rn, "updating existing route"); zfpm_trigger_update (rn, "updating existing route");
redistribute_delete (&rn->p, select); redistribute_delete (&rn->p, select);
if (! RIB_SYSTEM_ROUTE (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. */ /* Set real nexthop. */
/* Need to check if any NHs are active to clear the /* 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)) 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)) if (! RIB_SYSTEM_ROUTE (select))
rib_install_kernel (rn, select); rib_install_kernel (rn, select, update_ok);
redistribute_add (&rn->p, select); redistribute_add (&rn->p, select);
} }
else 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_SELECTED);
} }
UNSET_FLAG (select->flags, ZEBRA_FLAG_CHANGED); UNSET_FLAG (select->flags, ZEBRA_FLAG_CHANGED);
@ -1659,7 +1683,7 @@ rib_process (struct route_node *rn)
break; break;
} }
if (! installed) if (! installed)
rib_install_kernel (rn, select); rib_install_kernel (rn, select, 0);
} }
goto end; goto end;
} }
@ -1677,8 +1701,22 @@ rib_process (struct route_node *rn)
zfpm_trigger_update (rn, "removing existing route"); zfpm_trigger_update (rn, "removing existing route");
redistribute_delete (&rn->p, fib); redistribute_delete (&rn->p, fib);
if (! RIB_SYSTEM_ROUTE (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); UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED);
/* Set real nexthop. */ /* Set real nexthop. */
@ -1701,11 +1739,27 @@ rib_process (struct route_node *rn)
/* Set real nexthop. */ /* Set real nexthop. */
if (nexthop_active_update (rn, select, 1)) 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)) if (! RIB_SYSTEM_ROUTE (select))
rib_install_kernel (rn, select); rib_install_kernel (rn, select, update_ok);
SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
redistribute_add (&rn->p, select); 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); 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 /* This means someone else, other than Zebra, has deleted
* a Zebra router from the kernel. We will add it back */ * a Zebra router from the kernel. We will add it back */
rib_install_kernel(rn, fib); rib_install_kernel(rn, fib, 0);
} }
else else
{ {
@ -2824,13 +2878,14 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
{ {
redistribute_delete (&rn->p, rib); redistribute_delete (&rn->p, rib);
rib_uninstall_kernel (rn, rib); /* If there are other active nexthops, do an update. */
/* Are there other active nexthops? */
if (rib->nexthop_active_num > 1) if (rib->nexthop_active_num > 1)
{ {
rib_install_kernel (rn, rib); rib_install_kernel (rn, rib, 1);
redistribute_add (&rn->p, rib); redistribute_add (&rn->p, rib);
} }
else
rib_uninstall_kernel (rn, rib);
} }
/* Delete the nexthop and dereg from NHT */ /* 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 /* This means someone else, other than Zebra, has deleted a Zebra
* route from the kernel. We will add it back */ * route from the kernel. We will add it back */
rib_install_kernel(rn, fib); rib_install_kernel(rn, fib, 0);
} }
else else
{ {
@ -3569,7 +3624,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
/* Are there other active nexthops? */ /* Are there other active nexthops? */
if (rib->nexthop_active_num > 1) if (rib->nexthop_active_num > 1)
{ {
rib_install_kernel (rn, rib); rib_install_kernel (rn, rib, 0);
redistribute_add (&rn->p, rib); redistribute_add (&rn->p, rib);
} }
} }