2025-02-05 10:03:58 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/*
|
|
|
|
* Zebra SRv6 definitions
|
|
|
|
* Copyright (C) 2020 Hiroki Shirokura, LINE Corporation
|
|
|
|
* Copyright (C) 2020 Masakazu Asama
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
|
|
|
|
#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 "zebra/ge_netlink.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
|
|
|
|
DEFINE_MGROUP(SRV6_MGR, "SRv6 Manager");
|
|
|
|
DEFINE_MTYPE_STATIC(SRV6_MGR, SRV6M_CHUNK, "SRv6 Manager Chunk");
|
2025-02-05 10:17:11 +01:00
|
|
|
DEFINE_MTYPE_STATIC(SRV6_MGR, ZEBRA_SRV6_SID_BLOCK, "SRv6 SID block");
|
|
|
|
DEFINE_MTYPE_STATIC(SRV6_MGR, ZEBRA_SRV6_SID_FUNC, "SRv6 SID function");
|
|
|
|
DEFINE_MTYPE_STATIC(SRV6_MGR, ZEBRA_SRV6_USID_WLIB,
|
|
|
|
"SRv6 uSID Wide LIB information");
|
|
|
|
DEFINE_MTYPE_STATIC(SRV6_MGR, ZEBRA_SRV6_SID, "SRv6 SID");
|
|
|
|
DEFINE_MTYPE_STATIC(SRV6_MGR, ZEBRA_SRV6_SID_CTX, "SRv6 SID context");
|
|
|
|
|
|
|
|
/* Prototypes */
|
|
|
|
static int release_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block,
|
|
|
|
uint32_t sid_func);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
DEFINE_HOOK(srv6_manager_get_sid,
|
|
|
|
(struct zebra_srv6_sid **sid, struct zserv *client,
|
|
|
|
struct srv6_sid_ctx *ctx, struct in6_addr *sid_value,
|
|
|
|
const char *locator_name),
|
|
|
|
(sid, client, ctx, sid_value, locator_name));
|
|
|
|
DEFINE_HOOK(srv6_manager_release_sid,
|
|
|
|
(struct zserv *client, struct srv6_sid_ctx *ctx), (client, ctx));
|
|
|
|
DEFINE_HOOK(srv6_manager_get_locator,
|
|
|
|
(struct srv6_locator **locator, struct zserv *client,
|
|
|
|
const char *locator_name),
|
|
|
|
(locator, client, locator_name));
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
|
|
|
|
void srv6_manager_get_sid_call(struct zebra_srv6_sid **sid,
|
|
|
|
struct zserv *client, struct srv6_sid_ctx *ctx,
|
|
|
|
struct in6_addr *sid_value,
|
|
|
|
const char *locator_name)
|
|
|
|
{
|
|
|
|
hook_call(srv6_manager_get_sid, sid, client, ctx, sid_value,
|
|
|
|
locator_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void srv6_manager_release_sid_call(struct zserv *client,
|
|
|
|
struct srv6_sid_ctx *ctx)
|
|
|
|
{
|
|
|
|
hook_call(srv6_manager_release_sid, client, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void srv6_manager_get_locator_call(struct srv6_locator **locator,
|
|
|
|
struct zserv *client,
|
|
|
|
const char *locator_name)
|
|
|
|
{
|
|
|
|
hook_call(srv6_manager_get_locator, locator, client, locator_name);
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
static int zebra_srv6_cleanup(struct zserv *client)
|
|
|
|
{
|
2025-02-05 10:17:11 +01:00
|
|
|
/* Client has disconnected, let's release all the SIDs allocated by it. */
|
|
|
|
release_daemon_srv6_sids(client);
|
2025-02-05 10:03:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
/* --- Zebra SRv6 SID context management functions -------------------------- */
|
|
|
|
|
|
|
|
struct zebra_srv6_sid_ctx *zebra_srv6_sid_ctx_alloc(void)
|
|
|
|
{
|
|
|
|
struct zebra_srv6_sid_ctx *ctx = NULL;
|
|
|
|
|
|
|
|
ctx = XCALLOC(MTYPE_ZEBRA_SRV6_SID_CTX,
|
|
|
|
sizeof(struct zebra_srv6_sid_ctx));
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_srv6_sid_ctx_free(struct zebra_srv6_sid_ctx *ctx)
|
|
|
|
{
|
|
|
|
XFREE(MTYPE_ZEBRA_SRV6_SID_CTX, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free an SRv6 SID context.
|
|
|
|
*
|
|
|
|
* @param val SRv6 SID context to be freed
|
|
|
|
*/
|
|
|
|
void delete_zebra_srv6_sid_ctx(void *val)
|
|
|
|
{
|
|
|
|
zebra_srv6_sid_ctx_free((struct zebra_srv6_sid_ctx *)val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- Zebra SRv6 SID format management functions --------------------------- */
|
|
|
|
|
|
|
|
void srv6_sid_format_register(struct srv6_sid_format *format)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
|
|
|
|
/* Ensure that the format is registered only once */
|
|
|
|
assert(!srv6_sid_format_lookup(format->name));
|
|
|
|
|
|
|
|
listnode_add(srv6->sid_formats, format);
|
|
|
|
}
|
|
|
|
|
|
|
|
void srv6_sid_format_unregister(struct srv6_sid_format *format)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
|
|
|
|
listnode_delete(srv6->sid_formats, format);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct srv6_sid_format *srv6_sid_format_lookup(const char *name)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
struct listnode *node;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srv6->sid_formats, node, format))
|
|
|
|
if (!strncmp(name, format->name, sizeof(format->name)))
|
|
|
|
return format;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called to change the SID format of a locator.
|
|
|
|
*
|
|
|
|
* After switching the locator to a different format, the SIDs allocated
|
|
|
|
* from the locator may no longer be valid; we need to notify the
|
|
|
|
* interested zclient that the locator has changed, so that the
|
|
|
|
* zclients can withdraw/uninstall the old SIDs, allocate/advertise/program
|
|
|
|
* the new SIDs.
|
|
|
|
*/
|
|
|
|
void zebra_srv6_locator_format_set(struct srv6_locator *locator,
|
|
|
|
struct srv6_sid_format *format)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct zebra_srv6_sid_block *block_old, *block_new;
|
|
|
|
struct prefix_ipv6 block_pfx_new;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
struct zebra_srv6_sid_ctx *ctx;
|
|
|
|
|
|
|
|
if (!locator)
|
|
|
|
return;
|
|
|
|
|
|
|
|
locator->sid_format = format;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: Locator %s format has changed, old=%s new=%s",
|
|
|
|
__func__, locator->name,
|
|
|
|
locator->sid_format ? ((struct srv6_sid_format *)
|
|
|
|
locator->sid_format)
|
|
|
|
->name
|
|
|
|
: NULL,
|
|
|
|
format ? format->name : NULL);
|
|
|
|
|
|
|
|
/* Notify zclients that the locator is no longer valid */
|
|
|
|
zebra_notify_srv6_locator_delete(locator);
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS(srv6->sids, node, nnode, ctx)) {
|
|
|
|
if (!ctx->sid || ctx->sid->locator != locator)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ctx->sid)
|
|
|
|
zebra_srv6_sid_free(ctx->sid);
|
|
|
|
|
|
|
|
listnode_delete(srv6->sids, ctx);
|
|
|
|
zebra_srv6_sid_ctx_free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: Locator %s format has changed, send SRV6_LOCATOR_DEL notification to zclients",
|
|
|
|
__func__, locator->name);
|
|
|
|
|
|
|
|
/* Release the current parent block */
|
|
|
|
block_old = locator->sid_block;
|
|
|
|
if (block_old) {
|
|
|
|
block_old->refcnt--;
|
|
|
|
if (block_old->refcnt == 0) {
|
|
|
|
listnode_delete(srv6->sid_blocks, block_old);
|
|
|
|
zebra_srv6_sid_block_free(block_old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
locator->sid_block = NULL;
|
|
|
|
|
|
|
|
block_pfx_new = locator->prefix;
|
|
|
|
if (format)
|
|
|
|
block_pfx_new.prefixlen = format->block_len;
|
|
|
|
else
|
|
|
|
block_pfx_new.prefixlen = locator->block_bits_length;
|
|
|
|
apply_mask(&block_pfx_new);
|
|
|
|
|
|
|
|
/* Allocate the new parent block */
|
|
|
|
block_new = zebra_srv6_sid_block_lookup(&block_pfx_new);
|
|
|
|
if (!block_new) {
|
|
|
|
block_new = zebra_srv6_sid_block_alloc(format, &block_pfx_new);
|
|
|
|
listnode_add(srv6->sid_blocks, block_new);
|
|
|
|
}
|
|
|
|
|
|
|
|
block_new->refcnt++;
|
|
|
|
locator->sid_block = block_new;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: Locator %s format has changed, send SRV6_LOCATOR_ADD notification to zclients",
|
|
|
|
__func__, locator->name);
|
|
|
|
|
|
|
|
/* Notify zclients about the updated locator */
|
|
|
|
zebra_srv6_locator_add(locator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called when a SID format is modified by the user.
|
|
|
|
*
|
|
|
|
* After modifying a SID format, the SIDs that are using that format may no
|
|
|
|
* longer be valid.
|
|
|
|
* This function walks through the list of locators that are using the SID format
|
|
|
|
* and notifies the zclients that the locator has changed, so that the zclients
|
|
|
|
* can withdraw/uninstall the old SIDs, allocate/program/advertise the new SIDs.
|
|
|
|
*/
|
|
|
|
void zebra_srv6_sid_format_changed_cb(struct srv6_sid_format *format)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct srv6_locator *locator;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
struct zebra_srv6_sid_ctx *ctx;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: SID format %s has changed. Notifying zclients.",
|
|
|
|
__func__, format->name);
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator)) {
|
|
|
|
if (locator->sid_format == format) {
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: Locator %s has changed because its format (%s) has been modified. Notifying zclients.",
|
|
|
|
__func__, locator->name,
|
|
|
|
format->name);
|
|
|
|
|
|
|
|
/* Notify zclients that the locator is no longer valid */
|
|
|
|
zebra_notify_srv6_locator_delete(locator);
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS(srv6->sids, node, nnode, ctx)) {
|
|
|
|
if (!ctx->sid || ctx->sid->locator != locator)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ctx->sid)
|
|
|
|
zebra_srv6_sid_free(ctx->sid);
|
|
|
|
|
|
|
|
listnode_delete(srv6->sids, ctx);
|
|
|
|
zebra_srv6_sid_ctx_free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Notify zclients about the updated locator */
|
|
|
|
zebra_notify_srv6_locator_add(locator);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper function to create the SRv6 compressed format `usid-f3216`.
|
|
|
|
*/
|
|
|
|
static struct srv6_sid_format *create_srv6_sid_format_usid_f3216(void)
|
|
|
|
{
|
|
|
|
struct srv6_sid_format *format = NULL;
|
|
|
|
|
|
|
|
format = srv6_sid_format_alloc(SRV6_SID_FORMAT_USID_F3216_NAME);
|
|
|
|
|
|
|
|
format->type = SRV6_SID_FORMAT_TYPE_USID;
|
|
|
|
|
|
|
|
/* Define block/node/function length */
|
|
|
|
format->block_len = SRV6_SID_FORMAT_USID_F3216_BLOCK_LEN;
|
|
|
|
format->node_len = SRV6_SID_FORMAT_USID_F3216_NODE_LEN;
|
|
|
|
format->function_len = SRV6_SID_FORMAT_USID_F3216_FUNCTION_LEN;
|
|
|
|
format->argument_len = SRV6_SID_FORMAT_USID_F3216_ARGUMENT_LEN;
|
|
|
|
|
|
|
|
/* Define the ranges from which the SID function can be allocated */
|
|
|
|
format->config.usid.lib_start = SRV6_SID_FORMAT_USID_F3216_LIB_START;
|
|
|
|
format->config.usid.elib_start = SRV6_SID_FORMAT_USID_F3216_ELIB_START;
|
|
|
|
format->config.usid.elib_end = SRV6_SID_FORMAT_USID_F3216_ELIB_END;
|
|
|
|
format->config.usid.wlib_start = SRV6_SID_FORMAT_USID_F3216_WLIB_START;
|
|
|
|
format->config.usid.wlib_end = SRV6_SID_FORMAT_USID_F3216_WLIB_END;
|
|
|
|
format->config.usid.ewlib_start = SRV6_SID_FORMAT_USID_F3216_EWLIB_START;
|
|
|
|
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper function to create the SRv6 uncompressed format.
|
|
|
|
*/
|
|
|
|
static struct srv6_sid_format *create_srv6_sid_format_uncompressed(void)
|
|
|
|
{
|
|
|
|
struct srv6_sid_format *format = NULL;
|
|
|
|
|
|
|
|
format = srv6_sid_format_alloc(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NAME);
|
|
|
|
|
|
|
|
format->type = SRV6_SID_FORMAT_TYPE_UNCOMPRESSED;
|
|
|
|
|
|
|
|
/* Define block/node/function length */
|
|
|
|
format->block_len = SRV6_SID_FORMAT_UNCOMPRESSED_F4024_BLOCK_LEN;
|
|
|
|
format->node_len = SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE_LEN;
|
|
|
|
format->function_len = SRV6_SID_FORMAT_UNCOMPRESSED_F4024_FUNCTION_LEN;
|
|
|
|
format->argument_len = SRV6_SID_FORMAT_UNCOMPRESSED_F4024_ARGUMENT_LEN;
|
|
|
|
|
|
|
|
/* Define the ranges from which the SID function can be allocated */
|
|
|
|
format->config.uncompressed.explicit_start =
|
|
|
|
SRV6_SID_FORMAT_UNCOMPRESSED_F4024_EXPLICIT_RANGE_START;
|
|
|
|
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- Zebra SRv6 SID function management functions ---------------------------- */
|
|
|
|
|
|
|
|
uint32_t *zebra_srv6_sid_func_alloc(uint32_t func)
|
|
|
|
{
|
|
|
|
uint32_t *sid_func_ptr;
|
|
|
|
|
|
|
|
sid_func_ptr = XCALLOC(MTYPE_ZEBRA_SRV6_SID_FUNC, sizeof(uint32_t));
|
|
|
|
*sid_func_ptr = func;
|
|
|
|
|
|
|
|
return sid_func_ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_srv6_sid_func_free(uint32_t *func)
|
|
|
|
{
|
|
|
|
XFREE(MTYPE_ZEBRA_SRV6_SID_FUNC, func);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free an SRv6 SID function.
|
|
|
|
*
|
|
|
|
* @param val SRv6 SID function to be freed
|
|
|
|
*/
|
|
|
|
void delete_zebra_srv6_sid_func(void *val)
|
|
|
|
{
|
|
|
|
zebra_srv6_sid_func_free((uint32_t *)val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- Zebra SRv6 SID block management functions ---------------------------- */
|
|
|
|
|
|
|
|
static struct zebra_srv6_sid_block *zebra_srv6_sid_block_alloc_internal(void)
|
|
|
|
{
|
|
|
|
struct zebra_srv6_sid_block *block = NULL;
|
|
|
|
|
|
|
|
block = XCALLOC(MTYPE_ZEBRA_SRV6_SID_BLOCK,
|
|
|
|
sizeof(struct zebra_srv6_sid_block));
|
|
|
|
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct zebra_srv6_sid_block *
|
|
|
|
zebra_srv6_sid_block_alloc(struct srv6_sid_format *format,
|
|
|
|
struct prefix_ipv6 *prefix)
|
|
|
|
{
|
|
|
|
struct zebra_srv6_sid_block *block;
|
|
|
|
|
|
|
|
block = zebra_srv6_sid_block_alloc_internal();
|
|
|
|
block->sid_format = format;
|
|
|
|
block->prefix = *prefix;
|
|
|
|
|
|
|
|
if (format) {
|
|
|
|
if (format->type == SRV6_SID_FORMAT_TYPE_USID) {
|
|
|
|
uint32_t wlib_start, wlib_end, func;
|
|
|
|
|
|
|
|
/* Init uSID LIB */
|
|
|
|
block->u.usid.lib.func_allocated = list_new();
|
|
|
|
block->u.usid.lib.func_allocated->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.usid.lib.func_released = list_new();
|
|
|
|
block->u.usid.lib.func_released->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.usid.lib.first_available_func =
|
|
|
|
format->config.usid.lib_start;
|
|
|
|
|
|
|
|
/* Init uSID Wide LIB */
|
|
|
|
wlib_start = block->sid_format->config.usid.wlib_start;
|
|
|
|
wlib_end = block->sid_format->config.usid.wlib_end;
|
|
|
|
block->u.usid.wide_lib =
|
|
|
|
XCALLOC(MTYPE_ZEBRA_SRV6_USID_WLIB,
|
|
|
|
(wlib_end - wlib_start + 1) *
|
|
|
|
sizeof(struct wide_lib));
|
|
|
|
for (func = 0; func < wlib_end - wlib_start + 1;
|
|
|
|
func++) {
|
|
|
|
block->u.usid.wide_lib[func].func_allocated =
|
|
|
|
list_new();
|
|
|
|
block->u.usid.wide_lib[func].func_allocated->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.usid.wide_lib[func].func_released =
|
|
|
|
list_new();
|
|
|
|
block->u.usid.wide_lib[func].func_released->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.usid.wide_lib[func].func = func;
|
|
|
|
}
|
|
|
|
} else if (format->type == SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) {
|
|
|
|
block->u.uncompressed.func_allocated = list_new();
|
|
|
|
block->u.uncompressed.func_allocated->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.uncompressed.func_released = list_new();
|
|
|
|
block->u.uncompressed.func_released->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.uncompressed.first_available_func =
|
|
|
|
SRV6_SID_FORMAT_UNCOMPRESSED_F4024_FUNC_UNRESERVED_MIN;
|
|
|
|
} else {
|
|
|
|
/* We should never arrive here */
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
block->u.uncompressed.func_allocated = list_new();
|
|
|
|
block->u.uncompressed.func_allocated->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.uncompressed.func_released = list_new();
|
|
|
|
block->u.uncompressed.func_released->del =
|
|
|
|
delete_zebra_srv6_sid_func;
|
|
|
|
block->u.uncompressed.first_available_func = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_srv6_sid_block_free(struct zebra_srv6_sid_block *block)
|
|
|
|
{
|
|
|
|
if (block->sid_format) {
|
|
|
|
if (block->sid_format->type == SRV6_SID_FORMAT_TYPE_USID) {
|
|
|
|
uint32_t wlib_start, wlib_end, func;
|
|
|
|
|
|
|
|
/* Free uSID LIB */
|
|
|
|
list_delete(&block->u.usid.lib.func_allocated);
|
|
|
|
list_delete(&block->u.usid.lib.func_released);
|
|
|
|
|
|
|
|
/* Free uSID Wide LIB */
|
|
|
|
wlib_start = block->sid_format->config.usid.wlib_start;
|
|
|
|
wlib_end = block->sid_format->config.usid.wlib_end;
|
|
|
|
for (func = 0; func < wlib_end - wlib_start + 1;
|
|
|
|
func++) {
|
|
|
|
list_delete(&block->u.usid.wide_lib[func]
|
|
|
|
.func_allocated);
|
|
|
|
list_delete(&block->u.usid.wide_lib[func]
|
|
|
|
.func_released);
|
|
|
|
}
|
|
|
|
XFREE(MTYPE_ZEBRA_SRV6_USID_WLIB,
|
|
|
|
block->u.usid.wide_lib);
|
|
|
|
} else if (block->sid_format->type ==
|
|
|
|
SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) {
|
|
|
|
list_delete(&block->u.uncompressed.func_allocated);
|
|
|
|
list_delete(&block->u.uncompressed.func_released);
|
|
|
|
} else {
|
|
|
|
/* We should never arrive here */
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
list_delete(&block->u.uncompressed.func_allocated);
|
|
|
|
list_delete(&block->u.uncompressed.func_released);
|
|
|
|
}
|
|
|
|
|
|
|
|
XFREE(MTYPE_ZEBRA_SRV6_SID_BLOCK, block);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free an SRv6 SID block.
|
|
|
|
*
|
|
|
|
* @param val SRv6 SID block to be freed
|
|
|
|
*/
|
|
|
|
void delete_zebra_srv6_sid_block(void *val)
|
|
|
|
{
|
|
|
|
zebra_srv6_sid_block_free((struct zebra_srv6_sid_block *)val);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct zebra_srv6_sid_block *
|
|
|
|
zebra_srv6_sid_block_lookup(struct prefix_ipv6 *prefix)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct zebra_srv6_sid_block *block;
|
|
|
|
struct listnode *node;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srv6->sid_blocks, node, block))
|
|
|
|
if (prefix_match(prefix, &block->prefix))
|
|
|
|
return block;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- Zebra SRv6 SID management functions ---------------------------------- */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Alloc and fill an SRv6 SID.
|
|
|
|
*
|
|
|
|
* @param ctx Context associated with the SID to be created
|
|
|
|
* @param sid_value IPv6 address associated with the SID to be created
|
|
|
|
* @param locator Parent locator of the SID to be created
|
|
|
|
* @param sid_block Block from which the SID value has been allocated
|
|
|
|
* @param sid_func Function part of the SID to be created
|
|
|
|
* @param alloc_mode Allocation mode of the Function (dynamic vs explicit)
|
|
|
|
* @return The requested SID
|
|
|
|
*/
|
|
|
|
struct zebra_srv6_sid *
|
|
|
|
zebra_srv6_sid_alloc(struct zebra_srv6_sid_ctx *ctx, struct in6_addr *sid_value,
|
|
|
|
struct srv6_locator *locator,
|
|
|
|
struct zebra_srv6_sid_block *sid_block, uint32_t sid_func,
|
|
|
|
enum srv6_sid_alloc_mode alloc_mode)
|
|
|
|
{
|
|
|
|
struct zebra_srv6_sid *sid;
|
|
|
|
|
|
|
|
if (!ctx || !sid_value)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sid = XCALLOC(MTYPE_ZEBRA_SRV6_SID, sizeof(struct zebra_srv6_sid));
|
|
|
|
sid->ctx = ctx;
|
|
|
|
sid->value = *sid_value;
|
|
|
|
sid->locator = locator;
|
|
|
|
sid->block = sid_block;
|
|
|
|
sid->func = sid_func;
|
|
|
|
sid->alloc_mode = alloc_mode;
|
|
|
|
sid->client_list = list_new();
|
|
|
|
|
|
|
|
return sid;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_srv6_sid_free(struct zebra_srv6_sid *sid)
|
|
|
|
{
|
|
|
|
list_delete(&sid->client_list);
|
|
|
|
XFREE(MTYPE_ZEBRA_SRV6_SID, sid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free an SRv6 SID.
|
|
|
|
*
|
|
|
|
* @param val SRv6 SID to be freed
|
|
|
|
*/
|
|
|
|
void delete_zebra_srv6_sid(void *val)
|
|
|
|
{
|
|
|
|
zebra_srv6_sid_free((struct zebra_srv6_sid *)val);
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
void zebra_srv6_locator_add(struct srv6_locator *locator)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct srv6_locator *tmp;
|
|
|
|
struct listnode *node;
|
|
|
|
struct zserv *client;
|
|
|
|
|
|
|
|
tmp = zebra_srv6_locator_lookup(locator->name);
|
|
|
|
if (!tmp)
|
|
|
|
listnode_add(srv6->locators, locator);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Notify new locator info to zclients.
|
|
|
|
*
|
|
|
|
* The srv6 locators and their prefixes are managed by zserv(zebra).
|
|
|
|
* And an actual configuration the srv6 sid in the srv6 locator is done
|
|
|
|
* by zclient(bgpd, isisd, etc). The configuration of each locator
|
|
|
|
* allocation and specify it by zserv and zclient should be
|
|
|
|
* asynchronous. For that, zclient should be received the event via
|
|
|
|
* ZAPI when a srv6 locator is added on zebra.
|
|
|
|
* Basically, in SRv6, adding/removing SRv6 locators is performed less
|
|
|
|
* frequently than adding rib entries, so a broad to all zclients will
|
|
|
|
* not degrade the overall performance of FRRouting.
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
|
|
|
|
zsend_zebra_srv6_locator_add(client, locator);
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_srv6_locator_delete(struct srv6_locator *locator)
|
|
|
|
{
|
|
|
|
struct listnode *n;
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct zserv *client;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Notify deleted locator info to zclients if needed.
|
|
|
|
*
|
|
|
|
* zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and
|
|
|
|
* uses it for its own purpose. For example, in the case of BGP L3VPN,
|
|
|
|
* the SID assigned to vpn unicast rib will be given.
|
|
|
|
* And when the locator is deleted by zserv(zebra), those SIDs need to
|
|
|
|
* be withdrawn. The zclient must initiate the withdrawal of the SIDs
|
|
|
|
* by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the
|
|
|
|
* owner of each chunk.
|
|
|
|
*/
|
2025-02-05 10:17:11 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, n, client))
|
2025-02-05 10:03:58 +01:00
|
|
|
zsend_zebra_srv6_locator_delete(client, locator);
|
|
|
|
|
|
|
|
listnode_delete(srv6->locators, locator);
|
|
|
|
srv6_locator_free(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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_notify_srv6_locator_add(struct srv6_locator *locator)
|
|
|
|
{
|
|
|
|
struct listnode *node;
|
|
|
|
struct zserv *client;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Notify new locator info to zclients.
|
|
|
|
*
|
|
|
|
* The srv6 locators and their prefixes are managed by zserv(zebra).
|
|
|
|
* And an actual configuration the srv6 sid in the srv6 locator is done
|
|
|
|
* by zclient(bgpd, isisd, etc). The configuration of each locator
|
|
|
|
* allocation and specify it by zserv and zclient should be
|
|
|
|
* asynchronous. For that, zclient should be received the event via
|
|
|
|
* ZAPI when a srv6 locator is added on zebra.
|
|
|
|
* Basically, in SRv6, adding/removing SRv6 locators is performed less
|
|
|
|
* frequently than adding rib entries, so a broad to all zclients will
|
|
|
|
* not degrade the overall performance of FRRouting.
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
|
|
|
|
zsend_zebra_srv6_locator_add(client, locator);
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_notify_srv6_locator_delete(struct srv6_locator *locator)
|
|
|
|
{
|
|
|
|
struct listnode *n;
|
|
|
|
struct zserv *client;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Notify deleted locator info to zclients if needed.
|
|
|
|
*
|
|
|
|
* zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and
|
|
|
|
* uses it for its own purpose. For example, in the case of BGP L3VPN,
|
|
|
|
* the SID assigned to vpn unicast rib will be given.
|
|
|
|
* And when the locator is deleted by zserv(zebra), those SIDs need to
|
|
|
|
* be withdrawn. The zclient must initiate the withdrawal of the SIDs
|
|
|
|
* by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the
|
|
|
|
* owner of each chunk.
|
|
|
|
*/
|
2025-02-05 10:17:11 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, n, client))
|
2025-02-05 10:03:58 +01:00
|
|
|
zsend_zebra_srv6_locator_delete(client, locator);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct zebra_srv6 srv6;
|
|
|
|
|
|
|
|
struct zebra_srv6 *zebra_srv6_get_default(void)
|
|
|
|
{
|
|
|
|
static bool first_execution = true;
|
2025-02-05 10:17:11 +01:00
|
|
|
struct srv6_sid_format *format_usidf3216;
|
|
|
|
struct srv6_sid_format *format_uncompressed;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
if (first_execution) {
|
|
|
|
first_execution = false;
|
|
|
|
srv6.locators = list_new();
|
2025-02-05 10:17:11 +01:00
|
|
|
|
|
|
|
/* Initialize list of SID formats */
|
|
|
|
srv6.sid_formats = list_new();
|
|
|
|
srv6.sid_formats->del = delete_srv6_sid_format;
|
|
|
|
|
|
|
|
/* Create SID format `usid-f3216` */
|
|
|
|
format_usidf3216 = create_srv6_sid_format_usid_f3216();
|
|
|
|
srv6_sid_format_register(format_usidf3216);
|
|
|
|
|
|
|
|
/* Create SID format `uncompressed` */
|
|
|
|
format_uncompressed = create_srv6_sid_format_uncompressed();
|
|
|
|
srv6_sid_format_register(format_uncompressed);
|
|
|
|
|
|
|
|
/* Init list to store SRv6 SIDs */
|
|
|
|
srv6.sids = list_new();
|
|
|
|
srv6.sids->del = delete_zebra_srv6_sid_ctx;
|
|
|
|
|
|
|
|
/* Init list to store SRv6 SID blocks */
|
|
|
|
srv6.sid_blocks = list_new();
|
|
|
|
srv6.sid_blocks->del = delete_zebra_srv6_sid_block;
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) {
|
|
|
|
if (chunk->proto != NO_PROTO && chunk->proto != proto)
|
|
|
|
continue;
|
|
|
|
chunk_found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!chunk_found) {
|
|
|
|
zlog_info("%s: locator is already owned", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
chunk->proto = proto;
|
|
|
|
chunk->instance = instance;
|
|
|
|
chunk->session_id = session_id;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
*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);
|
|
|
|
|
|
|
|
if (*loc && (*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_encap_src_addr_set(struct in6_addr *encap_src_addr)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
|
|
|
|
if (!encap_src_addr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memcpy(&srv6->encap_src_addr, encap_src_addr, sizeof(struct in6_addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_srv6_encap_src_addr_unset(void)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
|
|
|
|
memset(&srv6->encap_src_addr, 0, sizeof(struct in6_addr));
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
/* --- SRv6 SID Allocation/Release functions -------------------------------- */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the SRv6 SID obtained composing the locator and function.
|
|
|
|
*
|
|
|
|
* @param sid_value SRv6 SID address returned
|
|
|
|
* @param locator Parent locator of the SRv6 SID
|
|
|
|
* @param sid_func Function part of the SID
|
|
|
|
* @return True if success, False otherwise
|
|
|
|
*/
|
|
|
|
static bool zebra_srv6_sid_compose(struct in6_addr *sid_value,
|
|
|
|
struct srv6_locator *locator,
|
|
|
|
uint32_t sid_func)
|
2025-02-05 10:03:58 +01:00
|
|
|
{
|
2025-02-05 10:17:11 +01:00
|
|
|
uint8_t offset, func_len;
|
|
|
|
struct srv6_sid_format *format;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
if (!sid_value || !locator)
|
|
|
|
return false;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
format = locator->sid_format;
|
|
|
|
if (format) {
|
|
|
|
offset = format->block_len + format->node_len;
|
|
|
|
func_len = format->function_len;
|
|
|
|
} else {
|
|
|
|
offset = locator->block_bits_length + locator->node_bits_length;
|
|
|
|
func_len = locator->function_bits_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
*sid_value = locator->prefix.prefix;
|
|
|
|
for (uint8_t idx = 0; idx < func_len; idx++) {
|
|
|
|
uint8_t tidx = offset + idx;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
sid_value->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8));
|
|
|
|
if (sid_func >> (func_len - 1 - idx) & 0x1)
|
|
|
|
sid_value->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8);
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
return true;
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
/**
|
|
|
|
* Return the parent locator and function of an SRv6 SID.
|
|
|
|
*
|
|
|
|
* @param sid_value SRv6 SID address to be decomposed
|
|
|
|
* @param sid_block Parent block of the SRv6 SID
|
|
|
|
* @param locator Parent locator of the SRv6 SID
|
|
|
|
* @param sid_func Function part of the SID
|
|
|
|
* @param sid_wide_func Wide function of the SID
|
|
|
|
* @return True if success, False otherwise
|
|
|
|
*/
|
|
|
|
static bool zebra_srv6_sid_decompose(struct in6_addr *sid_value,
|
|
|
|
struct zebra_srv6_sid_block **sid_block,
|
|
|
|
struct srv6_locator **locator,
|
|
|
|
uint32_t *sid_func, uint32_t *sid_wide_func)
|
2025-02-05 10:03:58 +01:00
|
|
|
{
|
2025-02-05 10:17:11 +01:00
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct srv6_locator *l;
|
|
|
|
struct zebra_srv6_sid_block *b;
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
struct listnode *node;
|
|
|
|
struct prefix_ipv6 tmp_prefix;
|
|
|
|
uint8_t offset, func_len;
|
|
|
|
|
|
|
|
if (!sid_value || !sid_func)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
*sid_func = 0;
|
|
|
|
*sid_wide_func = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build a temporary prefix_ipv6 object representing the SRv6 SID.
|
|
|
|
* This temporary prefix object is used below by the prefix_match
|
|
|
|
* function to check if the SID belongs to a specific locator.
|
|
|
|
*/
|
|
|
|
tmp_prefix.family = AF_INET6;
|
|
|
|
tmp_prefix.prefixlen = IPV6_MAX_BITLEN;
|
|
|
|
tmp_prefix.prefix = *sid_value;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup the parent locator of the SID and return the locator and
|
|
|
|
* the function of the SID.
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, l)) {
|
|
|
|
/*
|
|
|
|
* Check if the locator prefix includes the temporary prefix
|
|
|
|
* representing the SID.
|
|
|
|
*/
|
|
|
|
if (prefix_match((struct prefix *)&l->prefix,
|
|
|
|
(struct prefix *)&tmp_prefix)) {
|
|
|
|
format = l->sid_format;
|
|
|
|
|
|
|
|
if (format) {
|
|
|
|
offset = format->block_len + format->node_len;
|
|
|
|
func_len = format->function_len;
|
|
|
|
} else {
|
|
|
|
offset = l->block_bits_length +
|
|
|
|
l->node_bits_length;
|
|
|
|
func_len = l->function_bits_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint8_t idx = 0; idx < func_len; idx++) {
|
|
|
|
uint8_t tidx = offset + idx;
|
|
|
|
*sid_func |= (sid_value->s6_addr[tidx / 8] &
|
|
|
|
(0x1 << (7 - tidx % 8)))
|
|
|
|
<< (((func_len - 1 - idx) / 8) * 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If function comes from the Wide LIB range, we also
|
|
|
|
* need to get the Wide function.
|
|
|
|
*/
|
|
|
|
if (format && format->type == SRV6_SID_FORMAT_TYPE_USID) {
|
|
|
|
if (*sid_func >= format->config.usid.wlib_start &&
|
|
|
|
*sid_func <= format->config.usid.wlib_end) {
|
|
|
|
format = l->sid_format;
|
|
|
|
|
|
|
|
offset = format->block_len +
|
|
|
|
format->node_len +
|
|
|
|
format->function_len;
|
|
|
|
|
|
|
|
for (uint8_t idx = 0; idx < 16; idx++) {
|
|
|
|
uint8_t tidx = offset + idx;
|
|
|
|
*sid_wide_func |=
|
|
|
|
(sid_value->s6_addr[tidx /
|
|
|
|
8] &
|
|
|
|
(0x1 << (7 - tidx % 8)))
|
|
|
|
<< (((16 - 1 - idx) / 8) *
|
|
|
|
8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*locator = l;
|
|
|
|
*sid_block = l->sid_block;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we arrive here, the SID does not belong to any locator.
|
|
|
|
* Then, let's try to find the parent block from which the SID
|
|
|
|
* has been allocated.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup the parent block of the SID and return the block and
|
|
|
|
* the function of the SID.
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srv6->sid_blocks, node, b)) {
|
|
|
|
/*
|
|
|
|
* Check if the block prefix includes the temporary prefix
|
|
|
|
* representing the SID
|
|
|
|
*/
|
|
|
|
if (prefix_match((struct prefix *)&b->prefix,
|
|
|
|
(struct prefix *)&tmp_prefix)) {
|
|
|
|
format = b->sid_format;
|
|
|
|
|
|
|
|
if (!format)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
offset = format->block_len + format->node_len;
|
|
|
|
func_len = format->function_len;
|
|
|
|
|
|
|
|
for (uint8_t idx = 0; idx < func_len; idx++) {
|
|
|
|
uint8_t tidx = offset + idx;
|
|
|
|
*sid_func |= (sid_value->s6_addr[tidx / 8] &
|
|
|
|
(0x1 << (7 - tidx % 8)))
|
|
|
|
<< ((func_len - 1 - idx) / 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If function comes from the Wide LIB range, we also
|
|
|
|
* need to get the Wide function.
|
|
|
|
*/
|
|
|
|
if (*sid_func >= format->config.usid.wlib_start &&
|
|
|
|
*sid_func <= format->config.usid.wlib_end) {
|
|
|
|
format = b->sid_format;
|
|
|
|
|
|
|
|
offset = format->block_len + format->node_len +
|
|
|
|
format->function_len;
|
|
|
|
|
|
|
|
for (uint8_t idx = 0; idx < 16; idx++) {
|
|
|
|
uint8_t tidx = offset + idx;
|
|
|
|
*sid_wide_func |=
|
|
|
|
(sid_value->s6_addr[tidx / 8] &
|
|
|
|
(0x1 << (7 - tidx % 8)))
|
|
|
|
<< (((16 - 1 - idx) / 8) * 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*sid_block = b;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate an explicit SID function (i.e. specific SID function value) from a given SID block.
|
|
|
|
*
|
|
|
|
* @param block SRv6 SID block from which the SID function has to be allocated
|
|
|
|
* @param sid_func SID function to be allocated
|
|
|
|
* @param sid_wide_func SID wide function to be allocated
|
|
|
|
*
|
|
|
|
* @return true on success, false otherwise
|
|
|
|
*/
|
|
|
|
static bool alloc_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block,
|
|
|
|
uint32_t sid_func,
|
|
|
|
uint32_t sid_wide_func)
|
|
|
|
{
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
struct listnode *node;
|
|
|
|
uint32_t *sid_func_ptr = NULL;
|
|
|
|
|
|
|
|
if (!block)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
format = block->sid_format;
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: trying to allocate explicit SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate SID function from the corresponding range depending on the SID format type
|
|
|
|
*/
|
|
|
|
if (format) {
|
|
|
|
if (format->type == SRV6_SID_FORMAT_TYPE_USID) {
|
|
|
|
uint32_t elib_start = format->config.usid.elib_start;
|
|
|
|
uint32_t elib_end = format->config.usid.elib_end;
|
|
|
|
uint32_t wlib_end = format->config.usid.wlib_end;
|
|
|
|
uint32_t ewlib_start = format->config.usid.ewlib_start;
|
|
|
|
uint32_t ewlib_end = wlib_end;
|
|
|
|
uint32_t *sid_wide_func_ptr = NULL;
|
|
|
|
|
|
|
|
/* Figure out the range from which the SID function has been allocated and release it */
|
|
|
|
if ((sid_func >= elib_start) && (sid_func <= elib_end)) {
|
|
|
|
/* The SID function has to be allocated from the ELIB range */
|
|
|
|
|
|
|
|
/* Ensure that the requested SID function has not already been taken */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.usid.lib
|
|
|
|
.func_allocated,
|
|
|
|
node, sid_func_ptr))
|
|
|
|
if (*sid_func_ptr == sid_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (sid_func_ptr) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: SID function %u already taken",
|
|
|
|
__func__, sid_func);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the SID function as "taken" by adding it to the "func_allocated" list and
|
|
|
|
* increase the counter of function allocated
|
|
|
|
*/
|
|
|
|
sid_func_ptr =
|
|
|
|
zebra_srv6_sid_func_alloc(sid_func);
|
|
|
|
listnode_add(block->u.usid.lib.func_allocated,
|
|
|
|
sid_func_ptr);
|
|
|
|
block->u.usid.lib.num_func_allocated++;
|
|
|
|
} else if ((sid_func >= ewlib_start) &&
|
|
|
|
(sid_func <= ewlib_end)) {
|
|
|
|
/* The SID function has to be allocated from the EWLIB range */
|
|
|
|
|
|
|
|
/* Ensure that the requested SID function has not already been taken */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.usid
|
|
|
|
.wide_lib[sid_func]
|
|
|
|
.func_allocated,
|
|
|
|
node,
|
|
|
|
sid_wide_func_ptr))
|
|
|
|
if (*sid_wide_func_ptr == sid_wide_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (sid_wide_func_ptr) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: SID function %u already taken",
|
|
|
|
__func__, sid_func);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the SID function as "taken" by adding it to the "func_allocated" list and
|
|
|
|
* increase the counter of function allocated
|
|
|
|
*/
|
|
|
|
sid_wide_func_ptr = zebra_srv6_sid_func_alloc(
|
|
|
|
sid_wide_func);
|
|
|
|
listnode_add(block->u.usid.wide_lib[sid_func]
|
|
|
|
.func_allocated,
|
|
|
|
sid_wide_func_ptr);
|
|
|
|
block->u.usid.wide_lib[sid_func]
|
|
|
|
.num_func_allocated++;
|
|
|
|
} else {
|
|
|
|
zlog_warn("%s: function %u is outside ELIB [%u/%u] and EWLIB alloc ranges [%u/%u]",
|
|
|
|
__func__, sid_func, elib_start,
|
|
|
|
elib_end, ewlib_start, ewlib_end);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (format->type == SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) {
|
|
|
|
uint32_t explicit_start =
|
|
|
|
format->config.uncompressed.explicit_start;
|
|
|
|
uint32_t explicit_end =
|
|
|
|
(uint32_t)((1 << format->function_len) - 1);
|
|
|
|
|
|
|
|
/* Ensure that the SID function comes from the Explicit range */
|
|
|
|
if (!(sid_func >= explicit_start &&
|
|
|
|
sid_func <= explicit_end)) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: SID function %u out of explicit range (%u - %u)",
|
|
|
|
__func__, sid_func, explicit_start,
|
|
|
|
explicit_end);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ensure that the SID function has not already been taken */
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.uncompressed
|
|
|
|
.func_allocated,
|
|
|
|
node, sid_func_ptr))
|
|
|
|
if (*sid_func_ptr == sid_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* SID function already taken */
|
|
|
|
if (sid_func_ptr) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: SID function %u already taken",
|
|
|
|
__func__, sid_func);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the SID function as "taken" by adding it to the "func_allocated" list and
|
|
|
|
* increase the counter of function allocated
|
|
|
|
*/
|
|
|
|
sid_func_ptr = zebra_srv6_sid_func_alloc(sid_func);
|
|
|
|
listnode_add(block->u.uncompressed.func_allocated,
|
|
|
|
sid_func_ptr);
|
|
|
|
block->u.uncompressed.num_func_allocated++;
|
|
|
|
} else {
|
|
|
|
/* We should never arrive here */
|
|
|
|
zlog_err("%s: unknown SID format type: %u", __func__,
|
|
|
|
format->type);
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Ensure that the SID function has not already been taken */
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.uncompressed.func_allocated,
|
|
|
|
node, sid_func_ptr))
|
|
|
|
if (*sid_func_ptr == sid_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* SID function already taken */
|
|
|
|
if (sid_func_ptr) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: SID function %u already taken",
|
|
|
|
__func__, sid_func);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the SID function as "taken" by adding it to the "func_allocated" list and
|
|
|
|
* increase the counter of function allocated
|
|
|
|
*/
|
|
|
|
sid_func_ptr = zebra_srv6_sid_func_alloc(sid_func);
|
|
|
|
listnode_add(block->u.uncompressed.func_allocated, sid_func_ptr);
|
|
|
|
block->u.uncompressed.num_func_allocated++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: allocated explicit SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate a dynamic SID function (i.e. any available SID function value) from a given SID block.
|
|
|
|
*
|
|
|
|
* @param block SRv6 SID block from which the SID function has to be allocated
|
|
|
|
* @param sid_func SID function allocated
|
|
|
|
*
|
|
|
|
* @return true on success, false otherwise
|
|
|
|
*/
|
|
|
|
static bool alloc_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block,
|
|
|
|
uint32_t *sid_func)
|
|
|
|
{
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
uint32_t *sid_func_ptr = NULL;
|
|
|
|
|
|
|
|
if (!block || !sid_func)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
format = block->sid_format;
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: trying to allocate dynamic SID function from block %pFX",
|
|
|
|
__func__, &block->prefix);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate SID function from the corresponding range depending on the SID format type
|
|
|
|
*/
|
|
|
|
if (format) {
|
|
|
|
if (format->type == SRV6_SID_FORMAT_TYPE_USID) {
|
|
|
|
/* Format is uSID and behavior => allocate SID function from LIB range */
|
|
|
|
|
|
|
|
/* The Dynamic LIB range ends where the Explicit LIB range begins */
|
|
|
|
uint32_t dlib_end = format->config.usid.elib_start - 1;
|
|
|
|
|
|
|
|
/* Check if we ran out of available SID functions */
|
|
|
|
if (block->u.usid.lib.first_available_func > dlib_end) {
|
|
|
|
zlog_warn("%s: SRv6: Warning, SRv6 Dynamic LIB is depleted",
|
|
|
|
__func__);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First, let's check if there are any SID functions that were previously
|
|
|
|
* allocated and then released.
|
|
|
|
*/
|
|
|
|
if (listcount(block->u.usid.lib.func_released) != 0) {
|
|
|
|
/*
|
|
|
|
* There are SID functions previously allocated and then released,
|
|
|
|
* let's pick the first one and reuse it now.
|
|
|
|
*/
|
|
|
|
sid_func_ptr = listnode_head(
|
|
|
|
block->u.usid.lib.func_released);
|
|
|
|
*sid_func = *sid_func_ptr;
|
|
|
|
listnode_delete(block->u.usid.lib.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(sid_func_ptr);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* There are no SID functions previously allocated and then released,
|
|
|
|
* let's allocate a new function from the pool of available functions.
|
|
|
|
*/
|
|
|
|
*sid_func =
|
|
|
|
block->u.usid.lib.first_available_func;
|
|
|
|
block->u.usid.lib.first_available_func++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Increase the counter of SID functions allocated */
|
|
|
|
block->u.usid.lib.num_func_allocated++;
|
|
|
|
|
|
|
|
if (block->u.usid.lib.first_available_func > dlib_end)
|
|
|
|
zlog_warn("%s: SRv6: Warning, SRv6 Dynamic LIB is depleted and next SID request will fail",
|
|
|
|
__func__);
|
|
|
|
} else if (format->type == SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) {
|
|
|
|
/* Format is uncompressed => allocate SID function from Dynamic range */
|
|
|
|
|
|
|
|
uint32_t dynamic_end =
|
|
|
|
format->config.uncompressed.explicit_start - 1;
|
|
|
|
|
|
|
|
/* Check if we ran out of available SID functions */
|
|
|
|
if (block->u.uncompressed.first_available_func >
|
|
|
|
dynamic_end) {
|
|
|
|
zlog_warn("%s: SRv6: Warning, SRv6 SID Dynamic alloc space is depleted",
|
|
|
|
__func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First, let's check if there are any SID functions that were previously
|
|
|
|
* allocated and then released.
|
|
|
|
*/
|
|
|
|
if (listcount(block->u.uncompressed.func_released) != 0) {
|
|
|
|
/*
|
|
|
|
* There are SID functions previously allocated and then released,
|
|
|
|
* let's pick the first one and reuse it now.
|
|
|
|
*/
|
|
|
|
sid_func_ptr = listnode_head(
|
|
|
|
block->u.uncompressed.func_released);
|
|
|
|
*sid_func = *sid_func_ptr;
|
|
|
|
listnode_delete(block->u.uncompressed
|
|
|
|
.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(sid_func_ptr);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* There are no SID functions previously allocated and then released,
|
|
|
|
* let's allocate a new function from the pool of available functions.
|
|
|
|
*/
|
|
|
|
*sid_func = block->u.uncompressed
|
|
|
|
.first_available_func;
|
|
|
|
block->u.uncompressed.first_available_func++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Increase the counter of SID functions allocated */
|
|
|
|
block->u.uncompressed.num_func_allocated++;
|
|
|
|
|
|
|
|
if (block->u.uncompressed.first_available_func >
|
|
|
|
dynamic_end)
|
|
|
|
zlog_warn("%s: SRv6: Warning, SRv6 SID Dynamic alloc space is depleted and next SID request will fail",
|
|
|
|
__func__);
|
|
|
|
} else {
|
|
|
|
/* We should never arrive here */
|
|
|
|
zlog_err("%s: unknown SID format type: %u", __func__,
|
|
|
|
format->type);
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* First, let's check if there are any SID functions that were previously
|
|
|
|
* allocated and then released.
|
|
|
|
*/
|
|
|
|
if (listcount(block->u.uncompressed.func_released) != 0) {
|
|
|
|
/*
|
|
|
|
* There are SID functions previously allocated and then released,
|
|
|
|
* let's pick the first one and reuse it now.
|
|
|
|
*/
|
|
|
|
sid_func_ptr = listnode_head(
|
|
|
|
block->u.uncompressed.func_released);
|
|
|
|
*sid_func = *sid_func_ptr;
|
|
|
|
listnode_delete(block->u.uncompressed.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(sid_func_ptr);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* There are no SID functions previously allocated and then released,
|
|
|
|
* let's allocate a new function from the pool of available functions.
|
|
|
|
*/
|
|
|
|
*sid_func = block->u.uncompressed.first_available_func;
|
|
|
|
block->u.uncompressed.first_available_func++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Increase the counter of SID functions allocated */
|
|
|
|
block->u.uncompressed.num_func_allocated++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: allocated dynamic SID function %u from block %pFX",
|
|
|
|
__func__, *sid_func, &block->prefix);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an explicit SID (i.e., a specific SID value) for a given context.
|
|
|
|
*
|
|
|
|
* If a SID already exists associated with the context, it returns the existing SID.
|
|
|
|
* Otherwise, it allocates a new SID.
|
|
|
|
*
|
|
|
|
* @param sid SID returned
|
|
|
|
* @param ctx Context for which the SID has been requested
|
|
|
|
* @param sid_value specific SRv6 SID value (i.e. IPv6 address) to be
|
|
|
|
* allocated explicitly
|
|
|
|
*
|
|
|
|
* @return 0 if the function returned an existing SID and SID value has not changed,
|
|
|
|
* 1 if a new SID has been allocated or the existing SID value has changed, -1 if an error occurred
|
|
|
|
*/
|
|
|
|
static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid,
|
|
|
|
struct srv6_sid_ctx *ctx,
|
|
|
|
struct in6_addr *sid_value)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct zebra_srv6_sid_ctx *s = NULL;
|
|
|
|
struct zebra_srv6_sid_ctx *zctx = NULL;
|
|
|
|
struct listnode *node;
|
|
|
|
uint32_t sid_func = 0, sid_func_wide = 0;
|
|
|
|
struct srv6_locator *locator = NULL;
|
|
|
|
struct zebra_srv6_sid_block *block = NULL;
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if (!ctx || !sid_value)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Check if we already have a SID associated with the provided context */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srv6->sids, node, s)) {
|
|
|
|
if (memcmp(&s->ctx, ctx, sizeof(struct srv6_sid_ctx)) == 0) {
|
|
|
|
/*
|
|
|
|
* If the context is already associated with a SID that has the same SID value, then
|
|
|
|
* return the existing SID
|
|
|
|
*/
|
|
|
|
if (sid_same(&s->sid->value, sid_value)) {
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: returning existing SRv6 SID %pI6 ctx %s",
|
|
|
|
__func__, &s->sid->value,
|
|
|
|
srv6_sid_ctx2str(buf,
|
|
|
|
sizeof(buf),
|
|
|
|
ctx));
|
|
|
|
*sid = s->sid;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is not allowed to allocate an explicit SID for a given context if the context
|
|
|
|
* is already associated with an explicit SID
|
|
|
|
*/
|
|
|
|
if (s->sid->alloc_mode == SRV6_SID_ALLOC_MODE_EXPLICIT) {
|
|
|
|
zlog_err("%s: cannot alloc SID %pI6 for ctx %s: ctx already associated with SID %pI6",
|
|
|
|
__func__, sid_value,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf),
|
|
|
|
&s->ctx),
|
|
|
|
&s->sid->value);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
zctx = s;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get parent locator and function of the provided SID */
|
|
|
|
if (!zebra_srv6_sid_decompose(sid_value, &block, &locator, &sid_func,
|
|
|
|
&sid_func_wide)) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: parent block/locator not found for SID %pI6",
|
|
|
|
__func__, sid_value);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->behavior == ZEBRA_SEG6_LOCAL_ACTION_END) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: explicit SID allocation not allowed for End/uN behavior",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate an explicit SID function for the SID */
|
|
|
|
if (!alloc_srv6_sid_func_explicit(block, sid_func, sid_func_wide)) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: failed to allocate SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!zctx) {
|
|
|
|
/* If we don't have a zebra SID context for this context, allocate a new one */
|
|
|
|
zctx = zebra_srv6_sid_ctx_alloc();
|
|
|
|
zctx->ctx = *ctx;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If we already have a SID associated with this context, we need to
|
|
|
|
* deallocate the current SID function before allocating the new one
|
|
|
|
*/
|
|
|
|
if (zctx->sid) {
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: ctx %s already associated with a dynamic SID %pI6, releasing dynamic SID",
|
|
|
|
__func__,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf),
|
|
|
|
ctx),
|
|
|
|
&zctx->sid->value);
|
|
|
|
|
|
|
|
release_srv6_sid_func_dynamic(block, zctx->sid->func);
|
|
|
|
zebra_srv6_sid_free(zctx->sid);
|
|
|
|
zctx->sid = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the SID to store SID information */
|
|
|
|
*sid = zebra_srv6_sid_alloc(zctx, sid_value, locator, block, sid_func,
|
|
|
|
SRV6_SID_ALLOC_MODE_EXPLICIT);
|
|
|
|
if (!(*sid)) {
|
|
|
|
flog_err(EC_ZEBRA_SM_CANNOT_ASSIGN_SID,
|
|
|
|
"%s: failed to create SRv6 SID %s (%pI6)", __func__,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
(*sid)->ctx = zctx;
|
|
|
|
zctx->sid = *sid;
|
|
|
|
listnode_add(srv6->sids, zctx);
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: allocated explicit SRv6 SID %pI6 for context %s",
|
|
|
|
__func__, &(*sid)->value,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), ctx));
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a dynamic SID (i.e., any available SID value) for a given context.
|
|
|
|
*
|
|
|
|
* If a SID already exists associated with the context, it returns the existing SID.
|
|
|
|
* Otherwise, it allocates a new SID.
|
|
|
|
*
|
|
|
|
* @param sid SID returned
|
|
|
|
* @param ctx Context for which the SID has been requested
|
|
|
|
* @param locator SRv6 locator from which the SID has to be allocated
|
|
|
|
*
|
|
|
|
* @return 0 if the function returned an existing SID and SID value has not changed,
|
|
|
|
* 1 if a new SID has been allocated or the existing SID value has changed, -1 if an error occurred
|
|
|
|
*/
|
|
|
|
static int get_srv6_sid_dynamic(struct zebra_srv6_sid **sid,
|
|
|
|
struct srv6_sid_ctx *ctx,
|
|
|
|
struct srv6_locator *locator)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct zebra_srv6_sid_block *block;
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
struct zebra_srv6_sid_ctx *s = NULL;
|
|
|
|
struct zebra_srv6_sid_ctx *zctx;
|
|
|
|
struct listnode *node;
|
|
|
|
struct in6_addr sid_value;
|
|
|
|
uint32_t sid_func = 0;
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if (!ctx || !locator)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
block = locator->sid_block;
|
|
|
|
format = locator->sid_format;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we already have a SID for the provided context, we return the existing
|
|
|
|
* SID instead of allocating a new one.
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(srv6->sids, node, s)) {
|
|
|
|
if (locator && s->sid && s->sid->locator) {
|
|
|
|
if (strncmp(s->sid->locator->name, locator->name,
|
|
|
|
SRV6_LOCNAME_SIZE)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (memcmp(&s->ctx, ctx, sizeof(struct srv6_sid_ctx)) == 0) {
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: returning existing SID %s %pI6",
|
|
|
|
__func__,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf),
|
|
|
|
ctx),
|
|
|
|
&s->sid->value);
|
|
|
|
*sid = s->sid;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (format && format->type == SRV6_SID_FORMAT_TYPE_USID &&
|
|
|
|
ctx->behavior == ZEBRA_SEG6_LOCAL_ACTION_END) {
|
|
|
|
/* uN SID is allocated from the GIB range */
|
|
|
|
sid_value = locator->prefix.prefix;
|
|
|
|
} else if (!format && ctx->behavior == ZEBRA_SEG6_LOCAL_ACTION_END) {
|
|
|
|
/* uN SID is allocated from the GIB range */
|
|
|
|
sid_value = locator->prefix.prefix;
|
|
|
|
} else {
|
|
|
|
/* Allocate a dynamic SID function for the SID */
|
|
|
|
if (!alloc_srv6_sid_func_dynamic(block, &sid_func)) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: failed to allocate SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compose the SID as the locator followed by the SID function */
|
|
|
|
zebra_srv6_sid_compose(&sid_value, locator, sid_func);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate a zebra SID context to store SID context information */
|
|
|
|
zctx = zebra_srv6_sid_ctx_alloc();
|
|
|
|
zctx->ctx = *ctx;
|
|
|
|
|
|
|
|
/* Allocate the SID to store SID information */
|
|
|
|
*sid = zebra_srv6_sid_alloc(zctx, &sid_value, locator, block, sid_func,
|
|
|
|
SRV6_SID_ALLOC_MODE_DYNAMIC);
|
|
|
|
if (!(*sid)) {
|
|
|
|
flog_err(EC_ZEBRA_SM_CANNOT_ASSIGN_SID,
|
|
|
|
"%s: failed to create SRv6 SID ctx %s (%pI6)", __func__,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), ctx), &sid_value);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
(*sid)->ctx = zctx;
|
|
|
|
zctx->sid = *sid;
|
|
|
|
listnode_add(srv6->sids, zctx);
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: allocated new dynamic SRv6 SID %pI6 for context %s",
|
|
|
|
__func__, &(*sid)->value,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), ctx));
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an SRv6 SID for a given context.
|
|
|
|
*
|
|
|
|
* If a SID already exists associated with the context, it returns the existing SID.
|
|
|
|
* Otherwise, it allocates a new SID.
|
|
|
|
*
|
|
|
|
* If the sid_value parameter is non-NULL, it allocates the requested SID value
|
|
|
|
* if it is available (explicit SID allocation).
|
|
|
|
* If the sid_value parameter is NULL, it allocates any available SID value
|
|
|
|
* (dynamic SID allocation).
|
|
|
|
*
|
|
|
|
* @param sid SID returned
|
|
|
|
* @param ctx Context for which the SID has been requested
|
|
|
|
* @param sid_value SRv6 SID value to be allocated (for explicit SID allocation)
|
|
|
|
* @param locator_name Parent SRv6 locator from which the SID has to be allocated (for dynamic SID allocation)
|
|
|
|
*
|
|
|
|
* @return 0 if the function returned an existing SID and SID value has not changed,
|
|
|
|
* 1 if a new SID has been allocated or the existing SID value has changed, -1 if an error occurred
|
|
|
|
*/
|
|
|
|
int get_srv6_sid(struct zebra_srv6_sid **sid, struct srv6_sid_ctx *ctx,
|
|
|
|
struct in6_addr *sid_value, const char *locator_name)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct srv6_locator *locator;
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
enum srv6_sid_alloc_mode alloc_mode =
|
|
|
|
(sid_value) ? SRV6_SID_ALLOC_MODE_EXPLICIT
|
|
|
|
: SRV6_SID_ALLOC_MODE_DYNAMIC;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: received SRv6 SID alloc request: SID ctx %s (%pI6), mode=%s",
|
|
|
|
__func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx),
|
|
|
|
sid_value, srv6_sid_alloc_mode2str(alloc_mode));
|
|
|
|
|
|
|
|
if (alloc_mode == SRV6_SID_ALLOC_MODE_EXPLICIT) {
|
|
|
|
/*
|
|
|
|
* Explicit SID allocation: allocate a specific SID value
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!sid_value) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: missing SRv6 SID value, necessary for explicit allocation",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = get_srv6_sid_explicit(sid, ctx, sid_value);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Dynamic SID allocation: allocate any available SID value
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!locator_name) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: missing SRv6 locator, necessary for dynamic allocation",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
locator = zebra_srv6_locator_lookup(locator_name);
|
|
|
|
if (!locator) {
|
|
|
|
zlog_err("%s: invalid SM request arguments: SRv6 locator '%s' does not exist",
|
|
|
|
__func__, locator_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = get_srv6_sid_dynamic(sid, ctx, locator);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release an explicit SRv6 SID function.
|
|
|
|
*
|
|
|
|
* @param block Parent SRv6 SID block of the SID function that has to be released
|
|
|
|
* @param sid_func SID function to be released
|
|
|
|
* @return 0 on success, -1 otherwise
|
|
|
|
*/
|
|
|
|
static bool release_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block,
|
|
|
|
uint32_t sid_func,
|
|
|
|
uint32_t sid_wide_func)
|
|
|
|
{
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
struct listnode *node;
|
|
|
|
uint32_t *sid_func_ptr = NULL;
|
|
|
|
|
|
|
|
if (!block)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
format = block->sid_format;
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: trying to release explicit SRv6 SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release SID function from the corresponding range depending on the SID format type
|
|
|
|
*/
|
|
|
|
if (format) {
|
|
|
|
if (format->type == SRV6_SID_FORMAT_TYPE_USID) {
|
|
|
|
uint32_t elib_start = format->config.usid.elib_start;
|
|
|
|
uint32_t elib_end = format->config.usid.elib_end;
|
|
|
|
uint32_t ewlib_start = format->config.usid.ewlib_start;
|
|
|
|
uint32_t ewlib_end = format->config.usid.wlib_end;
|
|
|
|
uint32_t *sid_wide_func_ptr = NULL;
|
|
|
|
|
|
|
|
/* Figure out the range from which the SID function has been allocated and release it */
|
|
|
|
if ((sid_func >= elib_start) && (sid_func <= elib_end)) {
|
|
|
|
/* The SID function comes from the ELIB range */
|
|
|
|
|
|
|
|
/* Lookup SID function in the functions allocated list of ELIB range */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.usid.lib
|
|
|
|
.func_allocated,
|
|
|
|
node, sid_func_ptr))
|
|
|
|
if (*sid_func_ptr == sid_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Ensure that the SID function is allocated */
|
|
|
|
if (!sid_func_ptr) {
|
|
|
|
zlog_warn("%s: failed to release SID function %u, function is not allocated",
|
|
|
|
__func__, sid_func);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the SID function from the ELIB range */
|
|
|
|
listnode_delete(block->u.usid.lib.func_allocated,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(sid_func_ptr);
|
|
|
|
} else if ((sid_func >= ewlib_start) &&
|
|
|
|
(sid_func <= ewlib_end)) {
|
|
|
|
/* The SID function comes from the EWLIB range */
|
|
|
|
|
|
|
|
/* Lookup SID function in the functions allocated list of EWLIB range */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.usid
|
|
|
|
.wide_lib[sid_func]
|
|
|
|
.func_allocated,
|
|
|
|
node, sid_wide_func_ptr))
|
|
|
|
if (*sid_wide_func_ptr == sid_wide_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Ensure that the SID function is allocated */
|
|
|
|
if (!sid_wide_func_ptr) {
|
|
|
|
zlog_warn("%s: failed to release wide SID function %u, function is not allocated",
|
|
|
|
__func__, sid_wide_func);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the SID function from the EWLIB range */
|
|
|
|
listnode_delete(block->u.usid.wide_lib[sid_func]
|
|
|
|
.func_allocated,
|
|
|
|
sid_wide_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(sid_wide_func_ptr);
|
|
|
|
} else {
|
|
|
|
zlog_warn("%s: function %u is outside ELIB [%u/%u] and EWLIB alloc ranges [%u/%u]",
|
|
|
|
__func__, sid_func, elib_start,
|
|
|
|
elib_end, ewlib_start, ewlib_end);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (format->type == SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) {
|
|
|
|
uint32_t explicit_start =
|
|
|
|
format->config.uncompressed.explicit_start;
|
|
|
|
uint32_t explicit_end =
|
|
|
|
(uint32_t)((1 << format->function_len) - 1);
|
|
|
|
|
|
|
|
/* Ensure that the SID function comes from the Explicit range */
|
|
|
|
if (!(sid_func >= explicit_start &&
|
|
|
|
sid_func <= explicit_end)) {
|
|
|
|
zlog_warn("%s: function %u is outside explicit alloc range [%u/%u]",
|
|
|
|
__func__, sid_func, explicit_start,
|
|
|
|
explicit_end);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup SID function in the functions allocated list of Explicit range */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.uncompressed
|
|
|
|
.func_allocated,
|
|
|
|
node, sid_func_ptr))
|
|
|
|
if (*sid_func_ptr == sid_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Ensure that the SID function is allocated */
|
|
|
|
if (!sid_func_ptr) {
|
|
|
|
zlog_warn("%s: failed to release SID function %u, function is not allocated",
|
|
|
|
__func__, sid_func);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the SID function from the Explicit range */
|
|
|
|
listnode_delete(block->u.uncompressed.func_allocated,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(sid_func_ptr);
|
|
|
|
} else {
|
|
|
|
/* We should never arrive here */
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Lookup SID function in the functions allocated list of Explicit range */
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(block->u.uncompressed.func_allocated,
|
|
|
|
node, sid_func_ptr))
|
|
|
|
if (*sid_func_ptr == sid_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Ensure that the SID function is allocated */
|
|
|
|
if (!sid_func_ptr) {
|
|
|
|
zlog_warn("%s: failed to release SID function %u, function is not allocated",
|
|
|
|
__func__, sid_func);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the SID function from the Explicit range */
|
|
|
|
listnode_delete(block->u.uncompressed.func_allocated,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(sid_func_ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: released explicit SRv6 SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release a dynamic SRv6 SID function.
|
|
|
|
*
|
|
|
|
* @param block Parent SRv6 SID block of the SID function that has to be released
|
|
|
|
* @param sid_func SID function to be released
|
|
|
|
* @return 0 on success, -1 otherwise
|
|
|
|
*/
|
|
|
|
static int release_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block,
|
|
|
|
uint32_t sid_func)
|
|
|
|
{
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
uint32_t *sid_func_ptr = NULL;
|
|
|
|
|
|
|
|
if (!block)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
format = block->sid_format;
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: trying to release dynamic SRv6 SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release SID function from the corresponding range depending on the SID format type
|
|
|
|
*/
|
|
|
|
if (format && format->type == SRV6_SID_FORMAT_TYPE_USID) {
|
|
|
|
uint32_t dlib_start = format->config.usid.lib_start;
|
|
|
|
/* The Dynamic LIB range ends where the Explicit LIB range begins */
|
|
|
|
uint32_t dlib_end = format->config.usid.elib_start - 1;
|
|
|
|
|
|
|
|
/* Ensure that the SID function to be released comes from the Dynamic LIB (DLIB) range */
|
|
|
|
if (!(sid_func >= dlib_start && sid_func <= dlib_end)) {
|
|
|
|
zlog_warn("%s: function %u is outside Dynamic LIB range [%u/%u]",
|
|
|
|
__func__, sid_func, dlib_start, dlib_end);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sid_func == block->u.usid.lib.first_available_func - 1) {
|
|
|
|
/*
|
|
|
|
* The SID function to be released precedes the `first_available_func`.
|
|
|
|
* Reset first_available_func to the first available position.
|
|
|
|
*/
|
|
|
|
|
|
|
|
block->u.usid.lib.first_available_func -= 1;
|
|
|
|
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
do {
|
|
|
|
found = false;
|
|
|
|
for (ALL_LIST_ELEMENTS(block->u.usid.lib
|
|
|
|
.func_released,
|
|
|
|
node, nnode,
|
|
|
|
sid_func_ptr))
|
|
|
|
if (*sid_func_ptr ==
|
|
|
|
block->u.usid.lib.first_available_func -
|
|
|
|
1) {
|
|
|
|
listnode_delete(block->u.usid
|
|
|
|
.lib
|
|
|
|
.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(
|
|
|
|
sid_func_ptr);
|
|
|
|
block->u.usid.lib
|
|
|
|
.first_available_func -=
|
|
|
|
1;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (found);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The SID function to be released does not precede the `first_available_func`.
|
|
|
|
* Add the released function to the func_released array to indicate
|
|
|
|
* that it is available again for allocation.
|
|
|
|
*/
|
|
|
|
sid_func_ptr = zebra_srv6_sid_func_alloc(sid_func);
|
|
|
|
listnode_add_head(block->u.usid.lib.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
}
|
|
|
|
} else if (format && format->type == SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) {
|
|
|
|
uint32_t dynamic_start =
|
|
|
|
SRV6_SID_FORMAT_UNCOMPRESSED_F4024_FUNC_UNRESERVED_MIN;
|
|
|
|
/* The Dynamic range ends where the Explicit range begins */
|
|
|
|
uint32_t dynamic_end =
|
|
|
|
format->config.uncompressed.explicit_start - 1;
|
|
|
|
|
|
|
|
/* Ensure that the SID function to be released comes from the Dynamic range */
|
|
|
|
if (!(sid_func >= dynamic_start && sid_func <= dynamic_end)) {
|
|
|
|
zlog_warn("%s: function %u is outside dynamic range [%u/%u]",
|
|
|
|
__func__, sid_func, dynamic_start,
|
|
|
|
dynamic_end);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sid_func == block->u.uncompressed.first_available_func - 1) {
|
|
|
|
/*
|
|
|
|
* The released SID function precedes the `first_available_func`.
|
|
|
|
* Reset first_available_func to the first available position.
|
|
|
|
*/
|
|
|
|
|
|
|
|
block->u.uncompressed.first_available_func -= 1;
|
|
|
|
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
do {
|
|
|
|
found = false;
|
|
|
|
for (ALL_LIST_ELEMENTS(block->u.uncompressed
|
|
|
|
.func_released,
|
|
|
|
node, nnode,
|
|
|
|
sid_func_ptr))
|
|
|
|
if (*sid_func_ptr ==
|
|
|
|
block->u.uncompressed
|
|
|
|
.first_available_func -
|
|
|
|
1) {
|
|
|
|
listnode_delete(block->u.uncompressed
|
|
|
|
.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(
|
|
|
|
sid_func_ptr);
|
|
|
|
block->u.uncompressed
|
|
|
|
.first_available_func -=
|
|
|
|
1;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (found);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The released SID function does not precede the `first_available_func`.
|
|
|
|
* Add the released function to the func_released array to indicate
|
|
|
|
* that it is available again for allocation.
|
|
|
|
*/
|
|
|
|
sid_func_ptr = zebra_srv6_sid_func_alloc(sid_func);
|
|
|
|
listnode_add_head(block->u.uncompressed.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
}
|
|
|
|
} else if (!format) {
|
|
|
|
if (sid_func == block->u.uncompressed.first_available_func - 1) {
|
|
|
|
/*
|
|
|
|
* The released SID function precedes the `first_available_func`.
|
|
|
|
* Reset first_available_func to the first available position.
|
|
|
|
*/
|
|
|
|
|
|
|
|
block->u.uncompressed.first_available_func -= 1;
|
|
|
|
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
do {
|
|
|
|
found = false;
|
|
|
|
for (ALL_LIST_ELEMENTS(block->u.uncompressed
|
|
|
|
.func_released,
|
|
|
|
node, nnode,
|
|
|
|
sid_func_ptr))
|
|
|
|
if (*sid_func_ptr ==
|
|
|
|
block->u.uncompressed
|
|
|
|
.first_available_func -
|
|
|
|
1) {
|
|
|
|
listnode_delete(block->u.uncompressed
|
|
|
|
.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
zebra_srv6_sid_func_free(
|
|
|
|
sid_func_ptr);
|
|
|
|
block->u.uncompressed
|
|
|
|
.first_available_func -=
|
|
|
|
1;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (found);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The released SID function does not precede the `first_available_func`.
|
|
|
|
* Add the released function to the func_released array to indicate
|
|
|
|
* that it is available again for allocation.
|
|
|
|
*/
|
|
|
|
sid_func_ptr = zebra_srv6_sid_func_alloc(sid_func);
|
|
|
|
listnode_add_head(block->u.uncompressed.func_released,
|
|
|
|
sid_func_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: released dynamic SRv6 SID function %u from block %pFX",
|
|
|
|
__func__, sid_func, &block->prefix);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Core function, release the SRv6 SID associated with a given context.
|
|
|
|
*
|
|
|
|
* @param client The client for which the SID has to be released
|
|
|
|
* @param ctx Context associated with the SRv6 SID to be released
|
|
|
|
* @return 0 on success, -1 otherwise
|
|
|
|
*/
|
|
|
|
int release_srv6_sid(struct zserv *client, struct zebra_srv6_sid_ctx *zctx)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if (!zctx || !zctx->sid)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: releasing SRv6 SID %pI6 associated with ctx %s (proto=%u, instance=%u)",
|
|
|
|
__func__, &zctx->sid->value,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), &zctx->ctx),
|
|
|
|
client->proto, client->instance);
|
|
|
|
|
|
|
|
/* Ensures the SID is in use by the client */
|
|
|
|
if (!listnode_lookup(zctx->sid->client_list, client)) {
|
|
|
|
flog_err(EC_ZEBRA_SM_DAEMON_MISMATCH, "%s: Daemon mismatch!!",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the client from the list of clients using the SID */
|
|
|
|
listnode_delete(zctx->sid->client_list, client);
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: released SRv6 SID %pI6 associated with ctx %s (proto=%u, instance=%u)",
|
|
|
|
__func__, &zctx->sid->value,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), &zctx->ctx),
|
|
|
|
client->proto, client->instance);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the SID is not used by any other client, then deallocate it
|
|
|
|
* and remove it from the SRv6 database.
|
|
|
|
*/
|
|
|
|
if (listcount(zctx->sid->client_list) == 0) {
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: SRv6 SID %pI6 associated with ctx %s is no longer in use, removing it from SRv6 database",
|
|
|
|
__func__, &zctx->sid->value,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf),
|
|
|
|
&zctx->ctx));
|
|
|
|
|
|
|
|
if (!(zctx->sid->block->sid_format &&
|
|
|
|
zctx->sid->block->sid_format->type ==
|
|
|
|
SRV6_SID_FORMAT_TYPE_USID &&
|
|
|
|
zctx->ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END) &&
|
|
|
|
!(!zctx->sid->block->sid_format &&
|
|
|
|
zctx->ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END)) {
|
|
|
|
if (zctx->sid->alloc_mode ==
|
|
|
|
SRV6_SID_ALLOC_MODE_EXPLICIT)
|
|
|
|
/* Release SRv6 SID function */
|
|
|
|
release_srv6_sid_func_explicit(zctx->sid->block,
|
|
|
|
zctx->sid->func,
|
|
|
|
zctx->sid->wide_func);
|
|
|
|
else if (zctx->sid->alloc_mode ==
|
|
|
|
SRV6_SID_ALLOC_MODE_DYNAMIC)
|
|
|
|
/* Release SRv6 SID function */
|
|
|
|
release_srv6_sid_func_dynamic(zctx->sid->block,
|
|
|
|
zctx->sid->func);
|
|
|
|
else
|
|
|
|
/* We should never arrive here */
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free the SID */
|
|
|
|
zebra_srv6_sid_free(zctx->sid);
|
|
|
|
zctx->sid = NULL;
|
|
|
|
|
|
|
|
/* Remove the SID context from the list and free memory */
|
|
|
|
listnode_delete(srv6->sids, zctx);
|
|
|
|
zebra_srv6_sid_ctx_free(zctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a get SRv6 Locator request received from a client.
|
|
|
|
*
|
|
|
|
* It looks up the requested locator and send it to the client.
|
|
|
|
*
|
|
|
|
* @param locator SRv6 locator returned by this function
|
|
|
|
* @param client The client that sent the Get SRv6 Locator request
|
|
|
|
* @param locator_name Name of the locator to look up
|
|
|
|
*
|
|
|
|
* @return 0 on success
|
|
|
|
*/
|
|
|
|
static int srv6_manager_get_srv6_locator_internal(struct srv6_locator **locator,
|
|
|
|
struct zserv *client,
|
|
|
|
const char *locator_name)
|
|
|
|
{
|
|
|
|
*locator = zebra_srv6_locator_lookup(locator_name);
|
|
|
|
if (!*locator)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return zsend_zebra_srv6_locator_add(client, *locator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a get SID request received from a client.
|
|
|
|
*
|
|
|
|
* It gets a SID for a given context. If there is no SID associated with the context yet,
|
|
|
|
* we allocate one and return it to the client. Otherwise, we return the existing SID.
|
|
|
|
*
|
|
|
|
* - When the `sid_value` parameter is non-NULL, SRv6 Manager assigns the requested SID value
|
|
|
|
* if it is available (explicit SID allocation).
|
|
|
|
* - When the `sid_value` parameter is NULL, SRv6 Manager assigns any available SID value
|
|
|
|
* (dynamic SID allocation).
|
|
|
|
*
|
|
|
|
* Finally, notify the client whether the SID allocation was successful or failed.
|
|
|
|
*
|
|
|
|
* @param sid SID returned by this function
|
|
|
|
* @param client The client that requested the SID
|
|
|
|
* @param ctx Context for which the SID was requested
|
|
|
|
* @param sid_value SID value (i.e., IPv6 address) that has to be assigned to the SID
|
|
|
|
* (for explicit SID allocation)
|
|
|
|
* @param locator_name Locator from which the SID has to be allocated (for dynamic SID allocation)
|
|
|
|
*
|
|
|
|
* @return 0 on success, -1 otherwise
|
|
|
|
*/
|
|
|
|
static int srv6_manager_get_sid_internal(struct zebra_srv6_sid **sid,
|
|
|
|
struct zserv *client,
|
|
|
|
struct srv6_sid_ctx *ctx,
|
|
|
|
struct in6_addr *sid_value,
|
|
|
|
const char *locator_name)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct listnode *node;
|
|
|
|
struct zserv *c;
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: getting SRv6 SID for ctx %s, sid_value=%pI6, locator_name=%s",
|
|
|
|
__func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx),
|
|
|
|
sid_value ? sid_value : &in6addr_any, locator_name);
|
|
|
|
|
|
|
|
ret = get_srv6_sid(sid, ctx, sid_value, locator_name);
|
|
|
|
if (ret < 0) {
|
|
|
|
zlog_warn("%s: not got SRv6 SID for ctx %s, sid_value=%pI6, locator_name=%s",
|
|
|
|
__func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx),
|
|
|
|
sid_value ? sid_value : &in6addr_any, locator_name);
|
|
|
|
|
|
|
|
/* Notify client about SID alloc failure */
|
|
|
|
zsend_srv6_sid_notify(client, ctx, sid_value, 0, 0, NULL,
|
|
|
|
ZAPI_SRV6_SID_FAIL_ALLOC);
|
|
|
|
} else if (ret == 0) {
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: got existing SRv6 SID for ctx %s: sid_value=%pI6 (func=%u) (proto=%u, instance=%u, sessionId=%u), notify client",
|
|
|
|
__func__,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), ctx),
|
|
|
|
&(*sid)->value, (*sid)->func, client->proto,
|
|
|
|
client->instance, client->session_id);
|
|
|
|
if (!listnode_lookup((*sid)->client_list, client))
|
|
|
|
listnode_add((*sid)->client_list, client);
|
|
|
|
|
|
|
|
zsend_srv6_sid_notify(client, ctx, &(*sid)->value, (*sid)->func,
|
|
|
|
(*sid)->wide_func,
|
|
|
|
(*sid)->locator ? (*sid)->locator->name
|
|
|
|
: NULL,
|
|
|
|
ZAPI_SRV6_SID_ALLOCATED);
|
|
|
|
} else {
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: got new SRv6 SID for ctx %s: sid_value=%pI6 (func=%u) (proto=%u, instance=%u, sessionId=%u), notifying all clients",
|
|
|
|
__func__,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), ctx),
|
|
|
|
&(*sid)->value, (*sid)->func, client->proto,
|
|
|
|
client->instance, client->session_id);
|
|
|
|
if (!listnode_lookup((*sid)->client_list, client))
|
|
|
|
listnode_add((*sid)->client_list, client);
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO((*sid)->client_list, node, c))
|
|
|
|
zsend_srv6_sid_notify(c, ctx, &(*sid)->value,
|
|
|
|
(*sid)->func, (*sid)->wide_func,
|
|
|
|
(*sid)->locator
|
|
|
|
? (*sid)->locator->name
|
|
|
|
: NULL,
|
|
|
|
ZAPI_SRV6_SID_ALLOCATED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release SRv6 SIDs from a client.
|
|
|
|
*
|
|
|
|
* Called on client disconnection or reconnection.
|
|
|
|
*
|
|
|
|
* @param client The client to release SIDs from
|
|
|
|
* @return Number of SIDs released
|
|
|
|
*/
|
|
|
|
int release_daemon_srv6_sids(struct zserv *client)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
struct zebra_srv6_sid_ctx *ctx;
|
|
|
|
int count = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: releasing SRv6 SIDs for client proto %s, instance %d, session %u",
|
|
|
|
__func__, zebra_route_string(client->proto),
|
|
|
|
client->instance, client->session_id);
|
|
|
|
|
|
|
|
/* Iterate over the SIDs and release SIDs used by the client daemon */
|
|
|
|
for (ALL_LIST_ELEMENTS(srv6->sids, node, nnode, ctx)) {
|
|
|
|
if (!listnode_lookup(ctx->sid->client_list, client))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = release_srv6_sid(client, ctx);
|
|
|
|
if (ret == 0)
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: released %d SRv6 SIDs", __func__, count);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release SRv6 SIDs from a client.
|
|
|
|
*
|
|
|
|
* @param client The client zapi session
|
|
|
|
* @param ctx Context associated with the SRv6 SID
|
|
|
|
* @return 0 on success, -1 on failure
|
|
|
|
*/
|
|
|
|
static int srv6_manager_release_sid_internal(struct zserv *client,
|
|
|
|
struct srv6_sid_ctx *ctx)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
struct zebra_srv6_sid_ctx *zctx;
|
|
|
|
struct listnode *node, *nnode;
|
|
|
|
char buf[256];
|
|
|
|
const char *locator_name = NULL;
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: releasing SRv6 SID associated with ctx %s",
|
|
|
|
__func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx));
|
|
|
|
|
|
|
|
/* Lookup Zebra SID context and release it */
|
|
|
|
for (ALL_LIST_ELEMENTS(srv6->sids, node, nnode, zctx))
|
|
|
|
if (memcmp(&zctx->ctx, ctx, sizeof(struct srv6_sid_ctx)) == 0) {
|
|
|
|
if (zctx->sid && zctx->sid->locator)
|
|
|
|
locator_name =
|
|
|
|
(const char *)zctx->sid->locator->name;
|
|
|
|
ret = release_srv6_sid(client, zctx);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ZEBRA_DEBUG_PACKET)
|
|
|
|
zlog_debug("%s: no SID associated with ctx %s", __func__,
|
|
|
|
srv6_sid_ctx2str(buf, sizeof(buf), ctx));
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
zsend_srv6_sid_notify(client, ctx, NULL, 0, 0, locator_name,
|
|
|
|
ZAPI_SRV6_SID_RELEASED);
|
|
|
|
else
|
|
|
|
zsend_srv6_sid_notify(client, ctx, NULL, 0, 0, locator_name,
|
|
|
|
ZAPI_SRV6_SID_FAIL_RELEASE);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void zebra_srv6_terminate(void)
|
|
|
|
{
|
|
|
|
struct srv6_locator *locator;
|
|
|
|
struct srv6_sid_format *format;
|
|
|
|
struct zebra_srv6_sid_block *block;
|
|
|
|
struct zebra_srv6_sid_ctx *sid_ctx;
|
|
|
|
|
|
|
|
if (srv6.locators) {
|
|
|
|
while (listcount(srv6.locators)) {
|
|
|
|
locator = listnode_head(srv6.locators);
|
|
|
|
|
|
|
|
listnode_delete(srv6.locators, locator);
|
|
|
|
srv6_locator_free(locator);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_delete(&srv6.locators);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free SRv6 SIDs */
|
|
|
|
if (srv6.sids) {
|
|
|
|
while (listcount(srv6.sids)) {
|
|
|
|
sid_ctx = listnode_head(srv6.sids);
|
|
|
|
|
|
|
|
listnode_delete(srv6.sids, sid_ctx);
|
|
|
|
zebra_srv6_sid_ctx_free(sid_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_delete(&srv6.sids);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free SRv6 SID blocks */
|
|
|
|
if (srv6.sid_blocks) {
|
|
|
|
while (listcount(srv6.sid_blocks)) {
|
|
|
|
block = listnode_head(srv6.sid_blocks);
|
|
|
|
|
|
|
|
listnode_delete(srv6.sid_blocks, block);
|
|
|
|
zebra_srv6_sid_block_free(block);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_delete(&srv6.sid_blocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free SRv6 SID formats */
|
|
|
|
if (srv6.sid_formats) {
|
|
|
|
while (listcount(srv6.sid_formats)) {
|
|
|
|
format = listnode_head(srv6.sid_formats);
|
|
|
|
|
|
|
|
srv6_sid_format_unregister(format);
|
|
|
|
srv6_sid_format_free(format);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_delete(&srv6.sid_formats);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
hook_register(srv6_manager_get_sid, srv6_manager_get_sid_internal);
|
|
|
|
hook_register(srv6_manager_release_sid,
|
|
|
|
srv6_manager_release_sid_internal);
|
|
|
|
hook_register(srv6_manager_get_locator,
|
|
|
|
srv6_manager_get_srv6_locator_internal);
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool zebra_srv6_is_enable(void)
|
|
|
|
{
|
|
|
|
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
|
|
|
|
|
|
|
|
return listcount(srv6->locators);
|
|
|
|
}
|