lib,zebra: VRF table-direct support

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>
This commit is contained in:
Rafael Zalamena 2024-01-11 17:23:28 -03:00
parent dfdc12e164
commit 28a9ca3405
5 changed files with 238 additions and 49 deletions

View file

@ -31,6 +31,7 @@
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient"); DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient");
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs"); DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs");
DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct");
/* Zebra client events. */ /* Zebra client events. */
enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; 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) void redist_del_all_instances(struct redist_proto *red)
{ {
if (!red->instances) 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); 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. */ /* 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) 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) if (!zclient->mi_redist[afi][i].enabled)
continue; continue;
if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
zclient_send_table_direct(zclient, afi,
ZEBRA_REDISTRIBUTE_ADD);
continue;
}
struct listnode *node; struct listnode *node;
unsigned short *id; 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) if (!zclient->mi_redist[afi][i].enabled)
continue; continue;
if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
zclient_send_table_direct(zclient, afi,
ZEBRA_REDISTRIBUTE_DELETE);
continue;
}
struct listnode *node; struct listnode *node;
unsigned short *id; unsigned short *id;
@ -4637,9 +4742,42 @@ static void zclient_read(struct event *thread)
zclient_event(ZCLIENT_READ, zclient); 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, void zclient_redistribute(int command, struct zclient *zclient, afi_t afi,
int type, unsigned short instance, vrf_id_t vrf_id) 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 (instance) {
if (command == ZEBRA_REDISTRIBUTE_ADD) { if (command == ZEBRA_REDISTRIBUTE_ADD) {

View file

@ -268,6 +268,15 @@ struct redist_proto {
struct list *instances; 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 { struct zclient_capabilities {
uint32_t ecmp; uint32_t ecmp;
bool mpls_enabled; 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_instance(struct redist_proto *, unsigned short);
extern void redist_del_all_instances(struct redist_proto *red); 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 * Send to zebra that the specified vrf is using label to resolve
* itself for L3VPN's. Repeated calls of this function with * itself for L3VPN's. Repeated calls of this function with

View file

@ -82,8 +82,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
RNODE_FOREACH_RE (rn, newre) { RNODE_FOREACH_RE (rn, newre) {
if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED)) if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
client, rn, newre, false); newre, NULL);
} }
route_unlock_node(rn); route_unlock_node(rn);
@ -91,6 +91,24 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
} }
/* Redistribute routes. */ /* 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, static void zebra_redistribute(struct zserv *client, int type,
unsigned short instance, struct zebra_vrf *zvrf, unsigned short instance, struct zebra_vrf *zvrf,
int afi) int afi)
@ -102,13 +120,9 @@ static void zebra_redistribute(struct zserv *client, int type,
vrf_id_t vrf_id = zvrf_id(zvrf); vrf_id_t vrf_id = zvrf_id(zvrf);
if (type == ZEBRA_ROUTE_TABLE_DIRECT) { if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
if (vrf_id == VRF_DEFAULT) { table = zebra_router_find_table(zvrf, instance, afi, SAFI_UNICAST);
table = zebra_router_find_table(zvrf, instance, afi,
SAFI_UNICAST);
type = ZEBRA_ROUTE_ALL; type = ZEBRA_ROUTE_ALL;
is_table_direct = true; is_table_direct = true;
} else
return;
} else } else
table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); 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)) if (!zebra_check_addr(&rn->p))
continue; continue;
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, if (is_table_direct)
client, rn, newre, 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 * Checks if the route entry can be used as table-direct or not.
* return 0 otherwise * `table-direct` routes always belong to `VRF_DEFAULT` and has an table
* This function can be called only if zebra_redistribute_check returns TRUE * 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) 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); afi = family2afi(rn->p.family);
zvrf = zebra_vrf_lookup_by_id(re->vrf_id); 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) {
if (re->table && /*
redist_check_instance(&client->mi_redist * Routes with table ID different from VRFs can be used as
[afi][ZEBRA_ROUTE_TABLE_DIRECT], * `table-direct` if enabled.
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.
*/ */
if (re->table &&
redist_table_direct_has_id(&client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT],
re->table)) {
return true; return true;
} }
return false; return false;
@ -227,7 +245,6 @@ void redistribute_update(const struct route_node *rn,
{ {
struct listnode *node, *nnode; struct listnode *node, *nnode;
struct zserv *client; struct zserv *client;
bool is_table_direct;
if (IS_ZEBRA_DEBUG_RIB) if (IS_ZEBRA_DEBUG_RIB)
zlog_debug( zlog_debug(
@ -242,7 +259,6 @@ void redistribute_update(const struct route_node *rn,
return; return;
} }
for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
if (zebra_redistribute_check(rn, re, client)) { if (zebra_redistribute_check(rn, re, client)) {
if (IS_ZEBRA_DEBUG_RIB) { 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->vrf_id, re->table, re->type,
re->distance, re->metric); re->distance, re->metric);
} }
is_table_direct = zebra_redistribute_is_table_direct(re); if (zebra_redistribute_is_table_direct(re))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn,
client, rn, re, re);
is_table_direct); else
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
re, NULL);
} else if (zebra_redistribute_check(rn, prev_re, client)) { } else if (zebra_redistribute_check(rn, prev_re, client)) {
is_table_direct = zebra_redistribute_is_table_direct(prev_re); if (zebra_redistribute_is_table_direct(prev_re))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
client, rn, prev_re, prev_re);
is_table_direct); 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 listnode *node, *nnode;
struct zserv *client; struct zserv *client;
vrf_id_t vrfid; vrf_id_t vrfid;
bool is_table_direct;
if (old_re) if (old_re)
vrfid = old_re->vrf_id; vrfid = old_re->vrf_id;
@ -344,10 +363,12 @@ void redistribute_delete(const struct route_node *rn,
* happy. * happy.
*/ */
assert(old_re); assert(old_re);
is_table_direct = zebra_redistribute_is_table_direct(old_re); if (zebra_redistribute_is_table_direct(old_re))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
client, rn, old_re, old_re);
is_table_direct); 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 (instance) {
if (!redist_check_instance(&client->mi_redist[afi][type], if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
instance)) { 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], redist_add_instance(&client->mi_redist[afi][type],
instance); instance);
zebra_redistribute(client, type, instance, zvrf, afi); 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 * themselves should keep track of the received routes from zebra and
* withdraw them when necessary. * 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); redist_del_instance(&client->mi_redist[afi][type], instance);
else else
vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf)); vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf));

