Skip to content

Commit

Permalink
bgpd: add 'mpls label dynamic-block' command
Browse files Browse the repository at this point in the history
Sometimes, a manually configured label value on services like BGP
may not be available, as the label value is used by other CP daemons.
The below configuration steps show that the BGP configuration
remains valid until the next reboot.

1. bgp is configured with l3vpn to get label 60. The manual label
[60] is reserved in the zebra label manager.
2. ldp is configured: the dynamic label range [61;189] is reserved
in the zebra label manager.
3. the configuration is saved, and a restart happens. The bgp
label could not be obtained, because the ldp configuration has
obtained first the [16;80] label range. The BGP request failed.

The new command 'mpls label dynamic-block' configures the range
where the dynamic label values can be picked up. The user can
configure a BGP label value outside the range of that dynamic
block, and will be guaranteed to not conflict with other CP
daemons requesting dynamic chunks of memory.

This command introduces the new command. Each label chunk is said to
be dynamic, when no base is given in the request. The assign algorithm
is modified.

Signed-off-by: Philippe Guibert <[email protected]>
  • Loading branch information
pguibert6WIND committed Oct 2, 2023
1 parent bcc05ad commit 7eef99d
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 25 deletions.
2 changes: 1 addition & 1 deletion tests/zebra/test_lm_plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static int lm_get_chunk_pi(struct label_manager_chunk **lmc,
uint32_t base, vrf_id_t vrf_id)
{
if (base == 0)
*lmc = create_label_chunk(10, 55, 0, 1, 50, 50 + size);
*lmc = create_label_chunk(10, 55, 0, 1, 50, 50 + size, true);
else
*lmc = assign_label_chunk(10, 55, 0, 1, size, base);

Expand Down
168 changes: 147 additions & 21 deletions zebra/label_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,25 @@ DEFINE_HOOK(lm_get_chunk,
DEFINE_HOOK(lm_release_chunk,
(struct zserv *client, uint32_t start, uint32_t end),
(client, start, end));
DEFINE_HOOK(lm_write_label_block_config,
(struct vty * vty, struct zebra_vrf *zvrf), (vty, zvrf));
DEFINE_HOOK(lm_cbs_inited, (), ());

/* define wrappers to be called in zapi_msg.c (as hooks must be called in
* source file where they were defined)
static bool is_label_chunk_in_dynamic_range(uint32_t start, uint32_t end,
uint32_t *ref_start,
uint32_t *ref_end)
{
if (*ref_start == MPLS_LABEL_UNRESERVED_MIN &&
*ref_end == MPLS_LABEL_MAX)
return true;
if (start >= *ref_start && start <= *ref_end && end <= *ref_end &&
end >= *ref_start)
return true;
return false;
}

/* define wrappers to be called in zapi_msg.c or zebra_mpls_vty.c (as hooks
* must be called in source file where they were defined)
*/
void lm_client_connect_call(struct zserv *client, vrf_id_t vrf_id)
{
Expand All @@ -71,6 +86,11 @@ void lm_release_chunk_call(struct zserv *client, uint32_t start, uint32_t end)
hook_call(lm_release_chunk, client, start, end);
}

int lm_write_label_block_config_call(struct vty *vty, struct zebra_vrf *zvrf)
{
return hook_call(lm_write_label_block_config, vty, zvrf);
}

/* forward declarations of the static functions to be used for some hooks */
static int label_manager_connect(struct zserv *client, vrf_id_t vrf_id);
static int label_manager_disconnect(struct zserv *client);
Expand All @@ -80,6 +100,8 @@ static int label_manager_get_chunk(struct label_manager_chunk **lmc,
vrf_id_t vrf_id);
static int label_manager_release_label_chunk(struct zserv *client,
uint32_t start, uint32_t end);
static int label_manager_write_label_block_config(struct vty *vty,
struct zebra_vrf *zvrf);

void delete_label_chunk(void *val)
{
Expand Down Expand Up @@ -138,13 +160,17 @@ void lm_hooks_register(void)
hook_register(lm_client_disconnect, label_manager_disconnect);
hook_register(lm_get_chunk, label_manager_get_chunk);
hook_register(lm_release_chunk, label_manager_release_label_chunk);
hook_register(lm_write_label_block_config,
label_manager_write_label_block_config);
}
void lm_hooks_unregister(void)
{
hook_unregister(lm_client_connect, label_manager_connect);
hook_unregister(lm_client_disconnect, label_manager_disconnect);
hook_unregister(lm_get_chunk, label_manager_get_chunk);
hook_unregister(lm_release_chunk, label_manager_release_label_chunk);
hook_unregister(lm_write_label_block_config,
label_manager_write_label_block_config);
}

DEFPY(show_label_table, show_label_table_cmd, "show debugging label-table",
Expand All @@ -163,13 +189,79 @@ DEFPY(show_label_table, show_label_table_cmd, "show debugging label-table",
return CMD_SUCCESS;
}

DEFPY(mpls_label_dynamic_block, mpls_label_dynamic_block_cmd,
"[no$no] mpls label dynamic-block (16-1048575)$start (16-1048575)$end",
NO_STR MPLS_STR "Label configuration\n"
"Configure dynamic label block\n"
"Start label\n"
"End label\n")
{
bool must_restart = false;
bool must_abort = false;
struct listnode *node;
struct label_manager_chunk *lmc;

/* unset dynamic range */
if (no) {
lbl_mgr.dynamic_block_start = MPLS_LABEL_UNRESERVED_MIN;
lbl_mgr.dynamic_block_end = MPLS_LABEL_MAX;
return CMD_SUCCESS;
}

for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
if (lmc->proto == NO_PROTO)
continue;
if (lmc->is_dynamic == false &&
is_label_chunk_in_dynamic_range(lmc->start, lmc->end,
(uint32_t *)&start,
(uint32_t *)&end)) {
vty_out(vty,
"%% static label chunk conflict (%u/%u), aborting\n",
lmc->start, lmc->end);
must_abort = true;
} else if (lmc->start < (uint32_t)start ||
lmc->start > (uint32_t)end ||
lmc->end > (uint32_t)end ||
lmc->end < (uint32_t)start) {
vty_out(vty,
"%% dynamic label chunk out of range proto %s (%u/%u), need restart\n",
zebra_route_string(lmc->proto), lmc->start,
lmc->end);
must_restart = true;
}
}
if (must_abort)
return CMD_WARNING_CONFIG_FAILED;
if (must_restart)
vty_out(vty,
"%% Please restart the relevant daemons to update the dynamic blocks\n");
lbl_mgr.dynamic_block_start = start;
lbl_mgr.dynamic_block_end = end;
return CMD_SUCCESS;
}

static int label_manager_write_label_block_config(struct vty *vty,
struct zebra_vrf *zvrf)
{
if (zvrf_id(zvrf) != VRF_DEFAULT)
return 0;
if (lbl_mgr.dynamic_block_start == MPLS_LABEL_UNRESERVED_MIN &&
lbl_mgr.dynamic_block_end == MPLS_LABEL_MAX)
return 0;
vty_out(vty, "mpls label dynamic-block %u %u\n",
lbl_mgr.dynamic_block_start, lbl_mgr.dynamic_block_end);
return 1;
}

/**
* Init label manager (or proxy to an external one)
*/
void label_manager_init(void)
{
lbl_mgr.lc_list = list_new();
lbl_mgr.lc_list->del = delete_label_chunk;
lbl_mgr.dynamic_block_start = MPLS_LABEL_UNRESERVED_MIN;
lbl_mgr.dynamic_block_end = MPLS_LABEL_MAX;
hook_register(zserv_client_close, lm_client_disconnect_cb);

/* register default hooks for the label manager actions */
Expand All @@ -179,12 +271,13 @@ void label_manager_init(void)
hook_call(lm_cbs_inited);

install_element(VIEW_NODE, &show_label_table_cmd);
install_element(CONFIG_NODE, &mpls_label_dynamic_block_cmd);
}

/* alloc and fill a label chunk */
struct label_manager_chunk *
create_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
uint8_t keep, uint32_t start, uint32_t end)
uint8_t keep, uint32_t start, uint32_t end, bool is_dynamic)
{
/* alloc chunk, fill it and return it */
struct label_manager_chunk *lmc =
Expand All @@ -196,6 +289,7 @@ create_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
lmc->instance = instance;
lmc->session_id = session_id;
lmc->keep = keep;
lmc->is_dynamic = is_dynamic;

return lmc;
}
Expand Down Expand Up @@ -254,7 +348,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance,
/* insert chunk between existing chunks */
if (insert_node) {
lmc = create_label_chunk(proto, instance, session_id, keep,
base, end);
base, end, false);
listnode_add_before(lbl_mgr.lc_list, insert_node, lmc);
return lmc;
}
Expand All @@ -277,7 +371,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance,
}

