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 <louis.scalbert@6wind.com>
This commit is contained in:
Louis Scalbert 2022-07-06 15:37:44 +02:00
parent 667a4e92da
commit c6b38684bd

View File

@ -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)