diff options
-rw-r--r-- | ldpd/lde.c | 150 | ||||
-rw-r--r-- | ldpd/lde.h | 9 | ||||
-rw-r--r-- | ldpd/ldpd.c | 35 | ||||
-rw-r--r-- | ldpd/ldpd.h | 4 | ||||
-rw-r--r-- | lib/log.c | 3 | ||||
-rw-r--r-- | lib/mpls.h | 2 | ||||
-rw-r--r-- | lib/zclient.c | 218 | ||||
-rw-r--r-- | lib/zclient.h | 7 | ||||
-rw-r--r-- | tests/test_lblmgr.c | 150 | ||||
-rw-r--r-- | zebra/Makefile.am | 4 | ||||
-rw-r--r-- | zebra/label_manager.c | 317 | ||||
-rw-r--r-- | zebra/label_manager.h | 74 | ||||
-rw-r--r-- | zebra/main.c | 28 | ||||
-rw-r--r-- | zebra/zserv.c | 176 | ||||
-rw-r--r-- | zebra/zserv.h | 1 |
15 files changed, 1150 insertions, 28 deletions
diff --git a/ldpd/lde.c b/ldpd/lde.c index 08339c720..d4b95637f 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -33,6 +33,10 @@ #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; +} diff --git a/ldpd/lde.h b/ldpd/lde.h index b5bcb42c8..d9836097a 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -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); diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 98ea5ca53..8fa33ff09 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -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); diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 0a7e1177b..c665656aa 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -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 { @@ -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 diff --git a/lib/mpls.h b/lib/mpls.h index b5c1a653b..13a46e101 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -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 diff --git a/lib/zclient.c b/lib/zclient.c index 859751deb..6f7fb9b1b 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -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 diff --git a/lib/zclient.h b/lib/zclient.h index 89fc865c7..d3d0a202c 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -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 index 000000000..4a4aaa001 --- /dev/null +++ b/tests/test_lblmgr.c @@ -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; +} diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 1910e7b80..50bc065c6 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -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 index 000000000..2e96c4eba --- /dev/null +++ b/zebra/label_manager.c @@ -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 index 000000000..0c6a5ebc7 --- /dev/null +++ b/zebra/label_manager.h @@ -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 */ diff --git a/zebra/main.c b/zebra/main.c index 98177a423..26e66f961 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -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... */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 064489acd..f9205a12c 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -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; diff --git a/zebra/zserv.h b/zebra/zserv.h index 21cf1004b..9e3473823 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -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; |