View file

@ -509,9 +509,8 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp)
return zserv_send_message(client, s); return zserv_send_message(client, s);
} }
int zsend_redistribute_route(int cmd, struct zserv *client, int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn,
const struct route_node *rn, const struct route_entry *re, vrf_id_t *to_vrf)
const struct route_entry *re, bool is_table_direct)
{ {
struct zapi_route api; struct zapi_route api;
struct zapi_nexthop *api_nh; 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.vrf_id = re->vrf_id;
api.type = re->type; api.type = re->type;
api.safi = SAFI_UNICAST; api.safi = SAFI_UNICAST;
if (is_table_direct) { if (to_vrf != NULL) {
api.instance = re->table; api.instance = re->table;
api.type = ZEBRA_ROUTE_TABLE_DIRECT; api.type = ZEBRA_ROUTE_TABLE_DIRECT;
api.vrf_id = *to_vrf;
} else } else
api.instance = re->instance; api.instance = re->instance;
api.flags = re->flags; api.flags = re->flags;
@ -598,7 +598,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
/* Attributes. */ /* Attributes. */
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
if (is_table_direct) if (to_vrf != NULL)
api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT; api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT;
else else
api.distance = re->distance; api.distance = re->distance;

View file

@ -51,10 +51,8 @@ extern void nbr_connected_delete_ipv6(struct interface *ifp,
struct in6_addr *address); struct in6_addr *address);
extern int zsend_interface_update(int cmd, struct zserv *client, extern int zsend_interface_update(int cmd, struct zserv *client,
struct interface *ifp); struct interface *ifp);
extern int zsend_redistribute_route(int cmd, struct zserv *zclient, extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn,
const struct route_node *rn, const struct route_entry *re, vrf_id_t *to_vrf);
const struct route_entry *re,
bool is_table_direct);
extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, extern int zsend_router_id_update(struct zserv *zclient, afi_t afi,
struct prefix *p, vrf_id_t vrf_id); struct prefix *p, vrf_id_t vrf_id);