]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib,zebra: VRF table-direct support
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Thu, 11 Jan 2024 20:23:28 +0000 (17:23 -0300)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Thu, 23 Jan 2025 17:37:09 +0000 (14:37 -0300)
Implement the necessary data structures and code changes to support sending
table-direct routes to protocols running in different VRFs.

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
lib/zclient.c
lib/zclient.h
zebra/redistribute.c
zebra/zapi_msg.c
zebra/zapi_msg.h

index 6aebe4377ee79a69ebd483141199f0e9b3128020..8fc9addf2f8f6bd2518d7cba719be16656cf6484 100644 (file)
@@ -31,6 +31,7 @@
 
 DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient");
 DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs");
+DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct");
 
 /* Zebra client events. */
 enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT };
@@ -157,6 +158,87 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance)
        }
 }
 
+static void redist_free_table_direct(void *data)
+{
+       XFREE(MTYPE_REDIST_TABLE_DIRECT, data);
+}
+
+struct redist_table_direct *redist_lookup_table_direct(const struct redist_proto *red,
+                                                      const struct redist_table_direct *table)
+{
+       struct redist_table_direct *ntable;
+       struct listnode *node;
+
+       if (red->instances == NULL)
+               return NULL;
+
+       for (ALL_LIST_ELEMENTS_RO(red->instances, node, ntable)) {
+               if (table->vrf_id != ntable->vrf_id)
+                       continue;
+               if (table->table_id != ntable->table_id)
+                       continue;
+
+               return ntable;
+       }
+
+       return NULL;
+}
+
+bool redist_table_direct_has_id(const struct redist_proto *red, int table_id)
+{
+       struct redist_table_direct *table;
+       struct listnode *node;
+
+       if (red->instances == NULL)
+               return false;
+
+       for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) {
+               if (table->table_id != table_id)
+                       continue;
+
+               return true;
+       }
+
+       return false;
+}
+
+void redist_add_table_direct(struct redist_proto *red, const struct redist_table_direct *table)
+{
+       struct redist_table_direct *ntable;
+
+       ntable = redist_lookup_table_direct(red, table);
+       if (ntable != NULL)
+               return;
+
+       if (red->instances == NULL) {
+               red->instances = list_new();
+               red->instances->del = redist_free_table_direct;
+       }
+
+       red->enabled = 1;
+
+       ntable = XCALLOC(MTYPE_REDIST_TABLE_DIRECT, sizeof(*ntable));
+       ntable->vrf_id = table->vrf_id;
+       ntable->table_id = table->table_id;
+       listnode_add(red->instances, ntable);
+}
+
+void redist_del_table_direct(struct redist_proto *red, const struct redist_table_direct *table)
+{
+       struct redist_table_direct *ntable;
+
+       ntable = redist_lookup_table_direct(red, table);
+       if (ntable == NULL)
+               return;
+
+       listnode_delete(red->instances, ntable);
+       red->instances->del(ntable);
+       if (red->instances->count == 0) {
+               red->enabled = 0;
+               list_delete(&red->instances);
+       }
+}
+
 void redist_del_all_instances(struct redist_proto *red)
 {
        if (!red->instances)
@@ -483,6 +565,17 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient,
        return zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api);
 }
 
+static void zclient_send_table_direct(struct zclient *zclient, afi_t afi, int type)
+{
+       struct redist_table_direct *table;
+       struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT];
+       struct listnode *node;
+
+       for (ALL_LIST_ELEMENTS_RO(red->instances, node, table))
+               zebra_redistribute_send(type, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT,
+                                       table->table_id, table->vrf_id);
+}
+
 /* Send register requests to zebra daemon for the information in a VRF. */
 void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
 {
@@ -516,6 +609,12 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
                                if (!zclient->mi_redist[afi][i].enabled)
                                        continue;
 
+                               if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
+                                       zclient_send_table_direct(zclient, afi,
+                                                                 ZEBRA_REDISTRIBUTE_ADD);
+                                       continue;
+                               }
+
                                struct listnode *node;
                                unsigned short *id;
 
