]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: dynamic mpls label pool
authorG. Paul Ziemba <paulz@labn.net>
Sat, 7 Apr 2018 18:13:07 +0000 (11:13 -0700)
committerG. Paul Ziemba <paulz@labn.net>
Thu, 12 Apr 2018 06:18:28 +0000 (23:18 -0700)
MPLS label pool backed by allocations from the zebra label manager.

A caller requests a label (e.g., in support of an "auto" label
specification in the CLI) via lp_get(), supplying a unique ID and
a callback function. The callback function is invoked at a later
time with the unique ID and a label value to inform the requestor
of the assigned label.

Requestors may release their labels back to the pool via lp_release().

The label pool is stocked with labels allocated by the zebra label
manager. The interaction with zebra is asynchronous so that bgpd
is not blocked while awaiting a label allocation from zebra.

The label pool implementation allows for bgpd operation before (or
without) zebra, and gracefully handles loss and reconnection of
zebra. Of course, before initial connection with zebra, no labels
are assigned to requestors. If the zebra connection is lost and
regained, callbacks to requestors will invalidate old assignments
and then assign new labels.

Signed-off-by: G. Paul Ziemba <paulz@labn.net>
bgpd/Makefile.am
bgpd/bgp_debug.c
bgpd/bgp_debug.h
bgpd/bgp_labelpool.c [new file with mode: 0644]
bgpd/bgp_labelpool.h [new file with mode: 0644]
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/bgpd.h
lib/zclient.c
lib/zclient.h

index 61d46dfcb96a29c8d422f94342186c50c943e443..a2880b7b94801d0e56f22950d9bc99dac8254333 100644 (file)
@@ -87,7 +87,7 @@ libbgp_a_SOURCES = \
        bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \
        bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \
        bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \
-       bgp_flowspec_vty.c
+       bgp_flowspec_vty.c bgp_labelpool.c
 
 noinst_HEADERS = \
        bgp_memory.h \
@@ -100,7 +100,8 @@ noinst_HEADERS = \
        bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \
        $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \
         bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \
-       bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h
+       bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h \
+       bgp_labelpool.h
 
 bgpd_SOURCES = bgp_main.c
 bgpd_LDADD = libbgp.a  $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@
index ae4ff5d67e3f0e55b33a71d0016045a1843c03c5..29ac5f520de9ac7a979426db4a7b40db9659be3e 100644 (file)
@@ -58,6 +58,7 @@ unsigned long conf_bgp_debug_nht;
 unsigned long conf_bgp_debug_update_groups;
 unsigned long conf_bgp_debug_vpn;
 unsigned long conf_bgp_debug_flowspec;
+unsigned long conf_bgp_debug_labelpool;
 
 unsigned long term_bgp_debug_as4;
 unsigned long term_bgp_debug_neighbor_events;
@@ -73,6 +74,7 @@ unsigned long term_bgp_debug_nht;
 unsigned long term_bgp_debug_update_groups;
 unsigned long term_bgp_debug_vpn;
 unsigned long term_bgp_debug_flowspec;
+unsigned long term_bgp_debug_labelpool;
 
 struct list *bgp_debug_neighbor_events_peers = NULL;
 struct list *bgp_debug_keepalive_peers = NULL;
@@ -1655,6 +1657,44 @@ DEFUN (no_debug_bgp_vpn,
        return CMD_SUCCESS;
 }
 
