From 75fb51c184ef1c575aa8160d9c95b91c96b8e129 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Mar 2018 17:57:29 +0100 Subject: [PATCH 1/4] lib: add framework for allocating routing table identifier IDs The library changes add 3 new messages to exchange between daemons and ZEBRA. - ZEBRA_TABLE_MANAGER_CONNECT, - ZEBRA_GET_TABLE_CHUNK, - ZEBRA_RELEASE_TABLE_CHUNK, the need is that routing tables identifier are shared by various services. For the current case, policy routing enhancements are planned to be used in FRR. Poliy routing relies on routing tables identifiers from kernels. It will be mainly used by the future policy based routing daemon, but not only. In the flowspec case, the BGP will need also to inject policy routing information into specific routing tables. For that, the proposal is made to let zebra give the appropriate range that is needed for all daemons. Signed-off-by: Philippe Guibert --- lib/log.c | 3 + lib/zclient.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/zclient.h | 9 +++ 3 files changed, 174 insertions(+) diff --git a/lib/log.c b/lib/log.c index 35298f1fa3..f5aff756dd 100644 --- a/lib/log.c +++ b/lib/log.c @@ -965,6 +965,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_RULE_ADD), DESC_ENTRY(ZEBRA_RULE_DELETE), DESC_ENTRY(ZEBRA_RULE_NOTIFY_OWNER), + DESC_ENTRY(ZEBRA_TABLE_MANAGER_CONNECT), + DESC_ENTRY(ZEBRA_GET_TABLE_CHUNK), + DESC_ENTRY(ZEBRA_RELEASE_TABLE_CHUNK), }; #undef DESC_ENTRY diff --git a/lib/zclient.c b/lib/zclient.c index 4659bce1b1..7308beaaf2 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2123,6 +2123,168 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start, return 0; } +/** + * Connect to table 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 table manager (zebra) + * @result Result of response + */ +int tm_table_manager_connect(struct zclient *zclient) +{ + int ret; + struct stream *s; + uint8_t result; + + if (zclient_debug) + zlog_debug("Connecting to Table Manager"); + + if (zclient->sock < 0) + return -1; + + /* send request */ + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_TABLE_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 = zclient_send_message(zclient); + if (ret < 0) + return -1; + + if (zclient_debug) + zlog_debug("%s: Table manager connect request sent", + __func__); + + /* read response */ + if (zclient_read_sync_response(zclient, ZEBRA_TABLE_MANAGER_CONNECT) + != 0) + return -1; + + /* result */ + s = zclient->ibuf; + STREAM_GETC(s, result); + if (zclient_debug) + zlog_debug( + "%s: Table Manager connect response received, result %u", + __func__, result); + + return (int)result; +stream_failure: + return 0; +} + +/** + * Function to request a table chunk in a syncronous way + * + * It first writes the request to zclient output buffer and then + * immediately reads the answer from the input buffer. + * + * @param zclient Zclient used to connect to table manager (zebra) + * @param chunk_size Amount of table requested + * @param start to write first assigned chunk table RT ID to + * @param end To write last assigned chunk table RT ID to + * @result 0 on success, -1 otherwise + */ +int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, + uint32_t *start, uint32_t *end) +{ + int ret; + struct stream *s; + + if (zclient_debug) + zlog_debug("Getting Table Chunk"); + + if (zclient->sock < 0) + return -1; + + /* send request */ + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_GET_TABLE_CHUNK, VRF_DEFAULT); + /* 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; + } + if (zclient_debug) + zlog_debug("%s: Table chunk request (%d bytes) sent", __func__, + ret); + + /* read response */ + if (zclient_read_sync_response(zclient, ZEBRA_GET_TABLE_CHUNK) != 0) + return -1; + + s = zclient->ibuf; + /* start and end table IDs */ + STREAM_GETL(s, *start); + STREAM_GETL(s, *end); + + if (zclient_debug) + zlog_debug("Table Chunk assign: %u - %u ", *start, *end); + +stream_failure: + return 0; +} + +/** + * Function to release a table chunk + * + * @param zclient Zclient used to connect to table manager (zebra) + * @param start First label of table + * @param end Last label of chunk + * @result 0 on success, -1 otherwise + */ +int tm_release_table_chunk(struct zclient *zclient, uint32_t start, + uint32_t end) +{ + struct stream *s; + + if (zclient_debug) + zlog_debug("Releasing Table Chunk"); + + if (zclient->sock < 0) + return -1; + + /* send request */ + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_RELEASE_TABLE_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)); + + return zclient_send_message(zclient); +} + + int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) { struct stream *s; diff --git a/lib/zclient.h b/lib/zclient.h index b51c518463..04f2a5c178 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -132,6 +132,9 @@ typedef enum { ZEBRA_RULE_ADD, ZEBRA_RULE_DELETE, ZEBRA_RULE_NOTIFY_OWNER, + ZEBRA_TABLE_MANAGER_CONNECT, + ZEBRA_GET_TABLE_CHUNK, + ZEBRA_RELEASE_TABLE_CHUNK, } zebra_message_types_t; struct redist_proto { @@ -538,6 +541,12 @@ extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t *end); extern int lm_release_label_chunk(struct zclient *zclient, uint32_t start, uint32_t end); +extern int tm_table_manager_connect(struct zclient *zclient); +extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, + uint32_t *start, uint32_t *end); +extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, + uint32_t end); + extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw); extern void zebra_read_pw_status_update(int command, struct zclient *zclient, From 502612797f23a5e7e0044c0e74d10fe6a172c6c9 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Mar 2018 18:03:38 +0100 Subject: [PATCH 2/4] zebra: add table manager files The range is given from table manager from zebra daemon. There are 2 ranges available for table identifier: - [1;252] and [256;0xffffffff] If the wished size enters in the first range, then the start and end range of table identifier is given within the first range. Otherwise, the second range is given, and an appropriate range is given. Note that for now, the case of the VRF table identifier used is not taken into account. Meaning that there may be overlapping. There are two cases to handle: - case a vrf lite is allocated after the zebra and various other daemons started. - case a vrf lite is initialised and the daemons then start The second case is easy to handle. For the former case, I am not so sure. Signed-off-by: Philippe Guibert --- zebra/subdir.am | 2 + zebra/table_manager.c | 235 ++++++++++++++++++++++++++++++++++++++++++ zebra/table_manager.h | 63 +++++++++++ 3 files changed, 300 insertions(+) create mode 100644 zebra/table_manager.c create mode 100644 zebra/table_manager.h diff --git a/zebra/subdir.am b/zebra/subdir.am index ef157b7539..9dbff7d40c 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -70,6 +70,7 @@ zebra_zebra_SOURCES = \ zebra/zserv.c \ zebra/zebra_netns_id.c \ zebra/zebra_netns_notify.c \ + zebra/table_manager.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -113,6 +114,7 @@ noinst_HEADERS += \ zebra/zserv.h \ zebra/zebra_netns_id.h \ zebra/zebra_netns_notify.h \ + zebra/table_manager.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/table_manager.c b/zebra/table_manager.c new file mode 100644 index 0000000000..db07f402f3 --- /dev/null +++ b/zebra/table_manager.c @@ -0,0 +1,235 @@ +/* zebra table Manager for routing table identifier management + * Copyright (C) 2018 6WIND + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "zebra.h" +#include "zserv.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/table.h" +#include "lib/network.h" +#include "lib/stream.h" +#include "lib/zclient.h" +#include "lib/libfrr.h" +#include "lib/vrf.h" + +#include "zebra_vrf.h" +#include "label_manager.h" /* for NO_PROTO */ +#include "table_manager.h" + +/* routing table identifiers + * + */ +#ifdef SUNOS_5 +/* SunOS + */ +#else +#if !defined(GNU_LINUX) && !defined(SUNOS_5) +/* BSD systems + */ +#else +/* Linux Systems + */ +#define RT_TABLE_ID_LOCAL 255 +#define RT_TABLE_ID_MAIN 254 +#define RT_TABLE_ID_DEFAULT 253 +#define RT_TABLE_ID_COMPAT 252 +#define RT_TABLE_ID_UNSPEC 0 +#endif /* !def(GNU_LINUX) && !defined(SUNOS_5) */ +#endif /* SUNOS_5 */ +#define RT_TABLE_ID_UNRESERVED_MIN 1 +#define RT_TABLE_ID_UNRESERVED_MAX 0xffffffff + +struct table_manager tbl_mgr; + +DEFINE_MGROUP(TABLE_MGR, "Table Manager"); +DEFINE_MTYPE_STATIC(TABLE_MGR, TM_CHUNK, "Table Manager Chunk"); + +static void delete_table_chunk(void *val) +{ + XFREE(MTYPE_TM_CHUNK, val); +} + +/** + * Init table manager + */ +void table_manager_enable(ns_id_t ns_id) +{ + if (ns_id != NS_DEFAULT) + return; + tbl_mgr.lc_list = list_new(); + tbl_mgr.lc_list->del = delete_table_chunk; +} + +/** + * Core function, assigns table chunks + * + * 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 + * @para size Size of the table chunk + * @return Pointer to the assigned table chunk + */ +struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance, + uint32_t size) +{ + struct table_manager_chunk *tmc; + struct listnode *node; + uint32_t start; + + /* first check if there's one available */ + for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) { + if (tmc->proto == NO_PROTO + && tmc->end - tmc->start + 1 == size) { + tmc->proto = proto; + tmc->instance = instance; + return tmc; + } + } + /* otherwise create a new one */ + tmc = XCALLOC(MTYPE_TM_CHUNK, sizeof(struct table_manager_chunk)); + if (!tmc) + return NULL; + + /* table RT IDs range are [1;252] and [256;0xffffffff] + * - check if the requested range can be within the first range, + * otherwise elect second one + * - TODO : vrf-lites have their own table identifier. + * In that case, table_id should be removed from the table range. + */ + if (list_isempty(tbl_mgr.lc_list)) + start = RT_TABLE_ID_UNRESERVED_MIN; + else + start = ((struct table_manager_chunk *)listgetdata( + listtail(tbl_mgr.lc_list)))->end + 1; + +#ifdef SUNOS_5 +/* SunOS + */ +#else +#if !defined(GNU_LINUX) && !defined(SUNOS_5) +/* BSD systems + */ +#else +/* Linux Systems + */ + /* if not enough room space between MIN and COMPAT, + * then begin after LOCAL + */ + if (start < RT_TABLE_ID_COMPAT && (size > + RT_TABLE_ID_COMPAT + - RT_TABLE_ID_UNRESERVED_MIN)) + start = RT_TABLE_ID_LOCAL + 1; +#endif /* !def(GNU_LINUX) && !defined(SUNOS_5) */ +#endif /* SUNOS_5 */ + tmc->start = start; + if (RT_TABLE_ID_UNRESERVED_MAX - size + 1 < start) { + zlog_err("Reached max table id. Start/Size %u/%u", + start, size); + XFREE(MTYPE_TM_CHUNK, tmc); + return NULL; + } + tmc->end = tmc->start + size - 1; + tmc->proto = proto; + tmc->instance = instance; + listnode_add(tbl_mgr.lc_list, tmc); + + return tmc; +} + +/** + * Core function, release no longer used table chunks + * + * @param proto Daemon protocol of client, to identify the owner + * @param instance Instance, to identify the owner + * @param start First table RT ID of the chunk + * @param end Last table RT ID of the chunk + * @return 0 on success, -1 otherwise + */ +int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start, + uint32_t end) +{ + struct listnode *node; + struct table_manager_chunk *tmc; + int ret = -1; + + /* check that size matches */ + zlog_debug("Releasing table chunk: %u - %u", start, end); + /* find chunk and disown */ + for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) { + if (tmc->start != start) + continue; + if (tmc->end != end) + continue; + if (tmc->proto != proto || tmc->instance != instance) { + zlog_err("%s: Daemon mismatch!!", __func__); + continue; + } + tmc->proto = NO_PROTO; + tmc->instance = 0; + ret = 0; + break; + } + if (ret != 0) + zlog_err("%s: Table chunk not released!!", __func__); + + return ret; +} + +/** + * Release table 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_table_chunks(uint8_t proto, uint16_t instance) +{ + struct listnode *node; + struct table_manager_chunk *tmc; + int count = 0; + int ret; + + for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) { + if (tmc->proto == proto && tmc->instance == instance) { + ret = release_table_chunk(tmc->proto, tmc->instance, + tmc->start, tmc->end); + if (ret == 0) + count++; + } + } + + zlog_debug("%s: Released %d table chunks", __func__, count); + + return count; +} + +void table_manager_disable(ns_id_t ns_id) +{ + if (ns_id != NS_DEFAULT) + return; + list_delete_and_null(&tbl_mgr.lc_list); +} diff --git a/zebra/table_manager.h b/zebra/table_manager.h new file mode 100644 index 0000000000..527d5c29e8 --- /dev/null +++ b/zebra/table_manager.h @@ -0,0 +1,63 @@ +/* zebra table Manager for routing table identifier management + * Copyright (C) 2018 6WIND + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _TABLE_MANAGER_H +#define _TABLE_MANAGER_H + +#include + +#include "lib/linklist.h" +#include "lib/thread.h" + +/* + * Table chunk struct + * Client daemon which the chunk belongs to can be identified by either + * proto (daemon protocol) + instance + VRF. + * 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 table_manager_chunk { + vrf_id_t vrf_id; + uint8_t proto; + uint16_t instance; + uint32_t start; /* First table RT ID of the chunk */ + uint32_t end; /* Last table RT ID of the chunk */ +}; + +/* + * Main table manager struct + * Holds a linked list of table chunks. + */ +struct table_manager { + struct list *lc_list; +}; + +void table_manager_enable(ns_id_t ns_id); +struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance, + uint32_t size); +int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start, + uint32_t end); +int release_daemon_table_chunks(uint8_t proto, uint16_t instance); +void table_manager_disable(ns_id_t ns_id); + +#endif /* _TABLE_MANAGER_H */ From 8288a24fc1b65cca0ff41bfa213bb9528d998d4f Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Mar 2018 18:07:23 +0100 Subject: [PATCH 3/4] zebra: add the handling of table ids from remote daemons This commit is connecting the table manager with remote daemons by handling the queries. As the function is similar in many points with label allocator, a function has been renamed. Signed-off-by: Philippe Guibert --- zebra/label_manager.c | 2 +- zebra/label_manager.h | 2 +- zebra/zebra_ns.c | 6 ++ zebra/zebra_vrf.c | 2 +- zebra/zserv.c | 152 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 158 insertions(+), 6 deletions(-) diff --git a/zebra/label_manager.c b/zebra/label_manager.c index ad881b819c..38869e80ec 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -363,7 +363,7 @@ int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, * @param instance Instance, to identify the owner * @return Number of chunks released */ -int release_daemon_chunks(uint8_t proto, unsigned short instance) +int release_daemon_label_chunks(uint8_t proto, unsigned short instance) { struct listnode *node; struct label_manager_chunk *lmc; diff --git a/zebra/label_manager.h b/zebra/label_manager.h index a26e195b71..4395e6897e 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -69,7 +69,7 @@ struct label_manager_chunk *assign_label_chunk(uint8_t proto, uint8_t keep, uint32_t size); int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, uint32_t end); -int release_daemon_chunks(uint8_t proto, unsigned short instance); +int release_daemon_label_chunks(uint8_t proto, unsigned short instance); void label_manager_close(void); #endif /* _LABEL_MANAGER_H */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 66b1131e39..7393f767af 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -38,6 +38,7 @@ #include "zebra_netns_id.h" #include "zebra_pbr.h" #include "rib.h" +#include "table_manager.h" extern struct zebra_privs_t zserv_privs; @@ -147,6 +148,9 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) interface_list(zns); route_read(zns); + /* Initiate Table Manager per ZNS */ + table_manager_enable(ns_id); + return 0; } @@ -259,6 +263,8 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) kernel_terminate(zns); + table_manager_disable(zns->ns_id); + zns->ns_id = NS_DEFAULT; return 0; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index dfb02f15a9..fe1b100575 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -122,8 +122,8 @@ static int zebra_vrf_enable(struct vrf *vrf) /* Inform clients that the VRF is now active. This is an * add for the clients. */ - zebra_vrf_add_update(zvrf); + zebra_vrf_add_update(zvrf); /* Allocate tables */ for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) diff --git a/zebra/zserv.c b/zebra/zserv.c index 538487a9cd..76e5ea2f08 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -61,6 +61,7 @@ #include "zebra/zebra_vxlan.h" #include "zebra/rt.h" #include "zebra/zebra_pbr.h" +#include "zebra/table_manager.h" /* Event list of zebra. */ enum event { ZEBRA_READ, ZEBRA_WRITE }; @@ -2186,6 +2187,59 @@ stream_failure: return; } +static int zsend_table_manager_connect_response(struct zserv *client, + vrf_id_t vrf_id, uint16_t result) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_TABLE_MANAGER_CONNECT, vrf_id); + + /* result */ + stream_putc(s, result); + + stream_putw_at(s, 0, stream_get_endp(s)); + + return zebra_server_send_message(client, s); +} + +/* Send response to a table manager connect request to client */ +static void zread_table_manager_connect(struct zserv *client, struct stream *msg, + vrf_id_t vrf_id) +{ + struct stream *s; + uint8_t proto; + uint16_t instance; + + s = msg; + + /* Get data. */ + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); + + /* 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_table_manager_connect_response(client, vrf_id, 1); + return; + } + zlog_notice("client %d with vrf %u instance %u connected as %s", + client->sock, vrf_id, 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_table_chunks(proto, instance); + + zsend_table_manager_connect_response(client, vrf_id, 0); + + stream_failure: + return; +} + static void zread_label_manager_connect(struct zserv *client, struct stream *msg, vrf_id_t vrf_id) { @@ -2217,7 +2271,7 @@ static void zread_label_manager_connect(struct zserv *client, Release previous labels of same protocol and instance. This is done in case it restarted from an unexpected shutdown. */ - release_daemon_chunks(proto, instance); + release_daemon_label_chunks(proto, instance); zlog_debug( " Label Manager client connected: sock %d, proto %s, vrf %u instance %u", @@ -2225,7 +2279,7 @@ static void zread_label_manager_connect(struct zserv *client, /* send response back */ zsend_label_manager_connect_response(client, vrf_id, 0); -stream_failure: + stream_failure: return; } @@ -2305,6 +2359,92 @@ static void zread_label_manager_request(ZAPI_HANDLER_ARGS) } } +/* Send response to a get table chunk request to client */ +static int zsend_assign_table_chunk_response(struct zserv *client, + vrf_id_t vrf_id, + struct table_manager_chunk *tmc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_GET_TABLE_CHUNK, vrf_id); + + if (tmc) { + /* start and end labels */ + stream_putl(s, tmc->start); + stream_putl(s, tmc->end); + } + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zebra_server_send_message(client, s); +} + +static void zread_get_table_chunk(struct zserv *client, struct stream *msg, + vrf_id_t vrf_id) +{ + struct stream *s; + uint32_t size; + struct table_manager_chunk *tmc; + + /* Get input stream. */ + s = msg; + + /* Get data. */ + STREAM_GETL(s, size); + + tmc = assign_table_chunk(client->proto, client->instance, size); + if (!tmc) + zlog_err("%s: Unable to assign Table Chunk of size %u", + __func__, size); + else + zlog_debug("Assigned Table Chunk %u - %u", tmc->start, + tmc->end); + /* send response back */ + zsend_assign_table_chunk_response(client, vrf_id, tmc); + +stream_failure: + return; +} + +static void zread_release_table_chunk(struct zserv *client, struct stream *msg) +{ + struct stream *s; + uint32_t start, end; + + /* Get input stream. */ + s = msg; + + /* Get data. */ + STREAM_GETL(s, start); + STREAM_GETL(s, end); + + release_table_chunk(client->proto, client->instance, start, end); + +stream_failure: + return; +} + +static void zread_table_manager_request(ZAPI_HANDLER_ARGS) +{ + /* to avoid sending other messages like ZERBA_INTERFACE_UP */ + if (hdr->command == ZEBRA_TABLE_MANAGER_CONNECT) + zread_table_manager_connect(client, msg, zvrf_id(zvrf)); + else { + /* Sanity: don't allow 'unidentified' requests */ + if (!client->proto) { + zlog_err( + "Got table request from an unidentified client"); + return; + } + if (hdr->command == ZEBRA_GET_TABLE_CHUNK) + zread_get_table_chunk(client, msg, + zvrf_id(zvrf)); + else if (hdr->command == ZEBRA_RELEASE_TABLE_CHUNK) + zread_release_table_chunk(client, msg); + } +} + static void zread_pseudowire(ZAPI_HANDLER_ARGS) { struct stream *s; @@ -2627,6 +2767,9 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_PW_UNSET] = zread_pseudowire, [ZEBRA_RULE_ADD] = zread_rule, [ZEBRA_RULE_DELETE] = zread_rule, + [ZEBRA_TABLE_MANAGER_CONNECT] = zread_table_manager_request, + [ZEBRA_GET_TABLE_CHUNK] = zread_table_manager_request, + [ZEBRA_RELEASE_TABLE_CHUNK] = zread_table_manager_request, }; static inline void zserv_handle_commands(struct zserv *client, @@ -2658,7 +2801,10 @@ static void zebra_client_free(struct zserv *client) zebra_client_close_cleanup_rnh(client); /* Release Label Manager chunks */ - release_daemon_chunks(client->proto, client->instance); + release_daemon_label_chunks(client->proto, client->instance); + + /* Release Table Manager chunks */ + release_daemon_table_chunks(client->proto, client->instance); /* Cleanup any FECs registered by this client. */ zebra_mpls_cleanup_fecs_for_client(vrf_info_lookup(VRF_DEFAULT), From b98f77282b83f519f2134661d43b845fd69d5f60 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Mar 2018 18:09:57 +0100 Subject: [PATCH 4/4] bgpd: add API to allocate a range of table identifiers In BGP, doing policy-routing requires to use table identifiers. Flowspec protocol will need to have that. 1 API from bgp zebra has been done to get the table chunk. Internally, onec flowspec is enabled, the BGP engine will try to connect smoothly to the table manager. If zebra is not connected, it will try to connect 10 seconds later. If zebra is connected, and it is success, then a polling mechanism each 60 seconds is put in place. All the internal mechanism has no impact on the BGP process. Signed-off-by: Philippe Guibert --- bgpd/bgp_zebra.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_zebra.h | 3 +++ bgpd/bgpd.c | 4 ++++ lib/zebra.h | 3 ++- zebra/zserv.c | 3 ++- 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 77416e3cfd..269dcc7cb6 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -970,6 +970,64 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p, return 0; } +static struct thread *bgp_tm_thread_connect; +static bool bgp_tm_status_connected; + +static int bgp_zebra_tm_connect(struct thread *t) +{ + struct zclient *zclient; + int delay = 10, ret = 0; + + zclient = THREAD_ARG(t); + if (bgp_tm_status_connected && zclient->sock > 0) + delay = 60; + else { + bgp_tm_status_connected = false; + ret = tm_table_manager_connect(zclient); + } + if (ret < 0) { + zlog_warn("Error connecting to table manager!"); + bgp_tm_status_connected = false; + } else { + if (!bgp_tm_status_connected) + zlog_debug("Connecting to table manager. Success"); + bgp_tm_status_connected = true; + } + thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, + &bgp_tm_thread_connect); + return 0; +} + +void bgp_zebra_init_tm_connect(void) +{ + int delay = 1; + + /* if already set, do nothing + */ + if (bgp_tm_thread_connect != NULL) + return; + bgp_tm_status_connected = false; + thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, + &bgp_tm_thread_connect); +} + +int bgp_zebra_get_table_range(uint32_t chunk_size, + uint32_t *start, uint32_t *end) +{ + int ret; + + if (!bgp_tm_status_connected) + return -1; + ret = tm_get_table_chunk(zclient, chunk_size, start, end); + if (ret < 0) { + zlog_err("BGP: Error getting table chunk %u", chunk_size); + return -1; + } + zlog_info("BGP: Table Manager returns range from chunk %u is [%u %u]", + chunk_size, *start, *end); + return 0; +} + void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct bgp_info *info, struct bgp *bgp, afi_t afi, safi_t safi) diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 68c495cf8b..7263317b6f 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -24,7 +24,10 @@ #include "vxlan.h" extern void bgp_zebra_init(struct thread_master *master); +extern void bgp_zebra_init_tm_connect(void); extern void bgp_zebra_destroy(void); +extern int bgp_zebra_get_table_range(uint32_t chunk_size, + uint32_t *start, uint32_t *end); extern int bgp_if_update_all(void); extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t, safi_t); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 515d90e049..97f0ffcf2c 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1973,6 +1973,10 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); } + if (safi == SAFI_FLOWSPEC) { + /* connect to table manager */ + bgp_zebra_init_tm_connect(); + } return ret; } diff --git a/lib/zebra.h b/lib/zebra.h index ec530397be..3887602231 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -433,7 +433,8 @@ typedef enum { SAFI_ENCAP = 4, SAFI_EVPN = 5, SAFI_LABELED_UNICAST = 6, - SAFI_MAX = 7 + SAFI_FLOWSPEC = 7, + SAFI_MAX = 8 } safi_t; /* diff --git a/zebra/zserv.c b/zebra/zserv.c index 76e5ea2f08..645deac277 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -2203,7 +2203,8 @@ static int zsend_table_manager_connect_response(struct zserv *client, } /* Send response to a table manager connect request to client */ -static void zread_table_manager_connect(struct zserv *client, struct stream *msg, +static void zread_table_manager_connect(struct zserv *client, + struct stream *msg, vrf_id_t vrf_id) { struct stream *s;