From f792a215cdd5b747e8b8635c4033d26a7d317c5f Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 10:50:05 +0100 Subject: [PATCH 01/15] yang: Add SRv6 to frr-staticd YANG model Signed-off-by: Yuqing Zhao --- yang/frr-staticd.yang | 102 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/yang/frr-staticd.yang b/yang/frr-staticd.yang index 1e6c54c006..904e2058e9 100644 --- a/yang/frr-staticd.yang +++ b/yang/frr-staticd.yang @@ -20,6 +20,10 @@ module frr-staticd { prefix frr-bfdd; } + import frr-vrf { + prefix frr-vrf; + } + organization "FRRouting"; contact @@ -92,6 +96,64 @@ module frr-staticd { } } + typedef srv6-behavior-codepoint { + description + "SRv6 Endpoint Behaviors Codepoints as per + https://www.iana.org/assignments/segment-routing/segment-routing.xhtml."; + type enumeration { + enum End { + value 1; + description + "This enum indicates End endpoint behavior."; + } + enum End.X { + value 5; + description + "This enum indicates End.X endpoint behavior."; + } + enum End.DT6 { + value 18; + description + "This enum indicates End.DT6 endpoint behavior."; + } + enum End.DT4 { + value 19; + description + "This enum indicates End.DT4 endpoint behavior."; + } + enum End.DT46 { + value 20; + description + "This enum indicates End.DT46 endpoint behavior."; + } + enum uN { + value 43; + description + "This enum indicates End with NEXT-CSID endpoint behavior."; + } + enum uA { + value 52; + description + "This enum indicates End.X with NEXT-CSID endpoint behavior."; + } + enum uDT6 { + value 62; + description + "This enum indicates End.DT6 with NEXT-CSID endpoint behavior."; + } + enum uDT4 { + value 63; + description + "This enum indicates End.DT4 with NEXT-CSID endpoint behavior."; + } + enum uDT46 { + value 64; + description + "This enum indicates End.DT46 with NEXT-CSID endpoint behavior."; + } + } + } + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" { container staticd { when "../frr-rt:type = 'frr-staticd:staticd'" { @@ -144,6 +206,44 @@ module frr-staticd { uses staticd-prefix-attributes; } } + + container segment-routing { + description + "Segment Routing configuration."; + container srv6 { + description + "Segment Routing over IPv6 (SRv6) configuration."; + container static-sids { + description + "This container lists the SRv6 Static SIDs instantiated on the local node."; + list sid { + description + "List of SRv6 Static SIDs."; + key "sid"; + leaf sid { + type inet:ipv6-prefix; + description + "Value of the SRv6 SID."; + } + leaf behavior { + type srv6-behavior-codepoint; + description + "Behavior bound to the SRv6 SID."; + } + leaf locator-name { + type string; + description + "SRv6 locator name."; + } + leaf vrf-name { + type frr-vrf:vrf-ref; + description + "The VRF name."; + } + } + } + } + } } } -} +} \ No newline at end of file From 57031d43ce02faaf6aea32071a48f6a9aadd35a1 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Wed, 15 Jan 2025 18:43:37 +0100 Subject: [PATCH 02/15] staticd: Add debug option for SRv6 Signed-off-by: Yuqing Zhao --- staticd/static_debug.c | 7 +++++-- staticd/static_debug.h | 4 ++-- staticd/static_vty.c | 9 +++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/staticd/static_debug.c b/staticd/static_debug.c index 618ba91d12..b308860424 100644 --- a/staticd/static_debug.c +++ b/staticd/static_debug.c @@ -22,6 +22,7 @@ struct debug static_dbg_events = {0, "debug static events", "Staticd events"}; struct debug static_dbg_route = {0, "debug static route", "Staticd route"}; struct debug static_dbg_bfd = {0, "debug static bfd", "Staticd bfd"}; +struct debug static_dbg_srv6 = {0, "debug static srv6", "Staticd srv6"}; /* clang-format on */ /* @@ -37,8 +38,7 @@ struct debug static_dbg_bfd = {0, "debug static bfd", "Staticd bfd"}; * Debug general internal events * */ -void static_debug_set(int vtynode, bool onoff, bool events, bool route, - bool bfd) +void static_debug_set(int vtynode, bool onoff, bool events, bool route, bool bfd, bool srv6) { uint32_t mode = DEBUG_NODE2MODE(vtynode); @@ -50,6 +50,8 @@ void static_debug_set(int vtynode, bool onoff, bool events, bool route, DEBUG_MODE_SET(&static_dbg_bfd, mode, onoff); bfd_protocol_integration_set_debug(onoff); } + if (srv6) + DEBUG_MODE_SET(&static_dbg_srv6, mode, onoff); } /* @@ -61,4 +63,5 @@ void static_debug_init(void) debug_install(&static_dbg_events); debug_install(&static_dbg_route); debug_install(&static_dbg_bfd); + debug_install(&static_dbg_srv6); } diff --git a/staticd/static_debug.h b/staticd/static_debug.h index b990f7bcc9..a16e398eba 100644 --- a/staticd/static_debug.h +++ b/staticd/static_debug.h @@ -20,6 +20,7 @@ extern "C" { extern struct debug static_dbg_events; extern struct debug static_dbg_route; extern struct debug static_dbg_bfd; +extern struct debug static_dbg_srv6; /* * Initialize staticd debugging. @@ -41,8 +42,7 @@ void static_debug_init(void); * Debug general internal events * */ -void static_debug_set(int vtynode, bool onoff, bool events, bool route, - bool bfd); +void static_debug_set(int vtynode, bool onoff, bool events, bool route, bool bfd, bool srv6); #ifdef __cplusplus } diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 07b8bc3d28..68a4ef20c1 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -1603,17 +1603,18 @@ const struct frr_yang_module_info frr_staticd_cli_info = { #else /* ifdef INCLUDE_MGMTD_CMDDEFS_ONLY */ DEFPY_YANG(debug_staticd, debug_staticd_cmd, - "[no] debug static [{events$events|route$route|bfd$bfd}]", + "[no] debug static [{events$events|route$route|bfd$bfd|srv6$srv6}]", NO_STR DEBUG_STR STATICD_STR "Debug events\n" "Debug route\n" - "Debug bfd\n") + "Debug bfd\n" + "Debug srv6\n") { /* If no specific category, change all */ if (strmatch(argv[argc - 1]->text, "static")) - static_debug_set(vty->node, !no, true, true, true); + static_debug_set(vty->node, !no, true, true, true, true); else - static_debug_set(vty->node, !no, !!events, !!route, !!bfd); + static_debug_set(vty->node, !no, !!events, !!route, !!bfd, !!srv6); return CMD_SUCCESS; } From 2025a0c617954052d65f5f244679a224c30dba08 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Thu, 16 Jan 2025 08:57:04 +0100 Subject: [PATCH 03/15] lib: Add a function to print SRv6 Endpoint Behavior Codepoints Signed-off-by: Yuqing Zhao --- lib/srv6.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/srv6.h b/lib/srv6.h index 9a041e3d85..41e0a076f9 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -186,6 +186,42 @@ enum srv6_endpoint_behavior_codepoint { SRV6_ENDPOINT_BEHAVIOR_OPAQUE = 0xFFFF, }; +/* + * Convert SRv6 endpoint behavior codepoints to human-friendly string. + */ +static inline const char * +srv6_endpoint_behavior_codepoint2str(enum srv6_endpoint_behavior_codepoint behavior) +{ + switch (behavior) { + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + return "Reserved"; + case SRV6_ENDPOINT_BEHAVIOR_END: + return "End"; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + return "End.X"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + return "End.DT6"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + return "End.DT4"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + return "End.DT46"; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + return "uN"; + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + return "uA"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + return "uDT6"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + return "uDT4"; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + return "uDT46"; + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + return "Opaque"; + } + + return "Unspec"; +} + struct nexthop_srv6 { /* SRv6 localsid info for Endpoint-behaviour */ enum seg6local_action_t seg6local_action; From 807d90050d147a371c14687b85ccb67fbd4607c4 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:02:23 +0100 Subject: [PATCH 04/15] staticd: Add infrastructure for SRv6 This commit adds datastructures and helper functions required to support SRv6 in staticd. * List of locators * List of SIDs * Data structure to represent an SRv6 SID * Functions to allocate/deallocate an SRv6 SID * Functions to allocate, deallocate and lookup a locator * Function to initialize/Cleanup SRv6 Signed-off-by: Yuqing Zhao --- staticd/static_srv6.c | 134 ++++++++++++++++++++++++++++++++++++++++++ staticd/static_srv6.h | 99 +++++++++++++++++++++++++++++++ staticd/subdir.am | 2 + 3 files changed, 235 insertions(+) create mode 100644 staticd/static_srv6.c create mode 100644 staticd/static_srv6.h diff --git a/staticd/static_srv6.c b/staticd/static_srv6.c new file mode 100644 index 0000000000..d8e55894fe --- /dev/null +++ b/staticd/static_srv6.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * STATICd - Segment Routing over IPv6 (SRv6) code + * Copyright (C) 2025 Alibaba Inc. + * Yuqing Zhao + * Lingyu Zhang + */ +#include + +#include "vrf.h" +#include "nexthop.h" + +#include "static_routes.h" +#include "static_srv6.h" +#include "static_vrf.h" +#include "static_zebra.h" + +/* + * List of SRv6 SIDs. + */ +struct list *srv6_locators; +struct list *srv6_sids; + +DEFINE_MTYPE_STATIC(STATIC, STATIC_SRV6_LOCATOR, "Static SRv6 locator"); +DEFINE_MTYPE_STATIC(STATIC, STATIC_SRV6_SID, "Static SRv6 SID"); + +/* + * Allocate an SRv6 SID object and initialize the fields common to all the + * behaviors (i.e., SID address and behavor). + */ +struct static_srv6_sid *static_srv6_sid_alloc(struct prefix_ipv6 *addr) +{ + struct static_srv6_sid *sid = NULL; + + sid = XCALLOC(MTYPE_STATIC_SRV6_SID, sizeof(struct static_srv6_sid)); + sid->addr = *addr; + + return sid; +} + +void static_srv6_sid_free(struct static_srv6_sid *sid) +{ + XFREE(MTYPE_STATIC_SRV6_SID, sid); +} + +struct static_srv6_locator *static_srv6_locator_lookup(const char *name) +{ + struct static_srv6_locator *locator; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(srv6_locators, node, locator)) + if (!strncmp(name, locator->name, SRV6_LOCNAME_SIZE)) + return locator; + return NULL; +} + +/* + * Look-up an SRv6 SID in the list of SRv6 SIDs. + */ +struct static_srv6_sid *static_srv6_sid_lookup(struct prefix_ipv6 *sid_addr) +{ + struct static_srv6_sid *sid; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) + if (memcmp(&sid->addr, sid_addr, sizeof(struct prefix_ipv6)) == 0) + return sid; + + return NULL; +} + +struct static_srv6_locator *static_srv6_locator_alloc(const char *name) +{ + struct static_srv6_locator *locator = NULL; + + locator = XCALLOC(MTYPE_STATIC_SRV6_LOCATOR, sizeof(struct static_srv6_locator)); + strlcpy(locator->name, name, sizeof(locator->name)); + + return locator; +} + +void static_srv6_locator_free(struct static_srv6_locator *locator) +{ + XFREE(MTYPE_STATIC_SRV6_LOCATOR, locator); +} + +void delete_static_srv6_locator(void *val) +{ + static_srv6_locator_free((struct static_srv6_locator *)val); +} + +/* + * Remove an SRv6 SID from the zebra RIB (if it was previously installed) and + * release the memory previously allocated for the SID. + */ +void static_srv6_sid_del(struct static_srv6_sid *sid) +{ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + + XFREE(MTYPE_STATIC_SRV6_SID, sid); +} + +void delete_static_srv6_sid(void *val) +{ + static_srv6_sid_free((struct static_srv6_sid *)val); +} + +/* + * Initialize SRv6 data structures. + */ +void static_srv6_init(void) +{ + srv6_locators = list_new(); + srv6_locators->del = delete_static_srv6_locator; + srv6_sids = list_new(); + srv6_sids->del = delete_static_srv6_sid; +} + +/* + * Clean up all the SRv6 data structures. + */ +void static_srv6_cleanup(void) +{ + list_delete(&srv6_locators); + list_delete(&srv6_sids); +} diff --git a/staticd/static_srv6.h b/staticd/static_srv6.h new file mode 100644 index 0000000000..bed0ab7fa5 --- /dev/null +++ b/staticd/static_srv6.h @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * STATICd - Segment Routing over IPv6 (SRv6) header + * Copyright (C) 2025 Alibaba Inc. + * Yuqing Zhao + * Lingyu Zhang + */ +#ifndef __STATIC_SRV6_H__ +#define __STATIC_SRV6_H__ + +#include "vrf.h" +#include "srv6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Attributes for an SRv6 SID */ +struct static_srv6_sid_attributes { + /* VRF name */ + char vrf_name[VRF_NAMSIZ]; + char ifname[IFNAMSIZ]; + struct in6_addr nh6; +}; + +/* Static SRv6 SID */ +struct static_srv6_sid { + /* SRv6 SID address */ + struct prefix_ipv6 addr; + /* behavior bound to the SRv6 SID */ + enum srv6_endpoint_behavior_codepoint behavior; + /* SID attributes */ + struct static_srv6_sid_attributes attributes; + + /* SRv6 SID flags */ + uint8_t flags; +/* + * this SRv6 SID has been allocated by SID Manager + * and can be installed in the zebra RIB + */ +#define STATIC_FLAG_SRV6_SID_VALID (1 << 0) +/* this SRv6 SID has been installed in the zebra RIB */ +#define STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA (1 << 1) + + char locator_name[SRV6_LOCNAME_SIZE]; + struct static_srv6_locator *locator; +}; + +struct static_srv6_locator { + char name[SRV6_LOCNAME_SIZE]; + struct prefix_ipv6 prefix; + + /* + * Bit length of SRv6 locator described in + * draft-ietf-bess-srv6-services-05#section-3.2.1 + */ + uint8_t block_bits_length; + uint8_t node_bits_length; + uint8_t function_bits_length; + uint8_t argument_bits_length; + + uint8_t flags; +}; + +/* List of SRv6 SIDs. */ +extern struct list *srv6_locators; +extern struct list *srv6_sids; + +/* + * Allocate an SRv6 SID object and initialize its fields, SID address and + * behavor. + */ +extern struct static_srv6_sid *static_srv6_sid_alloc(struct prefix_ipv6 *addr); +extern void static_srv6_sid_free(struct static_srv6_sid *sid); +/* Look-up an SRv6 SID in the list of SRv6 SIDs. */ +extern struct static_srv6_sid *static_srv6_sid_lookup(struct prefix_ipv6 *sid_addr); +/* + * Remove an SRv6 SID from the zebra RIB (if it was previously installed) and + * release the memory previously allocated for the SID. + */ +extern void static_srv6_sid_del(struct static_srv6_sid *sid); + +/* Initialize SRv6 data structures. */ +extern void static_srv6_init(void); +/* Clean up all the SRv6 data structures. */ +extern void static_srv6_cleanup(void); + +struct static_srv6_locator *static_srv6_locator_alloc(const char *name); +void static_srv6_locator_free(struct static_srv6_locator *locator); +struct static_srv6_locator *static_srv6_locator_lookup(const char *name); + +void delete_static_srv6_sid(void *val); +void delete_static_srv6_locator(void *val); + +#ifdef __cplusplus +} +#endif + +#endif /* __STATIC_SRV6_H__ */ diff --git a/staticd/subdir.am b/staticd/subdir.am index 07ebe3c02c..bdbacbdd68 100644 --- a/staticd/subdir.am +++ b/staticd/subdir.am @@ -19,6 +19,7 @@ staticd_libstatic_a_SOURCES = \ staticd/static_vty.c \ staticd/static_nb.c \ staticd/static_nb_config.c \ + staticd/static_srv6.c \ # end noinst_HEADERS += \ @@ -29,6 +30,7 @@ noinst_HEADERS += \ staticd/static_vty.h \ staticd/static_vrf.h \ staticd/static_nb.h \ + staticd/static_srv6.h \ # end clippy_scan += \ From 7aefc15f64d771db98f6982ea1f3a6393f2ea997 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Wed, 15 Jan 2025 13:16:21 +0100 Subject: [PATCH 05/15] lib, isisd: Move DEFAULT_SRV6_IFNAME to global srv6.h header This commit moves DEFAULT_SRV6_IFNAME from isis_srv6.h to srv6.h because there are other daemons that might want to use it (e.g. staticd). Signed-off-by: Yuqing Zhao --- isisd/isis_srv6.c | 2 +- isisd/isis_srv6.h | 3 +-- lib/srv6.h | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/isisd/isis_srv6.c b/isisd/isis_srv6.c index 2348bd043a..4b97b5372e 100644 --- a/isisd/isis_srv6.c +++ b/isisd/isis_srv6.c @@ -698,7 +698,7 @@ void isis_srv6_area_init(struct isis_area *area) srv6db->config.max_end_pop_msd = ISIS_DEFAULT_SRV6_MAX_END_POP_MSD; srv6db->config.max_h_encaps_msd = ISIS_DEFAULT_SRV6_MAX_H_ENCAPS_MSD; srv6db->config.max_end_d_msd = ISIS_DEFAULT_SRV6_MAX_END_D_MSD; - strlcpy(srv6db->config.srv6_ifname, ISIS_DEFAULT_SRV6_IFNAME, sizeof(srv6db->config.srv6_ifname)); + strlcpy(srv6db->config.srv6_ifname, DEFAULT_SRV6_IFNAME, sizeof(srv6db->config.srv6_ifname)); #endif /* Initialize SRv6 Locator chunks list */ diff --git a/isisd/isis_srv6.h b/isisd/isis_srv6.h index bde14965f6..eeb76c0b86 100644 --- a/isisd/isis_srv6.h +++ b/isisd/isis_srv6.h @@ -16,8 +16,7 @@ #define ISIS_DEFAULT_SRV6_MAX_SEG_LEFT_MSD 3 #define ISIS_DEFAULT_SRV6_MAX_END_POP_MSD 3 #define ISIS_DEFAULT_SRV6_MAX_H_ENCAPS_MSD 2 -#define ISIS_DEFAULT_SRV6_MAX_END_D_MSD 5 -#define ISIS_DEFAULT_SRV6_IFNAME "sr0" +#define ISIS_DEFAULT_SRV6_MAX_END_D_MSD 5 /* SRv6 SID structure */ struct isis_srv6_sid_structure { diff --git a/lib/srv6.h b/lib/srv6.h index 41e0a076f9..7e4fb97ad1 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -22,6 +22,8 @@ #define SRV6_SID_FORMAT_NAME_SIZE 512 +#define DEFAULT_SRV6_IFNAME "sr0" + #ifdef __cplusplus extern "C" { #endif From e80f24df7e4c47924f274749b14143b894df7d3b Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:03:48 +0100 Subject: [PATCH 06/15] staticd: Request/Release SIDs to SID Manager Signed-off-by: Yuqing Zhao --- staticd/static_zebra.c | 653 +++++++++++++++++++++++++++++++++++++++++ staticd/static_zebra.h | 10 + 2 files changed, 663 insertions(+) diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index d76befc131..6e8d275d6b 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -30,6 +30,9 @@ #include "static_nht.h" #include "static_vty.h" #include "static_debug.h" +#include "zclient.h" +#include "static_srv6.h" +#include "lib_errors.h" DEFINE_MTYPE_STATIC(STATIC, STATIC_NHT_DATA, "Static Nexthop tracking data"); PREDECL_HASH(static_nht_hash); @@ -530,10 +533,660 @@ extern void static_zebra_route_add(struct static_path *pn, bool install) zclient, &api); } +/** + * Send SRv6 SID to ZEBRA for installation or deletion. + * + * @param cmd ZEBRA_ROUTE_ADD or ZEBRA_ROUTE_DELETE + * @param sid SRv6 SID to install or delete + * @param prefixlen Prefix length + * @param oif Outgoing interface + * @param action SID action + * @param context SID context + */ +static void static_zebra_send_localsid(int cmd, const struct in6_addr *sid, uint16_t prefixlen, + ifindex_t oif, enum seg6local_action_t action, + const struct seg6local_context *context) +{ + struct prefix_ipv6 p = {}; + struct zapi_route api = {}; + struct zapi_nexthop *znh; + + if (cmd != ZEBRA_ROUTE_ADD && cmd != ZEBRA_ROUTE_DELETE) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong ZEBRA command", __func__); + return; + } + + if (prefixlen > IPV6_MAX_BITLEN) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong prefixlen %u", __func__, prefixlen); + return; + } + + DEBUGD(&static_dbg_srv6, "%s: |- %s SRv6 SID %pI6 behavior %s", __func__, + cmd == ZEBRA_ROUTE_ADD ? "Add" : "Delete", sid, seg6local_action2str(action)); + + p.family = AF_INET6; + p.prefixlen = prefixlen; + p.prefix = *sid; + + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_STATIC; + api.instance = 0; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, &p, sizeof(p)); + + if (cmd == ZEBRA_ROUTE_DELETE) + return (void)zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + + znh = &api.nexthops[0]; + + memset(znh, 0, sizeof(*znh)); + + znh->type = NEXTHOP_TYPE_IFINDEX; + znh->ifindex = oif; + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6LOCAL); + znh->seg6local_action = action; + memcpy(&znh->seg6local_ctx, context, sizeof(struct seg6local_context)); + + api.nexthop_num = 1; + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); +} + +/** + * Install SRv6 SID in the forwarding plane through Zebra. + * + * @param sid SRv6 SID + */ +void static_zebra_srv6_sid_install(struct static_srv6_sid *sid) +{ + enum seg6local_action_t action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; + struct seg6local_context ctx = {}; + struct interface *ifp = NULL; + struct vrf *vrf; + + if (!sid) + return; + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) + return; + + if (!sid->locator) { + zlog_err("Failed to install SID %pFX: missing locator information", &sid->addr); + return; + } + + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + action = ZEBRA_SEG6_LOCAL_ACTION_END; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + action = ZEBRA_SEG6_LOCAL_ACTION_END; + SET_SRV6_FLV_OP(ctx.flv.flv_ops, ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID); + ctx.flv.lcblock_len = sid->locator->block_bits_length; + ctx.flv.lcnode_func_len = sid->locator->node_bits_length; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ctx.table = vrf->data.l.table_id; + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ctx.table = vrf->data.l.table_id; + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ctx.table = vrf->data.l.table_id; + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + break; + } + + ctx.block_len = sid->locator->block_bits_length; + ctx.node_len = sid->locator->node_bits_length; + ctx.function_len = sid->locator->function_bits_length; + ctx.argument_len = sid->locator->argument_bits_length; + + /* Attach the SID to the SRv6 interface */ + if (!ifp) { + ifp = if_lookup_by_name(DEFAULT_SRV6_IFNAME, VRF_DEFAULT); + if (!ifp) { + zlog_warn("Failed to install SRv6 SID %pFX: %s interface not found", + &sid->addr, DEFAULT_SRV6_IFNAME); + return; + } + } + + /* Send the SID to zebra */ + static_zebra_send_localsid(ZEBRA_ROUTE_ADD, &sid->addr.prefix, sid->addr.prefixlen, + ifp->ifindex, action, &ctx); + + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); +} + +void static_zebra_srv6_sid_uninstall(struct static_srv6_sid *sid) +{ + enum seg6local_action_t action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; + struct interface *ifp = NULL; + struct seg6local_context ctx = {}; + struct vrf *vrf; + + if (!sid) + return; + + if (!CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) + return; + + if (!sid->locator) { + zlog_err("Failed to uninstall SID %pFX: missing locator information", &sid->addr); + return; + } + + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) { + zlog_warn("Failed to install SID %pFX: VRF %s is inactive", &sid->addr, + sid->attributes.vrf_name); + return; + } + ifp = if_get_vrf_loopback(vrf->vrf_id); + if (!ifp) { + zlog_warn("Failed to install SID %pFX: failed to get loopback for vrf %s", + &sid->addr, sid->attributes.vrf_name); + return; + } + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + break; + } + + /* The SID is attached to the SRv6 interface */ + if (!ifp) { + ifp = if_lookup_by_name(DEFAULT_SRV6_IFNAME, VRF_DEFAULT); + if (!ifp) { + zlog_warn("%s interface not found: nothing to uninstall", + DEFAULT_SRV6_IFNAME); + return; + } + } + + ctx.block_len = sid->locator->block_bits_length; + ctx.node_len = sid->locator->node_bits_length; + ctx.function_len = sid->locator->function_bits_length; + ctx.argument_len = sid->locator->argument_bits_length; + + static_zebra_send_localsid(ZEBRA_ROUTE_DELETE, &sid->addr.prefix, sid->addr.prefixlen, + ifp->ifindex, action, &ctx); + + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); +} + +extern void static_zebra_request_srv6_sid(struct static_srv6_sid *sid) +{ + struct srv6_sid_ctx ctx = {}; + int ret = 0; + struct vrf *vrf; + + if (!sid) + return; + + /* convert `srv6_endpoint_behavior_codepoint` to `seg6local_action_t` */ + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + return; + } + + /* Request SRv6 SID from SID Manager */ + ret = srv6_manager_get_sid(zclient, &ctx, &sid->addr.prefix, sid->locator->name, NULL); + if (ret < 0) + zlog_warn("%s: error getting SRv6 SID!", __func__); +} + +extern void static_zebra_release_srv6_sid(struct static_srv6_sid *sid) +{ + struct srv6_sid_ctx ctx = {}; + struct vrf *vrf; + int ret = 0; + + if (!sid || !CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) + return; + + /* convert `srv6_endpoint_behavior_codepoint` to `seg6local_action_t` */ + switch (sid->behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END; + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + /* process SRv6 SID attributes */ + /* generate table ID from the VRF name, if configured */ + if (sid->attributes.vrf_name[0] != '\0') { + vrf = vrf_lookup_by_name(sid->attributes.vrf_name); + if (!vrf_is_enabled(vrf)) + return; + ctx.vrf_id = vrf->vrf_id; + } + + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + zlog_warn("unsupported behavior: %u", sid->behavior); + return; + } + + /* remove the SRv6 SID from the zebra RIB */ + ret = srv6_manager_release_sid(zclient, &ctx); + if (ret == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, "zclient_send_get_srv6_sid() delete failed: %s", + safe_strerror(errno)); +} + +/** + * Ask the SRv6 Manager (zebra) about a specific locator + * + * @param name Locator name + * @return 0 on success, -1 otherwise + */ +int static_zebra_srv6_manager_get_locator(const char *name) +{ + if (!name) + return -1; + + /* + * Send the Get Locator request to the SRv6 Manager and return the + * result + */ + return srv6_manager_get_locator(zclient, name); +} + +static void request_srv6_sids(struct static_srv6_locator *locator) +{ + struct static_srv6_sid *sid; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if (sid->locator == locator) + static_zebra_request_srv6_sid(sid); + } +} + +/** + * Internal function to process an SRv6 locator + * + * @param locator The locator to be processed + */ +static int static_zebra_process_srv6_locator_internal(struct srv6_locator *locator) +{ + struct static_srv6_locator *loc; + struct listnode *node; + struct static_srv6_sid *sid; + + if (!locator) + return -1; + + DEBUGD(&static_dbg_srv6, + "%s: Received SRv6 locator %s %pFX, loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u", + __func__, locator->name, &locator->prefix, locator->block_bits_length, + locator->node_bits_length, locator->function_bits_length, + locator->argument_bits_length); + + /* If we are already aware about the locator, nothing to do */ + loc = static_srv6_locator_lookup(locator->name); + if (loc) + return 0; + + loc = static_srv6_locator_alloc(locator->name); + + DEBUGD(&static_dbg_srv6, "%s: SRv6 locator (locator %s, prefix %pFX) set", __func__, + locator->name, &locator->prefix); + + /* Store the locator prefix */ + loc->prefix = locator->prefix; + loc->block_bits_length = locator->block_bits_length; + loc->node_bits_length = locator->node_bits_length; + loc->function_bits_length = locator->function_bits_length; + loc->argument_bits_length = locator->argument_bits_length; + loc->flags = locator->flags; + + listnode_add(srv6_locators, loc); + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if (strncmp(sid->locator_name, loc->name, sizeof(loc->name)) == 0) + sid->locator = loc; + } + + /* Request SIDs from the locator */ + request_srv6_sids(loc); + + return 0; +} + +/** + * Callback to process an SRv6 locator received from SRv6 Manager (zebra). + * + * @result 0 on success, -1 otherwise + */ +static int static_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + + if (!srv6_locators) + return -1; + + /* Decode the SRv6 locator */ + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + return static_zebra_process_srv6_locator_internal(&loc); +} + +/** + * Callback to process a notification from SRv6 Manager (zebra) of an SRv6 + * locator deleted. + * + * @result 0 on success, -1 otherwise + */ +static int static_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + struct listnode *node2, *nnode2; + struct static_srv6_sid *sid; + struct static_srv6_locator *locator; + + if (!srv6_locators) + return -1; + + /* Decode the received zebra message */ + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + DEBUGD(&static_dbg_srv6, + "%s: SRv6 locator deleted in zebra: name %s, prefix %pFX, block_len %u, node_len %u, func_len %u, arg_len %u", + __func__, loc.name, &loc.prefix, loc.block_bits_length, loc.node_bits_length, + loc.function_bits_length, loc.argument_bits_length); + + locator = static_srv6_locator_lookup(loc.name); + if (!locator) + return 0; + + DEBUGD(&static_dbg_srv6, "%s: Deleting srv6 sids from locator %s", __func__, locator->name); + + /* Delete SRv6 SIDs */ + for (ALL_LIST_ELEMENTS(srv6_sids, node2, nnode2, sid)) { + if (sid->locator != locator) + continue; + + + DEBUGD(&static_dbg_srv6, "%s: Deleting SRv6 SID (locator %s, sid %pFX)", __func__, + locator->name, &sid->addr); + + /* + * Uninstall the SRv6 SID from the forwarding plane + * through Zebra + */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + } + + listnode_delete(srv6_locators, locator); + static_srv6_locator_free(locator); + + return 0; +} + +static int static_zebra_srv6_sid_notify(ZAPI_CALLBACK_ARGS) +{ + struct srv6_sid_ctx ctx; + struct in6_addr sid_addr; + enum zapi_srv6_sid_notify note; + uint32_t sid_func; + struct listnode *node; + char buf[256]; + struct static_srv6_sid *sid = NULL; + char *loc_name; + bool found = false; + + if (!srv6_locators) + return -1; + + /* Decode the received notification message */ + if (!zapi_srv6_sid_notify_decode(zclient->ibuf, &ctx, &sid_addr, &sid_func, NULL, ¬e, + &loc_name)) { + zlog_err("%s : error in msg decode", __func__); + return -1; + } + + DEBUGD(&static_dbg_srv6, + "%s: received SRv6 SID notify: ctx %s sid_value %pI6 sid_func %u note %s", __func__, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx), &sid_addr, sid_func, + zapi_srv6_sid_notify2str(note)); + + /* Handle notification */ + switch (note) { + case ZAPI_SRV6_SID_ALLOCATED: + + DEBUGD(&static_dbg_srv6, "%s: SRv6 SID %pI6 %s ALLOCATED", __func__, &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if (IPV6_ADDR_SAME(&sid->addr.prefix, &sid_addr)) { + found = true; + break; + } + } + + if (!found || !sid) { + zlog_err("SRv6 SID %pI6 %s: not found", &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + return 0; + } + + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + + /* + * Install the new SRv6 End SID in the forwarding plane through + * Zebra + */ + static_zebra_srv6_sid_install(sid); + + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + + break; + case ZAPI_SRV6_SID_RELEASED: + + DEBUGD(&static_dbg_srv6, "%s: SRv6 SID %pI6 %s: RELEASED", __func__, &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + + break; + case ZAPI_SRV6_SID_FAIL_ALLOC: + zlog_err("SRv6 SID %pI6 %s: Failed to allocate", &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + /* Error will be logged by zebra module */ + break; + case ZAPI_SRV6_SID_FAIL_RELEASE: + zlog_err("%s: SRv6 SID %pI6 %s failure to release", __func__, &sid_addr, + srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + + /* Error will be logged by zebra module */ + break; + } + + return 0; +} + static zclient_handler *const static_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete, [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner, + [ZEBRA_SRV6_LOCATOR_ADD] = static_zebra_process_srv6_locator_add, + [ZEBRA_SRV6_LOCATOR_DELETE] = static_zebra_process_srv6_locator_delete, + [ZEBRA_SRV6_SID_NOTIFY] = static_zebra_srv6_sid_notify, }; void static_zebra_init(void) diff --git a/staticd/static_zebra.h b/staticd/static_zebra.h index c4f4ebdcbc..2a94c6dad9 100644 --- a/staticd/static_zebra.h +++ b/staticd/static_zebra.h @@ -7,6 +7,8 @@ #ifndef __STATIC_ZEBRA_H__ #define __STATIC_ZEBRA_H__ +#include "static_srv6.h" + #ifdef __cplusplus extern "C" { #endif @@ -22,6 +24,14 @@ extern void static_zebra_stop(void); extern void static_zebra_vrf_register(struct vrf *vrf); extern void static_zebra_vrf_unregister(struct vrf *vrf); +extern int static_zebra_srv6_manager_get_locator(const char *name); + +extern void static_zebra_request_srv6_sid(struct static_srv6_sid *sid); +extern void static_zebra_release_srv6_sid(struct static_srv6_sid *sid); + +extern void static_zebra_srv6_sid_install(struct static_srv6_sid *sid); +extern void static_zebra_srv6_sid_uninstall(struct static_srv6_sid *sid); + #ifdef __cplusplus } #endif From 33615773c45b791941d925e5a37af53b5a55b58d Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:04:19 +0100 Subject: [PATCH 07/15] staticd: Install SIDs when a dependent interface goes up/down Signed-off-by: Yuqing Zhao --- staticd/static_srv6.c | 40 ++++++++++++++++++++++++++++++++++++++++ staticd/static_srv6.h | 9 +++++++++ staticd/static_zebra.c | 4 ++++ 3 files changed, 53 insertions(+) diff --git a/staticd/static_srv6.c b/staticd/static_srv6.c index d8e55894fe..032bb9de9f 100644 --- a/staticd/static_srv6.c +++ b/staticd/static_srv6.c @@ -14,6 +14,7 @@ #include "static_srv6.h" #include "static_vrf.h" #include "static_zebra.h" +#include "static_debug.h" /* * List of SRv6 SIDs. @@ -24,6 +25,45 @@ struct list *srv6_sids; DEFINE_MTYPE_STATIC(STATIC, STATIC_SRV6_LOCATOR, "Static SRv6 locator"); DEFINE_MTYPE_STATIC(STATIC, STATIC_SRV6_SID, "Static SRv6 SID"); +/* + * When an interface is enabled in the kernel, go through all the static SRv6 SIDs in + * the system that use this interface and install/remove them in the zebra RIB. + * + * ifp - The interface being enabled + * is_up - Whether the interface is up or down + */ +void static_ifp_srv6_sids_update(struct interface *ifp, bool is_up) +{ + struct static_srv6_sid *sid; + struct listnode *node; + + if (!srv6_sids || !ifp) + return; + + DEBUGD(&static_dbg_srv6, "%s: Interface %s %s. %s SIDs that depend on the interface", + __func__, (is_up) ? "enabled" : "disabled", (is_up) ? "Removing" : "disabled", + ifp->name); + + /* + * iterate over the list of SRv6 SIDs and remove the SIDs that use this + * VRF from the zebra RIB + */ + for (ALL_LIST_ELEMENTS_RO(srv6_sids, node, sid)) { + if ((strcmp(sid->attributes.vrf_name, ifp->name) == 0) || + (strncmp(ifp->name, DEFAULT_SRV6_IFNAME, sizeof(ifp->name)) == 0 && + (sid->behavior == SRV6_ENDPOINT_BEHAVIOR_END || + sid->behavior == SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID))) { + if (is_up) { + static_zebra_srv6_sid_install(sid); + SET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } else { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + } + } +} + /* * Allocate an SRv6 SID object and initialize the fields common to all the * behaviors (i.e., SID address and behavor). diff --git a/staticd/static_srv6.h b/staticd/static_srv6.h index bed0ab7fa5..48986092ae 100644 --- a/staticd/static_srv6.h +++ b/staticd/static_srv6.h @@ -85,6 +85,15 @@ extern void static_srv6_init(void); /* Clean up all the SRv6 data structures. */ extern void static_srv6_cleanup(void); +/* + * When an interface is enabled in the kernel, go through all the static SRv6 SIDs in + * the system that use this interface and install/remove them in the zebra RIB. + * + * ifp - The interface being enabled + * is_up - Whether the interface is up or down + */ +void static_ifp_srv6_sids_update(struct interface *ifp, bool is_up); + struct static_srv6_locator *static_srv6_locator_alloc(const char *name); void static_srv6_locator_free(struct static_srv6_locator *locator); struct static_srv6_locator *static_srv6_locator_lookup(const char *name); diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 6e8d275d6b..e87eaed008 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -116,6 +116,8 @@ static int static_ifp_up(struct interface *ifp) { static_ifindex_update(ifp, true); + static_ifp_srv6_sids_update(ifp, true); + return 0; } @@ -123,6 +125,8 @@ static int static_ifp_down(struct interface *ifp) { static_ifindex_update(ifp, false); + static_ifp_srv6_sids_update(ifp, false); + return 0; } From 73fdae9bea086006f164c689ebd0f0f623808f28 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:04:38 +0100 Subject: [PATCH 08/15] staticd: Initialize/cleanup SRv6 Signed-off-by: Yuqing Zhao --- staticd/static_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/staticd/static_main.c b/staticd/static_main.c index 5e74326e38..3b59ca6a75 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -26,6 +26,7 @@ #include "static_zebra.h" #include "static_debug.h" #include "static_nb.h" +#include "static_srv6.h" #include "mgmt_be_client.h" @@ -76,6 +77,10 @@ static void sigint(void) static_vrf_terminate(); static_zebra_stop(); + + /* clean up SRv6 data structures */ + static_srv6_cleanup(); + frr_fini(); exit(0); @@ -161,6 +166,9 @@ int main(int argc, char **argv, char **envp) static_debug_init(); static_vrf_init(); + /* initialize SRv6 data structures */ + static_srv6_init(); + static_zebra_init(); static_vty_init(); From 07ba1cd8b8798958d533c607747c221b3740d965 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:10:20 +0100 Subject: [PATCH 09/15] staticd: Add Northbound APIs for SRv6 Add Northbound APIs to create/modify/destroy an SRv6 SID Signed-off-by: Yuqing Zhao --- staticd/static_nb.c | 29 +++++ staticd/static_nb.h | 30 +++++ staticd/static_nb_config.c | 222 +++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) diff --git a/staticd/static_nb.c b/staticd/static_nb.c index e6aa71a77b..356324126a 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -224,6 +224,35 @@ const struct frr_yang_module_info frr_staticd_info = { .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy, } }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid", + .cbs = { + .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish, + .create = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid/behavior", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid/vrf-name", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid/locator-name", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_destroy, + } + }, { .xpath = NULL, }, diff --git a/staticd/static_nb.h b/staticd/static_nb.h index be75d9d38c..9cf4126ba5 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -118,6 +118,34 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr struct nb_cb_modify_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_destroy( + struct nb_cb_destroy_args *args); /* Optional 'apply_finish' callbacks. */ @@ -125,6 +153,8 @@ void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_p struct nb_cb_apply_finish_args *args); void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( struct nb_cb_apply_finish_args *args); +void routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish( + struct nb_cb_apply_finish_args *args); /* Optional 'pre_validate' callbacks. */ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate( diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index 7de5f0474a..51de05c2ea 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -20,6 +20,9 @@ #include "static_nb.h" #include "static_zebra.h" +#include "static_srv6.h" +#include "static_debug.h" + static int static_path_list_create(struct nb_cb_create_args *args) { @@ -1367,3 +1370,222 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr return NB_OK; } + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create( + struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_destroy( + struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6 + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_create( + struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_destroy( + struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_create( + struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_destroy( + struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_create( + struct nb_cb_create_args *args) +{ + struct static_srv6_sid *sid; + struct prefix_ipv6 sid_value; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + yang_dnode_get_ipv6p(&sid_value, args->dnode, "sid"); + sid = static_srv6_sid_alloc(&sid_value); + nb_running_set_entry(args->dnode, sid); + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_destroy( + struct nb_cb_destroy_args *args) +{ + struct static_srv6_sid *sid; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + sid = nb_running_unset_entry(args->dnode); + listnode_delete(srv6_sids, sid); + static_srv6_sid_del(sid); + + return NB_OK; +} + +void routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct static_srv6_sid *sid; + struct static_srv6_locator *locator; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + locator = static_srv6_locator_lookup(sid->locator_name); + if (!locator) { + DEBUGD(&static_dbg_srv6, + "%s: Locator %s not found, trying to get locator information from zebra", + __func__, sid->locator_name); + static_zebra_srv6_manager_get_locator(sid->locator_name); + listnode_add(srv6_sids, sid); + return; + } + + sid->locator = locator; + + listnode_add(srv6_sids, sid); + static_zebra_request_srv6_sid(sid); +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid/behavior + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_modify( + struct nb_cb_modify_args *args) +{ + struct static_srv6_sid *sid; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + /* Release and uninstall existing SID, if any, before requesting the new one */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + + sid->behavior = yang_dnode_get_enum(args->dnode, "../behavior"); + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_behavior_destroy( + struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid/vrf-name + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_modify( + struct nb_cb_modify_args *args) +{ + struct static_srv6_sid *sid; + const char *vrf_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + /* Release and uninstall existing SID, if any, before requesting the new one */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + + vrf_name = yang_dnode_get_string(args->dnode, "../vrf-name"); + snprintf(sid->attributes.vrf_name, sizeof(sid->attributes.vrf_name), "%s", vrf_name); + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_vrf_name_destroy( + struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/locators/locator/static-sids/sid/vrf-name + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_modify( + struct nb_cb_modify_args *args) +{ + struct static_srv6_sid *sid; + const char *loc_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + sid = nb_running_get_entry(args->dnode, NULL, true); + + /* Release and uninstall existing SID, if any, before requesting the new one */ + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID)) { + static_zebra_release_srv6_sid(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_VALID); + } + + if (CHECK_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA)) { + static_zebra_srv6_sid_uninstall(sid); + UNSET_FLAG(sid->flags, STATIC_FLAG_SRV6_SID_SENT_TO_ZEBRA); + } + + loc_name = yang_dnode_get_string(args->dnode, "../locator-name"); + snprintf(sid->locator_name, sizeof(sid->locator_name), "%s", loc_name); + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_locator_name_destroy( + struct nb_cb_destroy_args *args) +{ + return NB_OK; +} From 4d958078b896404f19257985b48c4d066ea2b2ed Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:10:48 +0100 Subject: [PATCH 10/15] lib: Add CLI node for SRv6 static SIDs Signed-off-by: Yuqing Zhao --- lib/command.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/command.h b/lib/command.h index c60751789f..dfd732893b 100644 --- a/lib/command.h +++ b/lib/command.h @@ -154,6 +154,7 @@ enum node_type { PCEP_PCE_NODE, /* PCE configuration node */ PCEP_PCC_NODE, /* PCC configuration node */ SRV6_NODE, /* SRv6 node */ + SRV6_SIDS_NODE, /* SRv6 SIDs node */ SRV6_LOCS_NODE, /* SRv6 locators node */ SRV6_LOC_NODE, /* SRv6 locator node */ SRV6_ENCAP_NODE, /* SRv6 encapsulation node */ From 69a49c7359c4235f60453d67b94a27c1aab94dd3 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:13:07 +0100 Subject: [PATCH 11/15] staticd: Add CLI for SRv6 static SIDs Signed-off-by: Yuqing Zhao --- staticd/static_nb.h | 18 ++++ staticd/static_vty.c | 203 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) diff --git a/staticd/static_nb.h b/staticd/static_nb.h index 9cf4126ba5..d11bf5363b 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -236,6 +236,24 @@ int routing_control_plane_protocols_name_validate( FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \ FRR_STATIC_ROUTE_NH_KEY_XPATH +/* srv6 */ +#define FRR_STATIC_SRV6_INFO_KEY_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/segment-routing/srv6" + +/* srv6/static-sids */ +#define FRR_STATIC_SRV6_SID_KEY_XPATH \ + FRR_STATIC_SRV6_INFO_KEY_XPATH \ + "/static-sids/" \ + "sid[sid='%s']" + +#define FRR_STATIC_SRV6_SID_BEHAVIOR_XPATH "/behavior" + +#define FRR_STATIC_SRV6_SID_VRF_NAME_XPATH "/vrf-name" + +#define FRR_STATIC_SRV6_SID_LOCATOR_NAME_XPATH "/locator-name" + #ifdef __cplusplus } #endif diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 68a4ef20c1..fda8b14eb8 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -27,6 +27,8 @@ #include "static_debug.h" #include "staticd/static_vty_clippy.c" #include "static_nb.h" +#include "static_srv6.h" +#include "static_zebra.h" #define STATICD_STR "Static route daemon\n" @@ -1201,8 +1203,167 @@ DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd, return static_route_nb_run(vty, &args); } +DEFUN_NOSH (static_segment_routing, static_segment_routing_cmd, + "segment-routing", + "Segment Routing\n") +{ + VTY_PUSH_CONTEXT_NULL(SEGMENT_ROUTING_NODE); + return CMD_SUCCESS; +} + +DEFUN_NOSH (static_srv6, static_srv6_cmd, + "srv6", + "Segment Routing SRv6\n") +{ + VTY_PUSH_CONTEXT_NULL(SRV6_NODE); + return CMD_SUCCESS; +} + +DEFUN_YANG_NOSH (no_static_srv6, no_static_srv6_cmd, + "no srv6", + NO_STR + "Segment Routing SRv6\n") +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), FRR_STATIC_SRV6_INFO_KEY_XPATH, "frr-staticd:staticd", + "staticd", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, "%s", xpath); +} + +DEFUN_NOSH (static_srv6_sids, static_srv6_sids_cmd, + "static-sids", + "Segment Routing SRv6 SIDs\n") +{ + VTY_PUSH_CONTEXT_NULL(SRV6_SIDS_NODE); + return CMD_SUCCESS; +} + +DEFPY_YANG(srv6_sid, srv6_sid_cmd, + "sid X:X::X:X/M locator NAME$locator_name behavior ", + "Configure SRv6 SID\n" + "Specify SRv6 SID\n" + "Locator name\n" + "Specify Locator name\n" + "Specify SRv6 SID behavior\n" + "Apply the code to a uN SID\n" + "Apply the code to an uDT6 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT4 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT46 SID\n" + "Configure VRF name\n" + "Specify VRF name\n") +{ + enum srv6_endpoint_behavior_codepoint behavior = SRV6_ENDPOINT_BEHAVIOR_RESERVED; + int idx = 0; + const char *vrf_name = NULL; + char xpath_srv6[XPATH_MAXLEN]; + char xpath_sid[XPATH_MAXLEN]; + char xpath_behavior[XPATH_MAXLEN]; + char xpath_vrf_name[XPATH_MAXLEN]; + char xpath_locator_name[XPATH_MAXLEN]; + + if (argv_find(argv, argc, "uN", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID; + } else if (argv_find(argv, argc, "uDT6", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID; + vrf_name = argv[idx + 2]->arg; + } else if (argv_find(argv, argc, "uDT4", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID; + vrf_name = argv[idx + 2]->arg; + } else if (argv_find(argv, argc, "uDT46", &idx)) { + behavior = SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID; + vrf_name = argv[idx + 2]->arg; + } + + snprintf(xpath_srv6, sizeof(xpath_srv6), FRR_STATIC_SRV6_INFO_KEY_XPATH, + "frr-staticd:staticd", "staticd", VRF_DEFAULT_NAME); + + snprintf(xpath_sid, sizeof(xpath_sid), FRR_STATIC_SRV6_SID_KEY_XPATH, "frr-staticd:staticd", + "staticd", VRF_DEFAULT_NAME, sid_str); + + strlcpy(xpath_behavior, xpath_sid, sizeof(xpath_behavior)); + strlcat(xpath_behavior, FRR_STATIC_SRV6_SID_BEHAVIOR_XPATH, sizeof(xpath_behavior)); + + nb_cli_enqueue_change(vty, xpath_sid, NB_OP_CREATE, sid_str); + + nb_cli_enqueue_change(vty, xpath_behavior, NB_OP_MODIFY, + srv6_endpoint_behavior_codepoint2str(behavior)); + + if (vrf_name) { + strlcpy(xpath_vrf_name, xpath_sid, sizeof(xpath_vrf_name)); + strlcat(xpath_vrf_name, FRR_STATIC_SRV6_SID_VRF_NAME_XPATH, sizeof(xpath_vrf_name)); + + nb_cli_enqueue_change(vty, xpath_vrf_name, NB_OP_MODIFY, vrf_name); + } + + strlcpy(xpath_locator_name, xpath_sid, sizeof(xpath_locator_name)); + strlcat(xpath_locator_name, FRR_STATIC_SRV6_SID_LOCATOR_NAME_XPATH, + sizeof(xpath_locator_name)); + + nb_cli_enqueue_change(vty, xpath_locator_name, NB_OP_MODIFY, locator_name); + + return nb_cli_apply_changes(vty, "%s", xpath_sid); +} + +DEFPY_YANG(no_srv6_sid, no_srv6_sid_cmd, + "no sid X:X::X:X/M [locator NAME$locator_name] [behavior ]", + NO_STR + "Configure SRv6 SID\n" + "Specify SRv6 SID\n" + "Locator name\n" + "Specify Locator name\n" + "Specify SRv6 SID behavior\n" + "Apply the code to a uN SID\n" + "Apply the code to an uDT6 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT4 SID\n" + "Configure VRF name\n" + "Specify VRF name\n" + "Apply the code to an uDT46 SID\n" + "Configure VRF name\n" + "Specify VRF name\n") +{ + char xpath[XPATH_MAXLEN + 37]; + + snprintf(xpath, sizeof(xpath), FRR_STATIC_SRV6_INFO_KEY_XPATH, "frr-staticd:staticd", + "staticd", VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + #ifdef INCLUDE_MGMTD_CMDDEFS_ONLY +static struct cmd_node sr_node = { + .name = "sr", + .node = SEGMENT_ROUTING_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-sr)# ", +}; + +static struct cmd_node srv6_node = { + .name = "srv6", + .node = SRV6_NODE, + .parent_node = SEGMENT_ROUTING_NODE, + .prompt = "%s(config-srv6)# ", +}; + +static struct cmd_node srv6_sids_node = { + .name = "srv6-sids", + .node = SRV6_SIDS_NODE, + .parent_node = SRV6_NODE, + .prompt = "%s(config-srv6-sids)# ", +}; + static void static_cli_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { @@ -1594,6 +1755,33 @@ const struct frr_yang_module_info frr_staticd_cli_info = { .cli_cmp = static_nexthop_cli_cmp, } }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing", + .cbs = { + .cli_show = static_segment_routing_cli_show, + .cli_show_end = static_segment_routing_cli_show_end, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6", + .cbs = { + .cli_show = static_srv6_cli_show, + .cli_show_end = static_srv6_cli_show_end, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/local-sids", + .cbs = { + .cli_show = static_sids_cli_show, + .cli_show_end = static_sids_cli_show_end, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/local-sids/sid", + .cbs = { + .cli_show = static_srv6_sid_cli_show, + } + }, { .xpath = NULL, }, @@ -1670,6 +1858,21 @@ void static_vty_init(void) install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd); install_element(CONFIG_NODE, &ipv6_route_cmd); install_element(VRF_NODE, &ipv6_route_vrf_cmd); + + install_node(&sr_node); + install_node(&srv6_node); + install_node(&srv6_sids_node); + install_default(SEGMENT_ROUTING_NODE); + install_default(SRV6_NODE); + install_default(SRV6_SIDS_NODE); + + install_element(CONFIG_NODE, &static_segment_routing_cmd); + install_element(SEGMENT_ROUTING_NODE, &static_srv6_cmd); + install_element(SEGMENT_ROUTING_NODE, &no_static_srv6_cmd); + install_element(SRV6_NODE, &static_srv6_sids_cmd); + install_element(SRV6_SIDS_NODE, &srv6_sid_cmd); + install_element(SRV6_SIDS_NODE, &no_srv6_sid_cmd); + #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY From 2d23207290362164d836bc6fe664515d08b82010 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:13:38 +0100 Subject: [PATCH 12/15] staticd: Add CLIs to show SRv6 static SIDs Signed-off-by: Yuqing Zhao --- staticd/static_vty.c | 98 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/staticd/static_vty.c b/staticd/static_vty.c index fda8b14eb8..2fadc1f0d4 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -1706,6 +1706,100 @@ static int static_path_list_cli_cmp(const struct lyd_node *dnode1, return (int)distance1 - (int)distance2; } +static void static_segment_routing_cli_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, "segment-routing\n"); +} + +static void static_segment_routing_cli_show_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); + vty_out(vty, "!\n"); +} + +static void static_srv6_cli_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " srv6\n"); +} + +static void static_srv6_cli_show_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, " exit\n"); + vty_out(vty, " !\n"); +} + +static void static_sids_cli_show(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) +{ + vty_out(vty, " static-sids\n"); +} + +static void static_sids_cli_show_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, " exit\n"); + vty_out(vty, " !\n"); +} + +static void srv6_sid_cli_show(struct vty *vty, const struct lyd_node *sid, bool show_defaults) +{ + enum srv6_endpoint_behavior_codepoint srv6_behavior; + struct prefix_ipv6 sid_value; + + yang_dnode_get_ipv6p(&sid_value, sid, "sid"); + + vty_out(vty, " sid %pFX", &sid_value); + vty_out(vty, " locator %s", yang_dnode_get_string(sid, "locator-name")); + + srv6_behavior = yang_dnode_get_enum(sid, "behavior"); + switch (srv6_behavior) { + case SRV6_ENDPOINT_BEHAVIOR_END: + vty_out(vty, " behavior End"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X: + vty_out(vty, " behavior End.X"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6: + vty_out(vty, " behavior End.DT6"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4: + vty_out(vty, " behavior End.DT4"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46: + vty_out(vty, " behavior End.DT46"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: + vty_out(vty, " behavior uN"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: + vty_out(vty, " behavior uA"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: + vty_out(vty, " behavior uDT6"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: + vty_out(vty, " behavior uDT4"); + break; + case SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID: + vty_out(vty, " behavior uDT46"); + break; + case SRV6_ENDPOINT_BEHAVIOR_RESERVED: + case SRV6_ENDPOINT_BEHAVIOR_OPAQUE: + vty_out(vty, " behavior unknown"); + break; + } + + if (yang_dnode_exists(sid, "vrf-name")) + vty_out(vty, " vrf %s", yang_dnode_get_string(sid, "vrf-name")); + + vty_out(vty, "\n"); +} + +static void static_srv6_sid_cli_show(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + srv6_sid_cli_show(vty, dnode, show_defaults); +} + const struct frr_yang_module_info frr_staticd_cli_info = { .name = "frr-staticd", .ignore_cfg_cbs = true, @@ -1770,14 +1864,14 @@ const struct frr_yang_module_info frr_staticd_cli_info = { } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/local-sids", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids", .cbs = { .cli_show = static_sids_cli_show, .cli_show_end = static_sids_cli_show_end, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/local-sids/sid", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid", .cbs = { .cli_show = static_srv6_sid_cli_show, } From 52c4028195a66975e5f38248c70f5d70747755e6 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:14:04 +0100 Subject: [PATCH 13/15] vtysh: Add SRv6 static SIDs CLI to vtysh Signed-off-by: Yuqing Zhao --- vtysh/vtysh.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c460dea70c..a1b16c2b66 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1312,6 +1312,13 @@ static struct cmd_node srv6_node = { .prompt = "%s(config-srv6)# ", }; +static struct cmd_node srv6_sids_node = { + .name = "srv6-sids", + .node = SRV6_SIDS_NODE, + .parent_node = SRV6_NODE, + .prompt = "%s(config-srv6-sids)# ", +}; + static struct cmd_node srv6_locs_node = { .name = "srv6-locators", .node = SRV6_LOCS_NODE, @@ -1685,7 +1692,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_end_all, vtysh_end_all_cmd, "end", return vtysh_end(); } -DEFUNSH(VTYSH_ZEBRA, srv6, srv6_cmd, +DEFUNSH(VTYSH_ZEBRA | VTYSH_MGMTD, srv6, srv6_cmd, "srv6", "Segment-Routing SRv6 configuration\n") { @@ -1693,6 +1700,14 @@ DEFUNSH(VTYSH_ZEBRA, srv6, srv6_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_MGMTD, srv6_sids, srv6_sids_cmd, + "static-sids", + "Segment-Routing SRv6 SIDs configuration\n") +{ + vty->node = SRV6_SIDS_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ZEBRA, srv6_locators, srv6_locators_cmd, "locators", "Segment-Routing SRv6 locators configuration\n") @@ -2216,7 +2231,7 @@ DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfab } #endif /* HAVE_FABRICD */ -DEFUNSH(VTYSH_SR, segment_routing, segment_routing_cmd, +DEFUNSH(VTYSH_SR | VTYSH_MGMTD, segment_routing, segment_routing_cmd, "segment-routing", "Configure segment routing\n") { @@ -2535,7 +2550,7 @@ DEFUNSH(VTYSH_VRF, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf", return CMD_SUCCESS; } -DEFUNSH(VTYSH_ZEBRA, exit_srv6_config, exit_srv6_config_cmd, "exit", +DEFUNSH(VTYSH_ZEBRA | VTYSH_MGMTD, exit_srv6_config, exit_srv6_config_cmd, "exit", "Exit from SRv6 configuration mode\n") { if (vty->node == SRV6_NODE) @@ -2551,6 +2566,14 @@ DEFUNSH(VTYSH_ZEBRA, exit_srv6_locs_config, exit_srv6_locs_config_cmd, "exit", return CMD_SUCCESS; } +DEFUNSH(VTYSH_MGMTD, exit_srv6_sids_config, exit_srv6_sids_config_cmd, "exit", + "Exit from SRv6-SIDs configuration mode\n") +{ + if (vty->node == SRV6_SIDS_NODE) + vty->node = SRV6_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ZEBRA, exit_srv6_loc_config, exit_srv6_loc_config_cmd, "exit", "Exit from SRv6-locators configuration mode\n") { @@ -2806,13 +2829,13 @@ DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit", return vtysh_exit_keys(self, vty, argc, argv); } -DEFUNSH(VTYSH_SR, vtysh_exit_sr, vtysh_exit_sr_cmd, "exit", +DEFUNSH(VTYSH_SR | VTYSH_MGMTD, vtysh_exit_sr, vtysh_exit_sr_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_SR, vtysh_quit_sr, vtysh_quit_sr_cmd, "quit", +DEFUNSH(VTYSH_SR | VTYSH_MGMTD, vtysh_quit_sr, vtysh_quit_sr_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); @@ -4999,6 +5022,7 @@ void vtysh_init_vty(void) install_node(&rmap_node); install_node(&vty_node); install_node(&srv6_node); + install_node(&srv6_sids_node); install_node(&srv6_locs_node); install_node(&srv6_loc_node); install_node(&srv6_encap_node); @@ -5442,6 +5466,10 @@ void vtysh_init_vty(void) install_element(SRV6_NODE, &exit_srv6_config_cmd); install_element(SRV6_NODE, &vtysh_end_all_cmd); install_element(SRV6_NODE, &srv6_encap_cmd); + install_element(SRV6_NODE, &srv6_sids_cmd); + + install_element(SRV6_SIDS_NODE, &exit_srv6_sids_config_cmd); + install_element(SRV6_SIDS_NODE, &vtysh_end_all_cmd); install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); install_element(SRV6_LOCS_NODE, &exit_srv6_locs_config_cmd); From 2a50c7526af063cdb4b6fc086ada8dd0d7a9dcd5 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:27:36 +0100 Subject: [PATCH 14/15] tests: Add topotest for SRv6 static SIDs Signed-off-by: Yuqing Zhao --- tests/topotests/static_srv6_sids/__init__.py | 0 .../static_srv6_sids/expected_srv6_sids.json | 107 ++++++++++++++++++ tests/topotests/static_srv6_sids/r1/frr.conf | 16 +++ tests/topotests/static_srv6_sids/r1/setup.sh | 13 +++ .../static_srv6_sids/test_static_srv6_sids.py | 83 ++++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 tests/topotests/static_srv6_sids/__init__.py create mode 100644 tests/topotests/static_srv6_sids/expected_srv6_sids.json create mode 100644 tests/topotests/static_srv6_sids/r1/frr.conf create mode 100644 tests/topotests/static_srv6_sids/r1/setup.sh create mode 100755 tests/topotests/static_srv6_sids/test_static_srv6_sids.py diff --git a/tests/topotests/static_srv6_sids/__init__.py b/tests/topotests/static_srv6_sids/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids.json b/tests/topotests/static_srv6_sids/expected_srv6_sids.json new file mode 100644 index 0000000000..e1a2a16afe --- /dev/null +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids.json @@ -0,0 +1,107 @@ +{ + "fcbb:bbbb:1:fe10::/64": [ + { + "prefix": "fcbb:bbbb:1:fe10::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf10", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT4" + }, + "seg6localContext": { + "table": 10 + } + } + ] + } + ], + "fcbb:bbbb:1:fe20::/64": [ + { + "prefix": "fcbb:bbbb:1:fe20::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf20", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT6" + }, + "seg6localContext": { + "table": 20 + } + } + ] + } + ], + "fcbb:bbbb:1:fe30::/64": [ + { + "prefix": "fcbb:bbbb:1:fe30::/64", + "prefixLen": 64, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "Vrf30", + "active": true, + "weight": 1, + "seg6local": { + "action": "End.DT46" + }, + "seg6localContext": { + "table": 30 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/r1/frr.conf b/tests/topotests/static_srv6_sids/r1/frr.conf new file mode 100644 index 0000000000..999e35c35b --- /dev/null +++ b/tests/topotests/static_srv6_sids/r1/frr.conf @@ -0,0 +1,16 @@ +hostname r1 +! +segment-routing + srv6 + locators + locator MAIN + prefix fcbb:bbbb:1::/48 block-len 32 node-len 16 func-bits 16 + ! + ! + static-sids + sid fcbb:bbbb:1:fe10::/64 locator MAIN behavior uDT4 vrf Vrf10 + sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20 + sid fcbb:bbbb:1:fe30::/64 locator MAIN behavior uDT46 vrf Vrf30 + ! + ! +! \ No newline at end of file diff --git a/tests/topotests/static_srv6_sids/r1/setup.sh b/tests/topotests/static_srv6_sids/r1/setup.sh new file mode 100644 index 0000000000..040be73914 --- /dev/null +++ b/tests/topotests/static_srv6_sids/r1/setup.sh @@ -0,0 +1,13 @@ +ip link add sr0 type dummy +ip link set sr0 up + +ip link add Vrf10 type vrf table 10 +ip link set Vrf10 up + +ip link add Vrf20 type vrf table 20 +ip link set Vrf20 up + +ip link add Vrf30 type vrf table 30 +ip link set Vrf30 up + +sysctl -w net.vrf.strict_mode=1 diff --git a/tests/topotests/static_srv6_sids/test_static_srv6_sids.py b/tests/topotests/static_srv6_sids/test_static_srv6_sids.py new file mode 100755 index 0000000000..453a30af48 --- /dev/null +++ b/tests/topotests/static_srv6_sids/test_static_srv6_sids.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_static_srv6_sids.py +# +# Copyright (c) 2025 by +# Alibaba Inc, Yuqing Zhao +# Lingyu Zhang +# + +""" +test_static_srv6_sids.py: +Test for SRv6 static route on zebra +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.staticd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_frr_config("frr.conf") + tgen.start_router() + + +def teardown_module(): + tgen = get_topogen() + tgen.stop_topology() + + +def test_srv6_static_sids(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + def _check_srv6_static_sids(router, expected_route_file): + logger.info("checking zebra srv6 static sids") + output = json.loads(router.vtysh_cmd("show ipv6 route static json")) + expected = open_json_file("{}/{}".format(CWD, expected_route_file)) + return topotest.json_cmp(output, expected) + + def check_srv6_static_sids(router, expected_file): + func = functools.partial(_check_srv6_static_sids, router, expected_file) + _, result = topotest.run_and_expect(func, None, count=15, wait=1) + assert result is None, "Failed" + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Test for srv6 sids configuration") + check_srv6_static_sids(router, "expected_srv6_sids.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From f56a77359b475c67bb3ddc872f56cb04ec1cea45 Mon Sep 17 00:00:00 2001 From: Yuqing Zhao Date: Mon, 13 Jan 2025 11:51:34 +0100 Subject: [PATCH 15/15] doc: Add support for SRv6 static sid CLI Add the srv6 static sids configuration and show running-config sections in static.rst. Signed-off-by: Yuqing Zhao --- doc/user/static.rst | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/doc/user/static.rst b/doc/user/static.rst index 922c71a073..5bf5004a66 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -176,3 +176,52 @@ multiple segments instructions. router# show ipv6 route [..] S>* 2005::/64 [1/0] is directly connected, ens3, seg6 2001:db8:aaaa::7,2002::4,2002::3,2002::2, weight 1, 00:00:06 + +SRv6 Static SIDs Commands +========================= + +.. clicmd:: segment-routing + + Move from configure mode to segment-routing node. + +.. clicmd:: srv6 + + Move from segment-routing node to srv6 node. + +.. clicmd:: static-sids + + Move from srv6 node to static-sids node. In this static-sids node, user can + configure static SRv6 SIDs. + +.. clicmd:: sid X:X::X:X/M locator NAME behavior [vrf VRF] + + Specify the locator sid manually. Configuring a local sid in a purely static mode + by specifying the sid value would generate a unique SID. + This feature will support the configuration of static SRv6 decapsulation on the system. + + It supports four parameter options, corresponding to the following functions: + uN, uDT4, uDT6, uDT46 + + When configuring the local sid, if the action is set to 'uN', no vrf should be set. + While for any other action, it is necessary to specify a specific vrf. + +:: + + router# configure terminal + router(config)# segment-routing + router(config-sr)# srv6 + router(config-srv6)# static-sids + router(config-srv6-sids)# sid fcbb:bbbb:1:fe01::/64 locator LOC1 behavior uDT6 vrf Vrf1 + router(config-srv6-sids)# sid fcbb:bbbb:1:fe02::/64 locator LOC1 behavior uDT4 vrf Vrf1 + router(config-srv6-sids)# sid fcbb:bbbb:1:fe03::/64 locator LOC1 behavior uDT46 vrf Vrf2 + + router(config-srv6-locator)# show run + ... + segment-routing + srv6 + static-sids + sid fcbb:bbbb:1:fe01::/64 locator LOC1 behavior uDT6 vrf Vrf1 + sid fcbb:bbbb:1:fe02::/64 locator LOC1 behavior uDT4 vrf Vrf1 + sid fcbb:bbbb:1:fe03::/64 locator LOC1 behavior uDT46 vrf Vrf2 + ! + ... \ No newline at end of file