zebra: extend table manager per vrf, add vty configuration

Because vrf backend may be based on namespaces, each vrf can
use in the [16-(2^32-1)] range table identifier for daemons that
request it. Extend the table manager to be hosted by vrf.

That possibility is disabled in the case the vrf backend is vrflite.
In that case, all vrf context use the same table manager instance.

Add a configuration command to be able to configure the wished
range of tables to use. This is a solution that permits to give
chunks to bgp daemon when it works with bgp flowspec entries and
wants to use specific iptables that do not override vrf tables.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2021-07-23 16:56:28 +02:00
parent f334c88ed9
commit 42d4b30e00
9 changed files with 235 additions and 54 deletions

View file

@ -473,6 +473,21 @@ be updated with the new name. To illustrate, if you want to recompile with
./configure --with-defaultvrfname=global
.. _zebra-table-allocation:
Table Allocation
================
Some services like BGP flowspec allocate routing tables to perform policy
routing based on netfilter criteria and IP rules. In order to avoid
conflicts between VRF allocated routing tables and those services, Zebra
proposes to define a chunk of routing tables to use by other services.
Allocation configuration can be done like below, with the range of the
chunk of routing tables to be used by the given service.
.. clicmd:: ip table range <STARTTABLENO> <ENDTABLENO>
.. _zebra-ecmp:
ECMP

View file

@ -1,6 +1,7 @@
!
hostname r1
password zebra
ip table range 500 600
interface r1-eth0
ip address 10.0.1.1/24
ipv6 address 1001::1/112

View file