lmc = create_label_chunk(proto, instance, session_id, keep,
base, end);
base, end, false);
if (last_node)
listnode_add_before(lbl_mgr.lc_list, last_node, lmc);
else
Expand All @@ -288,7 +382,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance,
/* create a new chunk past all the existing ones and link at
* tail */
lmc = create_label_chunk(proto, instance, session_id, keep,
base, end);
base, end, false);
listnode_add(lbl_mgr.lc_list, lmc);
return lmc;
}
Expand All @@ -314,6 +408,7 @@ assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
struct label_manager_chunk *lmc;
struct listnode *node;
uint32_t prev_end = MPLS_LABEL_UNRESERVED_MIN;
struct label_manager_chunk *lmc_dynamic_block_last = NULL;

/* handle chunks request with a specific base label */
if (base != MPLS_LABEL_BASE_ANY)
Expand All @@ -325,47 +420,78 @@ assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,

/* first check if there's one available */
for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
if (lmc->proto == NO_PROTO
&& lmc->end - lmc->start + 1 == size) {
if (lmc->proto == NO_PROTO &&
lmc->end - lmc->start + 1 == size &&
is_label_chunk_in_dynamic_range(lmc->start, lmc->end,
&lbl_mgr.dynamic_block_start,
&lbl_mgr.dynamic_block_end)) {
lmc->proto = proto;
lmc->instance = instance;
lmc->session_id = session_id;
lmc->keep = keep;
lmc->is_dynamic = true;
return lmc;
}
/* check if we hadve a "hole" behind us that we can squeeze into
*/
if ((lmc->start > prev_end) && (lmc->start - prev_end > size)) {
if ((lmc->start > prev_end) && (lmc->start - prev_end > size) &&
is_label_chunk_in_dynamic_range(prev_end + 1,
prev_end + size,
&lbl_mgr.dynamic_block_start,
&lbl_mgr.dynamic_block_end)) {
lmc = create_label_chunk(proto, instance, session_id,
keep, prev_end + 1,
prev_end + size);
prev_end + size, true);
listnode_add_before(lbl_mgr.lc_list, node, lmc);
return lmc;
}
prev_end = lmc->end;

if (lmc->start < lbl_mgr.dynamic_block_start &&
lmc->end < lbl_mgr.dynamic_block_start)
continue;

/* check if we have a chunk that takes all the pool size */
if ((lmc->start == lbl_mgr.dynamic_block_start &&
lmc->end == lbl_mgr.dynamic_block_end &&
lmc->proto != NO_PROTO)) {
flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS,
"A chunk is already inuse by %s ( Start: %u, size: %u",
zebra_route_string(lmc->proto), lmc->start,
lmc->end - lmc->start + 1);
return NULL;
}
if (lmc->end > lbl_mgr.dynamic_block_end)
continue;
lmc_dynamic_block_last = lmc;
}
/* otherwise create a new one */
uint32_t start_free;

