diff --git a/zebra/main.c b/zebra/main.c index 259c70d0df..e36af51005 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -57,6 +57,7 @@ #include "zebra/zebra_nb.h" #include "zebra/zebra_opaque.h" #include "zebra/zebra_srte.h" +#include "zebra/zebra_srv6.h" #include "zebra/zebra_srv6_vty.h" #define ZEBRA_PTM_SUPPORT @@ -419,6 +420,7 @@ int main(int argc, char **argv) zebra_pbr_init(); zebra_opaque_init(); zebra_srte_init(); + zebra_srv6_init(); zebra_srv6_vty_init(); /* For debug purpose. */ diff --git a/zebra/subdir.am b/zebra/subdir.am index ca77502ba3..4d8115597b 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -93,6 +93,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_mpls_openbsd.c \ zebra/zebra_mpls_null.c \ zebra/zebra_mpls_vty.c \ + zebra/zebra_srv6.c \ zebra/zebra_srv6_vty.c \ zebra/zebra_mroute.c \ zebra/zebra_nb.c \ @@ -163,6 +164,7 @@ noinst_HEADERS += \ zebra/zebra_mlag.h \ zebra/zebra_mlag_vty.h \ zebra/zebra_mpls.h \ + zebra/zebra_srv6.h \ zebra/zebra_srv6_vty.h \ zebra/zebra_mroute.h \ zebra/zebra_nb.h \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 752738b214..fa6a4cfca7 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -60,6 +60,7 @@ #include "zebra/connected.h" #include "zebra/zebra_opaque.h" #include "zebra/zebra_srte.h" +#include "zebra/zebra_srv6.h" DEFINE_MTYPE_STATIC(ZEBRA, OPAQUE, "Opaque Data"); @@ -1136,6 +1137,29 @@ static int zsend_table_manager_connect_response(struct zserv *client, return zserv_send_message(client, s); } +static int zsend_srv6_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_SRV6_MANAGER_CONNECT, vrf_id); + + /* proto */ + stream_putc(s, client->proto); + + /* instance */ + stream_putw(s, client->instance); + + /* result */ + stream_putc(s, result); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* Inbound message handling ------------------------------------------------ */ const int cmd2type[] = { @@ -2624,6 +2648,74 @@ int zsend_client_close_notify(struct zserv *client, struct zserv *closed_client) return zserv_send_message(client, s); } +/* Send response to a srv6 manager connect request to client */ +static void zread_srv6_manager_connect(struct zserv *client, + struct stream *msg, vrf_id_t vrf_id) +{ + struct stream *s; + uint8_t proto; + uint16_t instance; + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + 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)) { + flog_err(EC_ZEBRA_TM_WRONG_PROTO, + "client %d has wrong protocol %s", client->sock, + zebra_route_string(proto)); + zsend_srv6_manager_connect_response(client, vrf_id, 1); + return; + } + zlog_notice("client %d with vrf %s(%u) instance %u connected as %s", + client->sock, VRF_LOGNAME(vrf), vrf_id, instance, + zebra_route_string(proto)); + client->proto = proto; + client->instance = instance; + + /* + * Release previous locators of same protocol and instance. + * This is done in case it restarted from an unexpected shutdown. + */ + release_daemon_srv6_locator_chunks(client); + + zsend_srv6_manager_connect_response(client, vrf_id, 0); + +stream_failure: + return; +} + +int zsend_srv6_manager_get_locator_chunk_response(struct zserv *client, + vrf_id_t vrf_id, + struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK, vrf_id); + + /* proto */ + stream_putc(s, client->proto); + + /* instance */ + stream_putw(s, client->instance); + + if (loc) { + stream_putw(s, strlen(loc->name)); + stream_put(s, loc->name, strlen(loc->name)); + stream_putw(s, loc->prefix.prefixlen); + stream_put(s, &loc->prefix.prefix, 16); + } + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_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) @@ -2833,6 +2925,77 @@ static void zread_table_manager_request(ZAPI_HANDLER_ARGS) } } +static void zread_srv6_manager_get_locator_chunk(struct zserv *client, + struct stream *msg, + vrf_id_t vrf_id) +{ + struct stream *s = msg; + uint8_t proto; + uint16_t instance; + uint16_t len; + char locator_name[SRV6_LOCNAME_SIZE] = {0}; + + /* Get data. */ + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); + STREAM_GETW(s, len); + STREAM_GET(locator_name, s, len); + + assert(proto == client->proto && instance == client->instance); + + /* call hook to get a chunk using wrapper */ + struct srv6_locator *loc = NULL; + srv6_manager_get_locator_chunk_call(&loc, client, locator_name, vrf_id); + +stream_failure: + return; +} + +static void zread_srv6_manager_release_locator_chunk(struct zserv *client, + struct stream *msg, + vrf_id_t vrf_id) +{ + struct stream *s = msg; + uint8_t proto; + uint16_t instance; + uint16_t len; + char locator_name[SRV6_LOCNAME_SIZE] = {0}; + + /* Get data. */ + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); + STREAM_GETW(s, len); + STREAM_GET(locator_name, s, len); + + assert(proto == client->proto && instance == client->instance); + + /* call hook to release a chunk using wrapper */ + srv6_manager_release_locator_chunk_call(client, locator_name, vrf_id); + +stream_failure: + return; +} + +static void zread_srv6_manager_request(ZAPI_HANDLER_ARGS) +{ + switch (hdr->command) { + case ZEBRA_SRV6_MANAGER_CONNECT: + zread_srv6_manager_connect(client, msg, zvrf_id(zvrf)); + break; + case ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK: + zread_srv6_manager_get_locator_chunk(client, msg, + zvrf_id(zvrf)); + break; + case ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_CHUNK: + zread_srv6_manager_release_locator_chunk(client, msg, + zvrf_id(zvrf)); + break; + default: + zlog_err("%s: unknown SRv6 Mamanger command", __func__); + break; + } +} + static void zread_pseudowire(ZAPI_HANDLER_ARGS) { struct stream *s; @@ -3592,6 +3755,9 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register, [ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister, [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, + [ZEBRA_SRV6_MANAGER_CONNECT] = zread_srv6_manager_request, + [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] = zread_srv6_manager_request, + [ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_CHUNK] = zread_srv6_manager_request, [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities, [ZEBRA_NEIGH_DISCOVER] = zread_neigh_discover, [ZEBRA_NHG_ADD] = zread_nhg_add, diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index 0beb3cc100..35bb554121 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -30,6 +30,7 @@ #include "zebra/zebra_pbr.h" #include "zebra/zebra_errors.h" #include "zebra/label_manager.h" +#include "zebra/zebra_srv6.h" #ifdef __cplusplus @@ -116,6 +117,14 @@ int zsend_nhg_notify(uint16_t type, uint16_t instance, uint32_t session_id, extern void zapi_opaque_free(struct opaque *opaque); +extern int zsend_zebra_srv6_locator_add(struct zserv *client, + struct srv6_locator *loc); +extern int zsend_zebra_srv6_locator_delete(struct zserv *client, + struct srv6_locator *loc); +extern int zsend_srv6_manager_get_locator_chunk_response(struct zserv *client, + vrf_id_t vrf_id, + struct srv6_locator *loc); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index 29b271425d..c3890f7220 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -785,6 +785,12 @@ static struct log_ref ferr_zebra_err[] = { .description = "Zebra has detected a situation where there are two vrf devices with the exact same tableid. This is considered a complete misconfiguration of VRF devices and breaks a fundamental assumption in FRR about how VRF's work", .suggestion = "Use different table id's for the VRF's in question" }, + { + .code = EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK, + .title = "Zebra did not free any srv6 locator chunks", + .description = "Zebra's srv6-locator chunk cleanup procedure ran, but no srv6 locator chunks were released.", + .suggestion = "Ignore this error.", + }, { .code = END_FERR, } diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 200a977a69..540c6dd7d0 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -135,6 +135,7 @@ enum zebra_log_refs { EC_ZEBRA_VRF_MISCONFIGURED, EC_ZEBRA_ES_CREATE, EC_ZEBRA_GRE_SET_UPDATE, + EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK, }; void zebra_error_init(void); diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c new file mode 100644 index 0000000000..b4b691e8e0 --- /dev/null +++ b/zebra/zebra_srv6.c @@ -0,0 +1,349 @@ +/* + * Zebra SRv6 definitions + * Copyright (C) 2020 Hiroki Shirokura, LINE Corporation + * Copyright (C) 2020 Masakazu Asama + * + * 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 "network.h" +#include "prefix.h" +#include "stream.h" +#include "srv6.h" +#include "zebra/debug.h" +#include "zebra/zapi_msg.h" +#include "zebra/zserv.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_srv6.h" +#include "zebra/zebra_errors.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +DEFINE_MGROUP(SRV6_MGR, "SRv6 Manager"); +DEFINE_MTYPE_STATIC(SRV6_MGR, SRV6M_CHUNK, "SRv6 Manager Chunk"); + +/* define hooks for the basic API, so that it can be specialized or served + * externally + */ + +DEFINE_HOOK(srv6_manager_client_connect, + (struct zserv *client, vrf_id_t vrf_id), + (client, vrf_id)); +DEFINE_HOOK(srv6_manager_client_disconnect, + (struct zserv *client), (client)); +DEFINE_HOOK(srv6_manager_get_chunk, + (struct srv6_locator **loc, + struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id), + (loc, client, locator_name, vrf_id)); +DEFINE_HOOK(srv6_manager_release_chunk, + (struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id), + (client, locator_name, vrf_id)); + +/* define wrappers to be called in zapi_msg.c (as hooks must be called in + * source file where they were defined) + */ + +void srv6_manager_client_connect_call(struct zserv *client, vrf_id_t vrf_id) +{ + hook_call(srv6_manager_client_connect, client, vrf_id); +} + +void srv6_manager_get_locator_chunk_call(struct srv6_locator **loc, + struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id) +{ + hook_call(srv6_manager_get_chunk, loc, client, locator_name, vrf_id); +} + +void srv6_manager_release_locator_chunk_call(struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id) +{ + hook_call(srv6_manager_release_chunk, client, locator_name, vrf_id); +} + +int srv6_manager_client_disconnect_cb(struct zserv *client) +{ + hook_call(srv6_manager_client_disconnect, client); + return 0; +} + +static int zebra_srv6_cleanup(struct zserv *client) +{ + return 0; +} + +void zebra_srv6_locator_add(struct srv6_locator *locator) +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct srv6_locator *tmp; + + tmp = zebra_srv6_locator_lookup(locator->name); + if (!tmp) + listnode_add(srv6->locators, locator); +} + +void zebra_srv6_locator_delete(struct srv6_locator *locator) +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + listnode_delete(srv6->locators, locator); +} + +struct srv6_locator *zebra_srv6_locator_lookup(const char *name) +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct srv6_locator *locator; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator)) + if (!strncmp(name, locator->name, SRV6_LOCNAME_SIZE)) + return locator; + return NULL; +} + +struct zebra_srv6 *zebra_srv6_get_default(void) +{ + static struct zebra_srv6 srv6; + static bool first_execution = true; + + if (first_execution) { + first_execution = false; + srv6.locators = list_new(); + } + return &srv6; +} + +/** + * Core function, assigns srv6-locator 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 + * @param session_id SessionID of client + * @param name Name of SRv6-locator + * @return Pointer to the assigned srv6-locator chunk, + * or NULL if the request could not be satisfied + */ +static struct srv6_locator * +assign_srv6_locator_chunk(uint8_t proto, + uint16_t instance, + uint32_t session_id, + const char *locator_name) +{ + bool chunk_found = false; + struct listnode *node = NULL; + struct srv6_locator *loc = NULL; + struct srv6_locator_chunk *chunk = NULL; + + loc = zebra_srv6_locator_lookup(locator_name); + if (!loc) { + zlog_info("%s: locator %s was not found", + __func__, locator_name); + + loc = srv6_locator_alloc(locator_name); + if (!loc) { + zlog_info("%s: locator %s can't allocated", + __func__, locator_name); + return NULL; + } + + loc->status_up = false; + chunk = srv6_locator_chunk_alloc(); + chunk->proto = 0; + listnode_add(loc->chunks, chunk); + zebra_srv6_locator_add(loc); + } + + for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { + if (chunk->proto != 0 && chunk->proto != proto) + continue; + chunk_found = true; + break; + } + + if (!chunk_found) { + zlog_info("%s: locator is already owned", __func__); + return NULL; + } + + chunk->proto = proto; + return loc; +} + +static int zebra_srv6_manager_get_locator_chunk(struct srv6_locator **loc, + struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id) +{ + *loc = assign_srv6_locator_chunk(client->proto, client->instance, + client->session_id, locator_name); + + if (!*loc) + zlog_err("Unable to assign locator chunk to %s instance %u", + zebra_route_string(client->proto), client->instance); + else if (IS_ZEBRA_DEBUG_PACKET) + zlog_info("Assigned locator chunk %s to %s instance %u", + (*loc)->name, zebra_route_string(client->proto), + client->instance); + + int ret = 0; + if ((*loc)->status_up) + ret = zsend_srv6_manager_get_locator_chunk_response(client, + vrf_id, + *loc); + return ret; +} + +/** + * Core function, release no longer used srv6-locator chunks + * + * @param proto Daemon protocol of client, to identify the owner + * @param instance Instance, to identify the owner + * @param session_id Zclient session ID, to identify the zclient session + * @param locator_name SRv6-locator name, to identify the actual locator + * @return 0 on success, -1 otherwise + */ +static int release_srv6_locator_chunk(uint8_t proto, uint16_t instance, + uint32_t session_id, + const char *locator_name) +{ + int ret = -1; + struct listnode *node; + struct srv6_locator_chunk *chunk; + struct srv6_locator *loc = NULL; + + loc = zebra_srv6_locator_lookup(locator_name); + if (!loc) { + return -1; + } + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Releasing srv6-locator on %s", __func__, + locator_name); + + for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { + if (chunk->proto != proto || + chunk->instance != instance || + chunk->session_id != session_id) + continue; + chunk->proto = NO_PROTO; + chunk->instance = 0; + chunk->session_id = 0; + chunk->keep = 0; + ret = 0; + break; + } + + if (ret != 0) + flog_err(EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK, + "%s: SRv6 locator chunk not released", __func__); + + return ret; +} + +static int zebra_srv6_manager_release_locator_chunk(struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id) +{ + if (vrf_id != VRF_DEFAULT) { + zlog_err("SRv6 locator doesn't support vrf"); + return -1; + } + + return release_srv6_locator_chunk(client->proto, client->instance, + client->session_id, locator_name); +} + +/** + * Release srv6-locator 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_srv6_locator_chunks(struct zserv *client) +{ + int ret; + int count = 0; + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct listnode *loc_node; + struct listnode *chunk_node; + struct srv6_locator *loc; + struct srv6_locator_chunk *chunk; + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u", + __func__, zebra_route_string(client->proto), + client->instance, client->session_id); + + for (ALL_LIST_ELEMENTS_RO(srv6->locators, loc_node, loc)) { + for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, chunk)) { + if (chunk->proto == client->proto && + chunk->instance == client->instance && + chunk->session_id == client->session_id && + chunk->keep == 0) { + ret = release_srv6_locator_chunk( + chunk->proto, chunk->instance, + chunk->session_id, loc->name); + if (ret == 0) + count++; + } + } + } + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Released %d srv6-locator chunks", + __func__, count); + + return count; +} + +void zebra_srv6_init(void) +{ + hook_register(zserv_client_close, zebra_srv6_cleanup); + hook_register(srv6_manager_get_chunk, + zebra_srv6_manager_get_locator_chunk); + hook_register(srv6_manager_release_chunk, + zebra_srv6_manager_release_locator_chunk); +} + +bool zebra_srv6_is_enable(void) +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + + return listcount(srv6->locators); +} diff --git a/zebra/zebra_srv6.h b/zebra/zebra_srv6.h new file mode 100644 index 0000000000..751cee6e70 --- /dev/null +++ b/zebra/zebra_srv6.h @@ -0,0 +1,79 @@ +/* + * Zebra SRv6 definitions + * Copyright (C) 2020 Hiroki Shirokura, LINE Corporation + * + * 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 _ZEBRA_SRV6_H +#define _ZEBRA_SRV6_H + +#include +#include +#include + +#include "qobj.h" +#include "prefix.h" +#include +#include + +/* SRv6 instance structure. */ +struct zebra_srv6 { + struct list *locators; +}; + +/* declare hooks for the basic API, so that it can be specialized or served + * externally. Also declare a hook when those functions have been registered, + * so that any external module wanting to replace those can react + */ + +DECLARE_HOOK(srv6_manager_client_connect, + (struct zserv *client, vrf_id_t vrf_id), + (client, vrf_id)); +DECLARE_HOOK(srv6_manager_client_disconnect, + (struct zserv *client), (client)); +DECLARE_HOOK(srv6_manager_get_chunk, + (struct srv6_locator **loc, + struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id), + (mc, client, keep, size, base, vrf_id)); +DECLARE_HOOK(srv6_manager_release_chunk, + (struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id), + (client, locator_name, vrf_id)); + + +extern void zebra_srv6_locator_add(struct srv6_locator *locator); +extern void zebra_srv6_locator_delete(struct srv6_locator *locator); +extern struct srv6_locator *zebra_srv6_locator_lookup(const char *name); + +extern void zebra_srv6_init(void); +extern struct zebra_srv6 *zebra_srv6_get_default(void); +extern bool zebra_srv6_is_enable(void); + +extern void srv6_manager_client_connect_call(struct zserv *client, vrf_id_t vrf_id); +extern void srv6_manager_get_locator_chunk_call(struct srv6_locator **loc, + struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id); +extern void srv6_manager_release_locator_chunk_call(struct zserv *client, + const char *locator_name, + vrf_id_t vrf_id); +extern int srv6_manager_client_disconnect_cb(struct zserv *client); +extern int release_daemon_srv6_locator_chunks(struct zserv *client); + +#endif /* _ZEBRA_SRV6_H */