+DEFUN (debug_bgp_labelpool,
+       debug_bgp_labelpool_cmd,
+       "debug bgp labelpool",
+       DEBUG_STR
+       BGP_STR
+       "label pool\n")
+{
+       if (vty->node == CONFIG_NODE)
+               DEBUG_ON(labelpool, LABELPOOL);
+       else
+               TERM_DEBUG_ON(labelpool, LABELPOOL);
+
+       if (vty->node != CONFIG_NODE)
+               vty_out(vty, "enabled debug bgp labelpool\n");
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_labelpool,
+       no_debug_bgp_labelpool_cmd,
+       "no debug bgp labelpool",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "label pool\n")
+{
+       if (vty->node == CONFIG_NODE)
+               DEBUG_OFF(labelpool, LABELPOOL);
+       else
+               TERM_DEBUG_OFF(labelpool, LABELPOOL);
+
+
+       if (vty->node != CONFIG_NODE)
+               vty_out(vty, "disabled debug bgp labelpool\n");
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (no_debug_bgp,
        no_debug_bgp_cmd,
        "no debug bgp",
@@ -1692,6 +1732,7 @@ DEFUN (no_debug_bgp,
        TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
        TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
        TERM_DEBUG_OFF(flowspec, FLOWSPEC);
+       TERM_DEBUG_OFF(labelpool, LABELPOOL);
        vty_out(vty, "All possible debugging has been turned off\n");
 
        return CMD_SUCCESS;
@@ -1764,6 +1805,8 @@ DEFUN_NOSH (show_debugging_bgp,
                vty_out(vty, "  BGP vpn label event debugging is on\n");
        if (BGP_DEBUG(flowspec, FLOWSPEC))
                vty_out(vty, "  BGP flowspec debugging is on\n");
+       if (BGP_DEBUG(labelpool, LABELPOOL))
+               vty_out(vty, "  BGP labelpool debugging is on\n");
 
        vty_out(vty, "\n");
        return CMD_SUCCESS;
@@ -1819,6 +1862,8 @@ int bgp_debug_count(void)
                ret++;
        if (BGP_DEBUG(flowspec, FLOWSPEC))
                ret++;
+       if (BGP_DEBUG(labelpool, LABELPOOL))
+               ret++;
 
        return ret;
 }
@@ -1916,6 +1961,10 @@ static int bgp_config_write_debug(struct vty *vty)
                vty_out(vty, "debug bgp flowspec\n");
                write++;
        }
+       if (CONF_BGP_DEBUG(labelpool, LABELPOOL)) {
+               vty_out(vty, "debug bgp labelpool\n");
+               write++;
+       }
 
        return write;
 }
@@ -2015,6 +2064,11 @@ void bgp_debug_init(void)
        install_element(CONFIG_NODE, &debug_bgp_vpn_cmd);
        install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd);
        install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd);
