diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index 05036fa7ad47..5b86caa4ca52 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -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 = "{}" diff --git a/zebra/connected.c b/zebra/connected.c index 404f892f6e5c..c401c035ee3c 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -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) { @@ -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++; } /* @@ -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)