@ -55,10 +55,9 @@
#define RT_TABLE_ID_UNRESERVED_MIN 1
#define RT_TABLE_ID_UNRESERVED_MAX 0xffffffff
struct table_manager tbl_mgr;
DEFINE_MGROUP(TABLE_MGR, "Table Manager");
DEFINE_MTYPE_STATIC(TABLE_MGR, TM_CHUNK, "Table Manager Chunk");
DEFINE_MTYPE_STATIC(TABLE_MGR, TM_TABLE, "Table Manager Context");
static void delete_table_chunk(void *val)
{
@ -68,12 +67,21 @@ static void delete_table_chunk(void *val)
/**
* Init table manager
*/
void table_manager_enable(ns_id_t ns_id)
void table_manager_enable(struct zebra_vrf *zvrf)
{
if (ns_id != NS_DEFAULT)
if (zvrf->tbl_mgr)
return;
tbl_mgr.lc_list = list_new();
tbl_mgr.lc_list->del = delete_table_chunk;
if (!vrf_is_backend_netns() && zvrf_id(zvrf) != VRF_DEFAULT) {
struct zebra_vrf *def = zebra_vrf_lookup_by_id(VRF_DEFAULT);
if (def)
zvrf->tbl_mgr = def->tbl_mgr;
return;
}
zvrf->tbl_mgr = XCALLOC(MTYPE_TM_TABLE, sizeof(struct table_manager));
zvrf->tbl_mgr->lc_list = list_new();
zvrf->tbl_mgr->lc_list->del = delete_table_chunk;
hook_register(zserv_client_close, release_daemon_table_chunks);
}
@ -89,14 +97,19 @@ void table_manager_enable(ns_id_t ns_id)
* @return Pointer to the assigned table chunk
*/
struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
uint32_t size)
uint32_t size,
struct zebra_vrf *zvrf)
{
struct table_manager_chunk *tmc;
struct listnode *node;
uint32_t start;
bool manual_conf = false;
if (!zvrf)
return NULL;
/* first check if there's one available */
for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) {
for (ALL_LIST_ELEMENTS_RO(zvrf->tbl_mgr->lc_list, node, tmc)) {
if (tmc->proto == NO_PROTO
&& tmc->end - tmc->start + 1 == size) {
tmc->proto = proto;
@ -109,17 +122,26 @@ struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
if (!tmc)
return NULL;
if (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end)
manual_conf = true;
/* table RT IDs range are [1;252] and [256;0xffffffff]
* - check if the requested range can be within the first range,
* otherwise elect second one
* - TODO : vrf-lites have their own table identifier.
* In that case, table_id should be removed from the table range.
*/
if (list_isempty(tbl_mgr.lc_list))
start = RT_TABLE_ID_UNRESERVED_MIN;
else
if (list_isempty(zvrf->tbl_mgr->lc_list)) {
if (!manual_conf)
start = RT_TABLE_ID_UNRESERVED_MIN;
else
start = zvrf->tbl_mgr->start;
} else
start = ((struct table_manager_chunk *)listgetdata(
listtail(tbl_mgr.lc_list)))->end + 1;
listtail(zvrf->tbl_mgr->lc_list)))
->end
+ 1;
if (!manual_conf) {
#if !defined(GNU_LINUX)
/* BSD systems
@ -127,25 +149,35 @@ struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
#else
/* Linux Systems
*/
/* if not enough room space between MIN and COMPAT,
* then begin after LOCAL
*/
if (start < RT_TABLE_ID_COMPAT && (size >
RT_TABLE_ID_COMPAT
- RT_TABLE_ID_UNRESERVED_MIN))
start = RT_TABLE_ID_LOCAL + 1;
/* if not enough room space between MIN and COMPAT,
* then begin after LOCAL
*/
if (start < RT_TABLE_ID_COMPAT
&& (size > RT_TABLE_ID_COMPAT - RT_TABLE_ID_UNRESERVED_MIN))
start = RT_TABLE_ID_LOCAL + 1;
#endif /* !def(GNU_LINUX) */
tmc->start = start;
if (RT_TABLE_ID_UNRESERVED_MAX - size + 1 < start) {
flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
"Reached max table id. Start/Size %u/%u", start, size);
XFREE(MTYPE_TM_CHUNK, tmc);
return NULL;
tmc->start = start;
if (RT_TABLE_ID_UNRESERVED_MAX - size + 1 < start) {
flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
"Reached max table id. Start/Size %u/%u",
start, size);
XFREE(MTYPE_TM_CHUNK, tmc);
return NULL;
}
} else {
tmc->start = start;
if (zvrf->tbl_mgr->end - size + 1 < start) {
flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
"Reached max table id. Start/Size %u/%u",
start, size);
XFREE(MTYPE_TM_CHUNK, tmc);
return NULL;
}
}
tmc->end = tmc->start + size - 1;
tmc->proto = proto;
tmc->instance = instance;
listnode_add(tbl_mgr.lc_list, tmc);
listnode_add(zvrf->tbl_mgr->lc_list, tmc);
return tmc;
}
@ -160,16 +192,23 @@ struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
* @return 0 on success, -1 otherwise
*/
int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start,
uint32_t end)
uint32_t end, struct zebra_vrf *zvrf)
{
struct listnode *node;
struct table_manager_chunk *tmc;
int ret = -1;
struct table_manager *tbl_mgr;
if (!zvrf)
return -1;
tbl_mgr = zvrf->tbl_mgr;
if (!tbl_mgr)
return ret;
/* check that size matches */
zlog_debug("Releasing table chunk: %u - %u", start, end);
/* find chunk and disown */
for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) {
for (ALL_LIST_ELEMENTS_RO(tbl_mgr->lc_list, node, tmc)) {
if (tmc->start != start)
continue;
if (tmc->end != end)
@ -208,24 +247,99 @@ int release_daemon_table_chunks(struct zserv *client)
struct table_manager_chunk *tmc;
int count = 0;
int ret;
struct vrf *vrf;
struct zebra_vrf *zvrf;
for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) {
if (tmc->proto == proto && tmc->instance == instance) {
ret = release_table_chunk(tmc->proto, tmc->instance,
tmc->start, tmc->end);
if (ret == 0)
count++;
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
zvrf = vrf->info;
if (!zvrf)
continue;
if (!vrf_is_backend_netns() && vrf->vrf_id != VRF_DEFAULT)
continue;
for (ALL_LIST_ELEMENTS_RO(zvrf->tbl_mgr->lc_list, node, tmc)) {
if (tmc->proto == proto && tmc->instance == instance) {
ret = release_table_chunk(
tmc->proto, tmc->instance, tmc->start,
tmc->end, zvrf);
if (ret == 0)
count++;
}
}
}
zlog_debug("%s: Released %d table chunks", __func__, count);
return count;
}
void table_manager_disable(ns_id_t ns_id)
static void table_range_add(struct zebra_vrf *zvrf, uint32_t start,
uint32_t end)
{
if (ns_id != NS_DEFAULT)
if (!zvrf->tbl_mgr)
return;
list_delete(&tbl_mgr.lc_list);
zvrf->tbl_mgr->start = start;
zvrf->tbl_mgr->end = end;
}
void table_manager_disable(struct zebra_vrf *zvrf)
{
if (!zvrf->tbl_mgr)
return;
if (!vrf_is_backend_netns() && zvrf_id(zvrf) != VRF_DEFAULT) {
zvrf->tbl_mgr = NULL;
return;
}
list_delete(&zvrf->tbl_mgr->lc_list);
XFREE(MTYPE_TM_TABLE, zvrf->tbl_mgr);
zvrf->tbl_mgr = NULL;
}
int table_manager_range(struct vty *vty, bool add, struct zebra_vrf *zvrf,
const char *start_table_str, const char *end_table_str)
{
uint32_t start;
uint32_t end;
if (add) {
if (!start_table_str || !end_table_str) {
vty_out(vty, "%% Labels not specified\n");
return CMD_WARNING_CONFIG_FAILED;
}
start = atoi(start_table_str);
end = atoi(end_table_str);
if (end < start) {
vty_out(vty, "%% End table is less than Start table\n");
return CMD_WARNING_CONFIG_FAILED;
}
#if !defined(GNU_LINUX)
/* BSD systems
*/
#else
/* Linux Systems
*/
if ((start >= RT_TABLE_ID_COMPAT && start <= RT_TABLE_ID_LOCAL)
|| (end >= RT_TABLE_ID_COMPAT
&& end <= RT_TABLE_ID_LOCAL)) {
vty_out(vty, "%% Values forbidden in range [%u;%u]\n",
RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL);
return CMD_WARNING_CONFIG_FAILED;
}
if (start < RT_TABLE_ID_COMPAT && end > RT_TABLE_ID_LOCAL) {
vty_out(vty,
"%% Range overlaps range [%u;%u] forbidden\n",
RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL);
return CMD_WARNING_CONFIG_FAILED;
}
#endif
if (zvrf->tbl_mgr
&& ((zvrf->tbl_mgr->start && zvrf->tbl_mgr->start != start)
|| (zvrf->tbl_mgr->end && zvrf->tbl_mgr->end != end))) {
vty_out(vty,
"%% New range will be taken into account at restart\n");
}
table_range_add(zvrf, start, end);
} else
table_range_add(zvrf, 0, 0);
return CMD_SUCCESS;
}

View file

@ -57,15 +57,20 @@ struct table_manager_chunk {
*/
struct table_manager {
struct list *lc_list;
uint32_t start;
uint32_t end;
};
void table_manager_enable(ns_id_t ns_id);
void table_manager_enable(struct zebra_vrf *zvrf);
struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
uint32_t size);
uint32_t size,
struct zebra_vrf *zvrf);
int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start,
uint32_t end);
uint32_t end, struct zebra_vrf *zvrf);
int release_daemon_table_chunks(struct zserv *client);
void table_manager_disable(ns_id_t ns_id);
void table_manager_disable(struct zebra_vrf *zvrf);
int table_manager_range(struct vty *vty, bool add, struct zebra_vrf *zvrf,
const char *min, const char *max);
#ifdef __cplusplus
}

