diff options
Diffstat (limited to 'zebra/label_manager.c')
| -rw-r--r-- | zebra/label_manager.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/zebra/label_manager.c b/zebra/label_manager.c new file mode 100644 index 0000000000..2e96c4ebaf --- /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); +} |
