From 0e3b6a926a98a028fa9ed8a8a1d00b429f547c03 Mon Sep 17 00:00:00 2001 From: Emanuele Di Pascale Date: Wed, 12 Jun 2019 16:33:12 +0200 Subject: [PATCH] lib, zebra: support label chunk requests for SRGB For SRGB, we need to support chunk requests starting at a specific point in the label space, rather than just asking for any sufficiently large chunk. To this purpose, we extend the label manager api to request a chunk with a base value; if the base is set to 0, the label manager will behave as it currently does, i.e. fetching the first free chunk big enough to satisfy the request. update all the existing calls to get chunks from the label manager so that they use MPLS_LABEL_BASE_ANY as the base for the requested chunk Signed-off-by: Emanuele Di Pascale --- bgpd/bgp_labelpool.c | 7 +- ldpd/lde.c | 3 +- lib/mpls.h | 1 + lib/zclient.c | 8 ++- lib/zclient.h | 10 ++- tests/test_lblmgr.c | 3 +- zebra/label_manager.c | 156 +++++++++++++++++++++++++++++++++++++----- zebra/label_manager.h | 3 +- zebra/zapi_msg.c | 5 +- 9 files changed, 165 insertions(+), 31 deletions(-) diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c index 7518f02ac..feda0328b 100644 --- a/bgpd/bgp_labelpool.c +++ b/bgpd/bgp_labelpool.c @@ -29,6 +29,7 @@ #include "skiplist.h" #include "workqueue.h" #include "zclient.h" +#include "mpls.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_labelpool.h" @@ -391,7 +392,8 @@ void bgp_lp_get( if (lp_fifo_count(&lp->requests) > lp->pending_count) { if (!zclient || zclient->sock < 0) return; - if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) + if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE, + MPLS_LABEL_BASE_ANY)) lp->pending_count += LP_CHUNK_SIZE; } } @@ -552,7 +554,8 @@ void bgp_lp_event_zebra_up(void) return; } - zclient_send_get_label_chunk(zclient, 0, labels_needed); + zclient_send_get_label_chunk(zclient, 0, labels_needed, + MPLS_LABEL_BASE_ANY); lp->pending_count = labels_needed; /* diff --git a/ldpd/lde.c b/ldpd/lde.c index 2aa96546e..0ddf4f07d 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -1660,7 +1660,8 @@ lde_get_label_chunk(void) uint32_t start, end; debug_labels("getting label chunk (size %u)", CHUNK_SIZE); - ret = lm_get_label_chunk(zclient_sync, 0, CHUNK_SIZE, &start, &end); + ret = lm_get_label_chunk(zclient_sync, 0, MPLS_LABEL_BASE_ANY, + CHUNK_SIZE, &start, &end); if (ret < 0) { log_warnx("Error getting label chunk!"); return -1; diff --git a/lib/mpls.h b/lib/mpls.h index b140c8e31..d7b56c47b 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -54,6 +54,7 @@ extern "C" { #define MPLS_LABEL_RESERVED_MAX 15 #define MPLS_LABEL_UNRESERVED_MIN 16 #define MPLS_LABEL_UNRESERVED_MAX 1048575 +#define MPLS_LABEL_BASE_ANY 0 /* Default min and max SRGB label range */ /* Even if the SRGB allows to manage different Label space between routers, diff --git a/lib/zclient.c b/lib/zclient.c index e9b4f5a58..c02ae5d0e 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1995,10 +1995,11 @@ int lm_label_manager_connect(struct zclient *zclient, int async) * @param zclient Zclient used to connect to label manager (zebra) * @param keep Avoid garbage collection * @param chunk_size Amount of labels requested + * @param base Base for the label chunk. if MPLS_LABEL_BASE_ANY we do not care * @result 0 on success, -1 otherwise */ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, - uint32_t chunk_size) + uint32_t chunk_size, uint32_t base) { struct stream *s; @@ -2018,6 +2019,7 @@ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, stream_putw(s, zclient->instance); stream_putc(s, keep); stream_putl(s, chunk_size); + stream_putl(s, base); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -2038,7 +2040,7 @@ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, * @param end To write last assigned chunk label to * @result 0 on success, -1 otherwise */ -int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, +int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base, uint32_t chunk_size, uint32_t *start, uint32_t *end) { int ret; @@ -2063,6 +2065,8 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, stream_putc(s, keep); /* chunk size */ stream_putl(s, chunk_size); + /* requested chunk base */ + stream_putl(s, base); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); diff --git a/lib/zclient.h b/lib/zclient.h index d65173868..be2ef69dc 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -609,15 +609,13 @@ extern struct interface *zebra_interface_link_params_read(struct stream *s, vrf_id_t vrf_id); extern size_t zebra_interface_link_params_write(struct stream *, struct interface *); -extern int zclient_send_get_label_chunk( - struct zclient *zclient, - uint8_t keep, - uint32_t chunk_size); +extern int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, + uint32_t chunk_size, uint32_t base); extern int lm_label_manager_connect(struct zclient *zclient, int async); extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, - uint32_t chunk_size, uint32_t *start, - uint32_t *end); + uint32_t base, uint32_t chunk_size, + uint32_t *start, uint32_t *end); extern int lm_release_label_chunk(struct zclient *zclient, uint32_t start, uint32_t end); extern int tm_table_manager_connect(struct zclient *zclient); diff --git a/tests/test_lblmgr.c b/tests/test_lblmgr.c index e71e680fa..530375823 100644 --- a/tests/test_lblmgr.c +++ b/tests/test_lblmgr.c @@ -80,7 +80,8 @@ static int zebra_send_get_label_chunk() printf("Ask for label chunk \n"); - ret = lm_get_label_chunk(zclient, KEEP, CHUNK_SIZE, &start, &end); + ret = lm_get_label_chunk(zclient, KEEP, MPLS_LABEL_BASE_ANY, CHUNK_SIZE, + &start, &end); if (ret != 0) { fprintf(stderr, "Error %d requesting label chunk %s\n", ret, strerror(errno)); diff --git a/zebra/label_manager.c b/zebra/label_manager.c index 8295e461c..6de94e63d 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -386,8 +386,112 @@ void label_manager_init(char *lm_zserv_path) hook_register(zserv_client_close, release_daemon_label_chunks); } +/* alloc and fill a label chunk */ +static struct label_manager_chunk * +create_label_chunk(uint8_t proto, unsigned short instance, uint8_t keep, + uint32_t start, uint32_t end) +{ + /* alloc chunk, fill it and return it */ + struct label_manager_chunk *lmc = + XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk)); + + lmc->start = start; + lmc->end = end; + lmc->proto = proto; + lmc->instance = instance; + lmc->keep = keep; + + return lmc; +} + +/* attempt to get a specific label chunk */ +struct label_manager_chunk * +assign_specific_label_chunk(uint8_t proto, unsigned short instance, + uint8_t keep, uint32_t size, uint32_t base) +{ + struct label_manager_chunk *lmc; + struct listnode *node, *next = NULL; + struct listnode *first_node = NULL; + struct listnode *last_node = NULL; + struct listnode *insert_node = NULL; + + /* precompute last label from base and size */ + uint32_t end = base + size - 1; + + /* sanities */ + if ((base < MPLS_LABEL_UNRESERVED_MIN) + || (end > MPLS_LABEL_UNRESERVED_MAX)) { + zlog_err("Invalid LM request arguments: base: %u, size: %u", + base, size); + return NULL; + } + + /* Scan the existing chunks to see if the requested range of labels + * falls inside any of such chunks */ + for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { + + /* skip chunks for labels < base */ + if (base > lmc->end) + continue; + + /* requested range is not covered by any existing, free chunk. + * Therefore, need to insert a chunk */ + if ((end < lmc->start) && !first_node) { + insert_node = node; + break; + } + + if (!first_node) + first_node = node; + + /* if chunk is used, cannot honor request */ + if (lmc->proto != NO_PROTO) + return NULL; + + if (end < lmc->end) { + last_node = node; + break; + } + } + + /* insert chunk between existing chunks */ + if (insert_node) { + lmc = create_label_chunk(proto, instance, keep, base, end); + listnode_add_before(lbl_mgr.lc_list, insert_node, lmc); + return lmc; + } + + if (first_node) { + /* get node past the last one, if there */ + if (last_node) + last_node = listnextnode(last_node); + + /* delete node coming after the above chunk whose labels are + * included in the previous one */ + for (node = first_node; node && (node != last_node); + node = next) { + next = listnextnode(node); + list_delete_node(lbl_mgr.lc_list, node); + } + + lmc = create_label_chunk(proto, instance, keep, base, end); + if (last_node) + listnode_add_before(lbl_mgr.lc_list, last_node, lmc); + else + listnode_add(lbl_mgr.lc_list, lmc); + + return lmc; + } else { + /* create a new chunk past all the existing ones and link at + * tail */ + lmc = create_label_chunk(proto, instance, keep, base, end); + listnode_add(lbl_mgr.lc_list, lmc); + return lmc; + } +} + /** - * Core function, assigns label cunks + * Core function, assigns label chunks * * It first searches through the list to check if there's one available * (previously released). Otherwise it creates and assigns a new one @@ -395,15 +499,26 @@ void label_manager_init(char *lm_zserv_path) * @param proto Daemon protocol of client, to identify the owner * @param instance Instance, to identify the owner * @param keep If set, avoid garbage collection - * @para size Size of the label chunk - * @return Pointer to the assigned label chunk + * @param size Size of the label chunk + * @param base Desired starting label of the chunk; if MPLS_LABEL_BASE_ANY it does not apply + * @return Pointer to the assigned label chunk, or NULL if the request could not be satisfied */ struct label_manager_chunk *assign_label_chunk(uint8_t proto, unsigned short instance, - uint8_t keep, uint32_t size) + uint8_t keep, uint32_t size, + uint32_t base) { struct label_manager_chunk *lmc; struct listnode *node; + uint32_t prev_end = 0; + + /* handle chunks request with a specific base label */ + if (base != MPLS_LABEL_BASE_ANY) + return assign_specific_label_chunk(proto, instance, keep, size, + base); + + /* appease scan-build, who gets confused by the use of macros */ + assert(lbl_mgr.lc_list); /* first check if there's one available */ for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { @@ -414,35 +529,44 @@ struct label_manager_chunk *assign_label_chunk(uint8_t proto, lmc->keep = keep; 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)) { + lmc = create_label_chunk(proto, instance, keep, + prev_end + 1, prev_end + size); + listnode_add_before(lbl_mgr.lc_list, node, lmc); + return lmc; + } + prev_end = lmc->end; } /* otherwise create a new one */ - lmc = XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk)); + uint32_t start_free; if (list_isempty(lbl_mgr.lc_list)) - lmc->start = MPLS_LABEL_UNRESERVED_MIN; + start_free = MPLS_LABEL_UNRESERVED_MIN; else - lmc->start = ((struct label_manager_chunk *)listgetdata( + start_free = ((struct label_manager_chunk *)listgetdata( listtail(lbl_mgr.lc_list))) ->end + 1; - if (lmc->start > MPLS_LABEL_UNRESERVED_MAX - size + 1) { + + if (start_free > MPLS_LABEL_UNRESERVED_MAX - size + 1) { flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS, - "Reached max labels. Start: %u, size: %u", lmc->start, + "Reached max labels. Start: %u, size: %u", start_free, size); - XFREE(MTYPE_LM_CHUNK, lmc); return NULL; } - lmc->end = lmc->start + size - 1; - lmc->proto = proto; - lmc->instance = instance; - lmc->keep = keep; - listnode_add(lbl_mgr.lc_list, lmc); + /* create chunk and link at tail */ + lmc = create_label_chunk(proto, instance, keep, start_free, + start_free + size - 1); + listnode_add(lbl_mgr.lc_list, lmc); return lmc; } /** - * Core function, release no longer used label cunks + * Core function, release no longer used label chunks * * @param proto Daemon protocol of client, to identify the owner * @param instance Instance, to identify the owner diff --git a/zebra/label_manager.h b/zebra/label_manager.h index 3ea89fbfc..f1b7d050c 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -72,7 +72,8 @@ int zread_relay_label_manager_request(int cmd, struct zserv *zserv, void label_manager_init(char *lm_zserv_path); struct label_manager_chunk *assign_label_chunk(uint8_t proto, unsigned short instance, - uint8_t keep, uint32_t size); + uint8_t keep, uint32_t size, + uint32_t base); int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, uint32_t end); int release_daemon_label_chunks(struct zserv *client); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 61200806b..f1081def9 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1927,7 +1927,7 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, { struct stream *s; uint8_t keep; - uint32_t size; + uint32_t size, base; struct label_manager_chunk *lmc; uint8_t proto; unsigned short instance; @@ -1940,8 +1940,9 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, STREAM_GETW(s, instance); STREAM_GETC(s, keep); STREAM_GETL(s, size); + STREAM_GETL(s, base); - lmc = assign_label_chunk(proto, instance, keep, size); + lmc = assign_label_chunk(proto, instance, keep, size, base); if (!lmc) flog_err( EC_ZEBRA_LM_CANNOT_ASSIGN_CHUNK, -- 2.39.2