Merge pull request #285 from bingen/label_manager_3

Implement generic label manager
This commit is contained in:
Donald Sharp 2017-03-21 11:40:20 -04:00 committed by GitHub
commit 80f16600af
15 changed files with 1157 additions and 28 deletions

View File

@ -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,38 @@ static struct quagga_signal_t lde_signals[] =
},
};
static void
lde_sleep (void)
{
sleep(1);
if (lde_signals[0].caught || lde_signals[1].caught)
lde_shutdown();
}
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");
lde_sleep();
}
/* Connect to label manager */
while (lm_label_manager_connect (zclient_sync) != 0) {
fprintf(stderr, "Error connecting to label manager!\n");
lde_sleep();
}
}
/* 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 +194,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 +633,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 +697,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 +1573,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");
lde_sleep();
}
}
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 label;
}
/* TODO: not used yet. Have to check label release */
static void
lde_release_label_chunk(void)
{
return;
}

View 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);

View 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);

View 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 {

View File

@ -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

View 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

View 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

View 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

150
tests/test_lblmgr.c Normal file
View File

@ -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;
}

View 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)

317
zebra/label_manager.c Normal file
View File

@ -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);
}

74
zebra/label_manager.h Normal file
View File

@ -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 */

View File

@ -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
@ -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'},
@ -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
@ -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... */

View 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;

View 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;