@@ -583,6 +682,12 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)
                                if (!zclient->mi_redist[afi][i].enabled)
                                        continue;
 
+                               if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
+                                       zclient_send_table_direct(zclient, afi,
+                                                                 ZEBRA_REDISTRIBUTE_DELETE);
+                                       continue;
+                               }
+
                                struct listnode *node;
                                unsigned short *id;
 
@@ -4637,9 +4742,42 @@ static void zclient_read(struct event *thread)
        zclient_event(ZCLIENT_READ, zclient);
 }
 
+static void zclient_redistribute_table_direct(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi,
+                                             int instance, int command)
+{
+       struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT];
+       bool has_table;
+       struct redist_table_direct table = {
+               .vrf_id = vrf_id,
+               .table_id = instance,
+       };
+
+       has_table = redist_lookup_table_direct(red, &table);
+
+       if (command == ZEBRA_REDISTRIBUTE_ADD) {
+               if (has_table)
+                       return;
+
+               redist_add_table_direct(red, &table);
+       } else {
+               if (!has_table)
+                       return;
+
+               redist_del_table_direct(red, &table);
+       }
+
+       if (zclient->sock > 0)
+               zebra_redistribute_send(command, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, instance,
+                                       vrf_id);
+}
+
 void zclient_redistribute(int command, struct zclient *zclient, afi_t afi,
                          int type, unsigned short instance, vrf_id_t vrf_id)
 {
+       if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
+               zclient_redistribute_table_direct(zclient, vrf_id, afi, instance, command);
+               return;
+       }
 
        if (instance) {
                if (command == ZEBRA_REDISTRIBUTE_ADD) {
index 2385a8a2197b3a103b50aadedc18f64a6e049dc5..35e23ddf63221825fcf17aa7e2b386e39903918b 100644 (file)
@@ -268,6 +268,15 @@ struct redist_proto {
        struct list *instances;
 };
 
+/**
+ * Redistribute table direct instance data structure: keeps the VRF
+ * that subscribed to the table ID.
+ */
+struct redist_table_direct {
+       vrf_id_t vrf_id;
+       int table_id;
+};
+
 struct zclient_capabilities {
        uint32_t ecmp;
        bool mpls_enabled;
@@ -924,6 +933,15 @@ extern void redist_add_instance(struct redist_proto *, unsigned short);
 extern void redist_del_instance(struct redist_proto *, unsigned short);
 extern void redist_del_all_instances(struct redist_proto *red);
 
+extern struct redist_table_direct *
+redist_lookup_table_direct(const struct redist_proto *red, const struct redist_table_direct *table);
+extern bool redist_table_direct_has_id(const struct redist_proto *red, int table_id);
+extern void redist_add_table_direct(struct redist_proto *red,
+                                   const struct redist_table_direct *table);
+extern void redist_del_table_direct(struct redist_proto *red,
+                                   const struct redist_table_direct *table);
+
+
 /*
  * Send to zebra that the specified vrf is using label to resolve
  * itself for L3VPN's.  Repeated calls of this function with
index 66dc5b4b5f821aa6e8c372188890438b771f81c8..9bf7e2cbb52b5cb8656110879fa23c738169c814 100644 (file)
@@ -82,8 +82,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
 
                RNODE_FOREACH_RE (rn, newre) {
                        if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED))
-                               zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
-                                                        client, rn, newre, false);
+                               zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
+                                                        newre, NULL);
                }
 
                route_unlock_node(rn);
@@ -91,6 +91,24 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
 }
 
 /* Redistribute routes. */
+static void redistribute_table_direct(struct zserv *client, int type, const struct route_node *rn,
+                                     const struct route_entry *re)
+{
+       struct redist_table_direct *table;
+       struct redist_proto *red;
+       struct listnode *node;
+       afi_t afi = family2afi(rn->p.family);
+
+       red = &client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT];
+
+       for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) {
+               if (table->table_id != (int)re->table)
+                       continue;
+
+               zsend_redistribute_route(type, client, rn, re, &table->vrf_id);
+       }
+}
+
 static void zebra_redistribute(struct zserv *client, int type,
                               unsigned short instance, struct zebra_vrf *zvrf,
                               int afi)
@@ -102,13 +120,9 @@ static void zebra_redistribute(struct zserv *client, int type,
        vrf_id_t vrf_id = zvrf_id(zvrf);
 
        if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
-               if (vrf_id == VRF_DEFAULT) {
-                       table = zebra_router_find_table(zvrf, instance, afi,
-                                                       SAFI_UNICAST);
-                       type = ZEBRA_ROUTE_ALL;
-                       is_table_direct = true;
-               } else
-                       return;
+               table = zebra_router_find_table(zvrf, instance, afi, SAFI_UNICAST);
+               type = ZEBRA_ROUTE_ALL;
+               is_table_direct = true;
        } else
                table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
 
@@ -140,15 +154,20 @@ static void zebra_redistribute(struct zserv *client, int type,
                        if (!zebra_check_addr(&rn->p))
                                continue;
 
-                       zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
-                                                client, rn, newre, is_table_direct);
+                       if (is_table_direct)
+                               redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn,
+                                                         newre);
+                       else
+                               zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
+                                                        newre, NULL);
                }
 }
 
 /*
- * Function to return a valid table id value if table-direct is used
- * return 0 otherwise
- * This function can be called only if zebra_redistribute_check returns TRUE
+ * Checks if the route entry can be used as table-direct or not.
+ * `table-direct` routes always belong to `VRF_DEFAULT` and has an table
+ * ID different than the VRF it belongs (example main VRF table is 254,
+ * so in order to be `table-direct` the route's table ID must be != 254).
  */
 static bool zebra_redistribute_is_table_direct(const struct route_entry *re)
 {
@@ -177,15 +196,14 @@ static bool zebra_redistribute_check(const struct route_node *rn,
 
        afi = family2afi(rn->p.family);
        zvrf = zebra_vrf_lookup_by_id(re->vrf_id);
-       if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) {
+       if (zvrf->table_id != re->table) {
+               /*
+                * Routes with table ID different from VRFs can be used as
+                * `table-direct` if enabled.
+                */
                if (re->table &&
-                   redist_check_instance(&client->mi_redist
-                                                  [afi][ZEBRA_ROUTE_TABLE_DIRECT],
-                                         re->table)) {
-                       /* table-direct redistribution only for route entries which
-                        * are on the default vrf, and that have table id different
-                        * from the default table.
-                        */
+                   redist_table_direct_has_id(&client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT],
+                                              re->table)) {
                        return true;
                }
                return false;
@@ -227,7 +245,6 @@ void redistribute_update(const struct route_node *rn,
 {
        struct listnode *node, *nnode;
        struct zserv *client;
-       bool is_table_direct;
 
        if (IS_ZEBRA_DEBUG_RIB)
                zlog_debug(
@@ -242,7 +259,6 @@ void redistribute_update(const struct route_node *rn,
                return;
        }
 
-
        for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
                if (zebra_redistribute_check(rn, re, client)) {
                        if (IS_ZEBRA_DEBUG_RIB) {
@@ -253,15 +269,19 @@ void redistribute_update(const struct route_node *rn,
                                        re->vrf_id, re->table, re->type,
                                        re->distance, re->metric);
                        }
-                       is_table_direct = zebra_redistribute_is_table_direct(re);
-                       zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
-                                                client, rn, re,
-                                                is_table_direct);
+                       if (zebra_redistribute_is_table_direct(re))
+                               redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn,
+                                                         re);
+                       else
+                               zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
+                                                        re, NULL);
                } else if (zebra_redistribute_check(rn, prev_re, client)) {
-                       is_table_direct = zebra_redistribute_is_table_direct(prev_re);
-                       zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL,
-                                                client, rn, prev_re,
-                                                is_table_direct);
+                       if (zebra_redistribute_is_table_direct(prev_re))
+                               redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
+                                                         prev_re);
+                       else
+                               zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn,
+                                                        prev_re, NULL);
                }
        }
 }