+
+       install_element(ENABLE_NODE, &debug_bgp_labelpool_cmd);
+       install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd);
+       install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd);
+       install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd);
 }
 
 /* Return true if this prefix is on the per_prefix_list of prefixes to debug
index a0b179e2139aa9f78bcd3ba7e7b8d4668a72e986..ad476ee918a54ec738c818667e45a26d57511a8e 100644 (file)
@@ -74,6 +74,7 @@ extern unsigned long conf_bgp_debug_nht;
 extern unsigned long conf_bgp_debug_update_groups;
 extern unsigned long conf_bgp_debug_vpn;
 extern unsigned long conf_bgp_debug_flowspec;
+extern unsigned long conf_bgp_debug_labelpool;
 
 extern unsigned long term_bgp_debug_as4;
 extern unsigned long term_bgp_debug_neighbor_events;
@@ -87,6 +88,7 @@ extern unsigned long term_bgp_debug_nht;
 extern unsigned long term_bgp_debug_update_groups;
 extern unsigned long term_bgp_debug_vpn;
 extern unsigned long term_bgp_debug_flowspec;
+extern unsigned long term_bgp_debug_labelpool;
 
 extern struct list *bgp_debug_neighbor_events_peers;
 extern struct list *bgp_debug_keepalive_peers;
@@ -120,6 +122,7 @@ struct bgp_debug_filter {
 #define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04
 #define BGP_DEBUG_VPN_LEAK_LABEL      0x08
 #define BGP_DEBUG_FLOWSPEC            0x01
+#define BGP_DEBUG_LABELPOOL           0x01
 
 #define BGP_DEBUG_PACKET_SEND         0x01
 #define BGP_DEBUG_PACKET_SEND_DETAIL  0x02
diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c
new file mode 100644 (file)
index 0000000..2c98cd9
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "mpls.h"
+#include "vty.h"
+#include "fifo.h"
+#include "linklist.h"
+#include "skiplist.h"
+#include "workqueue.h"
+#include "zclient.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_debug.h"
+
+/*
+ * Definitions and external declarations.
+ */
+extern struct zclient *zclient;
+
+/*
+ * Remember where pool data are kept
+ */
+static struct labelpool *lp;
+
+/* request this many labels at a time from zebra */
+#define LP_CHUNK_SIZE  50
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback")
+
+#define LABEL_FIFO_ADD(F, N)                                           \
+       do {                                                            \
+               FIFO_ADD((F), (N));                                     \
+               (F)->count++;                                           \
+       } while (0)
+
+#define LABEL_FIFO_DEL(F, N)                                           \
+       do {                                                            \
+               FIFO_DEL((N));                                          \
+               (F)->count--;                                           \
+       } while (0)
+
+#define LABEL_FIFO_INIT(F)                                             \
+       do {                                                            \
+               FIFO_INIT((F));                                         \
+               (F)->count = 0;                                         \
+       } while (0)
+
+#define LABEL_FIFO_COUNT(F) ((F)->count)
+
+#define LABEL_FIFO_EMPTY(F) FIFO_EMPTY(F)
+
+#define LABEL_FIFO_HEAD(F) ((F)->next == (F) ? NULL : (F)->next)
+
+struct lp_chunk {
+       uint32_t        first;
+       uint32_t        last;
+};
+
+/*
+ * label control block
+ */
+struct lp_lcb {
+       mpls_label_t    label;          /* MPLS_LABEL_NONE = not allocated */
+       int             type;
+       void            *labelid;       /* unique ID */
+       /*
+        * callback for label allocation and loss
+        *
+        * allocated: false = lost
+        */
+       int             (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+};
+
+/* XXX same first elements as "struct fifo" */
+struct lp_fifo {
+       struct lp_fifo  *next;
+       struct lp_fifo  *prev;
+
+       uint32_t        count;
+       struct lp_lcb   lcb;
+};
+
+struct lp_cbq_item {
+       int             (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+       int             type;
+       mpls_label_t    label;
+       void            *labelid;
+       bool            allocated;      /* false = lost */
+};
+
+static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data)
+{
+       struct lp_cbq_item *lcbq = data;
+       int rc;
+       int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+       if (debug)
+               zlog_debug("%s: calling callback with labelid=%p label=%u allocated=%d",
+                       __func__, lcbq->labelid, lcbq->label, lcbq->allocated);
+
+       if (lcbq->label == MPLS_LABEL_NONE) {
+               /* shouldn't happen */
+               zlog_err("%s: error: label==MPLS_LABEL_NONE", __func__);
+               return WQ_SUCCESS;
+       }
+
+       rc = (*(lcbq->cbfunc))(lcbq->label, lcbq->labelid, lcbq->allocated);
+
+       if (lcbq->allocated && rc) {
+               /*
+                * Callback rejected allocation. This situation could arise
+                * if there was a label request followed by the requestor
+                * deciding it didn't need the assignment (e.g., config
+                * change) while the reply to the original request (with
+                * label) was in the work queue.
+                */
+               if (debug)
+                       zlog_debug("%s: callback rejected allocation, releasing labelid=%p label=%u",
+                               __func__, lcbq->labelid, lcbq->label);
+
+               uintptr_t lbl = lcbq->label;
+               void *labelid;
+               struct lp_lcb *lcb;
+
+               /*
+                * If the rejected label was marked inuse by this labelid,
+                * release the label back to the pool.
+                *
+                * Further, if the rejected label was still assigned to
+                * this labelid in the LCB, delete the LCB.
+                */
+               if (!skiplist_search(lp->inuse, (void *)lbl, &labelid)) {
+                       if (labelid == lcbq->labelid) {
+                               if (!skiplist_search(lp->ledger, labelid,
+                                       (void **)&lcb)) {
+                                       if (lcbq->label == lcb->label)
+                                               skiplist_delete(lp->ledger,
+                                                       labelid, NULL);
+                               }
+                               skiplist_delete(lp->inuse, (void *)lbl, NULL);
+                       }
+               }
+       }
+
+       return WQ_SUCCESS;
+}
+
+static void lp_cbq_item_free(struct work_queue *wq, void *data)
+{
+       XFREE(MTYPE_BGP_LABEL_CBQ, data);
+}
+
+static void lp_lcb_free(void *goner)
+{
+       if (goner)
+               XFREE(MTYPE_BGP_LABEL_CB, goner);
+}
+
+static void lp_chunk_free(void *goner)
+{
+       if (goner)
+               XFREE(MTYPE_BGP_LABEL_CHUNK, goner);
+}
+
+void bgp_lp_init(struct thread_master *master, struct labelpool *pool)
+{
+       if (BGP_DEBUG(labelpool, LABELPOOL))
+               zlog_debug("%s: entry", __func__);
+
+       lp = pool;      /* Set module pointer to pool data */
+
+       lp->ledger = skiplist_new(0, NULL, lp_lcb_free);
+       lp->inuse = skiplist_new(0, NULL, NULL);
+       lp->chunks = list_new();
+       lp->chunks->del = lp_chunk_free;
+       lp->requests = XCALLOC(MTYPE_BGP_LABEL_FIFO, sizeof(struct lp_fifo));
+       LABEL_FIFO_INIT(lp->requests);
+       lp->callback_q = work_queue_new(master, "label callbacks");
+       if (!lp->callback_q) {
+               zlog_err("%s: Failed to allocate work queue", __func__);
+               exit(1);
+       }
+
+       lp->callback_q->spec.workfunc = lp_cbq_docallback;
+       lp->callback_q->spec.del_item_data = lp_cbq_item_free;
+       lp->callback_q->spec.max_retries = 0;
+}
+
+void bgp_lp_finish(void)
+{
+       struct lp_fifo *lf;
+
+       if (!lp)
+               return;
+
+       skiplist_free(lp->ledger);
+       lp->ledger = NULL;
+
+       skiplist_free(lp->inuse);
+       lp->inuse = NULL;
+
+       list_delete_and_null(&lp->chunks);
+
+       while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
+
+               LABEL_FIFO_DEL(lp->requests, lf);
+               XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+       }
+       XFREE(MTYPE_BGP_LABEL_FIFO, lp->requests);
+       lp->requests = NULL;
+
+       work_queue_free_and_null(&lp->callback_q);
+
+       lp = NULL;
+}
+
+static mpls_label_t get_label_from_pool(void *labelid)
+{
+       struct listnode *node;
+       struct lp_chunk *chunk;
+       int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+       /*
+        * Find a free label
+        * Linear search is not efficient but should be executed infrequently.
+        */
+       for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) {
+               uintptr_t lbl;
+
+               if (debug)
+                       zlog_debug("%s: chunk first=%u last=%u",
+                               __func__, chunk->first, chunk->last);
+
+               for (lbl = chunk->first; lbl <= chunk->last; ++lbl) {
+                       /* labelid is key to all-request "ledger" list */
+                       if (!skiplist_insert(lp->inuse, (void *)lbl, labelid)) {
+                               /*
+                                * Success
+                                */
+                               return lbl;
+                       }
+               }
+       }
+       return MPLS_LABEL_NONE;
+}
+
+/*
+ * Success indicated by value of "label" field in returned LCB
+ */
+static struct lp_lcb *lcb_alloc(
+       int     type,
+       void    *labelid,
+       int     (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+       /*
+        * Set up label control block
+        */
+       struct lp_lcb *new = XCALLOC(MTYPE_BGP_LABEL_CB,
+               sizeof(struct lp_lcb));
+
+       new->label = get_label_from_pool(labelid);
+       new->type = type;
+       new->labelid = labelid;
+       new->cbfunc = cbfunc;
+
+       return new;
+}
+
+/*
+ * Callers who need labels must supply a type, labelid, and callback.
+ * The type is a value defined in bgp_labelpool.h (add types as needed).
+ * The callback is for asynchronous notification of label allocation.
+ * The labelid is passed as an argument to the callback. It should be unique
+ * to the requested label instance.
+ *
+ * If zebra is not connected, callbacks with labels will be delayed
+ * until connection is established. If zebra connection is lost after
+ * labels have been assigned, existing assignments via this labelpool
+ * module will continue until reconnection.
+ *
+ * When connection to zebra is reestablished, previous label assignments
+ * will be invalidated (via callbacks having the "allocated" parameter unset)
+ * and new labels will be automatically reassigned by this labelpool module
+ * (that is, a requestor does not need to call lp_get() again if it is
+ * notified via callback that its label has been lost: it will eventually
+ * get another callback with a new label assignment).
+ *
+ * Prior requests for a given labelid are detected so that requests and
+ * assignments are not duplicated.
+ */
+void bgp_lp_get(
+       int     type,
+       void    *labelid,
+       int     (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+       struct lp_lcb *lcb;
+       int requested = 0;
+       int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+       if (debug)
+               zlog_debug("%s: labelid=%p", __func__, labelid);
+
+       /*
+        * Have we seen this request before?
+        */
+       if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+               requested = 1;
+       } else {
+               lcb = lcb_alloc(type, labelid, cbfunc);
+               if (debug)
+                       zlog_debug("%s: inserting lcb=%p label=%u",
+                               __func__, lcb, lcb->label);
+               int rc = skiplist_insert(lp->ledger, labelid, lcb);
+
+               if (rc) {
+                       /* shouldn't happen */
+                       zlog_err("%s: can't insert new LCB into ledger list",
+                               __func__);
+                       XFREE(MTYPE_BGP_LABEL_CB, lcb);
+                       return;
+               }
+       }
+
+       if (lcb->label != MPLS_LABEL_NONE) {
+               /*
+                * Fast path: we filled the request from local pool (or
+                * this is a duplicate request that we filled already).
+                * Enqueue response work item with new label.
+                */
+               struct lp_cbq_item *q;
+
+               q = XCALLOC(MTYPE_BGP_LABEL_CBQ, sizeof(struct lp_cbq_item));
+
+               q->cbfunc = lcb->cbfunc;
+               q->type = lcb->type;
+               q->label = lcb->label;
+               q->labelid = lcb->labelid;
+               q->allocated = true;
+
+               work_queue_add(lp->callback_q, q);
+
+               return;
+       }
+
+       if (requested)
+               return;
+
+       if (debug)
+               zlog_debug("%s: slow path. lcb=%p label=%u",
+                       __func__, lcb, lcb->label);
+
+       /*
+        * Slow path: we are out of labels in the local pool,
+        * so remember the request and also get another chunk from
+        * the label manager.
+        *
+        * We track number of outstanding label requests: don't
+        * need to get a chunk for each one.
+        */
+
+       struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+               sizeof(struct lp_fifo));
+
+       lf->lcb = *lcb;
+       LABEL_FIFO_ADD(lp->requests, lf);
+
+       if (LABEL_FIFO_COUNT(lp->requests) > lp->pending_count) {
+               if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) {
+                       lp->pending_count += LP_CHUNK_SIZE;
+                       return;
+               }
+       }
+}
+
+void bgp_lp_release(
+       int             type,
+       void            *labelid,
+       mpls_label_t    label)
+{
+       struct lp_lcb *lcb;
+
+       if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+               if (label == lcb->label && type == lcb->type) {
+                       uintptr_t lbl = label;
+
+                       /* no longer in use */
+                       skiplist_delete(lp->inuse, (void *)lbl, NULL);
+
+                       /* no longer requested */
+                       skiplist_delete(lp->ledger, labelid, NULL);
+               }
+       }
+}
+
+/*
+ * zebra response giving us a chunk of labels
+ */
+void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
+{
+       struct lp_chunk *chunk;
+       int debug = BGP_DEBUG(labelpool, LABELPOOL);
+       struct lp_fifo *lf;
+
+       if (last < first) {
+               zlog_err("%s: zebra label chunk invalid: first=%u, last=%u",
+                       __func__, first, last);
+               return;
+       }
+
+       chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk));
+
+       chunk->first = first;
+       chunk->last = last;
+
+       listnode_add(lp->chunks, chunk);
+
+       lp->pending_count -= (last - first + 1);
+
+       if (debug) {
+               zlog_debug("%s: %u pending requests", __func__,
+                       LABEL_FIFO_COUNT(lp->requests));
+       }
+
+       while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
+
+               struct lp_lcb *lcb;
+               void *labelid = lf->lcb.labelid;
+
+               if (skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+                       /* request no longer in effect */
+
+                       if (debug) {
+                               zlog_debug("%s: labelid %p: request no longer in effect",
+                                               __func__, labelid);
+                       }
+                       goto finishedrequest;
+               }
+
+               /* have LCB */
+               if (lcb->label != MPLS_LABEL_NONE) {
+                       /* request already has a label */
+                       if (debug) {
+                               zlog_debug("%s: labelid %p: request already has a label: %u=0x%x, lcb=%p",
+                                               __func__, labelid,
+                                               lcb->label, lcb->label, lcb);
+                       }
+                       goto finishedrequest;
+               }
+
+               lcb->label = get_label_from_pool(lcb->labelid);
+
+               if (lcb->label == MPLS_LABEL_NONE) {
+                       /*
+                        * Out of labels in local pool, await next chunk
+                        */
+                       if (debug) {
+                               zlog_debug("%s: out of labels, await more",
+                                               __func__);
+                       }
+                       break;
+               }
+
+               /*
+                * we filled the request from local pool.
+                * Enqueue response work item with new label.
+                */
+               struct lp_cbq_item *q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+                       sizeof(struct lp_cbq_item));
+
+               q->cbfunc = lcb->cbfunc;
+               q->type = lcb->type;
+               q->label = lcb->label;
+               q->labelid = lcb->labelid;
+               q->allocated = true;
+
+               if (debug)
+                       zlog_debug("%s: assigning label %u to labelid %p",
+                               __func__, q->label, q->labelid);
+
+               work_queue_add(lp->callback_q, q);
+
+finishedrequest:
+               LABEL_FIFO_DEL(lp->requests, lf);
+               XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+       }
+}
+
+/*
+ * continue using allocated labels until zebra returns
+ */
+void bgp_lp_event_zebra_down(void)
+{
+       /* rats. */
+}
+
+/*
+ * Inform owners of previously-allocated labels that their labels
+ * are not valid. Request chunk from zebra large enough to satisfy
+ * previously-allocated labels plus any outstanding requests.
+ */
+void bgp_lp_event_zebra_up(void)
+{
+       int labels_needed;
+       int chunks_needed;
+       void *labelid;
+       struct lp_lcb *lcb;
+
+       /*
+        * Get label chunk allocation request dispatched to zebra
+        */
+       labels_needed = LABEL_FIFO_COUNT(lp->requests) +
+               skiplist_count(lp->inuse);
+
+       /* round up */
+       chunks_needed = (labels_needed / LP_CHUNK_SIZE) + 1;
+       labels_needed = chunks_needed * LP_CHUNK_SIZE;
+
+       zclient_send_get_label_chunk(zclient, 0, labels_needed);
+       lp->pending_count = labels_needed;
+
+       /*
+        * Invalidate current list of chunks
+        */
+       list_delete_all_node(lp->chunks);
+
+       /*
+        * Invalidate any existing labels and requeue them as requests
+        */
+       while (!skiplist_first(lp->inuse, NULL, &labelid)) {
+
+               /*
+                * Get LCB
+                */
+               if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+
+                       if (lcb->label != MPLS_LABEL_NONE) {
+                               /*
+                                * invalidate
+                                */
+                               struct lp_cbq_item *q;
+
+                               q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+                                       sizeof(struct lp_cbq_item));
+                               q->cbfunc = lcb->cbfunc;
+                               q->type = lcb->type;
+                               q->label = lcb->label;
+                               q->labelid = lcb->labelid;
+                               q->allocated = false;
+                               work_queue_add(lp->callback_q, q);
+
+                               lcb->label = MPLS_LABEL_NONE;
+                       }
+
+                       /*
+                        * request queue
+                        */
+                       struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+                               sizeof(struct lp_fifo));
+
+                       lf->lcb = *lcb;
+                       LABEL_FIFO_ADD(lp->requests, lf);
+               }
+
+               skiplist_delete_first(lp->inuse);
+       }
+}
diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h
new file mode 100644 (file)
index 0000000..fa35cde
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_LABELPOOL_H
+#define _FRR_BGP_LABELPOOL_H
+
+#include <zebra.h>
+
+#include "mpls.h"
+
+/*
+ * Types used in bgp_lp_get for debug tracking; add more as needed
+ */
+#define LP_TYPE_VRF    0x00000001
+
+struct labelpool {
+       struct skiplist         *ledger;        /* all requests */
+       struct skiplist         *inuse;         /* individual labels */
+       struct list             *chunks;        /* granted by zebra */
+       struct lp_fifo          *requests;      /* blocked on zebra */
+       struct work_queue       *callback_q;
+       uint32_t                pending_count;  /* requested from zebra */
+};
+
+extern void bgp_lp_init(struct thread_master *master, struct labelpool *pool);
+extern void bgp_lp_finish(void);
+extern void bgp_lp_get(int type, void *labelid,
+       int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated));
+extern void bgp_lp_release(int type, void *labelid, mpls_label_t label);
+extern void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last);
+extern void bgp_lp_event_zebra_down(void);
+extern void bgp_lp_event_zebra_up(void);
+
+#endif /* _FRR_BGP_LABELPOOL_H */
index 023a86631558ac129de1ab33bad5e8636644b285..30ec0e96a6f5e4fd63040f27148b219b0a31f5f6 100644 (file)
@@ -55,6 +55,7 @@
 #endif
 #include "bgpd/bgp_evpn.h"
 #include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_labelpool.h"
 
 /* All information about zebra. */
 struct zclient *zclient = NULL;
