From c6b38684bd040bdf6e2877f60eda7d8dc009ab87 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 6 Jul 2022 15:37:44 +0200 Subject: [PATCH] zebra: delete kernel routes using an interface with no more IPv4 address When the last IPv4 address of an interface is deleted, Linux removes all routes using this interface without any Netlink advertisement. Routes that have a IPv4 nexthop are correctly removed from the FRR RIB. However, routes that only have an interface with no more IPv4 addresses as a nexthop remains in the FRR RIB. In this situation, among the routes that this particular interface nexthop: - remove from the zebra kernel routes - reinstall the routes that have been added from FRR. It is useful when the nexthop is for example a VRF interface. Add related test cases in the zebra_netlink topotest. Signed-off-by: Louis Scalbert --- zebra/connected.c | 72 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/zebra/connected.c b/zebra/connected.c index c01be58e82..57c7f1925b 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -387,10 +387,14 @@ void connected_down(struct interface *ifp, struct connected *ifc) .ifindex = ifp->ifindex, .vrf_id = ifp->vrf->vrf_id, }; - struct zebra_vrf *zvrf; - uint32_t count = 0; + struct zebra_vrf *zvrf, *zvrf_iter; + uint32_t count_ipv4 = 0; struct listnode *cnode; struct connected *c; + struct route_table *table; + struct route_node *rn; + struct route_entry *re, *next; + struct vrf *vrf; zvrf = ifp->vrf->info; if (!zvrf) { @@ -456,12 +460,14 @@ void connected_down(struct interface *ifp, struct connected *ifc) prefix_copy(&cp, CONNECTED_PREFIX(c)); apply_mask(&cp); - if (prefix_same(&p, &cp) && - !CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN)) - count++; + if (CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN)) + continue; - if (count >= 1) + if (prefix_same(&p, &cp)) return; + + if (cp.family == AF_INET) + count_ipv4++; } /* @@ -474,6 +480,60 @@ void connected_down(struct interface *ifp, struct connected *ifc) rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); + /* When the last IPv4 address of an interface is deleted, Linux removes + * all routes using this interface without any Netlink advertisement. + * The removed routes include those that only have this particular + * interface as a nexthop. Among those, remove the kernel one from the + * FRR RIB and reinstall the other that have been added from FRR. + */ + if (afi == AFI_IP && count_ipv4 == 0 && if_is_operative(ifp)) { + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + zvrf_iter = vrf->info; + + if (!zvrf_iter) + continue; + + table = zvrf_iter->table[AFI_IP][SAFI_UNICAST]; + if (!table) + continue; + + for (rn = route_top(table); rn; + rn = srcdest_route_next(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (CHECK_FLAG(re->status, + ROUTE_ENTRY_REMOVED)) + continue; + if (re->nhe->ifp != ifp) + continue; + if (re->type == ZEBRA_ROUTE_KERNEL) + rib_delete( + afi, SAFI_UNICAST, + zvrf_iter->vrf->vrf_id, + re->type, 0, re->flags, + &rn->p, NULL, &nh, 0, + zvrf_iter->table_id, + re->metric, + re->distance, false); + else if (re->type != + ZEBRA_ROUTE_CONNECT) { + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); + UNSET_FLAG( + re->status, + ROUTE_ENTRY_INSTALLED); + rib_add(afi, SAFI_UNICAST, + zvrf_iter->vrf->vrf_id, + re->type, 0, 0, &rn->p, + NULL, &nh, re->nhe_id, + zvrf_iter->table_id, + re->metric, 0, + re->distance, 0, false); + } + } + } + } + } + /* Schedule LSP forwarding entries for processing, if appropriate. */ if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS)