]> git.proxmox.com Git - mirror_frr.git/commitdiff
zebra: delete kernel routes using an interface with no more IPv4 address
authorLouis Scalbert <louis.scalbert@6wind.com>
Wed, 6 Jul 2022 13:37:44 +0000 (15:37 +0200)
committerLouis Scalbert <louis.scalbert@6wind.com>
Fri, 16 Dec 2022 14:07:46 +0000 (15:07 +0100)
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>
zebra/connected.c

index c01be58e826813582fac3ceeadafa9eae6357a49..57c7f1925bcf182f41079c53548a1add6352fab7 100644 (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)