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"
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

View 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 *);

View 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)
{

View 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);
}
}

View 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)
{

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
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);
}
}