From 260f5f6742d2c0a4364dab5920c0431f406d6ed7 Mon Sep 17 00:00:00 2001 From: cscarpitta Date: Mon, 16 Dec 2024 09:43:49 -0600 Subject: [PATCH] [FRR]: Bring PR FRRouting/frr#15604 from FRR mainline This commit brings PR FRRouting/frr#15604 from FRR mainline to SONiC Add support for SRv6 SID Manager https://github.com/FRRouting/frr/pull/15604 Signed-off-by: cscarpitta --- ...067-Add-support-for-SRv6-SID-Manager.patch | 4461 +++++++++++++++++ src/sonic-frr/patch/series | 1 + 2 files changed, 4462 insertions(+) create mode 100644 src/sonic-frr/patch/0067-Add-support-for-SRv6-SID-Manager.patch diff --git a/src/sonic-frr/patch/0067-Add-support-for-SRv6-SID-Manager.patch b/src/sonic-frr/patch/0067-Add-support-for-SRv6-SID-Manager.patch new file mode 100644 index 000000000000..06373a632640 --- /dev/null +++ b/src/sonic-frr/patch/0067-Add-support-for-SRv6-SID-Manager.patch @@ -0,0 +1,4461 @@ +From 021386a34eb49893ce6b7df16c2fe1096a8fedf6 Mon Sep 17 00:00:00 2001 + +From: Carmine Scarpitta + +Subject: [PATCH 01/25] lib: Add support for SRv6 SID formats + +Add functionalities to manage SRv6 SID formats (allocate / free). + +Signed-off-by: Carmine Scarpitta +--- + doc/user/zebra.rst | 84 ++ + lib/command.h | 3 + lib/log.c | 6 + lib/srv6.c | 187 ++++ + lib/srv6.h | 134 +++ + lib/zclient.c | 194 ++++ + lib/zclient.h | 51 + + vtysh/vtysh.c | 82 ++ + zebra/zapi_msg.c | 162 ++++ + zebra/zapi_msg.h | 8 + zebra/zebra_errors.c | 12 + zebra/zebra_errors.h | 2 + zebra/zebra_srv6.c | 2094 +++++++++++++++++++++++++++++++++++++++++++++++- + zebra/zebra_srv6.h | 247 ++++++ + zebra/zebra_srv6_vty.c | 593 +++++++++++++- + 15 files changed, 3768 insertions(+), 91 deletions(-) + +diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst +index 30b020425..172a8332f 100644 +--- a/doc/user/zebra.rst ++++ b/doc/user/zebra.rst +@@ -1021,6 +1021,35 @@ and this section also helps that case. + ! + ... + ++.. clicmd:: format NAME ++ ++ Specify the SID allocation schema for the SIDs allocated from this locator. Currently, ++ FRR supports supports the following allocation schemas: ++ ++ - `usid-f3216` ++ - `uncompressed` ++ ++:: ++ ++ router# configure terminal ++ router(config)# segment-routinig ++ router(config-sr)# srv6 ++ router(config-srv6)# locators ++ router(config-srv6-locators)# locator loc1 ++ router(config-srv6-locator)# prefix fc00:0:1::/48 ++ router(config-srv6-locator)# format usid-f3216 ++ ++ router(config-srv6-locator)# show run ++ ... ++ segment-routing ++ srv6 ++ locators ++ locator loc1 ++ prefix fc00:0:1::/48 ++ format usid-f3216 ++ ! ++ ... ++ + .. clicmd:: encapsulation + + Configure parameters for SRv6 encapsulation. +@@ -1029,6 +1058,61 @@ and this section also helps that case. + + Configure the source address of the outer encapsulating IPv6 header. + ++.. clicmd:: formats ++ ++ Configure SRv6 SID formats. ++ ++.. clicmd:: format NAME ++ ++ Configure SRv6 SID format. ++ ++.. clicmd:: compressed usid ++ ++ Enable SRv6 uSID compression and configure SRv6 uSID compression parameters. ++ ++.. clicmd:: local-id-block start START ++ ++ Configure the start value for the Local ID Block (LIB). ++ ++.. clicmd:: local-id-block explicit start START end END ++ ++ Configure the start/end values for the Explicit LIB (ELIB). ++ ++.. clicmd:: wide-local-id-block start START end END ++ ++ Configure the start/end values for the Wide LIB (W-LIB). ++ ++.. clicmd:: wide-local-id-block explicit start START ++ ++ Configure the start value for the Explicit Wide LIB (EW-LIB). ++ ++:: ++ ++ router# configure terminal ++ router(config)# segment-routinig ++ router(config-sr)# srv6 ++ router(config-srv6)# formats ++ router(config-srv6-formats)# format usid-f3216 ++ router(config-srv6-format)# compressed usid ++ router(config-srv6-format-usid)# local-id-block start 0xD000 ++ router(config-srv6-format-usid)# local-id-block explicit start 0xF000 end 0xFDFF ++ router(config-srv6-format-usid)# wide-local-id-block start 0xFFF4 end 0xFFF5 ++ router(config-srv6-format-usid)# wide-local-id-block explicit start 0xFFF4 ++ ++ router(config-srv6-locator)# show run ++ ... ++ segment-routing ++ srv6 ++ formats ++ format usid-f3216 ++ compressed usid ++ local-id-block start 0xD000 ++ local-id-block explicit start 0xF000 end 0xFDFF ++ wide-local-id-block start 0xFFF4 end 0xFFF5 ++ wide-local-id-block explicit start 0xFFF4 ++ ! ++ ... ++ + .. _multicast-rib-commands: + + Multicast RIB Commands +diff --git a/lib/command.h b/lib/command.h +index 04c66adb2..12c893b1b 100644 +--- a/lib/command.h ++++ b/lib/command.h +@@ -161,6 +161,9 @@ enum node_type { + SRV6_LOCS_NODE, /* SRv6 locators node */ + SRV6_LOC_NODE, /* SRv6 locator node */ + SRV6_ENCAP_NODE, /* SRv6 encapsulation node */ ++ SRV6_SID_FORMATS_NODE, /* SRv6 SID formats config node */ ++ SRV6_SID_FORMAT_USID_F3216_NODE, /* SRv6 uSID f3216 format config node */ ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, /* SRv6 uncompressed f4024 format config node */ + VTY_NODE, /* Vty node. */ + FPM_NODE, /* Dataplane FPM node. */ + LINK_PARAMS_NODE, /* Link-parameters node */ +diff --git a/lib/log.c b/lib/log.c +index 969ca7925..880180ae5 100644 +--- a/lib/log.c ++++ b/lib/log.c +@@ -436,6 +436,9 @@ static const struct zebra_desc_table command_types[] = { + DESC_ENTRY(ZEBRA_SRV6_LOCATOR_DELETE), + DESC_ENTRY(ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK), + DESC_ENTRY(ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_CHUNK), ++ DESC_ENTRY(ZEBRA_SRV6_MANAGER_GET_LOCATOR), ++ DESC_ENTRY(ZEBRA_SRV6_MANAGER_GET_SRV6_SID), ++ DESC_ENTRY(ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID), + DESC_ENTRY(ZEBRA_ERROR), + DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES), + DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE), +@@ -461,7 +464,8 @@ static const struct zebra_desc_table command_types[] = { + DESC_ENTRY(ZEBRA_TC_CLASS_DELETE), + DESC_ENTRY(ZEBRA_TC_FILTER_ADD), + DESC_ENTRY(ZEBRA_TC_FILTER_DELETE), +- DESC_ENTRY(ZEBRA_OPAQUE_NOTIFY) ++ DESC_ENTRY(ZEBRA_OPAQUE_NOTIFY), ++ DESC_ENTRY(ZEBRA_SRV6_SID_NOTIFY) + }; + #undef DESC_ENTRY + +diff --git a/lib/srv6.c b/lib/srv6.c +index dceb6ab48..4cb062256 100644 +--- a/lib/srv6.c ++++ b/lib/srv6.c +@@ -10,8 +10,11 @@ + #include "log.h" + + DEFINE_QOBJ_TYPE(srv6_locator); ++DEFINE_QOBJ_TYPE(srv6_sid_format); + DEFINE_MTYPE_STATIC(LIB, SRV6_LOCATOR, "SRV6 locator"); + DEFINE_MTYPE_STATIC(LIB, SRV6_LOCATOR_CHUNK, "SRV6 locator chunk"); ++DEFINE_MTYPE_STATIC(LIB, SRV6_SID_FORMAT, "SRv6 SID format"); ++DEFINE_MTYPE_STATIC(LIB, SRV6_SID_CTX, "SRv6 SID context"); + + const char *seg6local_action2str(uint32_t action) + { +@@ -137,6 +140,21 @@ struct srv6_locator_chunk *srv6_locator_chunk_alloc(void) + return chunk; + } + ++void srv6_locator_copy(struct srv6_locator *copy, ++ const struct srv6_locator *locator) ++{ ++ strlcpy(copy->name, locator->name, sizeof(locator->name)); ++ copy->prefix = locator->prefix; ++ copy->block_bits_length = locator->block_bits_length; ++ copy->node_bits_length = locator->node_bits_length; ++ copy->function_bits_length = locator->function_bits_length; ++ copy->argument_bits_length = locator->argument_bits_length; ++ copy->algonum = locator->algonum; ++ copy->current = locator->current; ++ copy->status_up = locator->status_up; ++ copy->flags = locator->flags; ++} ++ + void srv6_locator_free(struct srv6_locator *locator) + { + if (locator) { +@@ -152,6 +170,59 @@ void srv6_locator_chunk_free(struct srv6_locator_chunk **chunk) + XFREE(MTYPE_SRV6_LOCATOR_CHUNK, *chunk); + } + ++struct srv6_sid_format *srv6_sid_format_alloc(const char *name) ++{ ++ struct srv6_sid_format *format = NULL; ++ ++ format = XCALLOC(MTYPE_SRV6_SID_FORMAT, sizeof(struct srv6_sid_format)); ++ strlcpy(format->name, name, sizeof(format->name)); ++ ++ QOBJ_REG(format, srv6_sid_format); ++ return format; ++} ++ ++void srv6_sid_format_free(struct srv6_sid_format *format) ++{ ++ if (!format) ++ return; ++ ++ QOBJ_UNREG(format); ++ XFREE(MTYPE_SRV6_SID_FORMAT, format); ++} ++ ++/** ++ * Free an SRv6 SID format. ++ * ++ * @param val SRv6 SID format to be freed ++ */ ++void delete_srv6_sid_format(void *val) ++{ ++ srv6_sid_format_free((struct srv6_sid_format *)val); ++} ++ ++struct srv6_sid_ctx *srv6_sid_ctx_alloc(enum seg6local_action_t behavior, ++ struct in_addr *nh4, ++ struct in6_addr *nh6, vrf_id_t vrf_id) ++{ ++ struct srv6_sid_ctx *ctx = NULL; ++ ++ ctx = XCALLOC(MTYPE_SRV6_SID_CTX, sizeof(struct srv6_sid_ctx)); ++ ctx->behavior = behavior; ++ if (nh4) ++ ctx->nh4 = *nh4; ++ if (nh6) ++ ctx->nh6 = *nh6; ++ if (vrf_id) ++ ctx->vrf_id = vrf_id; ++ ++ return ctx; ++} ++ ++void srv6_sid_ctx_free(struct srv6_sid_ctx *ctx) ++{ ++ XFREE(MTYPE_SRV6_SID_CTX, ctx); ++} ++ + json_object *srv6_locator_chunk_json(const struct srv6_locator_chunk *chunk) + { + json_object *jo_root = NULL; +@@ -221,23 +292,47 @@ json_object *srv6_locator_json(const struct srv6_locator *loc) + /* set prefix */ + json_object_string_addf(jo_root, "prefix", "%pFX", &loc->prefix); + +- /* set block_bits_length */ +- json_object_int_add(jo_root, "blockBitsLength", loc->block_bits_length); +- +- /* set node_bits_length */ +- json_object_int_add(jo_root, "nodeBitsLength", loc->node_bits_length); +- +- /* set function_bits_length */ +- json_object_int_add(jo_root, "functionBitsLength", +- loc->function_bits_length); +- +- /* set argument_bits_length */ +- json_object_int_add(jo_root, "argumentBitsLength", +- loc->argument_bits_length); +- +- /* set true if the locator is a Micro-segment (uSID) locator */ +- if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID)) +- json_object_string_add(jo_root, "behavior", "usid"); ++ if (loc->sid_format) { ++ /* set block_bits_length */ ++ json_object_int_add(jo_root, "blockBitsLength", ++ loc->sid_format->block_len); ++ ++ /* set node_bits_length */ ++ json_object_int_add(jo_root, "nodeBitsLength", ++ loc->sid_format->node_len); ++ ++ /* set function_bits_length */ ++ json_object_int_add(jo_root, "functionBitsLength", ++ loc->sid_format->function_len); ++ ++ /* set argument_bits_length */ ++ json_object_int_add(jo_root, "argumentBitsLength", ++ loc->sid_format->argument_len); ++ ++ /* set true if the locator is a Micro-segment (uSID) locator */ ++ if (loc->sid_format->type == SRV6_SID_FORMAT_TYPE_USID) ++ json_object_string_add(jo_root, "behavior", "usid"); ++ } else { ++ /* set block_bits_length */ ++ json_object_int_add(jo_root, "blockBitsLength", ++ loc->block_bits_length); ++ ++ /* set node_bits_length */ ++ json_object_int_add(jo_root, "nodeBitsLength", ++ loc->node_bits_length); ++ ++ /* set function_bits_length */ ++ json_object_int_add(jo_root, "functionBitsLength", ++ loc->function_bits_length); ++ ++ /* set argument_bits_length */ ++ json_object_int_add(jo_root, "argumentBitsLength", ++ loc->argument_bits_length); ++ ++ /* set true if the locator is a Micro-segment (uSID) locator */ ++ if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID)) ++ json_object_string_add(jo_root, "behavior", "usid"); ++ } + + /* set status_up */ + json_object_boolean_add(jo_root, "statusUp", +@@ -270,23 +365,47 @@ json_object *srv6_locator_detailed_json(const struct srv6_locator *loc) + /* set prefix */ + json_object_string_addf(jo_root, "prefix", "%pFX", &loc->prefix); + +- /* set block_bits_length */ +- json_object_int_add(jo_root, "blockBitsLength", loc->block_bits_length); +- +- /* set node_bits_length */ +- json_object_int_add(jo_root, "nodeBitsLength", loc->node_bits_length); +- +- /* set function_bits_length */ +- json_object_int_add(jo_root, "functionBitsLength", +- loc->function_bits_length); +- +- /* set argument_bits_length */ +- json_object_int_add(jo_root, "argumentBitsLength", +- loc->argument_bits_length); +- +- /* set true if the locator is a Micro-segment (uSID) locator */ +- if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID)) +- json_object_string_add(jo_root, "behavior", "usid"); ++ if (loc->sid_format) { ++ /* set block_bits_length */ ++ json_object_int_add(jo_root, "blockBitsLength", ++ loc->sid_format->block_len); ++ ++ /* set node_bits_length */ ++ json_object_int_add(jo_root, "nodeBitsLength", ++ loc->sid_format->node_len); ++ ++ /* set function_bits_length */ ++ json_object_int_add(jo_root, "functionBitsLength", ++ loc->sid_format->function_len); ++ ++ /* set argument_bits_length */ ++ json_object_int_add(jo_root, "argumentBitsLength", ++ loc->sid_format->argument_len); ++ ++ /* set true if the locator is a Micro-segment (uSID) locator */ ++ if (loc->sid_format->type == SRV6_SID_FORMAT_TYPE_USID) ++ json_object_string_add(jo_root, "behavior", "usid"); ++ } else { ++ /* set block_bits_length */ ++ json_object_int_add(jo_root, "blockBitsLength", ++ loc->block_bits_length); ++ ++ /* set node_bits_length */ ++ json_object_int_add(jo_root, "nodeBitsLength", ++ loc->node_bits_length); ++ ++ /* set function_bits_length */ ++ json_object_int_add(jo_root, "functionBitsLength", ++ loc->function_bits_length); ++ ++ /* set argument_bits_length */ ++ json_object_int_add(jo_root, "argumentBitsLength", ++ loc->argument_bits_length); ++ ++ /* set true if the locator is a Micro-segment (uSID) locator */ ++ if (CHECK_FLAG(loc->flags, SRV6_LOCATOR_USID)) ++ json_object_string_add(jo_root, "behavior", "usid"); ++ } + + /* set algonum */ + json_object_int_add(jo_root, "algoNum", loc->algonum); +diff --git a/lib/srv6.h b/lib/srv6.h +index 53f5119aa..225bdf3ad 100644 +--- a/lib/srv6.h ++++ b/lib/srv6.h +@@ -20,6 +20,8 @@ + #define SRH_BASE_HEADER_LENGTH 8 + #define SRH_SEGMENT_LENGTH 16 + ++#define SRV6_SID_FORMAT_NAME_SIZE 512 ++ + #ifdef __cplusplus + extern "C" { + #endif +@@ -131,6 +133,12 @@ struct srv6_locator { + uint8_t flags; + #define SRV6_LOCATOR_USID (1 << 0) /* The SRv6 Locator is a uSID Locator */ + ++ /* Pointer to the SID format. */ ++ struct srv6_sid_format *sid_format; ++ ++ /* Pointer to the parent SID block of the locator. */ ++ void *sid_block; ++ + QOBJ_FIELDS; + }; + DECLARE_QOBJ_TYPE(srv6_locator); +@@ -187,6 +195,72 @@ struct nexthop_srv6 { + struct seg6_seg_stack *seg6_segs; + }; + ++/* SID format type */ ++enum srv6_sid_format_type { ++ SRV6_SID_FORMAT_TYPE_UNSPEC = 0, ++ /* SRv6 SID uncompressed format */ ++ SRV6_SID_FORMAT_TYPE_UNCOMPRESSED = 1, ++ /* SRv6 SID compressed uSID format */ ++ SRV6_SID_FORMAT_TYPE_USID = 2, ++}; ++ ++/* SRv6 SID format */ ++struct srv6_sid_format { ++ /* Name of the format */ ++ char name[SRV6_SID_FORMAT_NAME_SIZE]; ++ ++ /* Format type: uncompressed vs compressed */ ++ enum srv6_sid_format_type type; ++ ++ /* ++ * Lengths of block/node/function/argument parts of the SIDs allocated ++ * using this format ++ */ ++ uint8_t block_len; ++ uint8_t node_len; ++ uint8_t function_len; ++ uint8_t argument_len; ++ ++ union { ++ /* Configuration settings for compressed uSID format type */ ++ struct { ++ /* Start of the Local ID Block (LIB) range */ ++ uint32_t lib_start; ++ ++ /* Start/End of the Explicit LIB range */ ++ uint32_t elib_start; ++ uint32_t elib_end; ++ ++ /* Start/End of the Wide LIB range */ ++ uint32_t wlib_start; ++ uint32_t wlib_end; ++ ++ /* Start/End of the Explicit Wide LIB range */ ++ uint32_t ewlib_start; ++ } usid; ++ ++ /* Configuration settings for uncompressed format type */ ++ struct { ++ /* Start of the Explicit range */ ++ uint32_t explicit_start; ++ } uncompressed; ++ } config; ++ ++ QOBJ_FIELDS; ++}; ++DECLARE_QOBJ_TYPE(srv6_sid_format); ++ ++/* Context for an SRv6 SID */ ++struct srv6_sid_ctx { ++ /* Behavior associated with the SID */ ++ enum seg6local_action_t behavior; ++ ++ /* Behavior-specific attributes */ ++ struct in_addr nh4; ++ struct in6_addr nh6; ++ vrf_id_t vrf_id; ++}; ++ + static inline const char *seg6_mode2str(enum seg6_mode_t mode) + { + switch (mode) { +@@ -250,6 +324,54 @@ const char *seg6local_context2str(char *str, size_t size, + const struct seg6local_context *ctx, + uint32_t action); + ++static inline const char *srv6_sid_ctx2str(char *str, size_t size, ++ const struct srv6_sid_ctx *ctx) ++{ ++ int len = 0; ++ ++ len += snprintf(str + len, size - len, "%s", ++ seg6local_action2str(ctx->behavior)); ++ ++ switch (ctx->behavior) { ++ case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: ++ break; ++ ++ case ZEBRA_SEG6_LOCAL_ACTION_END: ++ len += snprintf(str + len, size - len, " USP"); ++ break; ++ ++ case ZEBRA_SEG6_LOCAL_ACTION_END_X: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_DX6: ++ len += snprintfrr(str + len, size - len, " nh6 %pI6", &ctx->nh6); ++ break; ++ ++ case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: ++ len += snprintfrr(str + len, size - len, " nh4 %pI4", &ctx->nh4); ++ break; ++ ++ case ZEBRA_SEG6_LOCAL_ACTION_END_T: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_DT46: ++ len += snprintf(str + len, size - len, " vrf_id %u", ++ ctx->vrf_id); ++ break; ++ ++ case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_B6: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_BM: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_S: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_AS: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_AM: ++ case ZEBRA_SEG6_LOCAL_ACTION_END_BPF: ++ default: ++ len += snprintf(str + len, size - len, " unknown(%s)", __func__); ++ } ++ ++ return str; ++} ++ + int snprintf_seg6_segs(char *str, + size_t size, const struct seg6_segs *segs); + +@@ -258,12 +380,24 @@ extern struct srv6_locator_chunk *srv6_locator_chunk_alloc(void); + extern void srv6_locator_free(struct srv6_locator *locator); + extern void srv6_locator_chunk_list_free(void *data); + extern void srv6_locator_chunk_free(struct srv6_locator_chunk **chunk); ++extern void srv6_locator_copy(struct srv6_locator *copy, ++ const struct srv6_locator *locator); + json_object *srv6_locator_chunk_json(const struct srv6_locator_chunk *chunk); + json_object *srv6_locator_json(const struct srv6_locator *loc); + json_object *srv6_locator_detailed_json(const struct srv6_locator *loc); + json_object * + srv6_locator_chunk_detailed_json(const struct srv6_locator_chunk *chunk); + ++extern struct srv6_sid_format *srv6_sid_format_alloc(const char *name); ++extern void srv6_sid_format_free(struct srv6_sid_format *format); ++extern void delete_srv6_sid_format(void *format); ++ ++extern struct srv6_sid_ctx *srv6_sid_ctx_alloc(enum seg6local_action_t behavior, ++ struct in_addr *nh4, ++ struct in6_addr *nh6, ++ vrf_id_t vrf_id); ++extern void srv6_sid_ctx_free(struct srv6_sid_ctx *ctx); ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/zclient.c b/lib/zclient.c +index 269c3e9ba..462a4362a 100644 +--- a/lib/zclient.c ++++ b/lib/zclient.c +@@ -1125,6 +1125,10 @@ int zapi_srv6_locator_encode(struct stream *s, const struct srv6_locator *l) + stream_put(s, l->name, strlen(l->name)); + stream_putw(s, l->prefix.prefixlen); + stream_put(s, &l->prefix.prefix, sizeof(l->prefix.prefix)); ++ stream_putc(s, l->block_bits_length); ++ stream_putc(s, l->node_bits_length); ++ stream_putc(s, l->function_bits_length); ++ stream_putc(s, l->argument_bits_length); + stream_putc(s, l->flags); + return 0; + } +@@ -1141,6 +1145,10 @@ int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l) + STREAM_GETW(s, l->prefix.prefixlen); + STREAM_GET(&l->prefix.prefix, s, sizeof(l->prefix.prefix)); + l->prefix.family = AF_INET6; ++ STREAM_GETC(s, l->block_bits_length); ++ STREAM_GETC(s, l->node_bits_length); ++ STREAM_GETC(s, l->function_bits_length); ++ STREAM_GETC(s, l->argument_bits_length); + STREAM_GETC(s, l->flags); + return 0; + +@@ -2125,6 +2133,46 @@ stream_failure: + return false; + } + ++bool zapi_srv6_sid_notify_decode(struct stream *s, struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, uint32_t *func, ++ uint32_t *wide_func, ++ enum zapi_srv6_sid_notify *note, ++ char **p_locator_name) ++{ ++ uint32_t f, wf; ++ uint16_t len; ++ static char locator_name[SRV6_LOCNAME_SIZE] = {}; ++ ++ STREAM_GET(note, s, sizeof(*note)); ++ STREAM_GET(ctx, s, sizeof(struct srv6_sid_ctx)); ++ STREAM_GET(sid_value, s, sizeof(struct in6_addr)); ++ STREAM_GETL(s, f); ++ STREAM_GETL(s, wf); ++ ++ if (func) ++ *func = f; ++ if (wide_func) ++ *wide_func = wf; ++ ++ STREAM_GETW(s, len); ++ if (len > SRV6_LOCNAME_SIZE) { ++ *p_locator_name = NULL; ++ return false; ++ } ++ if (p_locator_name) { ++ if (len == 0) ++ *p_locator_name = NULL; ++ else { ++ STREAM_GET(locator_name, s, len); ++ *p_locator_name = locator_name; ++ } ++ } ++ return true; ++ ++stream_failure: ++ return false; ++} ++ + struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) + { + struct nexthop *n = nexthop_new(); +@@ -3267,10 +3315,154 @@ int srv6_manager_release_locator_chunk(struct zclient *zclient, + return zclient_send_message(zclient); + } + ++/** ++ * Function to request a SRv6 locator in an asynchronous way ++ * ++ * @param zclient The zclient used to connect to SRv6 Manager (zebra) ++ * @param locator_name Name of SRv6 locator ++ * @return 0 on success, -1 otherwise ++ */ ++int srv6_manager_get_locator(struct zclient *zclient, const char *locator_name) ++{ ++ struct stream *s; ++ size_t len; ++ ++ if (!locator_name) ++ return -1; ++ ++ if (zclient->sock < 0) { ++ flog_err(EC_LIB_ZAPI_SOCKET, "%s: invalid zclient socket", ++ __func__); ++ return -1; ++ } ++ ++ if (zclient_debug) ++ zlog_debug("Getting SRv6 Locator %s", locator_name); ++ ++ len = strlen(locator_name); ++ ++ /* Send request */ ++ s = zclient->obuf; ++ stream_reset(s); ++ zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_LOCATOR, VRF_DEFAULT); ++ ++ /* Locator name */ ++ stream_putw(s, len); ++ stream_put(s, locator_name, len); ++ ++ /* Put length at the first point of the stream. */ ++ stream_putw_at(s, 0, stream_get_endp(s)); ++ ++ return zclient_send_message(zclient); ++} ++ ++/** ++ * Function to request an SRv6 SID in an asynchronous way ++ * ++ * @param zclient The zclient used to connect to SRv6 manager (zebra) ++ * @param ctx Context associated with the SRv6 SID ++ * @param sid_value SRv6 SID value for explicit SID allocation ++ * @param locator_name Name of the parent locator for dynamic SID allocation ++ * @param sid_func SID function assigned by the SRv6 Manager ++ * @result 0 on success, -1 otherwise ++ */ ++int srv6_manager_get_sid(struct zclient *zclient, const struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, const char *locator_name, ++ uint32_t *sid_func) ++{ ++ struct stream *s; ++ uint8_t flags = 0; ++ size_t len; ++ char buf[256]; ++ ++ if (zclient->sock < 0) { ++ flog_err(EC_LIB_ZAPI_SOCKET, "%s: invalid zclient socket", ++ __func__); ++ return ZCLIENT_SEND_FAILURE; ++ } ++ ++ if (zclient_debug) ++ zlog_debug("Getting SRv6 SID: %s", ++ srv6_sid_ctx2str(buf, sizeof(buf), ctx)); ++ ++ /* send request */ ++ s = zclient->obuf; ++ stream_reset(s); ++ ++ zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_SRV6_SID, VRF_DEFAULT); ++ ++ /* Context associated with the SRv6 SID */ ++ stream_put(s, ctx, sizeof(struct srv6_sid_ctx)); ++ ++ /* Flags */ ++ if (!sid_zero_ipv6(sid_value)) ++ SET_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_SID_VALUE); ++ if (locator_name) ++ SET_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_LOCATOR); ++ stream_putc(s, flags); ++ ++ /* SRv6 SID value */ ++ if (CHECK_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_SID_VALUE)) ++ stream_put(s, sid_value, sizeof(struct in6_addr)); ++ ++ /* SRv6 locator */ ++ if (CHECK_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_LOCATOR)) { ++ len = strlen(locator_name); ++ stream_putw(s, len); ++ stream_put(s, locator_name, len); ++ } ++ ++ /* Put length at the first point of the stream. */ ++ stream_putw_at(s, 0, stream_get_endp(s)); ++ ++ /* Send the request to SRv6 Manager */ ++ return zclient_send_message(zclient); ++} ++ ++/** ++ * Function to release an SRv6 SID ++ * ++ * @param zclient Zclient used to connect to SRv6 manager (zebra) ++ * @param ctx Context associated with the SRv6 SID to be removed ++ * @result 0 on success, -1 otherwise ++ */ ++int srv6_manager_release_sid(struct zclient *zclient, ++ const struct srv6_sid_ctx *ctx) ++{ ++ struct stream *s; ++ char buf[256]; ++ ++ if (zclient->sock < 0) { ++ flog_err(EC_LIB_ZAPI_SOCKET, "%s: invalid zclient socket", ++ __func__); ++ return -1; ++ } ++ ++ if (zclient_debug) ++ zlog_debug("Releasing SRv6 SID: %s", ++ srv6_sid_ctx2str(buf, sizeof(buf), ctx)); ++ ++ /* send request */ ++ s = zclient->obuf; ++ stream_reset(s); ++ ++ zclient_create_header(s, ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID, ++ VRF_DEFAULT); ++ ++ /* Context associated with the SRv6 SID */ ++ stream_put(s, ctx, sizeof(struct srv6_sid_ctx)); ++ ++ /* Put length at the first point of the stream. */ ++ stream_putw_at(s, 0, stream_get_endp(s)); ++ ++ /* Send the SID release message */ ++ return zclient_send_message(zclient); ++} ++ + /* + * Asynchronous label chunk request + * +- * @param zclient Zclient used to connect to label manager (zebra) ++ * @param zclient The zclient used to connect to label manager (zebra) + * @param keep Avoid garbage collection + * @param chunk_size Amount of labels requested + * @param base Base for the label chunk. if MPLS_LABEL_BASE_ANY we do not care +diff --git a/lib/zclient.h b/lib/zclient.h +index 1bf91064e..d2dba15a6 100644 +--- a/lib/zclient.h ++++ b/lib/zclient.h +@@ -209,6 +209,9 @@ typedef enum { + ZEBRA_SRV6_LOCATOR_DELETE, + ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK, + ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_CHUNK, ++ ZEBRA_SRV6_MANAGER_GET_LOCATOR, ++ ZEBRA_SRV6_MANAGER_GET_SRV6_SID, ++ ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID, + ZEBRA_ERROR, + ZEBRA_CLIENT_CAPABILITIES, + ZEBRA_OPAQUE_MESSAGE, +@@ -235,6 +238,7 @@ typedef enum { + ZEBRA_TC_FILTER_ADD, + ZEBRA_TC_FILTER_DELETE, + ZEBRA_OPAQUE_NOTIFY, ++ ZEBRA_SRV6_SID_NOTIFY, + } zebra_message_types_t; + /* Zebra message types. Please update the corresponding + * command_types array with any changes! +@@ -761,6 +765,13 @@ enum zapi_iptable_notify_owner { + ZAPI_IPTABLE_FAIL_REMOVE, + }; + ++enum zapi_srv6_sid_notify { ++ ZAPI_SRV6_SID_FAIL_ALLOC = 0, ++ ZAPI_SRV6_SID_ALLOCATED, ++ ZAPI_SRV6_SID_RELEASED, ++ ZAPI_SRV6_SID_FAIL_RELEASE, ++}; ++ + enum zclient_send_status { + ZCLIENT_SEND_FAILURE = -1, + ZCLIENT_SEND_SUCCESS = 0, +@@ -813,6 +824,28 @@ zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note) + return ret; + } + ++static inline const char *zapi_srv6_sid_notify2str(enum zapi_srv6_sid_notify note) ++{ ++ const char *ret = "UNKNOWN"; ++ ++ switch (note) { ++ case ZAPI_SRV6_SID_FAIL_ALLOC: ++ ret = "ZAPI_SRV6_SID_FAIL_ALLOC"; ++ break; ++ case ZAPI_SRV6_SID_ALLOCATED: ++ ret = "ZAPI_SRV6_SID_ALLOCATED"; ++ break; ++ case ZAPI_SRV6_SID_FAIL_RELEASE: ++ ret = "ZAPI_SRV6_SID_FAIL_RELEASE"; ++ break; ++ case ZAPI_SRV6_SID_RELEASED: ++ ret = "ZAPI_SRV6_SID_RELEASED"; ++ break; ++ } ++ ++ return ret; ++} ++ + /* Zebra MAC types */ + #define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/ + #define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/ +@@ -1070,10 +1103,23 @@ extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, + uint32_t *start, uint32_t *end); + extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, + uint32_t end); ++ ++/* Zebra SRv6 Manager flags */ ++#define ZAPI_SRV6_MANAGER_SID_FLAG_HAS_SID_VALUE 0x01 ++#define ZAPI_SRV6_MANAGER_SID_FLAG_HAS_LOCATOR 0x02 ++ + extern int srv6_manager_get_locator_chunk(struct zclient *zclient, + const char *locator_name); + extern int srv6_manager_release_locator_chunk(struct zclient *zclient, + const char *locator_name); ++extern int srv6_manager_get_locator(struct zclient *zclient, ++ const char *locator_name); ++extern int srv6_manager_get_sid(struct zclient *zclient, ++ const struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, ++ const char *locator_name, uint32_t *sid_func); ++extern int srv6_manager_release_sid(struct zclient *zclient, ++ const struct srv6_sid_ctx *ctx); + + extern enum zclient_send_status zebra_send_sr_policy(struct zclient *zclient, + int cmd, +@@ -1128,6 +1174,11 @@ bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, + bool zapi_ipset_notify_decode(struct stream *s, + uint32_t *unique, + enum zapi_ipset_notify_owner *note); ++bool zapi_srv6_sid_notify_decode(struct stream *s, struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, uint32_t *func, ++ uint32_t *wide_func, ++ enum zapi_srv6_sid_notify *note, ++ char **locator_name); + + /* Nexthop-group message apis */ + extern enum zclient_send_status +diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c +index 12d22b1fc..a6dcd7647 100644 +--- a/vtysh/vtysh.c ++++ b/vtysh/vtysh.c +@@ -1345,6 +1345,27 @@ static struct cmd_node srv6_encap_node = { + .prompt = "%s(config-srv6-encap)# " + }; + ++static struct cmd_node srv6_sid_formats_node = { ++ .name = "srv6-formats", ++ .node = SRV6_SID_FORMATS_NODE, ++ .parent_node = SRV6_NODE, ++ .prompt = "%s(config-srv6-formats)# ", ++}; ++ ++static struct cmd_node srv6_sid_format_usid_f3216_node = { ++ .name = "srv6-format-usid-f3216", ++ .node = SRV6_SID_FORMAT_USID_F3216_NODE, ++ .parent_node = SRV6_SID_FORMATS_NODE, ++ .prompt = "%s(config-srv6-format)# " ++}; ++ ++static struct cmd_node srv6_sid_format_uncompressed_f4024_node = { ++ .name = "srv6-format-uncompressed-f4024", ++ .node = SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, ++ .parent_node = SRV6_SID_FORMATS_NODE, ++ .prompt = "%s(config-srv6-format)# " ++}; ++ + #ifdef HAVE_PBRD + static struct cmd_node pbr_map_node = { + .name = "pbr-map", +@@ -1716,6 +1737,31 @@ DEFUNSH(VTYSH_ZEBRA, srv6_encap, srv6_encap_cmd, + return CMD_SUCCESS; + } + ++DEFUNSH(VTYSH_ZEBRA, srv6_sid_formats, srv6_sid_formats_cmd, "formats", ++ "Segment Routing SRv6 SID formats\n") ++{ ++ vty->node = SRV6_SID_FORMATS_NODE; ++ return CMD_SUCCESS; ++} ++ ++DEFUNSH(VTYSH_ZEBRA, srv6_sid_format_f3216_usid, srv6_sid_format_f3216_usid_cmd, ++ "format usid-f3216", ++ "Configure SRv6 SID format\n" ++ "Configure the uSID f3216 format\n") ++{ ++ vty->node = SRV6_SID_FORMAT_USID_F3216_NODE; ++ return CMD_SUCCESS; ++} ++ ++DEFUNSH(VTYSH_ZEBRA, srv6_sid_format_f4024_uncompressed, srv6_sid_format_f4024_uncompressed_cmd, ++ "format uncompressed-f4024", ++ "Configure SRv6 SID format\n" ++ "Configure the uncompressed f4024 format\n") ++{ ++ vty->node = SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE; ++ return CMD_SUCCESS; ++} ++ + #ifdef HAVE_BGPD + DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd, + "router bgp [ASNUM [ VIEWVRFNAME] [as-notation ]]", +@@ -2516,6 +2562,23 @@ DEFUNSH(VTYSH_ZEBRA, exit_srv6_encap, exit_srv6_encap_cmd, "exit", + return CMD_SUCCESS; + } + ++DEFUNSH(VTYSH_ZEBRA, exit_srv6_sid_formats, exit_srv6_sid_formats_cmd, "exit", ++ "Exit from SRv6 SID formats configuration mode\n") ++{ ++ if (vty->node == SRV6_SID_FORMATS_NODE) ++ vty->node = SRV6_NODE; ++ return CMD_SUCCESS; ++} ++ ++DEFUNSH(VTYSH_ZEBRA, exit_srv6_sid_format, exit_srv6_sid_format_cmd, ++ "exit", "Exit from SRv6 SID format configuration mode\n") ++{ ++ if (vty->node == SRV6_SID_FORMAT_USID_F3216_NODE || ++ vty->node == SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE) ++ vty->node = SRV6_SID_FORMATS_NODE; ++ return CMD_SUCCESS; ++} ++ + #ifdef HAVE_RIPD + DEFUNSH(VTYSH_MGMTD, vtysh_exit_ripd, vtysh_exit_ripd_cmd, "exit", + "Exit current mode and down to previous mode\n") +@@ -5117,6 +5180,7 @@ void vtysh_init_vty(void) + install_node(&srv6_node); + install_element(SEGMENT_ROUTING_NODE, &srv6_cmd); + install_element(SRV6_NODE, &srv6_locators_cmd); ++ install_element(SRV6_NODE, &srv6_sid_formats_cmd); + install_element(SRV6_NODE, &exit_srv6_config_cmd); + install_element(SRV6_NODE, &vtysh_end_all_cmd); + install_element(SRV6_NODE, &srv6_encap_cmd); +@@ -5134,6 +5198,24 @@ void vtysh_init_vty(void) + install_element(SRV6_ENCAP_NODE, &exit_srv6_encap_cmd); + install_element(SRV6_ENCAP_NODE, &vtysh_end_all_cmd); + ++ install_node(&srv6_sid_formats_node); ++ install_element(SRV6_SID_FORMATS_NODE, &srv6_sid_format_f3216_usid_cmd); ++ install_element(SRV6_SID_FORMATS_NODE, ++ &srv6_sid_format_f4024_uncompressed_cmd); ++ install_element(SRV6_SID_FORMATS_NODE, &exit_srv6_sid_formats_cmd); ++ install_element(SRV6_SID_FORMATS_NODE, &vtysh_end_all_cmd); ++ ++ install_node(&srv6_sid_format_usid_f3216_node); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &exit_srv6_sid_format_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, &vtysh_end_all_cmd); ++ ++ install_node(&srv6_sid_format_uncompressed_f4024_node); ++ install_element(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, ++ &exit_srv6_sid_format_cmd); ++ install_element(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, ++ &vtysh_end_all_cmd); ++ + install_element(ENABLE_NODE, &vtysh_show_running_config_cmd); + install_element(ENABLE_NODE, &vtysh_copy_running_config_cmd); + install_element(ENABLE_NODE, &vtysh_copy_to_running_cmd); +diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c +index 9ae8333d6..305faaa09 100644 +--- a/zebra/zapi_msg.c ++++ b/zebra/zapi_msg.c +@@ -999,6 +999,48 @@ void zsend_neighbor_notify(int cmd, struct interface *ifp, + } + } + ++void zsend_srv6_sid_notify(struct zserv *client, const struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, uint32_t func, ++ uint32_t wide_func, const char *locator_name, ++ enum zapi_srv6_sid_notify note) ++ ++{ ++ struct stream *s; ++ uint16_t cmd = ZEBRA_SRV6_SID_NOTIFY; ++ char buf[256]; ++ ++ if (IS_ZEBRA_DEBUG_PACKET) ++ zlog_debug("%s: notifying %s ctx %s sid %pI6 note %s (proto=%u, instance=%u, sessionId=%u)", ++ __func__, zserv_command_string(cmd), ++ srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value, ++ zapi_srv6_sid_notify2str(note), client->proto, ++ client->instance, client->session_id); ++ ++ s = stream_new(ZEBRA_MAX_PACKET_SIZ); ++ ++ zclient_create_header(s, cmd, VRF_DEFAULT); ++ /* Notification type (e.g. ZAPI_SRV6_SID_ALLOCATED, ZAPI_SRV6_SID_FAIL_ALLOC, ...) */ ++ stream_put(s, ¬e, sizeof(note)); ++ /* Context associated with the SRv6 SID */ ++ stream_put(s, ctx, sizeof(struct srv6_sid_ctx)); ++ /* SRv6 SID value (i.e. IPv6 address) */ ++ stream_put(s, sid_value, sizeof(struct in6_addr)); ++ /* SRv6 SID function */ ++ stream_putl(s, func); ++ /* SRv6 wide SID function */ ++ stream_putl(s, wide_func); ++ /* SRv6 locator name optional */ ++ if (locator_name) { ++ stream_putw(s, strlen(locator_name)); ++ stream_put(s, locator_name, strlen(locator_name)); ++ } else ++ stream_putw(s, 0); ++ ++ stream_putw_at(s, 0, stream_get_endp(s)); ++ ++ zserv_send_message(client, s); ++} ++ + + /* Router-id is updated. Send ZEBRA_ROUTER_ID_UPDATE to client. */ + int zsend_router_id_update(struct zserv *client, afi_t afi, struct prefix *p, +@@ -1136,9 +1178,25 @@ static int zsend_table_manager_connect_response(struct zserv *client, + int zsend_zebra_srv6_locator_add(struct zserv *client, struct srv6_locator *loc) + { + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); ++ struct srv6_locator locator = {}; ++ struct srv6_sid_format *format = loc->sid_format; ++ ++ /* ++ * Copy the locator and fill locator block/node/func/arg length from the format ++ * before sending the locator to the zclient ++ */ ++ srv6_locator_copy(&locator, loc); ++ if (format) { ++ locator.block_bits_length = format->block_len; ++ locator.node_bits_length = format->node_len; ++ locator.function_bits_length = format->function_len; ++ locator.argument_bits_length = format->argument_len; ++ if (format->type == SRV6_SID_FORMAT_TYPE_USID) ++ SET_FLAG(locator.flags, SRV6_LOCATOR_USID); ++ } + + zclient_create_header(s, ZEBRA_SRV6_LOCATOR_ADD, VRF_DEFAULT); +- zapi_srv6_locator_encode(s, loc); ++ zapi_srv6_locator_encode(s, &locator); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +@@ -2995,6 +3053,96 @@ stream_failure: + return; + } + ++/** ++ * Handle SRv6 SID request received from a client daemon protocol. ++ * ++ * @param client The client zapi session ++ * @param msg The request message ++ */ ++static void zread_srv6_manager_get_srv6_sid(struct zserv *client, ++ struct stream *msg) ++{ ++ struct stream *s; ++ struct srv6_sid_ctx ctx = {}; ++ struct in6_addr sid_value = {}; ++ struct in6_addr *sid_value_ptr = NULL; ++ char locator[SRV6_LOCNAME_SIZE] = { 0 }; ++ uint16_t len; ++ struct zebra_srv6_sid *sid = NULL; ++ uint8_t flags; ++ ++ /* Get input stream */ ++ s = msg; ++ ++ /* Get data */ ++ STREAM_GET(&ctx, s, sizeof(struct srv6_sid_ctx)); ++ STREAM_GETC(s, flags); ++ if (CHECK_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_SID_VALUE)) { ++ STREAM_GET(&sid_value, s, sizeof(struct in6_addr)); ++ sid_value_ptr = &sid_value; ++ } ++ if (CHECK_FLAG(flags, ZAPI_SRV6_MANAGER_SID_FLAG_HAS_LOCATOR)) { ++ STREAM_GETW(s, len); ++ STREAM_GET(locator, s, len); ++ } ++ ++ /* Call hook to get a SID using wrapper */ ++ srv6_manager_get_sid_call(&sid, client, &ctx, sid_value_ptr, locator); ++ ++stream_failure: ++ return; ++} ++ ++/** ++ * Handle SRv6 SID release request received from a client daemon protocol. ++ * ++ * @param client The client zapi session ++ * @param msg The request message ++ */ ++static void zread_srv6_manager_release_srv6_sid(struct zserv *client, ++ struct stream *msg) ++{ ++ struct stream *s; ++ struct srv6_sid_ctx ctx = {}; ++ ++ /* Get input stream */ ++ s = msg; ++ ++ /* Get data */ ++ STREAM_GET(&ctx, s, sizeof(struct srv6_sid_ctx)); ++ ++ /* Call hook to release a SID using wrapper */ ++ srv6_manager_release_sid_call(client, &ctx); ++ ++stream_failure: ++ return; ++} ++ ++/** ++ * Handle SRv6 locator get request received from a client daemon protocol. ++ * ++ * @param client The client zapi session ++ * @param msg The request message ++ */ ++static void zread_srv6_manager_get_locator(struct zserv *client, ++ struct stream *msg) ++{ ++ struct stream *s = msg; ++ uint16_t len; ++ char locator_name[SRV6_LOCNAME_SIZE] = { 0 }; ++ struct srv6_locator *locator = NULL; ++ ++ /* Get data */ ++ STREAM_GETW(s, len); ++ STREAM_GET(locator_name, s, len); ++ ++ /* Call hook to get the locator info using wrapper */ ++ srv6_manager_get_locator_call(&locator, client, locator_name); ++ ++stream_failure: ++ return; ++} ++ + static void zread_srv6_manager_request(ZAPI_HANDLER_ARGS) + { + switch (hdr->command) { +@@ -3006,6 +3154,15 @@ static void zread_srv6_manager_request(ZAPI_HANDLER_ARGS) + zread_srv6_manager_release_locator_chunk(client, msg, + zvrf_id(zvrf)); + break; ++ case ZEBRA_SRV6_MANAGER_GET_SRV6_SID: ++ zread_srv6_manager_get_srv6_sid(client, msg); ++ break; ++ case ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID: ++ zread_srv6_manager_release_srv6_sid(client, msg); ++ break; ++ case ZEBRA_SRV6_MANAGER_GET_LOCATOR: ++ zread_srv6_manager_get_locator(client, msg); ++ break; + default: + zlog_err("%s: unknown SRv6 Manager command", __func__); + break; +@@ -3954,6 +4111,9 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { + [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, + [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] = zread_srv6_manager_request, + [ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_CHUNK] = zread_srv6_manager_request, ++ [ZEBRA_SRV6_MANAGER_GET_SRV6_SID] = zread_srv6_manager_request, ++ [ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID] = zread_srv6_manager_request, ++ [ZEBRA_SRV6_MANAGER_GET_LOCATOR] = 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 43f734d26..9e3ea6fb6 100644 +--- a/zebra/zapi_msg.h ++++ b/zebra/zapi_msg.h +@@ -94,6 +94,11 @@ extern int zsend_sr_policy_notify_status(uint32_t color, + extern void zsend_neighbor_notify(int cmd, struct interface *ifp, + struct ipaddr *ipaddr, int ndm_state, + union sockunion *link_layer_ipv4, int ip_len); ++extern void zsend_srv6_sid_notify(struct zserv *client, ++ const struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, uint32_t func, ++ uint32_t wide_func, const char *locator_name, ++ enum zapi_srv6_sid_notify note); + + extern int zsend_client_close_notify(struct zserv *client, + struct zserv *closed_client); +@@ -110,6 +115,9 @@ extern int zsend_zebra_srv6_locator_delete(struct zserv *client, + extern int zsend_srv6_manager_get_locator_chunk_response(struct zserv *client, + vrf_id_t vrf_id, struct srv6_locator *loc); + ++extern int zsend_srv6_manager_get_locator_response(struct zserv *client, ++ struct srv6_locator *locator); ++ + #ifdef __cplusplus + } + #endif +diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c +index 09b369e23..dcfa37d26 100644 +--- a/zebra/zebra_errors.c ++++ b/zebra/zebra_errors.c +@@ -787,6 +787,18 @@ static struct log_ref ferr_zebra_err[] = { + .suggestion = + "Wait for Zebra to reattempt update.", + }, ++ { ++ .code = EC_ZEBRA_SM_CANNOT_ASSIGN_SID, ++ .title = "SRv6 manager unable to assign SID", ++ .description = "Zebra's SRv6 manager was unable to assign a SID to client.", ++ .suggestion = "Ensure that Zebra has a sufficient SID range available.", ++ }, ++ { ++ .code = EC_ZEBRA_SM_DAEMON_MISMATCH, ++ .title = "Daemon mismatch when releasing SRV6 SIDs", ++ .description = "Zebra noticed a mismatch between a SRv6 SID and a protocol daemon number or instance when releasing unused SRv6 SIDs.", ++ .suggestion = "Ignore this error.", ++ }, + { + .code = END_FERR, + } +diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h +index 3ac654bda..84632e1ad 100644 +--- a/zebra/zebra_errors.h ++++ b/zebra/zebra_errors.h +@@ -124,6 +124,8 @@ enum zebra_log_refs { + EC_ZEBRA_GRE_SET_UPDATE, + EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK, + EC_ZEBRA_INTF_UPDATE_FAILURE, ++ EC_ZEBRA_SM_CANNOT_ASSIGN_SID, ++ EC_ZEBRA_SM_DAEMON_MISMATCH, + }; + + void zebra_error_init(void); +diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c +index bb872ef91..0ca77a497 100644 +--- a/zebra/zebra_srv6.c ++++ b/zebra/zebra_srv6.c +@@ -33,6 +33,16 @@ + + DEFINE_MGROUP(SRV6_MGR, "SRv6 Manager"); + DEFINE_MTYPE_STATIC(SRV6_MGR, SRV6M_CHUNK, "SRv6 Manager Chunk"); ++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); + + /* define hooks for the basic API, so that it can be specialized or served + * externally +@@ -55,6 +65,18 @@ DEFINE_HOOK(srv6_manager_release_chunk, + vrf_id_t vrf_id), + (client, locator_name, vrf_id)); + ++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)); ++ + /* define wrappers to be called in zapi_msg.c (as hooks must be called in + * source file where they were defined) + */ +@@ -85,11 +107,502 @@ int srv6_manager_client_disconnect_cb(struct zserv *client) + return 0; + } + ++ ++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); ++} ++ + static int zebra_srv6_cleanup(struct zserv *client) + { ++ /* Client has disconnected, let's release all the SIDs allocated by it. */ ++ release_daemon_srv6_sids(client); + return 0; + } + ++/* --- 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); ++} ++ + void zebra_srv6_locator_add(struct srv6_locator *locator) + { + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); +@@ -121,7 +634,6 @@ void zebra_srv6_locator_add(struct srv6_locator *locator) + void zebra_srv6_locator_delete(struct srv6_locator *locator) + { + struct listnode *n; +- struct srv6_locator_chunk *c; + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct zserv *client; + +@@ -136,18 +648,8 @@ void zebra_srv6_locator_delete(struct srv6_locator *locator) + * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the + * owner of each chunk. + */ +- for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) { +- if (c->proto == ZEBRA_ROUTE_SYSTEM) +- continue; +- client = zserv_find_client(c->proto, c->instance); +- if (!client) { +- zlog_warn( +- "%s: Not found zclient(proto=%u, instance=%u).", +- __func__, c->proto, c->instance); +- continue; +- } ++ for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, n, client)) + zsend_zebra_srv6_locator_delete(client, locator); +- } + + listnode_delete(srv6->locators, locator); + srv6_locator_free(locator); +@@ -190,7 +692,6 @@ void zebra_notify_srv6_locator_add(struct srv6_locator *locator) + void zebra_notify_srv6_locator_delete(struct srv6_locator *locator) + { + struct listnode *n; +- struct srv6_locator_chunk *c; + struct zserv *client; + + /* +@@ -204,17 +705,8 @@ void zebra_notify_srv6_locator_delete(struct srv6_locator *locator) + * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the + * owner of each chunk. + */ +- for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) { +- if (c->proto == ZEBRA_ROUTE_SYSTEM) +- continue; +- client = zserv_find_client(c->proto, c->instance); +- if (!client) { +- zlog_warn("Not found zclient(proto=%u, instance=%u).", +- c->proto, c->instance); +- continue; +- } ++ for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, n, client)) + zsend_zebra_srv6_locator_delete(client, locator); +- } + } + + struct zebra_srv6 srv6; +@@ -222,10 +714,32 @@ struct zebra_srv6 srv6; + struct zebra_srv6 *zebra_srv6_get_default(void) + { + static bool first_execution = true; ++ struct srv6_sid_format *format_usidf3216; ++ struct srv6_sid_format *format_uncompressed; + + if (first_execution) { + first_execution = false; + srv6.locators = list_new(); ++ ++ /* 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; + } + return &srv6; + } +@@ -427,30 +941,1534 @@ void zebra_srv6_encap_src_addr_unset(void) + memset(&srv6->encap_src_addr, 0, sizeof(struct in6_addr)); + } + +-void zebra_srv6_terminate(void) ++/* --- 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) + { +- struct srv6_locator *locator; ++ uint8_t offset, func_len; ++ struct srv6_sid_format *format = locator->sid_format; + +- if (!srv6.locators) +- return; ++ if (!sid_value || !locator) ++ return false; ++ ++ 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; ++ } + +- while (listcount(srv6.locators)) { +- locator = listnode_head(srv6.locators); ++ *sid_value = locator->prefix.prefix; ++ for (uint8_t idx = 0; idx < func_len; idx++) { ++ uint8_t tidx = offset + idx; + +- listnode_delete(srv6.locators, locator); +- srv6_locator_free(locator); ++ 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); + } + +- list_delete(&srv6.locators); ++ return true; + } + +-void zebra_srv6_init(void) ++/** ++ * 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) + { +- 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); ++ 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)); ++ ++ switch (alloc_mode) { ++ case 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); ++ ++ break; ++ case SRV6_SID_ALLOC_MODE_DYNAMIC: ++ /* ++ * 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); ++ ++ break; ++ case SRV6_SID_ALLOC_MODE_MAX: ++ case SRV6_SID_ALLOC_MODE_UNSPEC: ++ default: ++ flog_err(EC_ZEBRA_SM_CANNOT_ASSIGN_SID, ++ "%s: SRv6 Manager: Unrecognized alloc mode %u", ++ __func__, alloc_mode); ++ /* We should never arrive here */ ++ assert(0); ++ } ++ ++ 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_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); + } + + bool zebra_srv6_is_enable(void) +diff --git a/zebra/zebra_srv6.h b/zebra/zebra_srv6.h +index 21936c332..1599fd7ad 100644 +--- a/zebra/zebra_srv6.h ++++ b/zebra/zebra_srv6.h +@@ -16,12 +16,197 @@ + #include + #include + ++/* Default config for SRv6 SID `usid-f3216` format */ ++#define SRV6_SID_FORMAT_USID_F3216_NAME "usid-f3216" ++#define SRV6_SID_FORMAT_USID_F3216_BLOCK_LEN 32 ++#define SRV6_SID_FORMAT_USID_F3216_NODE_LEN 16 ++#define SRV6_SID_FORMAT_USID_F3216_FUNCTION_LEN 16 ++#define SRV6_SID_FORMAT_USID_F3216_ARGUMENT_LEN 0 ++#define SRV6_SID_FORMAT_USID_F3216_LIB_START 0xE000 ++#define SRV6_SID_FORMAT_USID_F3216_ELIB_START 0xFE00 ++#define SRV6_SID_FORMAT_USID_F3216_ELIB_END 0xFEFF ++#define SRV6_SID_FORMAT_USID_F3216_WLIB_START 0xFFF0 ++#define SRV6_SID_FORMAT_USID_F3216_WLIB_END 0xFFF7 ++#define SRV6_SID_FORMAT_USID_F3216_EWLIB_START 0xFFF7 ++ ++/* Default config for SRv6 SID `uncompressed` format */ ++#define SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NAME "uncompressed-f4024" ++#define SRV6_SID_FORMAT_UNCOMPRESSED_F4024_BLOCK_LEN 40 ++#define SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE_LEN 24 ++#define SRV6_SID_FORMAT_UNCOMPRESSED_F4024_FUNCTION_LEN 16 ++#define SRV6_SID_FORMAT_UNCOMPRESSED_F4024_ARGUMENT_LEN 0 ++#define SRV6_SID_FORMAT_UNCOMPRESSED_F4024_EXPLICIT_RANGE_START 0xFF00 ++#define SRV6_SID_FORMAT_UNCOMPRESSED_F4024_FUNC_UNRESERVED_MIN 0x40 ++ ++/* uSID Wide LIB */ ++struct wide_lib { ++ uint32_t func; ++ uint32_t num_func_allocated; ++ uint32_t first_available_func; ++ struct list *func_allocated; ++ struct list *func_released; ++}; ++ ++/* ++ * SRv6 SID block. ++ * ++ * A SID block is an IPv6 prefix from which SRv6 SIDs are allocated. ++ * Example: ++ * SID block = fc00:0::/32 ++ * SID 1 = fc00:0:1:e000:: ++ * SID 2 = fc00:0:1:fe00:: ++ * ... ++ */ ++struct zebra_srv6_sid_block { ++ /* Prefix of this block, e.g. fc00:0::/32 */ ++ struct prefix_ipv6 prefix; ++ ++ /* Reference counter */ ++ unsigned long refcnt; ++ ++ /* ++ * Pointer to the SID format that defines the structure of the SIDs ++ * allocated from this block ++ */ ++ struct srv6_sid_format *sid_format; ++ ++ /* ++ * Run-time information/state of this SID block. ++ * ++ * This includes stuff like how many SID functions have been allocated ++ * from this block, which functions are still available to be allocated ++ * and so on... ++ */ ++ union { ++ /* Information/state for compressed uSID format */ ++ struct { ++ /* uSID Local ID Block (LIB) */ ++ struct { ++ uint32_t num_func_allocated; ++ uint32_t first_available_func; ++ struct list *func_allocated; ++ struct list *func_released; ++ } lib; ++ ++ /* uSID Wide LIB */ ++ struct wide_lib *wide_lib; ++ } usid; ++ ++ /* Information/state for uncompressed SID format */ ++ struct { ++ uint32_t num_func_allocated; ++ uint32_t first_available_func; ++ struct list *func_allocated; ++ struct list *func_released; ++ } uncompressed; ++ } u; ++}; ++ ++/** ++ * The function part of an SRv6 SID can be allocated in one ++ * of the following ways: ++ * - dynamic: allocate any available function ++ * - explicit: allocate a specific function ++ */ ++enum srv6_sid_alloc_mode { ++ SRV6_SID_ALLOC_MODE_UNSPEC = 0, ++ /* Dynamic SID allocation */ ++ SRV6_SID_ALLOC_MODE_DYNAMIC = 1, ++ /* Explicit SID allocation */ ++ SRV6_SID_ALLOC_MODE_EXPLICIT = 2, ++ SRV6_SID_ALLOC_MODE_MAX = 3, ++}; ++ ++/** ++ * Convert SID allocation mode to string. ++ * ++ * @param alloc_mode SID allocation mode ++ * @return String representing the allocation mode ++ */ ++static inline const char * ++srv6_sid_alloc_mode2str(enum srv6_sid_alloc_mode alloc_mode) ++{ ++ switch (alloc_mode) { ++ case SRV6_SID_ALLOC_MODE_EXPLICIT: ++ return "explicit"; ++ case SRV6_SID_ALLOC_MODE_DYNAMIC: ++ return "dynamic"; ++ case SRV6_SID_ALLOC_MODE_UNSPEC: ++ return "unspec"; ++ case SRV6_SID_ALLOC_MODE_MAX: ++ default: ++ return "unknown"; ++ } ++} ++ ++/* SRv6 SID instance. */ ++struct zebra_srv6_sid { ++ /* ++ * SID context associated with the SID. ++ * Defines behavior and attributes of the SID. ++ */ ++ struct zebra_srv6_sid_ctx *ctx; ++ ++ /* SID value (e.g. fc00:0:1:e000::) */ ++ struct in6_addr value; ++ ++ /* Pointer to the SRv6 locator from which the SID has been allocated */ ++ struct srv6_locator *locator; ++ ++ /* Pointer to the SRv6 block from which the SID has been allocated */ ++ struct zebra_srv6_sid_block *block; ++ ++ /* ++ * Function part of the SID ++ * Example: ++ * SID = fc00:0:1:e000:: => func = e000 ++ */ ++ uint32_t func; ++ ++ /* SID wide function. */ ++ uint32_t wide_func; ++ ++ /* SID allocation mode: dynamic or explicit */ ++ enum srv6_sid_alloc_mode alloc_mode; ++ ++ /* List of clients that are using the SID */ ++ struct list *client_list; ++}; ++ ++/* ++ * Zebra SRv6 SID context. ++ * A context defines a behavior and (optionally) some behavior-specific ++ * attributes. Client daemons (bgp, isis, ...) ask SRv6 Manager to allocate ++ * a SID for a particular context. SRv6 Manager is responsible for allocating ++ * a SID from a given SID block and associating with the context. ++ * ++ * Example: ++ * bgp asks to associate a SID to the context {behavior=End.DT46 vrf=Vrf10}. ++ * SRv6 Manager allocate SID fc00:0:1:e000:: for that context. ++ */ ++struct zebra_srv6_sid_ctx { ++ /* SRv6 SID context information. */ ++ struct srv6_sid_ctx ctx; ++ ++ /* SID associated with the context. */ ++ struct zebra_srv6_sid *sid; ++}; ++ + /* SRv6 instance structure. */ + struct zebra_srv6 { + struct list *locators; + + /* Source address for SRv6 encapsulation */ + struct in6_addr encap_src_addr; ++ ++ /* SRv6 SID formats */ ++ struct list *sid_formats; ++ ++ /* SRv6 SIDs */ ++ struct list *sids; ++ ++ /* SRv6 SID blocks */ ++ struct list *sid_blocks; + }; + + /* declare hooks for the basic API, so that it can be specialized or served +@@ -46,6 +231,17 @@ DECLARE_HOOK(srv6_manager_release_chunk, + vrf_id_t vrf_id), + (client, locator_name, vrf_id)); + ++DECLARE_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)); ++DECLARE_HOOK(srv6_manager_release_sid, ++ (struct zserv *client, struct srv6_sid_ctx *ctx), (client, ctx)); ++DECLARE_HOOK(srv6_manager_get_locator, ++ (struct srv6_locator **locator, struct zserv *client, ++ const char *locator_name), ++ (locator, client, locator_name)); + + extern void zebra_srv6_locator_add(struct srv6_locator *locator); + extern void zebra_srv6_locator_delete(struct srv6_locator *locator); +@@ -74,4 +270,55 @@ extern int release_daemon_srv6_locator_chunks(struct zserv *client); + extern void zebra_srv6_encap_src_addr_set(struct in6_addr *src_addr); + extern void zebra_srv6_encap_src_addr_unset(void); + ++void srv6_sid_format_register(struct srv6_sid_format *format); ++void srv6_sid_format_unregister(struct srv6_sid_format *format); ++struct srv6_sid_format *srv6_sid_format_lookup(const char *name); ++void zebra_srv6_locator_format_set(struct srv6_locator *locator, ++ struct srv6_sid_format *format); ++void zebra_srv6_sid_format_changed_cb(struct srv6_sid_format *format); ++ ++uint32_t *zebra_srv6_sid_func_alloc(uint32_t func); ++void zebra_srv6_sid_func_free(uint32_t *func); ++void delete_zebra_srv6_sid_func(void *val); ++ ++extern struct zebra_srv6_sid_block * ++zebra_srv6_sid_block_alloc(struct srv6_sid_format *format, ++ struct prefix_ipv6 *prefix); ++extern void zebra_srv6_sid_block_free(struct zebra_srv6_sid_block *block); ++extern void delete_zebra_srv6_sid_block(void *val); ++extern struct zebra_srv6_sid_block * ++zebra_srv6_sid_block_lookup(struct prefix_ipv6 *prefix); ++ ++extern 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); ++extern void zebra_srv6_sid_free(struct zebra_srv6_sid *sid); ++extern void delete_zebra_srv6_sid(void *val); ++ ++extern 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); ++extern void srv6_manager_release_sid_call(struct zserv *client, ++ struct srv6_sid_ctx *ctx); ++ ++extern void srv6_manager_get_locator_call(struct srv6_locator **locator, ++ struct zserv *client, ++ const char *locator_name); ++ ++extern int get_srv6_sid(struct zebra_srv6_sid **sid, struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, const char *locator_name); ++extern int release_srv6_sid(struct zserv *client, ++ struct zebra_srv6_sid_ctx *zctx); ++extern int release_daemon_srv6_sids(struct zserv *client); ++extern int srv6_manager_get_sid_response(struct zebra_srv6_sid *sid, ++ struct zserv *client); ++ ++extern struct zebra_srv6_sid_ctx *zebra_srv6_sid_ctx_alloc(void); ++extern void zebra_srv6_sid_ctx_free(struct zebra_srv6_sid_ctx *ctx); ++extern void delete_zebra_srv6_sid_ctx(void *val); ++ + #endif /* _ZEBRA_SRV6_H */ +diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c +index d5cd30e64..c664a9c69 100644 +--- a/zebra/zebra_srv6_vty.c ++++ b/zebra/zebra_srv6_vty.c +@@ -68,6 +68,27 @@ static struct cmd_node srv6_encap_node = { + .prompt = "%s(config-srv6-encap)# " + }; + ++static struct cmd_node srv6_sid_formats_node = { ++ .name = "srv6-formats", ++ .node = SRV6_SID_FORMATS_NODE, ++ .parent_node = SRV6_NODE, ++ .prompt = "%s(config-srv6-formats)# ", ++}; ++ ++static struct cmd_node srv6_sid_format_usid_f3216_node = { ++ .name = "srv6-format-usid-f3216", ++ .node = SRV6_SID_FORMAT_USID_F3216_NODE, ++ .parent_node = SRV6_SID_FORMATS_NODE, ++ .prompt = "%s(config-srv6-format)# " ++}; ++ ++static struct cmd_node srv6_sid_format_uncompressed_f4024_node = { ++ .name = "srv6-format-uncompressed-f4024", ++ .node = SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, ++ .parent_node = SRV6_SID_FORMATS_NODE, ++ .prompt = "%s(config-srv6-format)# " ++}; ++ + DEFPY (show_srv6_manager, + show_srv6_manager_cmd, + "show segment-routing srv6 manager [json]", +@@ -198,15 +219,32 @@ DEFUN (show_srv6_locator_detail, + prefix2str(&locator->prefix, str, sizeof(str)); + vty_out(vty, "Name: %s\n", locator->name); + vty_out(vty, "Prefix: %s\n", str); +- vty_out(vty, "Block-Bit-Len: %u\n", locator->block_bits_length); +- vty_out(vty, "Node-Bit-Len: %u\n", locator->node_bits_length); +- vty_out(vty, "Function-Bit-Len: %u\n", +- locator->function_bits_length); +- vty_out(vty, "Argument-Bit-Len: %u\n", +- locator->argument_bits_length); ++ if (locator->sid_format) { ++ vty_out(vty, "Block-Bit-Len: %u\n", ++ locator->sid_format->block_len); ++ vty_out(vty, "Node-Bit-Len: %u\n", ++ locator->sid_format->node_len); ++ vty_out(vty, "Function-Bit-Len: %u\n", ++ locator->sid_format->function_len); ++ vty_out(vty, "Argument-Bit-Len: %u\n", ++ locator->sid_format->argument_len); ++ ++ if (locator->sid_format->type == ++ SRV6_SID_FORMAT_TYPE_USID) ++ vty_out(vty, "Behavior: uSID\n"); ++ } else { ++ vty_out(vty, "Block-Bit-Len: %u\n", ++ locator->block_bits_length); ++ vty_out(vty, "Node-Bit-Len: %u\n", ++ locator->node_bits_length); ++ vty_out(vty, "Function-Bit-Len: %u\n", ++ locator->function_bits_length); ++ vty_out(vty, "Argument-Bit-Len: %u\n", ++ locator->argument_bits_length); + +- if (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) +- vty_out(vty, "Behavior: uSID\n"); ++ if (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) ++ vty_out(vty, "Behavior: uSID\n"); ++ } + + vty_out(vty, "Chunks:\n"); + for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, node, +@@ -248,9 +286,30 @@ DEFUN (no_srv6, + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct srv6_locator *locator; + struct listnode *node, *nnode; ++ struct zebra_srv6_sid_block *block; ++ struct zebra_srv6_sid_ctx *ctx; ++ ++ for (ALL_LIST_ELEMENTS(srv6->sids, node, nnode, ctx)) { ++ if (ctx->sid) ++ zebra_srv6_sid_free(ctx->sid); ++ ++ listnode_delete(srv6->sids, ctx); ++ zebra_srv6_sid_ctx_free(ctx); ++ } ++ ++ for (ALL_LIST_ELEMENTS(srv6->locators, node, nnode, locator)) { ++ block = locator->sid_block; ++ if (block) { ++ block->refcnt--; ++ if (block->refcnt == 0) { ++ listnode_delete(srv6->sid_blocks, block); ++ zebra_srv6_sid_block_free(block); ++ } ++ locator->sid_block = NULL; ++ } + +- for (ALL_LIST_ELEMENTS(srv6->locators, node, nnode, locator)) + zebra_srv6_locator_delete(locator); ++ } + return CMD_SUCCESS; + } + +@@ -297,12 +356,37 @@ DEFUN (no_srv6_locator, + "Segment Routing SRv6 locator\n" + "Specify locator-name\n") + { ++ struct zebra_srv6 *srv6 = zebra_srv6_get_default(); ++ struct zebra_srv6_sid_block *block; ++ struct listnode *node, *nnode; ++ struct zebra_srv6_sid_ctx *ctx; + struct srv6_locator *locator = zebra_srv6_locator_lookup(argv[2]->arg); + if (!locator) { + vty_out(vty, "%% Can't find SRv6 locator\n"); + return CMD_WARNING_CONFIG_FAILED; + } + ++ 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); ++ } ++ ++ block = locator->sid_block; ++ if (block) { ++ block->refcnt--; ++ if (block->refcnt == 0) { ++ listnode_delete(srv6->sid_blocks, block); ++ zebra_srv6_sid_block_free(block); ++ } ++ locator->sid_block = NULL; ++ } ++ + zebra_srv6_locator_delete(locator); + return CMD_SUCCESS; + } +@@ -323,14 +407,37 @@ DEFPY (locator_prefix, + VTY_DECLVAR_CONTEXT(srv6_locator, locator); + struct srv6_locator_chunk *chunk = NULL; + struct listnode *node = NULL; ++ uint8_t expected_prefixlen; ++ struct srv6_sid_format *format; + + locator->prefix = *prefix; + func_bit_len = func_bit_len ?: ZEBRA_SRV6_FUNCTION_LENGTH; + ++ expected_prefixlen = prefix->prefixlen; ++ format = locator->sid_format; ++ if (format) { ++ if (strmatch(format->name, SRV6_SID_FORMAT_USID_F3216_NAME)) ++ expected_prefixlen = ++ SRV6_SID_FORMAT_USID_F3216_BLOCK_LEN + ++ SRV6_SID_FORMAT_USID_F3216_NODE_LEN; ++ else if (strmatch(format->name, ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NAME)) ++ expected_prefixlen = ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_BLOCK_LEN + ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE_LEN; ++ } ++ ++ if (prefix->prefixlen != expected_prefixlen) { ++ vty_out(vty, ++ "%% Locator prefix length '%u' inconsistent with configured format '%s'. Please either use a prefix length that is consistent with the format or change the format.\n", ++ prefix->prefixlen, format->name); ++ return CMD_WARNING_CONFIG_FAILED; ++ } ++ + /* Resolve optional arguments */ + if (block_bit_len == 0 && node_bit_len == 0) { +- block_bit_len = +- prefix->prefixlen - ZEBRA_SRV6_LOCATOR_NODE_LENGTH; ++ block_bit_len = prefix->prefixlen - ++ ZEBRA_SRV6_LOCATOR_NODE_LENGTH; + node_bit_len = ZEBRA_SRV6_LOCATOR_NODE_LENGTH; + } else if (block_bit_len == 0) { + block_bit_len = prefix->prefixlen - node_bit_len; +@@ -401,7 +508,8 @@ DEFPY (locator_prefix, + } + } + +- zebra_srv6_locator_add(locator); ++ zebra_srv6_locator_format_set(locator, locator->sid_format); ++ + return CMD_SUCCESS; + } + +@@ -422,8 +530,9 @@ DEFPY (locator_behavior, + /* SRv6 locator uSID flag already set, nothing to do */ + return CMD_SUCCESS; + +- /* Remove old locator from zclients */ +- zebra_notify_srv6_locator_delete(locator); ++ if (!locator->sid_format) ++ /* Remove old locator from zclients */ ++ zebra_notify_srv6_locator_delete(locator); + + /* Set/Unset the SRV6_LOCATOR_USID */ + if (no) +@@ -431,8 +540,75 @@ DEFPY (locator_behavior, + else + SET_FLAG(locator->flags, SRV6_LOCATOR_USID); + +- /* Notify the new locator to zclients */ +- zebra_notify_srv6_locator_add(locator); ++ if (!locator->sid_format) ++ /* Notify the new locator to zclients */ ++ zebra_srv6_locator_add(locator); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(locator_sid_format, ++ locator_sid_format_cmd, ++ "format $format", ++ "Configure SRv6 SID format\n" ++ "Specify usid-f3216 format\n" ++ "Specify uncompressed-f4024 format\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_locator, locator); ++ struct srv6_sid_format *sid_format = NULL; ++ uint8_t expected_prefixlen; ++ ++ expected_prefixlen = locator->prefix.prefixlen; ++ if (strmatch(format, SRV6_SID_FORMAT_USID_F3216_NAME)) ++ expected_prefixlen = SRV6_SID_FORMAT_USID_F3216_BLOCK_LEN + ++ SRV6_SID_FORMAT_USID_F3216_NODE_LEN; ++ else if (strmatch(format, SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NAME)) ++ expected_prefixlen = ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_BLOCK_LEN + ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE_LEN; ++ ++ if (IPV6_ADDR_SAME(&locator->prefix, &in6addr_any)) { ++ vty_out(vty, ++ "%% Unexpected configuration sequence: the prefix of the locator is required before configuring the format. Please configure the prefix first and then configure the format.\n"); ++ return CMD_WARNING_CONFIG_FAILED; ++ } ++ ++ if (locator->prefix.prefixlen != expected_prefixlen) { ++ vty_out(vty, ++ "%% Locator prefix length '%u' inconsistent with configured format '%s'. Please either use a prefix length that is consistent with the format or change the format.\n", ++ locator->prefix.prefixlen, format); ++ return CMD_WARNING_CONFIG_FAILED; ++ } ++ ++ sid_format = srv6_sid_format_lookup(format); ++ if (!sid_format) { ++ vty_out(vty, "%% Cannot find SRv6 SID format '%s'\n", format); ++ return CMD_WARNING_CONFIG_FAILED; ++ } ++ ++ if (sid_format == locator->sid_format) ++ /* Format has not changed, nothing to do */ ++ return CMD_SUCCESS; ++ ++ zebra_srv6_locator_format_set(locator, sid_format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY (no_locator_sid_format, ++ no_locator_sid_format_cmd, ++ "no format [WORD]", ++ NO_STR ++ "Configure SRv6 SID format\n" ++ "Specify SRv6 SID format\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_locator, locator); ++ ++ if (!locator->sid_format) ++ /* SID format already unset, nothing to do */ ++ return CMD_SUCCESS; ++ ++ zebra_srv6_locator_format_set(locator, NULL); + + return CMD_SUCCESS; + } +@@ -469,11 +645,312 @@ DEFPY (no_srv6_src_addr, + return CMD_SUCCESS; + } + ++DEFUN_NOSH(srv6_sid_formats, ++ srv6_sid_formats_cmd, ++ "formats", ++ "Segment Routing SRv6 SID formats\n") ++{ ++ vty->node = SRV6_SID_FORMATS_NODE; ++ return CMD_SUCCESS; ++} ++ ++DEFUN_NOSH (srv6_sid_format_f3216_usid, ++ srv6_sid_format_f3216_usid_cmd, ++ "format usid-f3216", ++ "Configure SRv6 SID format\n" ++ "Configure the uSID f3216 format\n") ++{ ++ struct srv6_sid_format *format; ++ ++ format = srv6_sid_format_lookup(SRV6_SID_FORMAT_USID_F3216_NAME); ++ assert(format); ++ ++ VTY_PUSH_CONTEXT(SRV6_SID_FORMAT_USID_F3216_NODE, format); ++ return CMD_SUCCESS; ++} ++ ++DEFUN(no_srv6_sid_format_f3216_usid, ++ no_srv6_sid_format_f3216_usid_cmd, ++ "no format usid-f3216", ++ NO_STR ++ "Configure SRv6 SID format\n" ++ "Configure the uSID f3216 format\n") ++{ ++ struct srv6_sid_format *format; ++ ++ format = srv6_sid_format_lookup(SRV6_SID_FORMAT_USID_F3216_NAME); ++ assert(format); ++ ++ 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; ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFUN_NOSH (srv6_sid_format_f4024_uncompressed, ++ srv6_sid_format_uncompressed_cmd, ++ "format uncompressed-f4024", ++ "Configure SRv6 SID format\n" ++ "Configure the uncompressed f4024 format\n") ++{ ++ struct srv6_sid_format *format; ++ ++ format = srv6_sid_format_lookup(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NAME); ++ assert(format); ++ ++ VTY_PUSH_CONTEXT(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, format); ++ return CMD_SUCCESS; ++} ++ ++DEFUN(no_srv6_sid_format_f4024_uncompressed, ++ no_srv6_sid_format_f4024_uncompressed_cmd, ++ "no format uncompressed-f4024", ++ NO_STR ++ "Configure SRv6 SID format\n" ++ "Configure the uncompressed f4024 format\n") ++{ ++ struct srv6_sid_format *format; ++ ++ format = srv6_sid_format_lookup(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NAME); ++ assert(format); ++ ++ format->config.uncompressed.explicit_start = ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_EXPLICIT_RANGE_START; ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(srv6_sid_format_usid_lib, ++ srv6_sid_format_usid_lib_cmd, ++ "local-id-block start (0-4294967295)$start", ++ "Configure LIB\n" ++ "Configure the start value for the LIB\n" ++ "Specify the start value for the LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ format->config.usid.lib_start = start; ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(no_srv6_sid_format_usid_lib, ++ no_srv6_sid_format_usid_lib_cmd, ++ "no local-id-block [start (0-4294967295)]", ++ NO_STR ++ "Configure LIB\n" ++ "Configure the start value for the LIB\n" ++ "Specify the start value for the LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ if (strmatch(format->name, SRV6_SID_FORMAT_USID_F3216_NAME)) ++ format->config.usid.lib_start = ++ SRV6_SID_FORMAT_USID_F3216_LIB_START; ++ else ++ assert(0); ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(srv6_sid_format_usid_lib_explicit, ++ srv6_sid_format_usid_lib_explicit_cmd, ++ "local-id-block explicit start (0-4294967295)$start end (0-4294967295)$end", ++ "Configure LIB\n" ++ "Configure the Explicit LIB\n" ++ "Configure the start value for the Explicit LIB\n" ++ "Specify the start value for the Explicit LIB\n" ++ "Configure the end value for the Explicit LIB\n" ++ "Specify the end value for the Explicit LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ format->config.usid.elib_start = start; ++ format->config.usid.elib_end = end; ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(no_srv6_sid_format_usid_lib_explicit, ++ no_srv6_sid_format_usid_lib_explicit_cmd, ++ "no local-id-block explicit [start (0-4294967295) end (0-4294967295)]", ++ NO_STR ++ "Configure LIB\n" ++ "Configure the Explicit LIB\n" ++ "Configure the start value for the Explicit LIB\n" ++ "Specify the start value for the Explicit LIB\n" ++ "Configure the end value for the Explicit LIB\n" ++ "Specify the end value for the Explicit LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ if (strmatch(format->name, SRV6_SID_FORMAT_USID_F3216_NAME)) { ++ format->config.usid.elib_start = ++ SRV6_SID_FORMAT_USID_F3216_ELIB_START; ++ format->config.usid.elib_end = ++ SRV6_SID_FORMAT_USID_F3216_ELIB_END; ++ } else { ++ assert(0); ++ } ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(srv6_sid_format_usid_wlib, ++ srv6_sid_format_usid_wlib_cmd, ++ "wide-local-id-block start (0-4294967295)$start end (0-4294967295)$end", ++ "Configure Wide LIB\n" ++ "Configure the start value for the Wide LIB\n" ++ "Specify the start value for the Wide LIB\n" ++ "Configure the end value for the Wide LIB\n" ++ "Specify the end value for the Wide LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ format->config.usid.wlib_start = start; ++ format->config.usid.wlib_end = end; ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(no_srv6_sid_format_usid_wlib, ++ no_srv6_sid_format_usid_wlib_cmd, ++ "no wide-local-id-block [start (0-4294967295) end (0-4294967295)]", ++ NO_STR ++ "Configure Wide LIB\n" ++ "Configure the start value for the Wide LIB\n" ++ "Specify the start value for the Wide LIB\n" ++ "Configure the end value for the Wide LIB\n" ++ "Specify the end value for the Wide LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ if (strmatch(format->name, SRV6_SID_FORMAT_USID_F3216_NAME)) { ++ format->config.usid.wlib_start = ++ SRV6_SID_FORMAT_USID_F3216_WLIB_START; ++ format->config.usid.wlib_end = ++ SRV6_SID_FORMAT_USID_F3216_WLIB_END; ++ } else { ++ assert(0); ++ } ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(srv6_sid_format_usid_wide_lib_explicit, ++ srv6_sid_format_usid_wide_lib_explicit_cmd, ++ "wide-local-id-block explicit start (0-4294967295)$start", ++ "Configure Wide LIB\n" ++ "Configure Explicit Wide LIB\n" ++ "Configure the start value for the Explicit Wide LIB\n" ++ "Specify the start value for the Explicit Wide LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ format->config.usid.ewlib_start = start; ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(no_srv6_sid_format_usid_wide_lib_explicit, ++ no_srv6_sid_format_usid_wide_lib_explicit_cmd, ++ "no wide-local-id-block explicit [start (0-4294967295)]", ++ NO_STR ++ "Configure Wide LIB\n" ++ "Configure Explicit Wide LIB\n" ++ "Configure the start value for the Explicit Wide LIB\n" ++ "Specify the start value for the Explicit Wide LIB\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ if (strmatch(format->name, SRV6_SID_FORMAT_USID_F3216_NAME)) ++ format->config.usid.ewlib_start = ++ SRV6_SID_FORMAT_USID_F3216_EWLIB_START; ++ else ++ assert(0); ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(srv6_sid_format_explicit, ++ srv6_sid_format_explicit_cmd, ++ "explicit start (0-4294967295)$start", ++ "Configure Explicit range\n" ++ "Configure the start value for the Explicit range\n" ++ "Specify the start value for the Explicit range\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ format->config.uncompressed.explicit_start = start; ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ ++DEFPY(no_srv6_sid_format_explicit, ++ no_srv6_sid_format_explicit_cmd, ++ "no explicit [start (0-4294967295)$start]", ++ NO_STR ++ "Configure Explicit range\n" ++ "Configure the start value for the Explicit range\n" ++ "Specify the start value for the Explicit range\n") ++{ ++ VTY_DECLVAR_CONTEXT(srv6_sid_format, format); ++ ++ if (strmatch(format->name, SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NAME)) ++ format->config.usid.ewlib_start = ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_EXPLICIT_RANGE_START; ++ else ++ assert(0); ++ ++ /* Notify zclients that the format has changed */ ++ zebra_srv6_sid_format_changed_cb(format); ++ ++ return CMD_SUCCESS; ++} ++ + static int zebra_sr_config(struct vty *vty) + { + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct listnode *node; + struct srv6_locator *locator; ++ struct srv6_sid_format *format; + char str[256]; + bool display_source_srv6 = false; + +@@ -515,6 +992,54 @@ static int zebra_sr_config(struct vty *vty) + vty_out(vty, "\n"); + if (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + vty_out(vty, " behavior usid\n"); ++ if (locator->sid_format) { ++ format = locator->sid_format; ++ vty_out(vty, " format %s\n", format->name); ++ } ++ vty_out(vty, " exit\n"); ++ vty_out(vty, " !\n"); ++ } ++ vty_out(vty, " exit\n"); ++ vty_out(vty, " !\n"); ++ vty_out(vty, " formats\n"); ++ for (ALL_LIST_ELEMENTS_RO(srv6->sid_formats, node, format)) { ++ if (format->type == SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) { ++ vty_out(vty, " format %s\n", format->name); ++ if (format->config.uncompressed.explicit_start != ++ SRV6_SID_FORMAT_UNCOMPRESSED_F4024_EXPLICIT_RANGE_START) ++ vty_out(vty, " explicit start %u\n", ++ format->config.uncompressed ++ .explicit_start); ++ } ++ if (format->type == SRV6_SID_FORMAT_TYPE_USID) { ++ vty_out(vty, " format %s\n", format->name); ++ if (format->config.usid.lib_start != ++ SRV6_SID_FORMAT_USID_F3216_LIB_START) ++ vty_out(vty, ++ " local-id-block start %u\n", ++ format->config.usid.lib_start); ++ if (format->config.usid.elib_start != ++ SRV6_SID_FORMAT_USID_F3216_ELIB_START || ++ format->config.usid.elib_end != ++ SRV6_SID_FORMAT_USID_F3216_ELIB_END) ++ vty_out(vty, ++ " local-id-block explicit start %u end %u\n", ++ format->config.usid.elib_start, ++ format->config.usid.elib_end); ++ if (format->config.usid.wlib_start != ++ SRV6_SID_FORMAT_USID_F3216_WLIB_START || ++ format->config.usid.wlib_end != ++ SRV6_SID_FORMAT_USID_F3216_WLIB_END) ++ vty_out(vty, ++ " wide-local-id-block start %u end %u\n", ++ format->config.usid.wlib_start, ++ format->config.usid.wlib_end); ++ if (format->config.usid.ewlib_start != ++ SRV6_SID_FORMAT_USID_F3216_EWLIB_START) ++ vty_out(vty, ++ " wide-local-id-block explicit start %u\n", ++ format->config.usid.ewlib_start); ++ } + vty_out(vty, " exit\n"); + vty_out(vty, " !\n"); + } +@@ -538,11 +1063,17 @@ void zebra_srv6_vty_init(void) + install_node(&srv6_locs_node); + install_node(&srv6_loc_node); + install_node(&srv6_encap_node); ++ install_node(&srv6_sid_formats_node); ++ install_node(&srv6_sid_format_usid_f3216_node); ++ install_node(&srv6_sid_format_uncompressed_f4024_node); + install_default(SEGMENT_ROUTING_NODE); + install_default(SRV6_NODE); + install_default(SRV6_LOCS_NODE); + install_default(SRV6_LOC_NODE); + install_default(SRV6_ENCAP_NODE); ++ install_default(SRV6_SID_FORMATS_NODE); ++ install_default(SRV6_SID_FORMAT_USID_F3216_NODE); ++ install_default(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE); + + /* Command for change node */ + install_element(CONFIG_NODE, &segment_routing_cmd); +@@ -550,14 +1081,44 @@ void zebra_srv6_vty_init(void) + install_element(SEGMENT_ROUTING_NODE, &no_srv6_cmd); + install_element(SRV6_NODE, &srv6_locators_cmd); + install_element(SRV6_NODE, &srv6_encap_cmd); ++ install_element(SRV6_NODE, &srv6_sid_formats_cmd); + install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); + install_element(SRV6_LOCS_NODE, &no_srv6_locator_cmd); ++ install_element(SRV6_SID_FORMATS_NODE, &srv6_sid_format_f3216_usid_cmd); ++ install_element(SRV6_SID_FORMATS_NODE, ++ &srv6_sid_format_uncompressed_cmd); ++ install_element(SRV6_SID_FORMATS_NODE, ++ &no_srv6_sid_format_f3216_usid_cmd); ++ install_element(SRV6_SID_FORMATS_NODE, ++ &no_srv6_sid_format_f4024_uncompressed_cmd); + + /* Command for configuration */ + install_element(SRV6_LOC_NODE, &locator_prefix_cmd); + install_element(SRV6_LOC_NODE, &locator_behavior_cmd); ++ install_element(SRV6_LOC_NODE, &locator_sid_format_cmd); ++ install_element(SRV6_LOC_NODE, &no_locator_sid_format_cmd); + install_element(SRV6_ENCAP_NODE, &srv6_src_addr_cmd); + install_element(SRV6_ENCAP_NODE, &no_srv6_src_addr_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &srv6_sid_format_usid_lib_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &no_srv6_sid_format_usid_lib_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &srv6_sid_format_usid_lib_explicit_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &no_srv6_sid_format_usid_lib_explicit_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &srv6_sid_format_usid_wlib_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &no_srv6_sid_format_usid_wlib_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &srv6_sid_format_usid_wide_lib_explicit_cmd); ++ install_element(SRV6_SID_FORMAT_USID_F3216_NODE, ++ &no_srv6_sid_format_usid_wide_lib_explicit_cmd); ++ install_element(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, ++ &srv6_sid_format_explicit_cmd); ++ install_element(SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE, ++ &no_srv6_sid_format_explicit_cmd); + + /* Command for operation */ + install_element(VIEW_NODE, &show_srv6_locator_cmd); diff --git a/src/sonic-frr/patch/series b/src/sonic-frr/patch/series index 7f68378272ec..1eebafcae045 100644 --- a/src/sonic-frr/patch/series +++ b/src/sonic-frr/patch/series @@ -46,3 +46,4 @@ 0064-SRv6-BGP-SID-reachability.patch 0065-zebra-display-srv6-encapsulation-source-address-when-configured.patch 0066-lib-fix-srv6-locator-flags-propagated-to-isis.patch +0067-Add-support-for-SRv6-SID-Manager.patch