@@ -1876,6 +1877,9 @@ static void bgp_zebra_connected(struct zclient *zclient)
        /* Send the client registration */
        bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
 
+       /* tell label pool that zebra is connected */
+       lp_event_zebra_up();
+
        /* TODO - What if we have peers and networks configured, do we have to
         * kick-start them?
         */
@@ -2042,6 +2046,41 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient,
        }
 }
 
+static void bgp_zebra_process_label_chunk(
+       int cmd,
+       struct zclient *zclient,
+       zebra_size_t length,
+       vrf_id_t vrf_id)
+{
+       struct stream *s = NULL;
+       uint8_t response_keep;
+       uint32_t first;
+       uint32_t last;
+
+       s = zclient->ibuf;
+       STREAM_GETC(s, response_keep);
+       STREAM_GETL(s, first);
+       STREAM_GETL(s, last);
+
+       if (first > last ||
+               first < MPLS_LABEL_UNRESERVED_MIN ||
+               last > MPLS_LABEL_UNRESERVED_MAX) {
+
+               zlog_err("%s: Invalid Label chunk: %u - %u",
+                       __func__, first, last);
+               return;
+       }
+       if (BGP_DEBUG(zebra, ZEBRA)) {
+               zlog_debug("Label Chunk assign: %u - %u (%u) ",
+                       first, last, response_keep);
+       }
+
+       lp_event_chunk(response_keep, first, last);
+
+stream_failure:                /* for STREAM_GETX */
+       return;
+}
+
 extern struct zebra_privs_t bgpd_privs;
 
 void bgp_zebra_init(struct thread_master *master)
