summaryrefslogtreecommitdiff
path: root/zebra
diff options
context:
space:
mode:
Diffstat (limited to 'zebra')
-rw-r--r--zebra/label_manager.c2
-rw-r--r--zebra/label_manager.h2
-rw-r--r--zebra/subdir.am2
-rw-r--r--zebra/table_manager.c235
-rw-r--r--zebra/table_manager.h63
-rw-r--r--zebra/zebra_fpm_protobuf.c3
-rw-r--r--zebra/zebra_ns.c6
-rw-r--r--zebra/zebra_vrf.c2
-rw-r--r--zebra/zserv.c153
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),