]> git.puffer.fish Git - matthieu/frr.git/commitdiff
Implement generic label manager
authorßingen <bingen@voltanet.io>
Mon, 20 Mar 2017 14:34:49 +0000 (15:34 +0100)
committerßingen <bingen@voltanet.io>
Mon, 20 Mar 2017 16:10:41 +0000 (17:10 +0100)
Label Manager allows to share MPLS label space among different
daemons. Each daemon can request a chunk of consecutive labels and
release it if it doesn't need them anymore. Label Manager stores the
daemon protocol and instance to identify the owner client. It uses them
to perform garbage collection, releasing all label chunks from a client
when it gets disconnected or reconnected.

Additionally, every client can request that the chunk is never garbage
collected. In that case client has the responsibility to release
non-used labels.

Zebra can host the label manager itself (if no -l param is provided) or
connect to an external one using zserv/zclient (providing its address
with -l param).

Client code is in lib/zclient.c, but currently only LDP is using it.

TODO: Allow for custom ranges requests, i.e., specify the start label
besides the chunk.
TODO: Release labels from LDP.

Signed-off-by: Bingen Eguzkitza <bingen@voltanet.io>
15 files changed:
ldpd/lde.c
ldpd/lde.h
ldpd/ldpd.c
ldpd/ldpd.h
lib/log.c
lib/mpls.h
lib/zclient.c
lib/zclient.h
tests/test_lblmgr.c [new file with mode: 0644]
zebra/Makefile.am
zebra/label_manager.c [new file with mode: 0644]
zebra/label_manager.h [new file with mode: 0644]
zebra/main.c
zebra/zserv.c
zebra/zserv.h

index 08339c720ad7038f6de5f90602272d0f990e3b12..d4b95637f9fae7dd33f04f0cc9cc7cda9d739ca1 100644 (file)
 #include "privs.h"
 #include "sigevent.h"
 #include "mpls.h"
+#include <lib/linklist.h>
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
 
 static void             lde_shutdown(void);
 static int              lde_dispatch_imsg(struct thread *);
@@ -50,6 +54,11 @@ static void           lde_map_free(void *);
 static int              lde_address_add(struct lde_nbr *, struct lde_addr *);
 static int              lde_address_del(struct lde_nbr *, struct lde_addr *);
 static void             lde_address_list_free(struct lde_nbr *);
+static void             zclient_sync_init (u_short instance);
+static void             lde_label_list_init(void);
+static int              lde_get_label_chunk (void);
+static void             on_get_label_chunk_response(uint32_t start, uint32_t end);
+static uint32_t                 lde_get_next_label(void);
 
 RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare)
 RB_GENERATE(lde_map_head, lde_map, entry, lde_map_compare)
@@ -83,6 +92,10 @@ static struct zebra_privs_t lde_privs =
        .cap_num_i = 0
 };
 
+/* List of chunks of labels externally assigned by Zebra */
+struct list *label_chunk_list;
+struct listnode *current_label_chunk;
+
 /* SIGINT / SIGTERM handler. */
 static void
 sigint(void)
@@ -102,9 +115,31 @@ static struct quagga_signal_t lde_signals[] =
        },
 };
 
+struct zclient *zclient_sync = NULL;
+static void
+zclient_sync_init(u_short instance)
+{
+       /* Initialize special zclient for synchronous message exchanges. */
+       log_debug("Initializing synchronous zclient for label manager");
+       zclient_sync = zclient_new(master);
+       zclient_sync->sock = -1;
+       zclient_sync->redist_default = ZEBRA_ROUTE_LDP;
+       zclient_sync->instance = instance;
+       while (zclient_socket_connect(zclient_sync) < 0) {
+               fprintf(stderr, "Error connecting synchronous zclient!\n");
+               sleep(1);
+       }
+
+       /* Connect to label manager */
+       while (lm_label_manager_connect(zclient_sync) != 0) {
+               fprintf(stderr, "Error connecting to label manager!\n");
+               sleep(1);
+       }
+}
+
 /* label decision engine */
 void
-lde(const char *user, const char *group)
+lde(const char *user, const char *group, u_short instance)
 {
        struct thread            thread;
        struct timeval           now;
@@ -152,6 +187,10 @@ lde(const char *user, const char *group)
        gettimeofday(&now, NULL);
        global.uptime = now.tv_sec;
 
+       /* Init synchronous zclient and label list */
+       zclient_sync_init(instance);
+       lde_label_list_init();
+
        /* Fetch next active thread. */
        while (thread_fetch(master, &thread))
                thread_call(&thread);
@@ -587,7 +626,6 @@ lde_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen)
 uint32_t
 lde_update_label(struct fec_node *fn)
 {
-       static uint32_t  label = MPLS_LABEL_RESERVED_MAX;
        struct fec_nh   *fnh;
        int              connected = 0;
 
@@ -652,12 +690,7 @@ lde_update_label(struct fec_node *fn)
            fn->local_label > MPLS_LABEL_RESERVED_MAX)
                return (fn->local_label);
 
-       /*
-        * TODO: request label to zebra or define a range of labels for ldpd.
-        */
-
-       label++;
-       return (label);
+       return lde_get_next_label ();
 }
 
 void
@@ -1533,3 +1566,104 @@ lde_address_list_free(struct lde_nbr *ln)
                free(lde_addr);
        }
 }