@@ -2076,6 +2115,7 @@ void bgp_zebra_init(struct thread_master *master)
        zclient->local_l3vni_del = bgp_zebra_process_local_l3vni;
        zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix;
        zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix;
+       zclient->label_chunk = bgp_zebra_process_label_chunk;
 }
 
 void bgp_zebra_destroy(void)
index c46111e1fb325eb6c1847ad3446f752aed5a2aa6..7c17cc3dae28d0b7904fee7044ab5d40cdeb23c2 100644 (file)
@@ -81,7 +81,7 @@
 #include "bgpd/bgp_io.h"
 #include "bgpd/bgp_ecommunity.h"
 #include "bgpd/bgp_flowspec.h"
-
+#include "bgpd/bgp_labelpool.h"
 
 DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
 DEFINE_QOBJ_TYPE(bgp_master)
@@ -7536,6 +7536,9 @@ void bgp_master_init(struct thread_master *master)
        /* Enable multiple instances by default. */
        bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE);
 
+       /* mpls label dynamic allocation pool */
+       lp_init(bm->master, &bm->labelpool);
+
        QOBJ_REG(bm, bgp_master);
 }
 
@@ -7714,4 +7717,6 @@ void bgp_terminate(void)
 
        if (bm->t_rmap_update)
                BGP_TIMER_OFF(bm->t_rmap_update);
