From 0aaa722883245c2109d9856ca0656749860fc579 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Thu, 27 Aug 2020 15:00:55 -0400 Subject: [PATCH] zebra: When shutting down an interface immediately notify about rnh Imagine a situation where a interface is bouncing up/down. The interface comes up and daemons like pbr will get a nht tracking callback for a connected interface up and will install the routes down to zebra. At this same time the interface can go down. But since zebra is busy handling route changes ( from pbr ) it has not read the netlink message and can get into a situation where the route resolves properly and then we attempt to install it into the kernel( which is rejected ). If the interface bounces back up fast at this point, the down then up netlink message will be read and create two route entries off the connected route node. Zebra will then enqueue both route entries for future processing. After this processing happens the down/up is collapsed into an up and nexthop tracking sees no changes and does not inform any upper level protocol( in this case pbr ) that nexthop tracking has changed. So pbr still believes the nexthops are good but the routes are not installed since pbr has taken no action. Fix this by immediately running rnh when we signal a connected route entry is scheduled for removal. This should cause upper level protocols to get a rnh notification for the small amount of time that the connected route was bouncing around like a madman. Signed-off-by: Donald Sharp --- zebra/connected.c | 4 ++-- zebra/kernel_socket.c | 4 ++-- zebra/redistribute.c | 2 +- zebra/rib.h | 2 +- zebra/rt_netlink.c | 6 +++--- zebra/zapi_msg.c | 2 +- zebra/zebra_rib.c | 15 ++++++++++++++- 7 files changed, 24 insertions(+), 11 deletions(-) diff --git a/zebra/connected.c b/zebra/connected.c index 3b9ebe14a4..8c4ba163bd 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -393,10 +393,10 @@ void connected_down(struct interface *ifp, struct connected *ifc) * head. */ rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); + 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false, true); rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, - 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); + 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false, true); /* Schedule LSP forwarding entries for processing, if appropriate. */ if (zvrf->vrf->vrf_id == VRF_DEFAULT) { diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 4c29b999f0..02963651a0 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1136,7 +1136,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, NULL, 0, RT_TABLE_MAIN, 0, - 0, true); + 0, true, false); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, @@ -1145,7 +1145,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, &nh, 0, RT_TABLE_MAIN, 0, - 0, true); + 0, true, false); } /* Interface function for the kernel routing table updates. Support diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 0b97562424..c0f89e6afe 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -727,7 +727,7 @@ int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE, re->table, re->flags, &p, NULL, re->nhe->nhg.nexthop, re->nhe_id, zvrf->table_id, re->metric, re->distance, - false); + false, false); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index b9f4e56905..be680a112f 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -379,7 +379,7 @@ extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, uint32_t nhe_id, uint32_t table_id, uint32_t metric, - uint8_t distance, bool fromkernel); + uint8_t distance, bool fromkernel, bool connected_down); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, union g_addr *addr, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 4a6839d3b1..50b1a62d86 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -837,7 +837,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (nhe_id) { rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, NULL, nhe_id, table, metric, - distance, true); + distance, true, false); } else { if (!tb[RTA_MULTIPATH]) { struct nexthop nh; @@ -847,13 +847,13 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, gate, afi, vrf_id); rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, &nh, 0, table, - metric, distance, true); + metric, distance, true, false); } else { /* XXX: need to compare the entire list of * nexthops here for NLM_F_APPEND stupidity */ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, NULL, 0, table, - metric, distance, true); + metric, distance, true, false); } } } diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ec61be0b39..4b31b46bce 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1890,7 +1890,7 @@ static void zread_route_del(ZAPI_HANDLER_ARGS) rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, api.flags, &api.prefix, src_p, NULL, 0, table_id, api.metric, - api.distance, false); + api.distance, false, false); /* Stats */ switch (api.prefix.family) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 5fcb513e3c..e0aff10d90 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3041,7 +3041,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, uint32_t nhe_id, uint32_t table_id, uint32_t metric, - uint8_t distance, bool fromkernel) + uint8_t distance, bool fromkernel, bool connected_down) { struct route_table *table; struct route_node *rn; @@ -3248,6 +3248,19 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, rib_delnode(rn, same); } + /* + * This is to force an immediate re-eval of this particular + * node via nexthop tracking. Why? Because there are scenarios + * where the interface is flapping and the normal queuing methodology + * will cause down/up events to very very rarely be combined into + * a non-event from nexthop tracking perspective. Leading + * to some fun timing situations with upper level routing protocol + * trying to and failing to install routes during this blip. Especially + * when zebra is under load. + */ + if (connected_down) + zebra_rib_evaluate_rn_nexthops(rn, + zebra_router_get_next_sequence()); route_unlock_node(rn); return; } -- 2.39.5