+
+static void
+lde_del_label_chunk(void *val)
+{
+       free(val);
+}
+static int
+lde_get_label_chunk(void)
+{
+       int ret;
+       uint32_t start, end;
+
+       log_debug("Getting label chunk");
+       ret = lm_get_label_chunk(zclient_sync, 0, CHUNK_SIZE, &start, &end);
+       if (ret < 0)
+       {
+               log_warnx("Error getting label chunk!");
+               close(zclient_sync->sock);
+               zclient_sync->sock = -1;
+               return -1;
+       }
+
+       on_get_label_chunk_response(start, end);
+
+       return 0;
+}
+static void
+lde_label_list_init(void)
+{
+       label_chunk_list = list_new();
+       label_chunk_list->del = lde_del_label_chunk;
+
+       /* get first chunk */
+       while (lde_get_label_chunk() != 0) {
+               fprintf(stderr, "Error getting first label chunk!\n");
+               sleep(1);
+       }
+}
+
+static void
+on_get_label_chunk_response(uint32_t start, uint32_t end)
+{
+       struct label_chunk *new_label_chunk;
+
+       log_debug("Label Chunk assign: %u - %u", start, end);
+
+       new_label_chunk = calloc(1, sizeof(struct label_chunk));
+
+       new_label_chunk->start = start;
+       new_label_chunk->end = end;
+       new_label_chunk->used_mask = 0;
+
+       listnode_add(label_chunk_list, (void *)new_label_chunk);
+
+       /* let's update current if needed */
+       if (!current_label_chunk)
+               current_label_chunk = listtail(label_chunk_list);
+}
+
+static uint32_t
+lde_get_next_label(void)
+{
+       struct label_chunk *label_chunk;
+       uint32_t i, pos, size;
+       uint32_t label = NO_LABEL;
+
+       while (current_label_chunk) {
+               label_chunk = listgetdata(current_label_chunk);
+               if (!label_chunk)
+                       goto end;
+
+               /* try to get next free label in currently used label chunk */
+               size = label_chunk->end - label_chunk->start + 1;
+               for (i = 0, pos = 1; i < size; i++, pos <<= 1) {
+                       if (!(pos & label_chunk->used_mask)) {
+                               label_chunk->used_mask |= pos;
+                               label = label_chunk->start + i;
+                               goto end;
+                       }
+               }
+               current_label_chunk = listnextnode(current_label_chunk);
+       }
+
+end:
+       /* we moved till the last chunk, or were not able to find a label,
+          so let's ask for another one */
+       if (!current_label_chunk || current_label_chunk == listtail(label_chunk_list)
+               || label == NO_LABEL) {
+               if (lde_get_label_chunk() != 0)
+                       log_warn("%s: Error getting label chunk!", __func__);
+
+       }
+
+       return NO_LABEL;
+}
+/* TODO: not used yet. Have to check label release */
+static void
+lde_release_label_chunk(void)
+{
+       return;
+}
index b5bcb42c8b561d98e44b5c66f36eedb865174688..d9836097af0d1799bc28ae3045e064be18ee9ef8 100644 (file)
@@ -124,6 +124,13 @@ struct fec_node {
        void                    *data;          /* fec specific data */
 };
 
+#define CHUNK_SIZE             64
+struct label_chunk {
+       uint32_t                 start;
+       uint32_t                 end;
+       uint64_t                 used_mask;
+};
+
 #define LDE_GC_INTERVAL 300
 
 extern struct ldpd_conf        *ldeconf;
@@ -132,7 +139,7 @@ extern struct nbr_tree       lde_nbrs;
 extern struct thread   *gc_timer;
 
 /* lde.c */
-void            lde(const char *, const char *);
+void            lde(const char *, const char *, u_short instance);
 int             lde_imsg_compose_parent(int, pid_t, void *, uint16_t);
 int             lde_imsg_compose_ldpe(int, uint32_t, pid_t, void *, uint16_t);
 int             lde_acl_check(char *, int, union ldpd_addr *, uint8_t);
index 98ea5ca53e2116db8048c9050d3a80a6a7eb5204..8fa33ff0992152058f4cdb1e81389a13960efdd7 100644 (file)
@@ -45,7 +45,7 @@
 
 static void             ldpd_shutdown(void);
 static pid_t            start_child(enum ldpd_process, char *, int, int,
-                           const char *, const char *, const char *);
+                           const char *, const char *, const char *, const char *);
 static int              main_dispatch_ldpe(struct thread *);
 static int              main_dispatch_lde(struct thread *);
 static int              main_imsg_send_ipc_sockets(struct imsgbuf *,
@@ -119,6 +119,7 @@ char ctl_sock_path[MAXPATHLEN] = LDPD_SOCKET;
 static struct option longopts[] =
 {
        { "ctl_socket",  required_argument, NULL, OPTION_CTLSOCK},
+       { "instance",    required_argument, NULL, 'n'},
        { 0 }
 };
 
@@ -186,6 +187,8 @@ main(int argc, char *argv[])
        char                    *ctl_sock_name;
        const char              *user = NULL;
        const char              *group = NULL;
+       u_short                  instance = 0;
+       const char              *instance_char = NULL;
 
        ldpd_process = PROC_MAIN;
 
@@ -194,8 +197,9 @@ main(int argc, char *argv[])
                saved_argv0 = (char *)"ldpd";
 
        frr_preinit(&ldpd_di, argc, argv);
-       frr_opt_add("LE", longopts,
-               "      --ctl_socket   Override ctl socket path\n");
+       frr_opt_add("LEn:", longopts,
+               "      --ctl_socket   Override ctl socket path\n"
+               "-n,   --instance     Instance id\n");
 
        while (1) {
                int opt;
@@ -227,6 +231,12 @@ main(int argc, char *argv[])
                        strlcat(ctl_sock_path, ctl_sock_name,
                            sizeof(ctl_sock_path));
                        break;
+               case 'n':
+                       instance = atoi(optarg);
+                       instance_char = optarg;
+                       if (instance < 1)
+                               exit(0);
+                       break;
                case 'L':
                        lflag = 1;
                        break;
@@ -258,7 +268,7 @@ main(int argc, char *argv[])
            LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
 
        if (lflag)
-               lde(user, group);
+               lde(user, group, instance);
        else if (eflag)
                ldpe(user, group, ctl_sock_path);
 
@@ -308,10 +318,10 @@ main(int argc, char *argv[])
        /* start children */
        lde_pid = start_child(PROC_LDE_ENGINE, saved_argv0,
            pipe_parent2lde[1], pipe_parent2lde_sync[1],
-           user, group, ctl_sock_custom_path);
+           user, group, ctl_sock_custom_path, instance_char);
        ldpe_pid = start_child(PROC_LDP_ENGINE, saved_argv0,
            pipe_parent2ldpe[1], pipe_parent2ldpe_sync[1],
-           user, group, ctl_sock_custom_path);
+           user, group, ctl_sock_custom_path, instance_char);
 
        /* drop privileges */
        zprivs_init(&ldpd_privs);
@@ -414,9 +424,10 @@ ldpd_shutdown(void)
 
 static pid_t
 start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync,
-    const char *user, const char *group, const char *ctl_sock_custom_path)
+    const char *user, const char *group, const char *ctl_sock_custom_path,
+    const char *instance)
 {
-       char    *argv[9];
+       char    *argv[13];
        int      argc = 0;
        pid_t    pid;
 
@@ -459,6 +470,14 @@ start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync,
                argv[argc++] = (char *)"--ctl_socket";
                argv[argc++] = (char *)ctl_sock_custom_path;
        }