+
+       lp_finish();
 }
index 0c5f72662cf4ce7307df94781ceeccb17d547611..2f3a732acde46a12b84e6cb5498bffbda9f00c2b 100644 (file)
@@ -37,6 +37,7 @@
 #include "bgp_memory.h"
 #include "bitfield.h"
 #include "vxlan.h"
+#include "bgp_labelpool.h"
 
 #define BGP_MAX_HOSTNAME 64    /* Linux max, is larger than most other sys */
 #define BGP_PEER_MAX_HASH_SIZE 16384
@@ -140,6 +141,9 @@ struct bgp_master {
        /* Id space for automatic RD derivation for an EVI/VRF */
        bitfield_t rd_idspace;
 
+       /* dynamic mpls label allocation pool */
+       struct labelpool labelpool;
+
        QOBJ_FIELDS
 };
 DECLARE_QOBJ_TYPE(bgp_master)
index d23f62dcd72732526c439772a14d777d10bec93e..07029c1f5d99b9e8503aeb5b98f9c6b74541d787 100644 (file)
@@ -1991,6 +1991,40 @@ int lm_label_manager_connect(struct zclient *zclient)
        return (int)result;
 }
 
+/*
+ * Asynchronous label chunk request
+ *
+ * @param zclient Zclient used to connect to label manager (zebra)
+ * @param keep Avoid garbage collection
+ * @param chunk_size Amount of labels requested
+ * @result 0 on success, -1 otherwise
+ */
+int zclient_send_get_label_chunk(
+       struct zclient  *zclient,
+       uint8_t         keep,
+       uint32_t        chunk_size)
+{
+       struct stream *s;
+
+       if (zclient_debug)
+               zlog_debug("Getting Label Chunk");
+
+       if (zclient->sock < 0)
+               return -1;
+
+       s = zclient->obuf;
+       stream_reset(s);
+
+       zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, VRF_DEFAULT);
+       stream_putc(s, keep);
+       stream_putl(s, chunk_size);
+
+       /* Put length at the first point of the stream. */
+       stream_putw_at(s, 0, stream_get_endp(s));
+
+       return zclient_send_message(zclient);
+}
+
 /**
  * Function to request a label chunk in a syncronous way
  *
@@ -2604,6 +2638,12 @@ static int zclient_read(struct thread *thread)
                if (zclient->rule_notify_owner)
                        (*zclient->rule_notify_owner)(command, zclient, length,
                                                      vrf_id);
+               break;
+       case ZEBRA_GET_LABEL_CHUNK:
+               if (zclient->label_chunk)
+                       (*zclient->label_chunk)(command, zclient, length,
+                                                     vrf_id);
+               break;
        default:
                break;
        }
index 9d3e5c370297e2d9abf03de311eb1e9d01d2377d..e85eac73fb322b57b020f4693ea02047142d2074 100644 (file)
@@ -223,6 +223,8 @@ struct zclient {
                                  uint16_t length, vrf_id_t vrf_id);
        int (*rule_notify_owner)(int command, struct zclient *zclient,
                                 uint16_t length, vrf_id_t vrf_id);
+       void (*label_chunk)(int command, struct zclient *zclient,
+                               uint16_t length, vrf_id_t vrf_id);
 };
 
 /* Zebra API message flag. */
@@ -535,6 +537,11 @@ extern int zapi_ipv4_route(uint8_t, struct zclient *, struct prefix_ipv4 *,
 extern struct interface *zebra_interface_link_params_read(struct stream *);
 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 lm_label_manager_connect(struct zclient *zclient);
 extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep,
                              uint32_t chunk_size, uint32_t *start,