View file

@ -2827,7 +2827,7 @@ static void zread_label_manager_request(ZAPI_HANDLER_ARGS)
}
static void zread_get_table_chunk(struct zserv *client, struct stream *msg,
vrf_id_t vrf_id)
struct zebra_vrf *zvrf)
{
struct stream *s;
uint32_t size;
@ -2839,7 +2839,7 @@ static void zread_get_table_chunk(struct zserv *client, struct stream *msg,
/* Get data. */
STREAM_GETL(s, size);
tmc = assign_table_chunk(client->proto, client->instance, size);
tmc = assign_table_chunk(client->proto, client->instance, size, zvrf);
if (!tmc)
flog_err(EC_ZEBRA_TM_CANNOT_ASSIGN_CHUNK,
"%s: Unable to assign Table Chunk of size %u",
@ -2848,13 +2848,14 @@ static void zread_get_table_chunk(struct zserv *client, struct stream *msg,
zlog_debug("Assigned Table Chunk %u - %u", tmc->start,
tmc->end);
/* send response back */
zsend_assign_table_chunk_response(client, vrf_id, tmc);
zsend_assign_table_chunk_response(client, zvrf_id(zvrf), tmc);
stream_failure:
return;
}
static void zread_release_table_chunk(struct zserv *client, struct stream *msg)
static void zread_release_table_chunk(struct zserv *client, struct stream *msg,
struct zebra_vrf *zvrf)
{
struct stream *s;
uint32_t start, end;
@ -2866,7 +2867,7 @@ static void zread_release_table_chunk(struct zserv *client, struct stream *msg)
STREAM_GETL(s, start);
STREAM_GETL(s, end);
release_table_chunk(client->proto, client->instance, start, end);
release_table_chunk(client->proto, client->instance, start, end, zvrf);
stream_failure:
return;
@ -2886,9 +2887,9 @@ static void zread_table_manager_request(ZAPI_HANDLER_ARGS)
return;
}
if (hdr->command == ZEBRA_GET_TABLE_CHUNK)
zread_get_table_chunk(client, msg, zvrf_id(zvrf));
zread_get_table_chunk(client, msg, zvrf);
else if (hdr->command == ZEBRA_RELEASE_TABLE_CHUNK)
zread_release_table_chunk(client, msg);
zread_release_table_chunk(client, msg, zvrf);
}
}

View file

@ -127,9 +127,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
route_read(zns);
kernel_read_pbr_rules(zns);
/* Initiate Table Manager per ZNS */
table_manager_enable(ns_id);
return 0;
}
@ -142,8 +139,6 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete)
kernel_terminate(zns, complete);
table_manager_disable(zns->ns_id);
zns->ns_id = NS_DEFAULT;
return 0;