+       /* zclient serv path */
+       argv[argc++] = (char *)"-z";
+       argv[argc++] = (char *)zclient_serv_path_get();
+       /* instance */
+       if (instance) {
+               argv[argc++] = (char *)"-n";
+               argv[argc++] = (char *)instance;
+       }
        argv[argc++] = NULL;
 
        execvp(argv0, argv);
index 0a7e1177bc54f468d1c185672cccaee3ee3e7a31..c665656aa6404c03a1d70f3ed1a33426d7c8503b 100644 (file)
@@ -140,7 +140,9 @@ enum imsg_type {
        IMSG_RECONF_END,
        IMSG_DEBUG_UPDATE,
        IMSG_LOG,
-       IMSG_ACL_CHECK
+       IMSG_ACL_CHECK,
+       IMSG_GET_LABEL_CHUNK,
+       IMSG_RELEASE_LABEL_CHUNK
 };
 
 union ldpd_addr {
index 0fd9621f371fb9c2c05f63f95a4569860ea61ed7..69225dbf7ae1b08c5bda5a699395abc6c1c825c0 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -964,6 +964,9 @@ static const struct zebra_desc_table command_types[] = {
   DESC_ENTRY   (ZEBRA_IPV6_NEXTHOP_ADD),
   DESC_ENTRY   (ZEBRA_IPV6_NEXTHOP_DELETE),
   DESC_ENTRY    (ZEBRA_IPMR_ROUTE_STATS),
+  DESC_ENTRY    (ZEBRA_LABEL_MANAGER_CONNECT),
+  DESC_ENTRY    (ZEBRA_GET_LABEL_CHUNK),
+  DESC_ENTRY    (ZEBRA_RELEASE_LABEL_CHUNK),
 };
 #undef DESC_ENTRY
 
index b5c1a653b1f9150a45a11873b44250d90b5d2b20..13a46e10127cacdd67002c07ec8309aede0a9ac6 100644 (file)
@@ -23,6 +23,8 @@
 #ifndef _QUAGGA_MPLS_H
 #define _QUAGGA_MPLS_H
 
+#include <arpa/inet.h>
+
 /* Well-known MPLS label values (RFC 3032 etc). */
 #define MPLS_V4_EXP_NULL_LABEL             0
 #define MPLS_RA_LABEL                      1
index 859751deb8a761698f7c34d470d0e5f8b03a7a0a..6f7fb9b1be8d0b653191223b0f6e1ccf26076005 100644 (file)
@@ -33,6 +33,7 @@
 #include "memory.h"
 #include "table.h"
 #include "nexthop.h"
+#include "mpls.h"
 
 DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
 DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
@@ -1461,6 +1462,223 @@ zebra_interface_vrf_update_read (struct stream *s, vrf_id_t vrf_id,
   *new_vrf_id = new_id;
   return ifp;
 }
+/**
+ * Connect to label manager in a syncronous way
+ *
+ * It first writes the request to zcient output buffer and then
+ * immediately reads the answer from the input buffer.
+ *
+ * @param zclient Zclient used to connect to label manager (zebra)
+ * @result Result of response
+ */
+int
+lm_label_manager_connect (struct zclient *zclient)
+{
+  int ret;
+  struct stream *s;
+  u_char result;
+  u_int16_t size;
+  u_char marker;
+  u_char version;
+  vrf_id_t vrf_id;
+  u_int16_t cmd;
+
+  zlog_debug ("Connecting to Label Manager");
+  if (zclient->sock < 0)
+    return -1;
+
+  /* send request */
+  s = zclient->obuf;
+  stream_reset (s);
+  zclient_create_header (s, ZEBRA_LABEL_MANAGER_CONNECT, VRF_DEFAULT);
+
+  /* proto */
+  stream_putc (s, zclient->redist_default);
+  /* instance */
+  stream_putw (s, zclient->instance);
+
+  /* Put length at the first point of the stream. */
+  stream_putw_at(s, 0, stream_get_endp(s));
+
+  ret = writen (zclient->sock, s->data, stream_get_endp (s));
+  if (ret < 0)
+    {
+      zlog_err ("%s: can't write to zclient->sock", __func__);
+      close (zclient->sock);
+      zclient->sock = -1;
+      return -1;
+    }
+  if (ret == 0)
+    {
+      zlog_err ("%s: zclient->sock connection closed", __func__);
+      close (zclient->sock);
+      zclient->sock = -1;
+      return -1;
+    }
+  zlog_debug ("%s: Label manager connect request (%d bytes) sent", __func__, ret);
+
+  /* read response */
+  s = zclient->ibuf;
+  stream_reset (s);
+
+  ret = zclient_read_header (s, zclient->sock, &size, &marker, &version,
+                             &vrf_id, &cmd);
+  if (ret != 0 || cmd != ZEBRA_LABEL_MANAGER_CONNECT) {
+      zlog_err ("%s: Invalid Label Manager Connect Message Reply Header", __func__);
+      return -1;
+  }
+  /* result */
+  result = stream_getc(s);
+  zlog_debug ("%s: Label Manager connect response (%d bytes) received, result %u",
+              __func__, size, result);
+
+  return (int)result;
+}
+
+/**
+ * Function to request a label chunk in a syncronous way
+ *
+ * It first writes the request to zlcient output buffer and then
+ * immediately reads the answer from the input buffer.
+ *
+ * @param zclient Zclient used to connect to label manager (zebra)
+ * @param keep Avoid garbage collection
+ * @param chunk_size Amount of labels requested
+ * @param start To write first assigned chunk label to
+ * @param end To write last assigned chunk label to
+ * @result 0 on success, -1 otherwise
+ */
+int
+lm_get_label_chunk (struct zclient *zclient, u_char keep, uint32_t chunk_size,
+                    uint32_t *start, uint32_t *end)
+{
+  int ret;
+  struct stream *s;
+  u_int16_t size;
+  u_char marker;
+  u_char version;
+  vrf_id_t vrf_id;
+  u_int16_t cmd;
+  u_char response_keep;
+
+  zlog_debug ("Getting Label Chunk");
+  if (zclient->sock < 0)
+    return -1;
+
+  /* send request */
+  s = zclient->obuf;
+  stream_reset (s);
+  zclient_create_header (s, ZEBRA_GET_LABEL_CHUNK, VRF_DEFAULT);
+  /* keep */
+  stream_putc (s, keep);
+  /* chunk size */
+  stream_putl (s, chunk_size);
+  /* Put length at the first point of the stream. */
+  stream_putw_at(s, 0, stream_get_endp(s));
+
+  ret = writen (zclient->sock, s->data, stream_get_endp (s));
+  if (ret < 0)
+    {
+      zlog_err ("%s: can't write to zclient->sock", __func__);
+      close (zclient->sock);
+      zclient->sock = -1;
+      return -1;
+    }
+  if (ret == 0)
+    {
+      zlog_err ("%s: zclient->sock connection closed", __func__);
+      close (zclient->sock);
+      zclient->sock = -1;
+      return -1;
+    }
+  zlog_debug ("%s: Label chunk request (%d bytes) sent", __func__, ret);
+
+  /* read response */
+  s = zclient->ibuf;
+  stream_reset (s);
+
+  ret = zclient_read_header (s, zclient->sock, &size, &marker, &version,
+                             &vrf_id, &cmd);
+  if (ret != 0 || cmd != ZEBRA_GET_LABEL_CHUNK) {
+      zlog_err ("%s: Invalid Get Label Chunk Message Reply Header", __func__);
+      return -1;
+  }
+  zlog_debug ("%s: Label chunk response (%d bytes) received", __func__, size);
+  /* keep */
+  response_keep = stream_getc(s);
+  /* start and end labels */
+  *start = stream_getl(s);
+  *end = stream_getl(s);
+
+  /* not owning this response */
+  if (keep != response_keep) {
+          zlog_err ("%s: Invalid Label chunk: %u - %u, keeps mismatch %u != %u",
+                    __func__, *start, *end, keep, response_keep);
+  }
+  /* sanity */
+  if (*start > *end
+      || *start < MPLS_MIN_UNRESERVED_LABEL
+      || *end > MPLS_MAX_UNRESERVED_LABEL) {
+          zlog_err ("%s: Invalid Label chunk: %u - %u", __func__,
+                    *start, *end);
+          return -1;
+  }
+
+  zlog_debug ("Label Chunk assign: %u - %u (%u) ",
+              *start, *end, response_keep);
+
+  return 0;
+}
+
+/**
+ * Function to release a label chunk
+ *
+ * @param zclient Zclient used to connect to label manager (zebra)
+ * @param start First label of chunk
+ * @param end Last label of chunk
+ * @result 0 on success, -1 otherwise
+ */
+int
+lm_release_label_chunk (struct zclient *zclient, uint32_t start, uint32_t end)
+{
+  int ret;
+  struct stream *s;
+
+  zlog_debug ("Releasing Label Chunk");
+  if (zclient->sock < 0)
+    return -1;
+
+  /* send request */
+  s = zclient->obuf;
+  stream_reset (s);
+  zclient_create_header (s, ZEBRA_RELEASE_LABEL_CHUNK, VRF_DEFAULT);
+
+  /* start */
+  stream_putl (s, start);
+  /* end */
+  stream_putl (s, end);
+
+  /* Put length at the first point of the stream. */
+  stream_putw_at(s, 0, stream_get_endp(s));
+
+  ret = writen (zclient->sock, s->data, stream_get_endp (s));
+  if (ret < 0)
+    {
+      zlog_err ("%s: can't write to zclient->sock", __func__);
+      close (zclient->sock);
+      zclient->sock = -1;
+      return -1;
+    }
+  if (ret == 0)
+    {
+      zlog_err ("%s: zclient->sock connection closed", __func__);
+      close (zclient->sock);
+      zclient->sock = -1;
+      return -1;
+    }
+
+  return 0;
+}
 
 /* Zebra client message read function. */
 static int
index 89fc865c78319a70a1a95d2fbeafc07c5a033de6..d3d0a202c5caf379bcaf3d56324fb9bd6db33b87 100644 (file)
@@ -91,6 +91,9 @@ typedef enum {
   ZEBRA_IPV6_NEXTHOP_ADD,
   ZEBRA_IPV6_NEXTHOP_DELETE,
   ZEBRA_IPMR_ROUTE_STATS,
+  ZEBRA_LABEL_MANAGER_CONNECT,
+  ZEBRA_GET_LABEL_CHUNK,
+  ZEBRA_RELEASE_LABEL_CHUNK,
 } zebra_message_types_t;
 
 struct redist_proto
@@ -271,6 +274,10 @@ extern int zapi_ipv4_route (u_char, 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 lm_label_manager_connect (struct zclient *zclient);
+extern int lm_get_label_chunk (struct zclient *zclient, u_char keep,
+                               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);
 /* IPv6 prefix add and delete function prototype. */
 
 struct zapi_ipv6
diff --git a/tests/test_lblmgr.c b/tests/test_lblmgr.c
new file mode 100644 (file)
index 0000000..4a4aaa0
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Label Manager Test
+ *
+ * Copyright (C) 2017 by Bingen Eguzkitza,
+ *                       Volta Networks Inc.
+ *
+ * This file is part of FreeRangeRouting (FRR)
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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 FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "lib/stream.h"
+#include "lib/zclient.h"
+
+#define ZSERV_PATH "/tmp/zserv.api" // TODO!!
+#define KEEP 0 /* change to 1 to avoid garbage collection */
+#define CHUNK_SIZE 32
+
+struct zclient *zclient;
+u_short instance = 1;
+
+const char *sequence = "GGRGGGRRG";
+
+static int zebra_send_get_label_chunk (void);
+static int zebra_send_release_label_chunk (uint32_t start, uint32_t end);
+
+static void
+process_next_call (uint32_t start, uint32_t end)
+{
+               sleep (3);
+               if (!*sequence)
+                               exit (0);
+               if (*sequence == 'G')
+                               zebra_send_get_label_chunk ();
+               else if (*sequence == 'R')
+                               zebra_send_release_label_chunk (start, end);
+}
+
+/* Connect to Label Manager */
+
+static int
+zebra_send_label_manager_connect ()
+{
+               int ret;
+
+               printf("Connect to Label Manager\n");
+
+               ret = lm_label_manager_connect (zclient);
+               printf ("Label Manager connection result: %u \n", ret);
+               if (ret != 0 ) {
+                               fprintf (stderr, "Error %d connecting to Label Manager %s\n", ret,
+                                                strerror(errno));
+                               exit (1);
+               }
+
+               process_next_call (0, 0);
+}
+
+/* Get Label Chunk */
+
+static int
+zebra_send_get_label_chunk ()
+{
+               uint32_t start;
+               uint32_t end;
+               int ret;
+
+               printf("Ask for label chunk \n");
+
+               ret = lm_get_label_chunk (zclient, KEEP, CHUNK_SIZE, &start, &end);
+               if (ret != 0 ) {
+                               fprintf (stderr, "Error %d requesting label chunk %s\n", ret, strerror(errno));
+                               exit (1);
+               }
+
+               sequence++;
+
+               printf ("Label Chunk assign: %u - %u \n",
+                               start, end);
+
+               process_next_call (start, end);
+}
+
+/* Release Label Chunk */
+
+static int
+zebra_send_release_label_chunk (uint32_t start, uint32_t end)
+{
+               struct stream *s;
+               int ret;
+
+               printf("Release label chunk: %u - %u\n", start, end);
+
+               ret = lm_release_label_chunk (zclient, start, end);
+               if (ret != 0 ) {
+                               fprintf (stderr, "Error releasing label chunk\n");
+                               exit (1);
+               }
+
+               sequence++;
+
+               process_next_call (start-CHUNK_SIZE, end-CHUNK_SIZE);
+}
+
+
+void init_zclient (struct thread_master *master, char *lm_zserv_path)
+{
+               if (lm_zserv_path)
+                               zclient_serv_path_set(lm_zserv_path);
+
+               zclient = zclient_new(master);
+               /* zclient_init(zclient, ZEBRA_LABEL_MANAGER, 0); */
+               zclient->sock = -1;
+               zclient->redist_default = ZEBRA_ROUTE_LDP;
+               zclient->instance = instance;
+               if (zclient_socket_connect (zclient) < 0) {
+                               printf ("Error connecting synchronous zclient!\n");
+                               exit (1);
+               }
+
+}
+
+int main (int argc, char *argv[])
+{
+               struct thread_master *master;
+               struct thread            thread;
+               int ret;
+
+               printf ("Sequence to be tested: %s\n", sequence);
+
+               master = thread_master_create();
+               init_zclient (master, ZSERV_PATH);
+
+               zebra_send_label_manager_connect ();
+
+               return 0;
+}
index 1910e7b80f0871e02df885870d70f1eff83bd71b..50bc065c6ed84d7d9acf2b1e9f828cb986573cd2 100644 (file)
@@ -45,7 +45,7 @@ zebra_SOURCES = \
        $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \
        zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \
        $(protobuf_srcs) zebra_mroute.c \
-       $(dev_srcs)
+       $(dev_srcs) label_manager.c
 
 testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
        zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \
@@ -60,7 +60,7 @@ noinst_HEADERS = \
        rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_rnh.h \
        zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \
        zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \
-       kernel_netlink.h if_netlink.h zebra_mroute.h
+       kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h
 
 zebra_LDADD = $(otherobj) ../lib/libfrr.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS)
 
diff --git a/zebra/label_manager.c b/zebra/label_manager.c
new file mode 100644 (file)
index 0000000..2e96c4e
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Label Manager for FRR
+ *
+ * Copyright (C) 2017 by Bingen Eguzkitza,
+ *                       Volta Networks Inc.
+ *
+ * This file is part of FreeRangeRouting (FRR)
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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 FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "zebra.h"
+#include "zserv.h"
+#include "lib/log.h"
+#include "lib/memory.h"
+#include "lib/mpls.h"
+#include "lib/network.h"
+#include "lib/stream.h"
+#include "lib/zclient.h"
+
+#include "label_manager.h"
+
+#define CONNECTION_DELAY 5
+
+struct label_manager lbl_mgr;
+
+DEFINE_MGROUP(LBL_MGR, "Label Manager");
+DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk");
+
+/* In case this zebra daemon is not acting as label manager,
+ * it will be a proxy to relay messages to external label manager
+ * This zclient thus is to connect to it
+ */
+static struct zclient *zclient;
+bool lm_is_external;
+
+static void delete_label_chunk(void *val)
+{
+       XFREE(MTYPE_LM_CHUNK, val);
+}
+
+/**
+ * Receive a request to get or release a label chunk and forward it to external
+ * label manager.
+ *
+ * It's called from zserv in case it's not an actual label manager, but just a
+ * proxy.
+ *
+ * @param cmd Type of request (connect, get or release)
+ * @param src Input buffer from zserv
+ * @return 0 on success, -1 otherwise
+ */
+int zread_relay_label_manager_request(int cmd, struct zserv *zserv)
+{
+       struct stream *src, *dst;
+       int ret;
+
+       if (zclient->sock < 0) {
+               zlog_err("%s: Error relaying label chunk request: no zclient socket",
+                                __func__);
+               return -1;
+       }
+       /* Send request to external label manager */
+       src = zserv->ibuf;
+       dst = zclient->obuf;
+
+       stream_copy(dst, src);
+
+       ret = writen(zclient->sock, dst->data, stream_get_endp(dst));
+       if (ret <= 0) {
+               zlog_err("%s: Error relaying label chunk request: %s", __func__,
+                        strerror(errno));
+               return -1;
+       }
+       zlog_debug("%s: Label chunk request relayed. %d bytes sent", __func__,
+                  ret);
+
+       /* Release label chunk has no response */
+       if (cmd == ZEBRA_RELEASE_LABEL_CHUNK)
+               return 0;
+
+       /* read response */
+       src = zclient->ibuf;
+       dst = zserv->obuf;
+
+       stream_reset(src);
+
+       u_int16_t size;
+       u_char marker;
+       u_char version;
+       vrf_id_t vrf_id;
+       u_int16_t resp_cmd;
+       ret = zclient_read_header(src, zclient->sock, &size, &marker, &version,
+                                 &vrf_id, &resp_cmd);
+       if (ret < 0) {
+               zlog_err("%s: Error reading label chunk response: %s", __func__,
+                        strerror(errno));
+               return -1;
+       }
+       zlog_debug("%s: Label chunk response received, %d bytes", __func__,
+                  size);
+
+       /* send response back */
+       stream_copy(dst, src);
+       stream_copy(zserv->obuf, zclient->ibuf);
+       ret = writen(zserv->sock, dst->data, stream_get_endp(dst));
+       if (ret <= 0) {
+               zlog_err("%s: Error sending label chunk response back: %s",
+                        __func__, strerror(errno));
+               return -1;
+       }
+       zlog_debug("%s: Label chunk response (%d bytes) sent back", __func__,
+                  ret);
+
+       return 0;
+}
+
+static int zclient_connect(struct thread *t)
+{
+       zclient->t_connect = NULL;
+
+       if (zclient->sock >= 0)
+               return 0;
+
+       if (zclient_socket_connect(zclient) < 0) {
+               zlog_err("Error connecting synchronous zclient!");
+               THREAD_TIMER_ON(zebrad.master, zclient->t_connect,
+                                               zclient_connect,
+                                               zclient, CONNECTION_DELAY);
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Function to initialize zclient in case this is not an actual
+ * label manager, but just a proxy to an external one.
+ *
+ * @param lm_zserv_path Path to zserv socket of external label manager
+ */
+static void lm_zclient_init(char *lm_zserv_path)
+{
+       if (lm_zserv_path)
+               zclient_serv_path_set(lm_zserv_path);
+
+       /* Set default values. */
+       zclient = zclient_new(zebrad.master);
+       zclient->sock = -1;
+       zclient->t_connect = NULL;
+       zclient_connect (NULL);
+}
+
+/**
+ * Init label manager (or proxy to an external one)
+ */
+void label_manager_init(char *lm_zserv_path)
+{
+       /* this is an actual label manager */
+       if (!lm_zserv_path) {
+               zlog_debug("Initializing own label manager");
+               lm_is_external = false;
+               lbl_mgr.lc_list = list_new();
+               lbl_mgr.lc_list->del = delete_label_chunk;
+       } else {                /* it's acting just as a proxy */
+               zlog_debug("Initializing external label manager at %s",
+                          lm_zserv_path);
+               lm_is_external = true;
+               lm_zclient_init(lm_zserv_path);
+       }
+}
+
+/**
+ * Core function, assigns label cunks
+ *
+ * It first searches through the list to check if there's one available
+ * (previously released). Otherwise it creates and assigns a new one
+ *
+ * @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
+ */
+struct label_manager_chunk *assign_label_chunk(u_char proto, u_short instance,
+                                              u_char keep, uint32_t size)
+{
+       struct label_manager_chunk *lmc;
+       struct listnode *node;
+
+       node = lbl_mgr.lc_list->head;
+       /* 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) {
+                       lmc->proto = proto;
+                       lmc->instance = instance;
+                       lmc->keep = keep;
+                       return lmc;
+               }
+       }
+       /* otherwise create a new one */
+       lmc = XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk));
+
+       if (list_isempty(lbl_mgr.lc_list))
+               lmc->start = MPLS_MIN_UNRESERVED_LABEL;
+       else
+               lmc->start = ((struct label_manager_chunk *)
+                             listgetdata(listtail(lbl_mgr.lc_list)))->end + 1;
+       if (lmc->start > MPLS_MAX_UNRESERVED_LABEL - size + 1) {
+               zlog_err("Reached max labels. Start: %u, size: %u", lmc->start,
+                        size);
+               return NULL;
+       }
+       lmc->end = lmc->start + size - 1;
+       lmc->proto = proto;
+       lmc->instance = instance;
+       lmc->keep = keep;
+       listnode_add(lbl_mgr.lc_list, lmc);
+
+       return lmc;
+}
+
+/**
+ * Core function, release no longer used label cunks
+ *
+ * @param proto Daemon protocol of client, to identify the owner
+ * @param instance Instance, to identify the owner
+ * @param start First label of the chunk
+ * @param end Last label of the chunk
+ * @return 0 on success, -1 otherwise
+ */
+int
+release_label_chunk(u_char proto, u_short instance, uint32_t start,
+                   uint32_t end)
+{
+       struct listnode *node;
+       struct label_manager_chunk *lmc;
+       int ret = -1;
+
+       /* check that size matches */
+       zlog_debug("Releasing label chunk: %u - %u", start, end);
+       /* find chunk and disown */
+       for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
+               if (lmc->start != start)
+                       continue;
+               if (lmc->end != end)
+                       continue;
+               if (lmc->proto != proto || lmc->instance != instance) {
+                       zlog_err("%s: Daemon mismatch!!", __func__);
+                       continue;
+               }
+               lmc->proto = NO_PROTO;
+               lmc->instance = 0;
+               lmc->keep = 0;
+               ret = 0;
+               break;
+       }
+       if (ret != 0)
+               zlog_err("%s: Label chunk not released!!", __func__);
+
+       return ret;
+}
+
+/**
+ * Release label chunks from a client.
+ *
+ * Called on client disconnection or reconnection. It only releases chunks
+ * with empty keep value.
+ *
+ * @param proto Daemon protocol of client, to identify the owner
+ * @param instance Instance, to identify the owner
+ * @return Number of chunks released
+ */
+int release_daemon_chunks(u_char proto, u_short instance)
+{
+       struct listnode *node;
+       struct label_manager_chunk *lmc;
+       int count = 0;
+       int ret;
+
+       for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
+               if (lmc->proto == proto && lmc->instance == instance
+                   && lmc->keep == 0) {
+                       ret =
+                           release_label_chunk(lmc->proto, lmc->instance,
+                                               lmc->start, lmc->end);
+                       if (ret == 0)
+                               count++;
+               }
+       }
+
+       zlog_debug("%s: Released %d label chunks", __func__, count);
+
+       return count;
+}
+
+void label_manager_close()
+{
+       list_delete(lbl_mgr.lc_list);
+}
diff --git a/zebra/label_manager.h b/zebra/label_manager.h
new file mode 100644 (file)
index 0000000..0c6a5eb
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Label Manager header
+ *
+ * Copyright (C) 2017 by Bingen Eguzkitza,
+ *                       Volta Networks Inc.
+ *
+ * This file is part of FreeRangeRouting (FRR)
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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 FRR; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _LABEL_MANAGER_H
+#define _LABEL_MANAGER_H
+
+#include <stdint.h>
+
+#include "lib/linklist.h"
+#include "lib/thread.h"
+
+#define NO_PROTO 0
+
+/*
+ * Label chunk struct
+ * Client daemon which the chunk belongs to can be identified by either
+ * proto (daemon protocol) + instance.
+ * If the client then passes a non-empty value to keep field when it requests
+ * for chunks, the chunks won't be garbage collected and the client will be
+ * responsible of its release.
+ * Otherwise, if the keep field is not set (value 0) for the chunk, it will be
+ * automatically released when the client disconnects or when it reconnects
+ * (in case it died unexpectedly, we can know it's the same because it will have
+ * the same proto and instance values)
+ */
+struct label_manager_chunk {
+       u_char proto;
+       u_short instance;
+       u_char keep;
+       uint32_t start;         /* First label of the chunk */
+       uint32_t end;           /* Last label of the chunk */
+};
+
+/*
+ * Main label manager struct
+ * Holds a linked list of label chunks.
+ */
+struct label_manager {
+       struct list *lc_list;
+};
+
+bool lm_is_external;
+
+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(u_char proto, u_short instance,
+                                              u_char keep, uint32_t size);
+int release_label_chunk(u_char proto, u_short instance, uint32_t start,
+                       uint32_t end);
+int release_daemon_chunks(u_char proto, u_short instance);
+void label_manager_close(void);
+
+#endif                         /* _LABEL_MANAGER_H */
index 98177a423e5bfacdf363c03b9482d2aefd283b8d..26e66f961d20398c447b40946d7649da2390d078 100644 (file)
@@ -16,7 +16,7 @@
  * You should have received a copy of the GNU General Public License
  * along with GNU Zebra; see the file COPYING.  If not, write to the Free
  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.  
