diff options
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/label_manager.c | 2 | ||||
| -rw-r--r-- | zebra/label_manager.h | 2 | ||||
| -rw-r--r-- | zebra/subdir.am | 2 | ||||
| -rw-r--r-- | zebra/table_manager.c | 235 | ||||
| -rw-r--r-- | zebra/table_manager.h | 63 | ||||
| -rw-r--r-- | zebra/zebra_fpm_protobuf.c | 3 | ||||
| -rw-r--r-- | zebra/zebra_ns.c | 6 | ||||
| -rw-r--r-- | zebra/zebra_vrf.c | 2 | ||||
| -rw-r--r-- | zebra/zserv.c | 153 |
9 files changed, 462 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/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 <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/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 <stdint.h> + +#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 */ diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index e661b6efc7..ebd632270c 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -163,6 +163,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; msg->key = fpm_route_key_create(allocator, rib_dest_prefix(dest)); qpb_protocol_set(&msg->protocol, re->type); + msg->has_route_type = 1; msg->route_type = FPM__ROUTE_TYPE__NORMAL; msg->metric = re->metric; @@ -245,6 +246,7 @@ static Fpm__Message *create_route_message(qpb_allocator_t *allocator, fpm__message__init(msg); if (!re) { + msg->has_type = 1; msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE; msg->delete_route = create_delete_route_message(allocator, dest, re); @@ -255,6 +257,7 @@ static Fpm__Message *create_route_message(qpb_allocator_t *allocator, return msg; } + msg->has_type = 1; msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE; msg->add_route = create_add_route_message(allocator, dest, re); if (!msg->add_route) { 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..645deac277 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,60 @@ 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 +2272,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 +2280,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 +2360,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 +2768,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 +2802,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), |