if (list_isempty(lbl_mgr.lc_list))
start_free = MPLS_LABEL_UNRESERVED_MIN;
if (list_isempty(lbl_mgr.lc_list) || lmc_dynamic_block_last == NULL)
start_free = lbl_mgr.dynamic_block_start;
else if (lmc_dynamic_block_last)
start_free = lmc_dynamic_block_last->end + 1;
else
start_free = ((struct label_manager_chunk *)listgetdata(
listtail(lbl_mgr.lc_list)))
->end
+ 1;

if (start_free > MPLS_LABEL_UNRESERVED_MAX - size + 1) {
->end +
1;
if (start_free > lbl_mgr.dynamic_block_end - size + 1) {
flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS,
"Reached max labels. Start: %u, size: %u", start_free,
size);
return NULL;
}

/* create chunk and link at tail */
lmc = create_label_chunk(proto, instance, session_id, keep, start_free,
start_free + size - 1);
listnode_add(lbl_mgr.lc_list, lmc);
if (is_label_chunk_in_dynamic_range(start_free, start_free + size,
&lbl_mgr.dynamic_block_start,
&lbl_mgr.dynamic_block_end)) {
/* create chunk and link at tail */
lmc = create_label_chunk(proto, instance, session_id, keep,
start_free, start_free + size - 1,
true);
listnode_add(lbl_mgr.lc_list, lmc);
}
return lmc;
}

