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))
}
/* 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;
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;
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
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);
}
}
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);
}
}
+/* 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;
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:
struct nexthop *nexthop = NULL, *tnexthop;
int recursing;
char buf[INET6_ADDRSTRLEN];
+ int update_ok = 0;
assert (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
*/
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);
break;
}
if (! installed)
- rib_install_kernel (rn, select);
+ rib_install_kernel (rn, select, 0);
}
goto end;
}
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. */
/* 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);
}
}
/* 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
{
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 */
}
/* 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
{
/* 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);
}
}