@@ -281,7 +301,6 @@ void redistribute_delete(const struct route_node *rn,
        struct listnode *node, *nnode;
        struct zserv *client;
        vrf_id_t vrfid;
-       bool is_table_direct;
 
        if (old_re)
                vrfid = old_re->vrf_id;
@@ -344,10 +363,12 @@ void redistribute_delete(const struct route_node *rn,
                         * happy.
                         */
                        assert(old_re);
-                       is_table_direct = zebra_redistribute_is_table_direct(old_re);
-                       zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL,
-                                                client, rn, old_re,
-                                                is_table_direct);
+                       if (zebra_redistribute_is_table_direct(old_re))
+                               redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
+                                                         old_re);
+                       else
+                               zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn,
+                                                        old_re, NULL);
                }
        }
 }
@@ -383,8 +404,16 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS)
        }
 
        if (instance) {
-               if (!redist_check_instance(&client->mi_redist[afi][type],
-                                          instance)) {
+               if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
+                       struct redist_table_direct table = {
+                               .vrf_id = zvrf->vrf->vrf_id,
+                               .table_id = instance,
+                       };
+                       if (!redist_lookup_table_direct(&client->mi_redist[afi][type], &table)) {
+                               redist_add_table_direct(&client->mi_redist[afi][type], &table);
+                               zebra_redistribute(client, type, instance, zvrf, afi);
+                       }
+               } else if (!redist_check_instance(&client->mi_redist[afi][type], instance)) {
                        redist_add_instance(&client->mi_redist[afi][type],
                                            instance);
                        zebra_redistribute(client, type, instance, zvrf, afi);
@@ -443,7 +472,13 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS)
         * themselves should keep track of the received routes from zebra and
         * withdraw them when necessary.
         */
-       if (instance)
+       if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
+               struct redist_table_direct table = {
+                       .vrf_id = zvrf->vrf->vrf_id,
+                       .table_id = instance,
+               };
+               redist_del_table_direct(&client->mi_redist[afi][type], &table);
+       } else if (instance)
                redist_del_instance(&client->mi_redist[afi][type], instance);
        else
                vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf));