Expand Down
12 changes: 9 additions & 3 deletions zebra/label_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct label_manager_chunk {
unsigned short instance;
uint32_t session_id;
uint8_t keep;
uint8_t is_dynamic; /* Tell if chunk is dynamic or static */
uint32_t start; /* First label of the chunk */
uint32_t end; /* Last label of the chunk */
};
Expand All @@ -61,18 +62,21 @@ DECLARE_HOOK(lm_get_chunk,
DECLARE_HOOK(lm_release_chunk,
(struct zserv *client, uint32_t start, uint32_t end),
(client, start, end));
DECLARE_HOOK(lm_write_label_block_config,
(struct vty * vty, struct zebra_vrf *zvrf), (vty, zvrf));
DECLARE_HOOK(lm_cbs_inited, (), ());


/* declare wrappers to be called in zapi_msg.c (as hooks must be called in
* source file where they were defined)
/* declare wrappers to be called in zapi_msg.c or zebra_mpls_vty.c (as hooks
* must be called in source file where they were defined)
*/
void lm_client_connect_call(struct zserv *client, vrf_id_t vrf_id);
void lm_get_chunk_call(struct label_manager_chunk **lmc, struct zserv *client,
uint8_t keep, uint32_t size, uint32_t base,
vrf_id_t vrf_id);
void lm_release_chunk_call(struct zserv *client, uint32_t start,
uint32_t end);
int lm_write_label_block_config_call(struct vty *vty, struct zebra_vrf *zvrf);

/* API for an external LM to return responses for requests */
int lm_client_connect_response(uint8_t proto, uint16_t instance,
Expand All @@ -84,7 +88,7 @@ int lm_get_chunk_response(struct label_manager_chunk *lmc, struct zserv *client,
/* convenience function to allocate an lmc to be consumed by the above API */
struct label_manager_chunk *
create_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
uint8_t keep, uint32_t start, uint32_t end);
uint8_t keep, uint32_t start, uint32_t end, bool is_dynamic);
void delete_label_chunk(void *val);

/* register/unregister callbacks for hooks */
Expand All @@ -97,6 +101,8 @@ void lm_hooks_unregister(void);
*/
struct label_manager {
struct list *lc_list;
uint32_t dynamic_block_start;
uint32_t dynamic_block_end;
};

void label_manager_init(void);
Expand Down
3 changes: 3 additions & 0 deletions zebra/zebra_mpls_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "zebra/zebra_rnh.h"
#include "zebra/redistribute.h"
#include "zebra/zebra_routemap.h"
#include "zebra/label_manager.h"

static int zebra_mpls_transit_lsp(struct vty *vty, int add_cmd,
const char *inlabel_str, const char *gate_str,
Expand Down Expand Up @@ -270,6 +271,8 @@ static int zebra_mpls_config(struct vty *vty)
write += zebra_mpls_write_lsp_config(vty, zvrf);
write += zebra_mpls_write_fec_config(vty, zvrf);
write += zebra_mpls_write_label_block_config(vty, zvrf);
write += lm_write_label_block_config_call(vty, zvrf);

return write;
}

Expand Down

0 comments on commit 7eef99d

Please sign in to comment.