+ * 02111-1307, USA.
  */
 
 #include <zebra.h>
@@ -48,6 +48,7 @@
 #include "zebra/zebra_ns.h"
 #include "zebra/redistribute.h"
 #include "zebra/zebra_mpls.h"
+#include "zebra/label_manager.h"
 
 #define ZEBRA_PTM_SUPPORT
 
@@ -78,7 +79,7 @@ u_int32_t nl_rcvbufsize = 4194304;
 #endif /* HAVE_NETLINK */
 
 /* Command line options. */
-struct option longopts[] = 
+struct option longopts[] =
 {
   { "batch",        no_argument,       NULL, 'b'},
   { "allow_delete", no_argument,       NULL, 'a'},
@@ -86,6 +87,7 @@ struct option longopts[] =
   { "fpm_format",   required_argument, NULL, 'F'},
   { "socket",       required_argument, NULL, 'z'},
   { "ecmp",         required_argument, NULL, 'e'},
+  { "label_socket", no_argument,       NULL, 'l'},
   { "retain",       no_argument,       NULL, 'r'},
 #ifdef HAVE_NETLINK
   { "nl-bufsize",   required_argument, NULL, 's'},
@@ -93,7 +95,7 @@ struct option longopts[] =
   { 0 }
 };
 
-zebra_capabilities_t _caps_p [] = 
+zebra_capabilities_t _caps_p [] =
 {
   ZCAP_NET_ADMIN,
   ZCAP_SYS_ADMIN,
@@ -118,7 +120,7 @@ struct zebra_privs_t zserv_privs =
 unsigned int multipath_num = MULTIPATH_NUM;
 
 /* SIGHUP handler. */
-static void 
+static void
 sighup (void)
 {
   zlog_info ("SIGHUP received");
@@ -182,8 +184,8 @@ sigusr1 (void)
 
 struct quagga_signal_t zebra_signals[] =
 {
-  { 
-    .signal = SIGHUP, 
+  {
+    .signal = SIGHUP,
     .handler = &sighup,
   },
   {
@@ -220,10 +222,13 @@ main (int argc, char **argv)
   // int batch_mode = 0;
   char *zserv_path = NULL;
   char *fpm_format = NULL;
+  /* Socket to external label manager */
+  char *lblmgr_path = NULL;
+
 
   frr_preinit(&zebra_di, argc, argv);
 
-  frr_opt_add("bakF:z:e:r"
+  frr_opt_add("bakF:z:e:l:r"
 #ifdef HAVE_NETLINK
        "s:"
 #endif
@@ -233,6 +238,7 @@ main (int argc, char **argv)
        "  -F, --fpm_format   Set fpm format to 'netlink' or 'protobuf'\n"
        "  -z, --socket       Set path of zebra socket\n"
        "  -e, --ecmp         Specify ECMP to use.\n"
+       "  -l, --label_socket Socket to external label manager\n"\
        "  -k, --keep_kernel  Don't delete old routes which installed by zebra.\n"
        "  -r, --retain       When program terminates, retain added route by zebra.\n"
 #ifdef HAVE_NETLINK
@@ -247,7 +253,7 @@ main (int argc, char **argv)
       if (opt == EOF)
        break;
 
-      switch (opt) 
+      switch (opt)
        {
        case 0:
          break;
@@ -274,6 +280,9 @@ main (int argc, char **argv)
        case 'z':
          zserv_path = optarg;
          break;
+       case 'l':
+         lblmgr_path = optarg;
+         break;
        case 'r':
          retain_mode = 1;
          break;
@@ -359,6 +368,9 @@ main (int argc, char **argv)
   /* This must be done only after locking pidfile (bug #403). */
   zebra_zserv_socket_init (zserv_path);
 
+  /* Init label manager */
+  label_manager_init (lblmgr_path);
+
   frr_run (zebrad.master);
 
   /* Not reached... */
index 064489acd99377ea896dfe42efb8455b449e66e8..f9205a12c80fd8ae8ee21dcefba3094b81b119ef 100644 (file)
@@ -55,6 +55,7 @@
 #include "zebra/zebra_mpls.h"
 #include "zebra/zebra_fpm.h"
 #include "zebra/zebra_mroute.h"
+#include "zebra/label_manager.h"
 
 /* Event list of zebra. */
 enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE };
@@ -113,6 +114,9 @@ zebra_server_send_message(struct zserv *client)
   if (client->t_suicide)
     return -1;
 
+  if (client->is_synchronous)
+    return 0;
+
   stream_set_getp(client->obuf, 0);
   client->last_write_cmd = stream_getw_from(client->obuf, 6);
   switch (buffer_write(client->wb, client->sock, STREAM_DATA(client->obuf),
@@ -1770,6 +1774,167 @@ zread_mpls_labels (int command, struct zserv *client, u_short length,
                         distance, out_label);
     }
 }
+/* Send response to a label manager connect request to client */
+static int
+zsend_label_manager_connect_response (struct zserv *client, vrf_id_t vrf_id, u_short result)
+{
+  struct stream *s;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_LABEL_MANAGER_CONNECT, vrf_id);
+
+  /* result */
+  stream_putc (s, result);
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return writen (client->sock, s->data, stream_get_endp (s));
+}
+
+static void
+zread_label_manager_connect (struct zserv *client, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  /* type of protocol (lib/zebra.h) */
+  u_char proto;
+  u_short instance;
+
+  /* Get input stream.  */
+  s = client->ibuf;
+
+  /* Get data. */
+  proto = stream_getc (s);
+  instance = stream_getw (s);
+
+  /* accept only dynamic routing protocols */
+  if ((proto >= ZEBRA_ROUTE_MAX)
+      ||  (proto <= ZEBRA_ROUTE_STATIC))
+    {
+      zlog_err ("client %d has wrong protocol %s",
+                client->sock, zebra_route_string(proto));
+      zsend_label_manager_connect_response (client, vrf_id, 1);
+      return;
+    }
+  zlog_notice ("client %d with instance %u connected as %s",
+               client->sock, instance, zebra_route_string(proto));
+  client->proto = proto;
+  client->instance = instance;
+
+  /*
+    Release previous labels of same protocol and instance.
+    This is done in case it restarted from an unexpected shutdown.
+  */
+  release_daemon_chunks (proto, instance);
+
+  zlog_debug (" Label Manager client connected: sock %d, proto %s, instance %u",
+              client->sock, zebra_route_string(proto), instance);
+  /* send response back */
+  zsend_label_manager_connect_response (client, vrf_id, 0);
+}
+/* Send response to a get label chunk request to client */
+static int
+zsend_assign_label_chunk_response (struct zserv *client, vrf_id_t vrf_id,
+                                   struct label_manager_chunk *lmc)
+{
+  struct stream *s;
+
+  s = client->obuf;
+  stream_reset (s);
+
+  zserv_create_header (s, ZEBRA_GET_LABEL_CHUNK, vrf_id);
+
+  if (lmc)
+    {
+      /* keep */
+      stream_putc (s, lmc->keep);
+      /* start and end labels */
+      stream_putl (s, lmc->start);
+      stream_putl (s, lmc->end);
+
+    }
+
+  /* Write packet size. */
+  stream_putw_at (s, 0, stream_get_endp (s));
+
+  return writen (client->sock, s->data, stream_get_endp (s));
+}
+
+static void
+zread_get_label_chunk (struct zserv *client, vrf_id_t vrf_id)
+{
+  struct stream *s;
+  u_char keep;
+  uint32_t size;
+  struct label_manager_chunk *lmc;
+
+  /* Get input stream.  */
+  s = client->ibuf;
+
+  /* Get data. */
+  keep = stream_getc (s);
+  size = stream_getl (s);
+
+  lmc = assign_label_chunk (client->proto, client->instance, keep, size);
+  if (!lmc)
+    zlog_err ("%s: Unable to assign Label Chunk of size %u", __func__, size);
+  else
+    zlog_debug ("Assigned Label Chunk %u - %u to %u",
+                lmc->start, lmc->end, keep);
+  /* send response back */
+  zsend_assign_label_chunk_response (client, vrf_id, lmc);
+}
+
+static void
+zread_release_label_chunk (struct zserv *client)
+{
+  struct stream *s;
+  uint32_t start, end;
+
+  /* Get input stream.  */
+  s = client->ibuf;
+
+  /* Get data. */
+  start = stream_getl (s);
+  end = stream_getl (s);
+
+  release_label_chunk (client->proto, client->instance, start, end);
+}
+static void
+zread_label_manager_request (int cmd, struct zserv *client, vrf_id_t vrf_id)
+{
+  /* to avoid sending other messages like ZERBA_INTERFACE_UP */
+  if (cmd == ZEBRA_LABEL_MANAGER_CONNECT)
+    client->is_synchronous = 1;
+
+  /* external label manager */
+  if (lm_is_external)
+    {
+      if (zread_relay_label_manager_request (cmd, client) != 0)
+        zsend_label_manager_connect_response (client, vrf_id, 1);
+    }
+  /* this is a label manager */
+  else
+    {
+      if (cmd == ZEBRA_LABEL_MANAGER_CONNECT)
+        zread_label_manager_connect (client, vrf_id);
+      else
+        {
+          /* Sanity: don't allow 'unidentified' requests */
+          if (!client->proto)
+            {
+              zlog_err ("Got label request from an unidentified client");
+              return;
+            }
+          if (cmd == ZEBRA_GET_LABEL_CHUNK)
+            zread_get_label_chunk (client, vrf_id);
+          else if (cmd == ZEBRA_RELEASE_LABEL_CHUNK)
+            zread_release_label_chunk (client);
+        }
+    }
+}
 
 /* Cleanup registered nexthops (across VRFs) upon client disconnect. */
 static void
@@ -1807,6 +1972,9 @@ zebra_client_close (struct zserv *client)
   /* Cleanup any registered nexthops - across all VRFs. */
   zebra_client_close_cleanup_rnh (client);
 
+  /* Release Label Manager chunks */
+  release_daemon_chunks (client->proto, client->instance);
+
   /* Close file descriptor. */
   if (client->sock)
     {
@@ -1868,6 +2036,9 @@ zebra_client_create (int sock)
   client->ifinfo = vrf_bitmap_init ();
   client->ridinfo = vrf_bitmap_init ();
 
+  /* by default, it's not a synchronous client */
+  client->is_synchronous = 0;
+
   /* Add this client to linked list. */
   listnode_add (zebrad.client_list, client);
   
@@ -2087,6 +2258,11 @@ zebra_client_read (struct thread *thread)
     case ZEBRA_IPMR_ROUTE_STATS:
       zebra_ipmr_route_stats (client, sock, length, zvrf);
       break;
+    case ZEBRA_LABEL_MANAGER_CONNECT:
+    case ZEBRA_GET_LABEL_CHUNK:
+    case ZEBRA_RELEASE_LABEL_CHUNK:
+      zread_label_manager_request (command, client, vrf_id);
+      break;
     default:
       zlog_info ("Zebra received unknown command %d", command);
       break;
index 21cf1004bf62b33c03399ea2c3273f0b253ccf15..9e3473823893c0c18d66980ebbc84b97dbe8ac6e 100644 (file)
@@ -78,6 +78,7 @@ struct zserv
   /* client's protocol */
   u_char proto;
   u_short instance;
+  u_char is_synchronous;
 
   /* Statistics */
   u_int32_t redist_v4_add_cnt;