index ab55998af046160ed5824bc90e42b4fa13c15ab5..f32d8ea6c65c9b905a01d41e23caaf0e7d6df9d9 100644 (file)
@@ -509,9 +509,8 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp)
        return zserv_send_message(client, s);
 }
 
-int zsend_redistribute_route(int cmd, struct zserv *client,
-                            const struct route_node *rn,
-                            const struct route_entry *re, bool is_table_direct)
+int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn,
+                            const struct route_entry *re, vrf_id_t *to_vrf)
 {
        struct zapi_route api;
        struct zapi_nexthop *api_nh;
@@ -527,9 +526,10 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
        api.vrf_id = re->vrf_id;
        api.type = re->type;
        api.safi = SAFI_UNICAST;
-       if (is_table_direct) {
+       if (to_vrf != NULL) {
                api.instance = re->table;
                api.type = ZEBRA_ROUTE_TABLE_DIRECT;
+               api.vrf_id = *to_vrf;
        } else
                api.instance = re->instance;
        api.flags = re->flags;
@@ -598,7 +598,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
 
        /* Attributes. */
        SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
-       if (is_table_direct)
+       if (to_vrf != NULL)
                api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT;
        else
                api.distance = re->distance;
index a59ccc838b9787e57408f44d619b598fa55ba306..29a5b69f1815d4d13e5dd7e394e036efdba657a9 100644 (file)
@@ -51,10 +51,8 @@ extern void nbr_connected_delete_ipv6(struct interface *ifp,
                                      struct in6_addr *address);
 extern int zsend_interface_update(int cmd, struct zserv *client,
                                  struct interface *ifp);
-extern int zsend_redistribute_route(int cmd, struct zserv *zclient,
-                                   const struct route_node *rn,
-                                   const struct route_entry *re,
-                                   bool is_table_direct);
+extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn,
+                                   const struct route_entry *re, vrf_id_t *to_vrf);
 
 extern int zsend_router_id_update(struct zserv *zclient, afi_t afi,
                                  struct prefix *p, vrf_id_t vrf_id);