Skip to content

Commit

Permalink
zebra: delete kernel routes using an interface with no more IPv4 address
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
louis-6wind committed May 14, 2024
1 parent 1320779 commit dba8e5e
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 6 deletions.
1 change: 1 addition & 0 deletions tests/topotests/zebra_rib/test_zebra_rib.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def test_zebra_kernel_route_vrf():
# Change the interface's vrf
r1.run("ip link add {} type vrf table 1".format(vrf))
r1.run("ip link set {} up".format(vrf))

r1.run("ip link set dev r1-eth0 master {}".format(vrf))

expected = "{}"
Expand Down
72 changes: 66 additions & 6 deletions zebra/connected.c
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,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 connected *c;
bool remove_local = true;
struct route_table *table;
struct route_node *rn;
struct route_entry *re, *next;
struct vrf *vrf;

zvrf = ifp->vrf->info;
if (!zvrf) {
Expand Down Expand Up @@ -471,12 +475,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++;
}

/*
Expand All @@ -503,6 +509,60 @@ void connected_down(struct interface *ifp, struct connected *ifc)
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 &&
re->type != ZEBRA_ROUTE_LOCAL) {
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)
Expand Down

0 comments on commit dba8e5e

Please sign in to comment.