View file

@ -44,6 +44,7 @@
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_vrf_clippy.c"
#endif
#include "zebra/table_manager.h"
static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
safi_t safi);
@ -113,6 +114,10 @@ static int zebra_vrf_new(struct vrf *vrf)
otable_init(&zvrf->other_tables);
router_id_init(zvrf);
/* Initiate Table Manager per ZNS */
table_manager_enable(zvrf);
return 0;
}
@ -176,6 +181,8 @@ static int zebra_vrf_disable(struct vrf *vrf)
zlog_debug("VRF %s id %u is now inactive", zvrf_name(zvrf),
zvrf_id(zvrf));
table_manager_disable(zvrf);
/* Stop any VxLAN-EVPN processing. */
zebra_vxlan_vrf_disable(zvrf);
@ -503,6 +510,12 @@ static int vrf_config_write(struct vty *vty)
if (zvrf->zebra_rnh_ipv6_default_route)
vty_out(vty, "ipv6 nht resolve-via-default\n");
if (zvrf->tbl_mgr
&& (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end))
vty_out(vty, "ip table range %u %u\n",
zvrf->tbl_mgr->start,
zvrf->tbl_mgr->end);
} else {
vty_frame(vty, "vrf %s\n", zvrf_name(zvrf));
if (zvrf->l3vni)
@ -517,6 +530,12 @@ static int vrf_config_write(struct vty *vty)
if (zvrf->zebra_rnh_ipv6_default_route)
vty_out(vty, " ipv6 nht resolve-via-default\n");
if (zvrf->tbl_mgr && vrf_is_backend_netns()
&& (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end))
vty_out(vty, " ip table range %u %u\n",
zvrf->tbl_mgr->start,
zvrf->tbl_mgr->end);
}

View file

@ -177,6 +177,8 @@ struct zebra_vrf {
uint64_t lsp_installs;
uint64_t lsp_removals;
struct table_manager *tbl_mgr;
#if defined(HAVE_RTADV)
struct rtadv rtadv;
#endif /* HAVE_RTADV */

View file

@ -59,6 +59,7 @@
#include "northbound_cli.h"
#include "zebra/zebra_nb.h"
#include "zebra/kernel_netlink.h"
#include "zebra/table_manager.h"
extern int allow_delete;
@ -4298,6 +4299,31 @@ DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf,
#endif /* HAVE_NETLINK */
DEFUN(ip_table_range, ip_table_range_cmd,
"[no] ip table range (1-4294967295) (1-4294967295)",
NO_STR IP_STR
"table configuration\n"
"Configure table range\n"
"Start Routing Table\n"
"End Routing Table\n")
{
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
if (!zvrf)
return CMD_WARNING;
if (zvrf_id(zvrf) != VRF_DEFAULT && !vrf_is_backend_netns()) {
vty_out(vty,
"VRF subcommand does not make any sense in l3mdev based vrf's\n");
return CMD_WARNING;
}
if (strmatch(argv[0]->text, "no"))
return table_manager_range(vty, false, zvrf, NULL, NULL);
return table_manager_range(vty, true, zvrf, argv[3]->arg, argv[4]->arg);
}
/* IP node for static routes. */
static int zebra_ip_config(struct vty *vty);
static struct cmd_node ip_node = {
@ -4446,6 +4472,9 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd);
install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd);
install_element(CONFIG_NODE, &ip_table_range_cmd);
install_element(VRF_NODE, &ip_table_range_cmd);
#ifdef HAVE_NETLINK
install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd);
install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd);