diff --git a/src/sonic-frr/patch/0064-SRv6-BGP-SID-reachability.patch b/src/sonic-frr/patch/0064-SRv6-BGP-SID-reachability.patch new file mode 100644 index 000000000000..65a15f4b65f3 --- /dev/null +++ b/src/sonic-frr/patch/0064-SRv6-BGP-SID-reachability.patch @@ -0,0 +1,845 @@ +From f49fc7682f3e1f0e82181d03a1b4f9af3d955abb Mon Sep 17 00:00:00 2001 + +From: Dmytro Shytyi + +Subject: [PATCH 1/4] doc: bgp ipv4 l3vpn sid reachability + +BGP IPv4 L3VPN with SRv6 SID reachability example +provided in the documentation + +Signed-off-by: Dmytro Shytyi +--- + bgpd/bgp_nht.c | 52 ++++-- + doc/user/bgp.rst | 25 +++ + .../bgp_srv6_sid_reachability/c11/bgpd.conf | 0 + .../bgp_srv6_sid_reachability/c11/staticd.conf | 4 + .../bgp_srv6_sid_reachability/c11/zebra.conf | 6 + + .../bgp_srv6_sid_reachability/c12/bgpd.conf | 0 + .../bgp_srv6_sid_reachability/c12/staticd.conf | 4 + .../bgp_srv6_sid_reachability/c12/zebra.conf | 6 + + .../bgp_srv6_sid_reachability/c21/bgpd.conf | 0 + .../bgp_srv6_sid_reachability/c21/staticd.conf | 4 + .../bgp_srv6_sid_reachability/c21/zebra.conf | 6 + + .../bgp_srv6_sid_reachability/c22/bgpd.conf | 0 + .../bgp_srv6_sid_reachability/c22/staticd.conf | 5 + + .../bgp_srv6_sid_reachability/c22/zebra.conf | 9 + + .../bgp_srv6_sid_reachability/c31/bgpd.conf | 0 + .../bgp_srv6_sid_reachability/c31/staticd.conf | 4 + .../bgp_srv6_sid_reachability/c31/zebra.conf | 6 + + .../bgp_srv6_sid_reachability/c32/bgpd.conf | 0 + .../bgp_srv6_sid_reachability/c32/staticd.conf | 4 + .../bgp_srv6_sid_reachability/c32/zebra.conf | 6 + + .../bgp_srv6_sid_reachability/r1/bgpd.conf | 61 +++++++ + .../bgp_srv6_sid_reachability/r1/staticd.conf | 4 + .../bgp_srv6_sid_reachability/r1/zebra.conf | 32 ++++ + .../bgp_srv6_sid_reachability/r2/bgpd.conf | 50 ++++++ + .../bgp_srv6_sid_reachability/r2/staticd.conf | 4 + .../bgp_srv6_sid_reachability/r2/zebra.conf | 29 +++ + .../bgp_srv6_sid_reachability/r3/bgpd.conf | 50 ++++++ + .../bgp_srv6_sid_reachability/r3/staticd.conf | 6 + + .../bgp_srv6_sid_reachability/r3/zebra.conf | 29 +++ + .../test_bgp_srv6_sid_reachability.py | 169 ++++++++++++++++++++ + 30 files changed, 557 insertions(+), 18 deletions(-) + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c11/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c12/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c21/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c22/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c31/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c32/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf + create mode 100644 tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf + create mode 100755 tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py + +diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c +index e2c103bb5..6f77d6201 100644 +--- a/bgpd/bgp_nht.c ++++ b/bgpd/bgp_nht.c +@@ -116,24 +116,36 @@ static int bgp_isvalid_nexthop_for_mplsovergre(struct bgp_nexthop_cache *bnc, + static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path) + { ++ return (bnc && (bnc->nexthop_num > 0 && ++ (CHECK_FLAG(path->flags, BGP_PATH_ACCEPT_OWN) || ++ CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) || ++ bgp_isvalid_nexthop_for_ebgp(bnc, path) || ++ bgp_isvalid_nexthop_for_mplsovergre(bnc, path)))); ++} ++ ++static bool bgp_isvalid_nexthop_for_l3vpn(struct bgp_nexthop_cache *bnc, ++ struct bgp_path_info *path) ++{ ++ if (bgp_zebra_num_connects() == 0) ++ return 1; ++ ++ if (path->attr->srv6_l3vpn || path->attr->srv6_vpn) { ++ /* In the case of SRv6-VPN, we need to track the reachability to the ++ * SID (in other words, IPv6 address). We check that the SID is ++ * available in the BGP update; then if it is available, we check ++ * for the nexthop reachability. ++ */ ++ if (bnc && (bnc->nexthop_num > 0 && bgp_isvalid_nexthop(bnc))) ++ return 1; ++ return 0; ++ } + /* +- * - In the case of MPLS-VPN, the label is learned from LDP or other ++ * In the case of MPLS-VPN, the label is learned from LDP or other + * protocols, and nexthop tracking is enabled for the label. + * The value is recorded as BGP_NEXTHOP_LABELED_VALID. +- * - In the case of SRv6-VPN, we need to track the reachability to the +- * SID (in other words, IPv6 address). As in MPLS, we need to record +- * the value as BGP_NEXTHOP_SID_VALID. However, this function is +- * currently not implemented, and this function assumes that all +- * Transit routes for SRv6-VPN are valid. + * - Otherwise check for mpls-gre acceptance + */ +- return (bgp_zebra_num_connects() == 0 || +- (bnc && (bnc->nexthop_num > 0 && +- (CHECK_FLAG(path->flags, BGP_PATH_ACCEPT_OWN) || +- CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) || +- bnc->bgp->srv6_enabled || +- bgp_isvalid_nexthop_for_ebgp(bnc, path) || +- bgp_isvalid_nexthop_for_mplsovergre(bnc, path))))); ++ return bgp_isvalid_nexthop_for_mpls(bnc, path); + } + + static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) +@@ -496,7 +508,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, + else if (safi == SAFI_UNICAST && pi && + pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra && + pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) +- return bgp_isvalid_nexthop_for_mpls(bnc, pi); ++ return bgp_isvalid_nexthop_for_l3vpn(bnc, pi); + else if (safi == SAFI_MPLS_VPN && pi && + pi->sub_type != BGP_ROUTE_IMPORTED) + /* avoid not redistributing mpls vpn routes */ +@@ -1045,8 +1057,11 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) + break; + case AFI_IP6: + p->family = AF_INET6; +- +- if (is_bgp_static) { ++ if (pi->attr && pi->attr->srv6_l3vpn) { ++ IPV6_ADDR_COPY(&(p->u.prefix6), ++ &(pi->attr->srv6_l3vpn->sid)); ++ p->prefixlen = IPV6_MAX_BITLEN; ++ } else if (is_bgp_static) { + p->u.prefix6 = p_orig->u.prefix6; + p->prefixlen = p_orig->prefixlen; + } else { +@@ -1294,8 +1309,9 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) + && (path->attr->evpn_overlay.type + != OVERLAY_INDEX_GATEWAY_IP)) { + bnc_is_valid_nexthop = +- bgp_isvalid_nexthop_for_mpls(bnc, path) ? true +- : false; ++ bgp_isvalid_nexthop_for_l3vpn(bnc, path) ++ ? true ++ : false; + } else if (safi == SAFI_MPLS_VPN && + path->sub_type != BGP_ROUTE_IMPORTED) { + /* avoid not redistributing mpls vpn routes */ +diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst +index 99d2a8c35..1b7039b23 100644 +--- a/doc/user/bgp.rst ++++ b/doc/user/bgp.rst +@@ -3201,6 +3201,31 @@ L3VPN SRv6 + Specify the SRv6 locator to be used for SRv6 L3VPN. The Locator name must + be set in zebra, but user can set it in any order. + ++L3VPN SRv6 SID reachability ++--------------------------- ++ ++In the context of IPv4 L3VPN over SRv6 specific usecase, 2001:db8:12::2 ++is the peer IPv6 address of r2, and 2001:db8:2:2:: is the SRv6 SID ++advertised by router r2 for prefix P. On r1, the SID reachability is ++checked in order to install the prefix P. The below output indicates ++that the 2001:db8:2:2:: prefix is valid. ++ ++ ++.. code-block:: frr ++ ++ r1# show bgp nexthop detail ++ Current BGP nexthop cache: ++ 2001:db8:2:2:: valid [IGP metric 0], #paths 4 ++ gate 2001:db8:12::2, if eth0 ++ Last update: Tue Nov 14 10:36:28 2023 ++ Paths: ++ 1/1 192.168.2.0/24 VRF vrf10 flags 0x4018 ++ 1/3 192.168.2.0/24 RD 65002:10 VRF default flags 0x418 ++ 2001:db8:12::2 valid [IGP metric 0], #paths 0, peer 2001:db8:12::2 ++ if eth0 ++ Last update: Tue Nov 14 10:36:26 2023 ++ Paths: ++ + General configuration + ^^^^^^^^^^^^^^^^^^^^^ + +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c11/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c11/bgpd.conf +new file mode 100644 +index 000000000..e69de29bb +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf +new file mode 100644 +index 000000000..bcf5a0499 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf +@@ -0,0 +1,4 @@ ++! ++ip route 0.0.0.0/0 192.168.1.254 ++ipv6 route ::/0 2001:1::ffff ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf +new file mode 100644 +index 000000000..0615cf9a9 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf +@@ -0,0 +1,6 @@ ++hostname c11 ++! ++interface eth0 ++ ip address 192.168.1.1/24 ++ ipv6 address 2001:1::1/64 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c12/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c12/bgpd.conf +new file mode 100644 +index 000000000..e69de29bb +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf +new file mode 100644 +index 000000000..bcf5a0499 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf +@@ -0,0 +1,4 @@ ++! ++ip route 0.0.0.0/0 192.168.1.254 ++ipv6 route ::/0 2001:1::ffff ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf +new file mode 100644 +index 000000000..18985aa38 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf +@@ -0,0 +1,6 @@ ++hostname c12 ++! ++interface eth0 ++ ip address 192.168.1.1/24 ++ ipv6 address 2001:1::1/64 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c21/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c21/bgpd.conf +new file mode 100644 +index 000000000..e69de29bb +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf +new file mode 100644 +index 000000000..608e60060 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf +@@ -0,0 +1,4 @@ ++! ++ip route 0.0.0.0/0 192.168.2.254 ++ipv6 route ::/0 2001:2::ffff ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf +new file mode 100644 +index 000000000..b8b70ac96 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf +@@ -0,0 +1,6 @@ ++hostname c21 ++! ++interface eth0 ++ ip address 192.168.2.1/24 ++ ipv6 address 2001:2::1/64 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c22/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c22/bgpd.conf +new file mode 100644 +index 000000000..e69de29bb +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf +new file mode 100644 +index 000000000..277aae998 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf +@@ -0,0 +1,5 @@ ++ ++! ++ip route 0.0.0.0/0 192.168.2.254 ++ipv6 route ::/0 2001:2::ffff ++! +\ No newline at end of file +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf +new file mode 100644 +index 000000000..cc764cc35 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf +@@ -0,0 +1,9 @@ ++hostname c22 ++! ++interface eth0 ++ ip address 192.168.2.1/24 ++ ipv6 address 2001:2::1/64 ++! ++ip route 0.0.0.0/0 192.168.2.254 ++ipv6 route ::/0 2001:2::ffff ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c31/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c31/bgpd.conf +new file mode 100644 +index 000000000..e69de29bb +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf +new file mode 100644 +index 000000000..0c88575ab +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf +@@ -0,0 +1,4 @@ ++! ++ip route 0.0.0.0/0 192.168.3.254 ++ipv6 route ::/0 2001:3::ffff ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf +new file mode 100644 +index 000000000..3f75641ea +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf +@@ -0,0 +1,6 @@ ++hostname c31 ++! ++interface eth0 ++ ip address 192.168.3.1/24 ++ ipv6 address 2001:3::1/64 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c32/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c32/bgpd.conf +new file mode 100644 +index 000000000..e69de29bb +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf +new file mode 100644 +index 000000000..0c88575ab +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf +@@ -0,0 +1,4 @@ ++! ++ip route 0.0.0.0/0 192.168.3.254 ++ipv6 route ::/0 2001:3::ffff ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf +new file mode 100644 +index 000000000..c06a7d19f +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf +@@ -0,0 +1,6 @@ ++hostname c32 ++! ++interface eth0 ++ ip address 192.168.3.1/24 ++ ipv6 address 2001:3::1/64 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf +new file mode 100644 +index 000000000..573dbe095 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf +@@ -0,0 +1,61 @@ ++frr defaults traditional ++! ++hostname r1 ++password zebra ++! ++log commands ++! ++router bgp 65001 ++ bgp router-id 192.0.2.1 ++ no bgp ebgp-requires-policy ++ no bgp default ipv4-unicast ++ neighbor 2001:db8:12::2 remote-as 65002 ++ neighbor 2001:db8:12::2 timers 3 10 ++ neighbor 2001:db8:12::2 timers connect 1 ++ neighbor 2001:db8:12::2 capability extended-nexthop ++ neighbor 2001:db8:13::3 remote-as 65001 ++ neighbor 2001:db8:13::3 timers 3 10 ++ neighbor 2001:db8:13::3 timers connect 1 ++ neighbor 2001:db8:13::3 capability extended-nexthop ++ ! ++ segment-routing srv6 ++ locator default ++ ! ++ address-family ipv4 vpn ++ neighbor 2001:db8:12::2 activate ++ neighbor 2001:db8:13::3 activate ++ exit-address-family ++ ! ++! ++router bgp 65001 vrf vrf10 ++ bgp router-id 192.0.2.1 ++ ! ++ address-family ipv4 unicast ++ redistribute connected ++ sid vpn export 1 ++ rd vpn export 65001:10 ++ rt vpn both 0:10 ++ import vpn ++ export vpn ++ exit-address-family ++ ! ++! ++router bgp 65001 vrf vrf20 ++ bgp router-id 192.0.2.1 ++ ! ++ address-family ipv4 unicast ++ redistribute connected ++ sid vpn export 2 ++ rd vpn export 65001:20 ++ rt vpn both 0:20 ++ import vpn ++ export vpn ++ exit-address-family ++ ! ++! ++interface eth0 ++ mpls bgp forwarding ++! ++interface eth1 ++ mpls bgp forwarding ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf +new file mode 100644 +index 000000000..49b64fd7a +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf +@@ -0,0 +1,4 @@ ++! ++ipv6 route 2001:db8:2:2::/64 2001:db8:12::2 ++ipv6 route 2001:db8:3:3::/64 2001:db8:13::3 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf +new file mode 100644 +index 000000000..79dbf9559 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf +@@ -0,0 +1,32 @@ ++log file zebra.log ++! ++hostname r1 ++! ++interface lo ++ ipv6 address 2001:db8:1:1::1/128 ++! ++interface eth0 ++ ipv6 address 2001:db8:12::1/64 ++! ++interface eth1 ++ ipv6 address 2001:db8:13::1/64 ++! ++interface eth2 vrf vrf10 ++ ip address 192.168.1.254/24 ++! ++interface eth3 vrf vrf20 ++ ip address 192.168.1.254/24 ++! ++segment-routing ++ srv6 ++ locators ++ locator default ++ prefix 2001:db8:1:1::/64 ++ ! ++ ! ++! ++ip forwarding ++ipv6 forwarding ++! ++line vty ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf +new file mode 100644 +index 000000000..723d6fca2 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf +@@ -0,0 +1,50 @@ ++frr defaults traditional ++! ++hostname r2 ++password zebra ++! ++log commands ++! ++router bgp 65002 ++ bgp router-id 192.0.2.2 ++ no bgp ebgp-requires-policy ++ no bgp default ipv4-unicast ++ neighbor 2001:db8:12::1 remote-as 65001 ++ neighbor 2001:db8:12::1 timers 3 10 ++ neighbor 2001:db8:12::1 timers connect 1 ++ neighbor 2001:db8:12::1 capability extended-nexthop ++ ! ++ segment-routing srv6 ++ locator default ++ ! ++ address-family ipv4 vpn ++ neighbor 2001:db8:12::1 activate ++ exit-address-family ++ ! ++! ++router bgp 65002 vrf vrf10 ++ bgp router-id 192.0.2.2 ++ ! ++ address-family ipv4 unicast ++ redistribute connected ++ sid vpn export 1 ++ rd vpn export 65002:10 ++ rt vpn both 0:10 ++ import vpn ++ export vpn ++ exit-address-family ++ ! ++! ++router bgp 65002 vrf vrf20 ++ bgp router-id 192.0.2.2 ++ ! ++ address-family ipv4 unicast ++ redistribute connected ++ sid vpn export 2 ++ rd vpn export 65002:20 ++ rt vpn both 0:20 ++ import vpn ++ export vpn ++ exit-address-family ++ ! ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf +new file mode 100644 +index 000000000..8d80c1ead +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf +@@ -0,0 +1,4 @@ ++! ++ipv6 route 2001:db8:1:1::/64 2001:db8:12::1 ++ipv6 route 2001:db8:3:3::/64 2001:db8:12::1 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf +new file mode 100644 +index 000000000..09a65b989 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf +@@ -0,0 +1,29 @@ ++log file zebra.log ++! ++hostname r2 ++! ++interface lo ++ ipv6 address 2001:db8:2:2::1/128 ++! ++interface eth0 ++ ipv6 address 2001:db8:12::2/64 ++! ++interface eth1 vrf vrf10 ++ ip address 192.168.2.254/24 ++! ++interface eth2 vrf vrf20 ++ ip address 192.168.2.254/24 ++! ++segment-routing ++ srv6 ++ locators ++ locator default ++ prefix 2001:db8:2:2::/64 ++ ! ++ ! ++! ++ip forwarding ++ipv6 forwarding ++! ++line vty ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf +new file mode 100644 +index 000000000..c412cb6d2 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf +@@ -0,0 +1,50 @@ ++frr defaults traditional ++! ++hostname r2 ++password zebra ++! ++log commands ++! ++router bgp 65001 ++ bgp router-id 192.0.2.3 ++ no bgp ebgp-requires-policy ++ no bgp default ipv4-unicast ++ neighbor 2001:db8:13::1 remote-as 65001 ++ neighbor 2001:db8:13::1 timers 3 10 ++ neighbor 2001:db8:13::1 timers connect 1 ++ neighbor 2001:db8:13::1 capability extended-nexthop ++ ! ++ segment-routing srv6 ++ locator default ++ ! ++ address-family ipv4 vpn ++ neighbor 2001:db8:13::1 activate ++ exit-address-family ++ ! ++! ++router bgp 65001 vrf vrf10 ++ bgp router-id 192.0.2.3 ++ ! ++ address-family ipv4 unicast ++ redistribute connected ++ sid vpn export 1 ++ rd vpn export 65001:10 ++ rt vpn both 0:10 ++ import vpn ++ export vpn ++ exit-address-family ++ ! ++! ++router bgp 65001 vrf vrf20 ++ bgp router-id 192.0.2.2 ++ ! ++ address-family ipv4 unicast ++ redistribute connected ++ sid vpn export 2 ++ rd vpn export 65001:20 ++ rt vpn both 0:20 ++ import vpn ++ export vpn ++ exit-address-family ++ ! ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf +new file mode 100644 +index 000000000..9d672d51b +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf +@@ -0,0 +1,6 @@ ++! ++ipv6 route 2001:db8:12::/64 2001:db8:13::1 ++! ++ipv6 route 2001:db8:1:1::/64 2001:db8:13::1 ++ipv6 route 2001:db8:2:2::/64 2001:db8:13::1 ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf +new file mode 100644 +index 000000000..a20cb76a7 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf +@@ -0,0 +1,29 @@ ++log file zebra.log ++! ++hostname r2 ++! ++interface lo ++ ipv6 address 2001:db8:3:3::1/128 ++! ++interface eth0 ++ ipv6 address 2001:db8:13::3/64 ++! ++interface eth1 vrf vrf10 ++ ip address 192.168.3.254/24 ++! ++interface eth2 vrf vrf20 ++ ip address 192.168.3.254/24 ++! ++segment-routing ++ srv6 ++ locators ++ locator default ++ prefix 2001:db8:3:3::/64 ++ ! ++ ! ++! ++ip forwarding ++ipv6 forwarding ++! ++line vty ++! +diff --git a/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py +new file mode 100755 +index 000000000..92315bce0 +--- /dev/null ++++ b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py +@@ -0,0 +1,169 @@ ++#!/usr/bin/env python ++# SPDX-License-Identifier: ISC ++ ++# ++# Part of NetDEF Topology Tests ++# ++# Copyright (c) 2023 by 6WIND ++# ++ ++import os ++import re ++import sys ++import json ++import functools ++import pytest ++ ++CWD = os.path.dirname(os.path.realpath(__file__)) ++sys.path.append(os.path.join(CWD, "../")) ++ ++# pylint: disable=C0413 ++# Import topogen and topotest helpers ++from lib import topotest ++from lib.topogen import Topogen, TopoRouter, get_topogen ++from lib.topolog import logger ++from lib.common_config import required_linux_kernel_version ++from lib.checkping import check_ping ++ ++pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] ++ ++ ++def build_topo(tgen): ++ tgen.add_router("r1") ++ tgen.add_router("r2") ++ tgen.add_router("r3") ++ ++ tgen.add_router("c11") ++ tgen.add_router("c12") ++ tgen.add_router("c21") ++ tgen.add_router("c22") ++ tgen.add_router("c31") ++ tgen.add_router("c32") ++ ++ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") ++ tgen.add_link(tgen.gears["r1"], tgen.gears["r3"], "eth1", "eth0") ++ tgen.add_link(tgen.gears["r1"], tgen.gears["c11"], "eth2", "eth0") ++ tgen.add_link(tgen.gears["r1"], tgen.gears["c12"], "eth3", "eth0") ++ tgen.add_link(tgen.gears["r2"], tgen.gears["c21"], "eth1", "eth0") ++ tgen.add_link(tgen.gears["r2"], tgen.gears["c22"], "eth2", "eth0") ++ tgen.add_link(tgen.gears["r3"], tgen.gears["c31"], "eth1", "eth0") ++ tgen.add_link(tgen.gears["r3"], tgen.gears["c32"], "eth2", "eth0") ++ ++ ++def setup_module(mod): ++ result = required_linux_kernel_version("5.15") ++ if result is not True: ++ pytest.skip("Kernel requirements are not met") ++ ++ tgen = Topogen(build_topo, mod.__name__) ++ tgen.start_topology() ++ ++ for rname, router in tgen.routers().items(): ++ router.load_config( ++ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ++ ) ++ router.load_config( ++ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) ++ ) ++ router.load_config( ++ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ++ ) ++ ++ tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") ++ tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") ++ tgen.gears["r1"].run("ip link set vrf10 up") ++ tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") ++ tgen.gears["r1"].run("ip link set vrf20 up") ++ tgen.gears["r1"].run("ip link set eth2 master vrf10") ++ tgen.gears["r1"].run("ip link set eth3 master vrf20") ++ ++ tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") ++ tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") ++ tgen.gears["r2"].run("ip link set vrf10 up") ++ tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") ++ tgen.gears["r2"].run("ip link set vrf20 up") ++ tgen.gears["r2"].run("ip link set eth1 master vrf10") ++ tgen.gears["r2"].run("ip link set eth2 master vrf20") ++ ++ tgen.gears["r3"].run("sysctl net.vrf.strict_mode=1") ++ tgen.gears["r3"].run("ip link add vrf10 type vrf table 10") ++ tgen.gears["r3"].run("ip link set vrf10 up") ++ tgen.gears["r3"].run("ip link add vrf20 type vrf table 20") ++ tgen.gears["r3"].run("ip link set vrf20 up") ++ tgen.gears["r3"].run("ip link set eth1 master vrf10") ++ tgen.gears["r3"].run("ip link set eth2 master vrf20") ++ ++ tgen.start_router() ++ ++ ++def teardown_module(mod): ++ tgen = get_topogen() ++ tgen.stop_topology() ++ ++ ++def test_ping(): ++ tgen = get_topogen() ++ ++ check_ping("c11", "192.168.2.1", True, 10, 1) ++ check_ping("c11", "192.168.3.1", True, 10, 1) ++ check_ping("c12", "192.168.2.1", True, 10, 1) ++ check_ping("c12", "192.168.3.1", True, 10, 1) ++ check_ping("c21", "192.168.3.1", True, 10, 1) ++ check_ping("c22", "192.168.3.1", True, 10, 1) ++ ++ ++def test_sid_unreachable_nht(): ++ get_topogen().gears["r1"].vtysh_cmd( ++ """ ++ configure terminal ++ no ipv6 route 2001:db8:2:2::/64 2001:db8:12::2 ++ """ ++ ) ++ check_ping("c11", "192.168.2.1", False, 10, 1) ++ ++ ++def test_sid_reachable_again_nht(): ++ get_topogen().gears["r1"].vtysh_cmd( ++ """ ++ configure terminal ++ ipv6 route 2001:db8:2:2::/64 2001:db8:12::2 ++ """ ++ ) ++ check_ping("c11", "192.168.2.1", True, 10, 1) ++ ++ ++def test_sid_unreachable_bgp_update(): ++ get_topogen().gears["r2"].vtysh_cmd( ++ """ ++ configure terminal ++ router bgp 65002 ++ no segment-routing srv6 ++ exit ++ router bgp 65002 vrf vrf10 ++ address-family ipv4 unicast ++ no sid vpn export 1 ++ """ ++ ) ++ check_ping("c11", "192.168.2.1", False, 10, 1) ++ ++ ++def test_sid_reachable_again_bgp_update(): ++ get_topogen().gears["r2"].vtysh_cmd( ++ """ ++ configure terminal ++ router bgp 65002 ++ segment-routing srv6 ++ locator default ++ exit ++ exit ++ router bgp 65002 vrf vrf10 ++ address-family ipv4 unicast ++ sid vpn export 1 ++ """ ++ ) ++ check_ping("c11", "192.168.2.1", True, 10, 1) ++ ++ ++if __name__ == "__main__": ++ args = ["-s"] + sys.argv[1:] ++ sys.exit(pytest.main(args)) diff --git a/src/sonic-frr/patch/0065-zebra-display-srv6-encapsulation-source-address-when-configured.patch b/src/sonic-frr/patch/0065-zebra-display-srv6-encapsulation-source-address-when-configured.patch new file mode 100644 index 000000000000..431d9e07ad9c --- /dev/null +++ b/src/sonic-frr/patch/0065-zebra-display-srv6-encapsulation-source-address-when-configured.patch @@ -0,0 +1,57 @@ +From 890b67d7a9eb6ee9cbb6b5623934ad4d0b781232 Mon Sep 17 00:00:00 2001 + +From: Philippe Guibert + +Subject: [PATCH] zebra: display srv6 encapsulation source-address when + configured + +The 'show running-config' does not display the ipv6 source address +when a locator is not configured. Fix this by systematically displaying +the ipv6 source address. + +Fixes: 6a0956169b31 ("zebra: Add encap source address to SRv6 config write function") + +Signed-off-by: Philippe Guibert +--- + zebra/zebra_srv6_vty.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c +index c5b850599..d5cd30e64 100644 +--- a/zebra/zebra_srv6_vty.c ++++ b/zebra/zebra_srv6_vty.c +@@ -475,16 +475,24 @@ static int zebra_sr_config(struct vty *vty) + struct listnode *node; + struct srv6_locator *locator; + char str[256]; ++ bool display_source_srv6 = false; ++ ++ if (srv6 && !IPV6_ADDR_SAME(&srv6->encap_src_addr, &in6addr_any)) ++ display_source_srv6 = true; + + vty_out(vty, "!\n"); +- if (zebra_srv6_is_enable()) { ++ if (display_source_srv6 || zebra_srv6_is_enable()) { + vty_out(vty, "segment-routing\n"); + vty_out(vty, " srv6\n"); ++ } ++ if (display_source_srv6) { + if (!IPV6_ADDR_SAME(&srv6->encap_src_addr, &in6addr_any)) { + vty_out(vty, " encapsulation\n"); + vty_out(vty, " source-address %pI6\n", + &srv6->encap_src_addr); + } ++ } ++ if (zebra_srv6_is_enable()) { + vty_out(vty, " locators\n"); + for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator)) { + inet_ntop(AF_INET6, &locator->prefix.prefix, +@@ -514,6 +522,8 @@ static int zebra_sr_config(struct vty *vty) + vty_out(vty, " !\n"); + vty_out(vty, " exit\n"); + vty_out(vty, " !\n"); ++ } ++ if (display_source_srv6 || zebra_srv6_is_enable()) { + vty_out(vty, "exit\n"); + vty_out(vty, "!\n"); + } diff --git a/src/sonic-frr/patch/0066-lib-fix-srv6-locator-flags-propagated-to-isis.patch b/src/sonic-frr/patch/0066-lib-fix-srv6-locator-flags-propagated-to-isis.patch new file mode 100644 index 000000000000..b872ff9cbf8c --- /dev/null +++ b/src/sonic-frr/patch/0066-lib-fix-srv6-locator-flags-propagated-to-isis.patch @@ -0,0 +1,41 @@ +From 03d2ad01a4fd4aa6df4b1ec64249fb391405a61f Mon Sep 17 00:00:00 2001 + +From: Philippe Guibert + +Subject: [PATCH] lib: fix srv6 locator flags propagated to isis + +When usid is not used, the isis_srv6_topo1 test does not work. +The SID prefix allocated by isis is different when the usid +flags is set or not. When the flags is not transmitted to isis, +the SID allocated is supposed to be a 128 bit mask length SID, +which is not what the isis_srv6_topo1 test is supposed to obtain. + +Fix this by exchanging the flags locator value in the zclient api. + +Fixes: 9b7491e1fc04 ("lib: Add support for flags to the SRv6 locator") + +Signed-off-by: Philippe Guibert +--- + lib/zclient.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/zclient.c b/lib/zclient.c +index 64515c754..269c3e9ba 100644 +--- a/lib/zclient.c ++++ b/lib/zclient.c +@@ -1125,6 +1125,7 @@ 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->flags); + return 0; + } + +@@ -1140,6 +1141,7 @@ 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->flags); + return 0; + + stream_failure: 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/0068-bgpd-Extend-BGP-to-communicate-with-the-SRv6-SID-Manager-to-allocate-release-SRv6-SIDs.patch b/src/sonic-frr/patch/0068-bgpd-Extend-BGP-to-communicate-with-the-SRv6-SID-Manager-to-allocate-release-SRv6-SIDs.patch new file mode 100644 index 000000000000..df60dfa9404b --- /dev/null +++ b/src/sonic-frr/patch/0068-bgpd-Extend-BGP-to-communicate-with-the-SRv6-SID-Manager-to-allocate-release-SRv6-SIDs.patch @@ -0,0 +1,1126 @@ +From c50b4b9afc2a23369f1fd26e5c8bd47a412fa04f Mon Sep 17 00:00:00 2001 + +From: Carmine Scarpitta + +Subject: [PATCH 01/10] bgpd: Add API to get SRv6 locator info + +Add an API to request information from the SRv6 SID Manager (zebra) +regarding a specific SRv6 locator. + +Signed-off-by: Carmine Scarpitta +--- + bgpd/bgp_mplsvpn.c | 293 +++++++++++++++++++++------------------ + bgpd/bgp_mplsvpn.h | 2 + bgpd/bgp_vty.c | 38 +++-- + bgpd/bgp_zebra.c | 394 ++++++++++++++++++++++++++++++++++++++++++++++++++-- + bgpd/bgp_zebra.h | 7 + + bgpd/bgpd.c | 17 ++ + bgpd/bgpd.h | 5 - + 7 files changed, 588 insertions(+), 168 deletions(-) + +diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c +index ef8ca39e8..29644b19f 100644 +--- a/bgpd/bgp_mplsvpn.c ++++ b/bgpd/bgp_mplsvpn.c +@@ -491,6 +491,7 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_af(struct bgp *bgp, afi_t afi) + { + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + struct seg6local_context seg6localctx = {}; ++ struct srv6_sid_ctx ctx = {}; + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) +@@ -523,6 +524,11 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_af(struct bgp *bgp, afi_t afi) + XFREE(MTYPE_BGP_SRV6_SID, + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent); + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = NULL; ++ ++ ctx.vrf_id = bgp->vrf_id; ++ ctx.behavior = afi == AFI_IP ? ZEBRA_SEG6_LOCAL_ACTION_END_DT4 ++ : ZEBRA_SEG6_LOCAL_ACTION_END_DT6; ++ bgp_zebra_release_srv6_sid(&ctx); + } + + /* +@@ -533,6 +539,7 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_vrf(struct bgp *bgp) + { + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + struct seg6local_context seg6localctx = {}; ++ struct srv6_sid_ctx ctx = {}; + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) +@@ -561,6 +568,10 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_vrf(struct bgp *bgp) + &seg6localctx); + XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent); + bgp->tovpn_zebra_vrf_sid_last_sent = NULL; ++ ++ ctx.vrf_id = bgp->vrf_id; ++ ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; ++ bgp_zebra_release_srv6_sid(&ctx); + } + + /* +@@ -637,8 +648,8 @@ int vpn_leak_label_callback( + return 0; + } + +-static void sid_register(struct bgp *bgp, const struct in6_addr *sid, +- const char *locator_name) ++void sid_register(struct bgp *bgp, const struct in6_addr *sid, ++ const char *locator_name) + { + struct bgp_srv6_function *func; + func = XCALLOC(MTYPE_BGP_SRV6_FUNCTION, +@@ -677,108 +688,97 @@ static bool sid_exist(struct bgp *bgp, const struct in6_addr *sid) + return false; + } + +-/* +- * This function generates a new SID based on bgp->srv6_locator_chunks and +- * index. The locator and generated SID are stored in arguments sid_locator +- * and sid, respectively. ++/** ++ * Return the SRv6 SID value obtained by composing the LOCATOR and FUNCTION. + * +- * if index != 0: try to allocate as index-mode +- * else: try to allocate as auto-mode ++ * @param sid_value SRv6 SID value returned ++ * @param locator Parent locator of the SRv6 SID ++ * @param sid_func Function part of the SID ++ * @return True if success, False otherwise + */ +-static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index, +- struct srv6_locator_chunk *sid_locator_chunk, +- struct in6_addr *sid) ++static bool srv6_sid_compose(struct in6_addr *sid_value, ++ struct srv6_locator *locator, uint32_t sid_func) + { + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); +- struct listnode *node; +- struct srv6_locator_chunk *chunk; +- bool alloced = false; + int label = 0; + uint8_t offset = 0; + uint8_t func_len = 0, shift_len = 0; +- uint32_t index_max = 0; ++ uint32_t sid_func_max = 0; + +- if (!bgp || !sid_locator_chunk || !sid) ++ if (!locator || !sid_value) + return false; + +- for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) { +- if (chunk->function_bits_length > +- BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH) { +- if (debug) +- zlog_debug( +- "%s: invalid SRv6 Locator chunk (%pFX): Function Length must be less or equal to %d", +- __func__, &chunk->prefix, +- BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH); +- continue; +- } ++ if (locator->function_bits_length > ++ BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH) { ++ if (debug) ++ zlog_debug("%s: invalid SRv6 Locator (%pFX): Function Length must be less or equal to %d", ++ __func__, &locator->prefix, ++ BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH); ++ return false; ++ } + +- index_max = (1 << chunk->function_bits_length) - 1; ++ /* Max value that can be encoded in the Function part of the SID */ ++ sid_func_max = (1 << locator->function_bits_length) - 1; + +- if (index > index_max) { +- if (debug) +- zlog_debug( +- "%s: skipped SRv6 Locator chunk (%pFX): Function Length is too short to support specified index (%u)", +- __func__, &chunk->prefix, index); +- continue; +- } ++ if (sid_func > sid_func_max) { ++ if (debug) ++ zlog_debug("%s: invalid SRv6 Locator (%pFX): Function Length is too short to support specified function (%u)", ++ __func__, &locator->prefix, sid_func); ++ return false; ++ } + +- *sid = chunk->prefix.prefix; +- *sid_locator_chunk = *chunk; +- offset = chunk->block_bits_length + chunk->node_bits_length; +- func_len = chunk->function_bits_length; +- shift_len = BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH - func_len; ++ /** ++ * Let's build the SID value. ++ * sid_value = LOC:FUNC:: ++ */ + +- if (index != 0) { +- label = index << shift_len; +- if (label < MPLS_LABEL_UNRESERVED_MIN) { +- if (debug) +- zlog_debug( +- "%s: skipped to allocate SRv6 SID (%pFX): Label (%u) is too small to use", +- __func__, &chunk->prefix, +- label); +- continue; +- } ++ /* First, we put the locator (LOC) in the most significant bits of sid_value */ ++ *sid_value = locator->prefix.prefix; + +- transpose_sid(sid, label, offset, func_len); +- if (sid_exist(bgp, sid)) +- continue; +- alloced = true; +- break; +- } ++ /* ++ * Then, we compute the offset at which we have to place the function (FUNC). ++ * FUNC will be placed immediately after LOC, i.e. at block_bits_length + node_bits_length ++ */ ++ offset = locator->block_bits_length + locator->node_bits_length; + +- for (uint32_t i = 1; i < index_max; i++) { +- label = i << shift_len; +- if (label < MPLS_LABEL_UNRESERVED_MIN) { +- if (debug) +- zlog_debug( +- "%s: skipped to allocate SRv6 SID (%pFX): Label (%u) is too small to use", +- __func__, &chunk->prefix, +- label); +- continue; +- } +- transpose_sid(sid, label, offset, func_len); +- if (sid_exist(bgp, sid)) +- continue; +- alloced = true; +- break; +- } ++ /* ++ * The FUNC part of the SID is advertised in the label field of SRv6 Service TLV. ++ * (see SID Transposition Scheme, RFC 9252 section #4). ++ * Therefore, we need to encode the FUNC in the most significant bits of the ++ * 20-bit label. ++ */ ++ func_len = locator->function_bits_length; ++ shift_len = BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH - func_len; ++ ++ label = sid_func << shift_len; ++ if (label < MPLS_LABEL_UNRESERVED_MIN) { ++ if (debug) ++ zlog_debug("%s: skipped to allocate SRv6 SID (%pFX): Label (%u) is too small to use", ++ __func__, &locator->prefix, label); ++ return false; + } + +- if (!alloced) +- return 0; ++ if (sid_exist(bgp_get_default(), sid_value)) { ++ zlog_warn("%s: skipped to allocate SRv6 SID (%pFX): SID %pI6 already in use", ++ __func__, &locator->prefix, sid_value); ++ return false; ++ } + +- sid_register(bgp, sid, bgp->srv6_locator_name); +- return label; ++ /* Finally, we put the FUNC in sid_value at the computed offset */ ++ transpose_sid(sid_value, label, offset, func_len); ++ ++ return true; + } + + void ensure_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + afi_t afi) + { + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); +- struct srv6_locator_chunk *tovpn_sid_locator; +- struct in6_addr *tovpn_sid; +- uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label; ++ struct in6_addr tovpn_sid = {}; ++ uint32_t tovpn_sid_index = 0; + bool tovpn_sid_auto = false; ++ struct srv6_sid_ctx ctx = {}; ++ uint32_t sid_func; + + if (debug) + zlog_debug("%s: try to allocate new SID for vrf %s: afi %s", +@@ -790,10 +790,17 @@ void ensure_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + + /* + * skip when bgp vpn instance ins't allocated +- * or srv6 locator chunk isn't allocated ++ * or srv6 locator isn't allocated + */ +- if (!bgp_vpn || !bgp_vpn->srv6_locator_chunks) ++ if (!bgp_vpn || !bgp_vpn->srv6_locator) ++ return; ++ ++ if (bgp_vrf->vrf_id == VRF_UNKNOWN) { ++ if (debug) ++ zlog_debug("%s: vrf %s: vrf_id not set, can't set zebra vrf SRv6 SID", ++ __func__, bgp_vrf->name_pretty); + return; ++ } + + tovpn_sid_index = bgp_vrf->vpn_policy[afi].tovpn_sid_index; + tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, +@@ -810,40 +817,34 @@ void ensure_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + return; + } + +- tovpn_sid_locator = srv6_locator_chunk_alloc(); +- tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); +- +- tovpn_sid_transpose_label = alloc_new_sid(bgp_vpn, tovpn_sid_index, +- tovpn_sid_locator, tovpn_sid); ++ if (!tovpn_sid_auto) { ++ if (!srv6_sid_compose(&tovpn_sid, bgp_vpn->srv6_locator, ++ tovpn_sid_index)) { ++ zlog_err("%s: failed to compose sid for vrf %s: afi %s", ++ __func__, bgp_vrf->name_pretty, afi2str(afi)); ++ return; ++ } ++ } + +- if (tovpn_sid_transpose_label == 0) { +- if (debug) +- zlog_debug( +- "%s: not allocated new sid for vrf %s: afi %s", +- __func__, bgp_vrf->name_pretty, afi2str(afi)); +- srv6_locator_chunk_free(&tovpn_sid_locator); +- XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid); ++ ctx.vrf_id = bgp_vrf->vrf_id; ++ ctx.behavior = afi == AFI_IP ? ZEBRA_SEG6_LOCAL_ACTION_END_DT4 ++ : ZEBRA_SEG6_LOCAL_ACTION_END_DT6; ++ if (!bgp_zebra_request_srv6_sid(&ctx, &tovpn_sid, ++ bgp_vpn->srv6_locator_name, &sid_func)) { ++ zlog_err("%s: failed to request sid for vrf %s: afi %s", ++ __func__, bgp_vrf->name_pretty, afi2str(afi)); + return; + } +- +- if (debug) +- zlog_debug("%s: new sid %pI6 allocated for vrf %s: afi %s", +- __func__, tovpn_sid, bgp_vrf->name_pretty, +- afi2str(afi)); +- +- bgp_vrf->vpn_policy[afi].tovpn_sid = tovpn_sid; +- bgp_vrf->vpn_policy[afi].tovpn_sid_locator = tovpn_sid_locator; +- bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label = +- tovpn_sid_transpose_label; + } + + void ensure_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) + { + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); +- struct srv6_locator_chunk *tovpn_sid_locator; +- struct in6_addr *tovpn_sid; +- uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label; ++ struct in6_addr tovpn_sid = {}; ++ uint32_t tovpn_sid_index = 0; + bool tovpn_sid_auto = false; ++ struct srv6_sid_ctx ctx = {}; ++ uint32_t sid_func; + + if (debug) + zlog_debug("%s: try to allocate new SID for vrf %s", __func__, +@@ -855,11 +856,18 @@ void ensure_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) + + /* + * skip when bgp vpn instance ins't allocated +- * or srv6 locator chunk isn't allocated ++ * or srv6 locator isn't allocated + */ +- if (!bgp_vpn || !bgp_vpn->srv6_locator_chunks) ++ if (!bgp_vpn || !bgp_vpn->srv6_locator) + return; + ++ if (bgp_vrf->vrf_id == VRF_UNKNOWN) { ++ if (debug) ++ zlog_debug("%s: vrf %s: vrf_id not set, can't set zebra vrf SRv6 SID", ++ __func__, bgp_vrf->name_pretty); ++ return; ++ } ++ + tovpn_sid_index = bgp_vrf->tovpn_sid_index; + tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_TOVPN_SID_AUTO); + +@@ -874,28 +882,23 @@ void ensure_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) + return; + } + +- tovpn_sid_locator = srv6_locator_chunk_alloc(); +- tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); +- +- tovpn_sid_transpose_label = alloc_new_sid(bgp_vpn, tovpn_sid_index, +- tovpn_sid_locator, tovpn_sid); ++ if (!tovpn_sid_auto) { ++ if (!srv6_sid_compose(&tovpn_sid, bgp_vpn->srv6_locator, ++ bgp_vrf->tovpn_sid_index)) { ++ zlog_err("%s: failed to compose new sid for vrf %s", ++ __func__, bgp_vrf->name_pretty); ++ return; ++ } ++ } + +- if (tovpn_sid_transpose_label == 0) { +- if (debug) +- zlog_debug("%s: not allocated new sid for vrf %s", +- __func__, bgp_vrf->name_pretty); +- srv6_locator_chunk_free(&tovpn_sid_locator); +- XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid); ++ ctx.vrf_id = bgp_vrf->vrf_id; ++ ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; ++ if (!bgp_zebra_request_srv6_sid(&ctx, &tovpn_sid, ++ bgp_vpn->srv6_locator_name, &sid_func)) { ++ zlog_err("%s: failed to request new sid for vrf %s", __func__, ++ bgp_vrf->name_pretty); + return; + } +- +- if (debug) +- zlog_debug("%s: new sid %pI6 allocated for vrf %s", __func__, +- tovpn_sid, bgp_vrf->name_pretty); +- +- bgp_vrf->tovpn_sid = tovpn_sid; +- bgp_vrf->tovpn_sid_locator = tovpn_sid_locator; +- bgp_vrf->tovpn_sid_transpose_label = tovpn_sid_transpose_label; + } + + void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) +@@ -918,6 +921,7 @@ void delete_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + uint32_t tovpn_sid_index = 0; + bool tovpn_sid_auto = false; ++ struct srv6_sid_ctx ctx = {}; + + if (debug) + zlog_debug("%s: try to remove SID for vrf %s: afi %s", __func__, +@@ -931,9 +935,22 @@ void delete_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + if (tovpn_sid_index != 0 || tovpn_sid_auto) + return; + +- srv6_locator_chunk_free(&bgp_vrf->vpn_policy[afi].tovpn_sid_locator); ++ if (bgp_vrf->vrf_id == VRF_UNKNOWN) { ++ if (debug) ++ zlog_debug("%s: vrf %s: vrf_id not set, can't set zebra vrf label", ++ __func__, bgp_vrf->name_pretty); ++ return; ++ } ++ ++ srv6_locator_free(bgp_vrf->vpn_policy[afi].tovpn_sid_locator); ++ bgp_vrf->vpn_policy[afi].tovpn_sid_locator = NULL; + + if (bgp_vrf->vpn_policy[afi].tovpn_sid) { ++ ctx.vrf_id = bgp_vrf->vrf_id; ++ ctx.behavior = afi == AFI_IP ? ZEBRA_SEG6_LOCAL_ACTION_END_DT4 ++ : ZEBRA_SEG6_LOCAL_ACTION_END_DT6; ++ bgp_zebra_release_srv6_sid(&ctx); ++ + sid_unregister(bgp_vpn, bgp_vrf->vpn_policy[afi].tovpn_sid); + XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->vpn_policy[afi].tovpn_sid); + } +@@ -945,6 +962,7 @@ void delete_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + uint32_t tovpn_sid_index = 0; + bool tovpn_sid_auto = false; ++ struct srv6_sid_ctx ctx = {}; + + if (debug) + zlog_debug("%s: try to remove SID for vrf %s", __func__, +@@ -958,9 +976,21 @@ void delete_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) + if (tovpn_sid_index != 0 || tovpn_sid_auto) + return; + +- srv6_locator_chunk_free(&bgp_vrf->tovpn_sid_locator); ++ if (bgp_vrf->vrf_id == VRF_UNKNOWN) { ++ if (debug) ++ zlog_debug("%s: vrf %s: vrf_id not set, can't set zebra vrf label", ++ __func__, bgp_vrf->name_pretty); ++ return; ++ } ++ ++ srv6_locator_free(bgp_vrf->tovpn_sid_locator); ++ bgp_vrf->tovpn_sid_locator = NULL; + + if (bgp_vrf->tovpn_sid) { ++ ctx.vrf_id = bgp_vrf->vrf_id; ++ ctx.behavior = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; ++ bgp_zebra_release_srv6_sid(&ctx); ++ + sid_unregister(bgp_vpn, bgp_vrf->tovpn_sid); + XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); + } +@@ -1829,8 +1859,9 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ + + /* Set SID for SRv6 VPN */ + if (from_bgp->vpn_policy[afi].tovpn_sid_locator) { +- struct srv6_locator_chunk *locator = ++ struct srv6_locator *locator = + from_bgp->vpn_policy[afi].tovpn_sid_locator; ++ + encode_label( + from_bgp->vpn_policy[afi].tovpn_sid_transpose_label, + &label); +@@ -1871,8 +1902,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ + .tovpn_sid_locator->prefix.prefix, + sizeof(struct in6_addr)); + } else if (from_bgp->tovpn_sid_locator) { +- struct srv6_locator_chunk *locator = +- from_bgp->tovpn_sid_locator; ++ struct srv6_locator *locator = from_bgp->tovpn_sid_locator; ++ + encode_label(from_bgp->tovpn_sid_transpose_label, &label); + static_attr.srv6_l3vpn = + XCALLOC(MTYPE_BGP_SRV6_L3VPN, +diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h +index cd2589996..0593a736d 100644 +--- a/bgpd/bgp_mplsvpn.h ++++ b/bgpd/bgp_mplsvpn.h +@@ -429,6 +429,8 @@ struct bgp_mplsvpn_nh_label_bind_cache *bgp_mplsvpn_nh_label_bind_find( + struct bgp_mplsvpn_nh_label_bind_cache_head *tree, struct prefix *p, + mpls_label_t orig_label); + void bgp_mplsvpn_nexthop_init(void); ++extern void sid_register(struct bgp *bgp, const struct in6_addr *sid, ++ const char *locator_name); + extern void sid_unregister(struct bgp *bgp, const struct in6_addr *sid); + + #endif /* _QUAGGA_BGP_MPLSVPN_H */ +diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c +index 9530a66fc..4b30f087c 100644 +--- a/bgpd/bgp_vty.c ++++ b/bgpd/bgp_vty.c +@@ -298,18 +298,11 @@ static const char *get_afi_safi_json_str(afi_t afi, safi_t safi) + /* unset srv6 locator */ + static int bgp_srv6_locator_unset(struct bgp *bgp) + { +- int ret; + struct listnode *node, *nnode; + struct srv6_locator_chunk *chunk; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + +- /* release chunk notification via ZAPI */ +- ret = bgp_zebra_srv6_manager_release_locator_chunk( +- bgp->srv6_locator_name); +- if (ret < 0) +- return -1; +- + /* refresh chunks */ + for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) { + listnode_delete(bgp->srv6_locator_chunks, chunk); +@@ -348,20 +341,28 @@ static int bgp_srv6_locator_unset(struct bgp *bgp) + continue; + + /* refresh vpnv4 tovpn_sid_locator */ +- srv6_locator_chunk_free( +- &bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator); ++ srv6_locator_free(bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator); ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = NULL; + + /* refresh vpnv6 tovpn_sid_locator */ +- srv6_locator_chunk_free( +- &bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator); ++ srv6_locator_free( ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator); ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = NULL; + + /* refresh per-vrf tovpn_sid_locator */ +- srv6_locator_chunk_free(&bgp_vrf->tovpn_sid_locator); ++ srv6_locator_free(bgp_vrf->tovpn_sid_locator); ++ bgp_vrf->tovpn_sid_locator = NULL; + } + + /* clear locator name */ + memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name)); + ++ /* clear SRv6 locator */ ++ if (bgp->srv6_locator) { ++ srv6_locator_free(bgp->srv6_locator); ++ bgp->srv6_locator = NULL; ++ } ++ + return 0; + } + +@@ -10515,7 +10516,7 @@ DEFPY (bgp_srv6_locator, + snprintf(bgp->srv6_locator_name, + sizeof(bgp->srv6_locator_name), "%s", name); + +- ret = bgp_zebra_srv6_manager_get_locator_chunk(name); ++ ret = bgp_zebra_srv6_manager_get_locator(name); + if (ret < 0) + return CMD_WARNING_CONFIG_FAILED; + +@@ -10566,6 +10567,17 @@ DEFPY (show_bgp_srv6, + return CMD_SUCCESS; + + vty_out(vty, "locator_name: %s\n", bgp->srv6_locator_name); ++ if (bgp->srv6_locator) { ++ vty_out(vty, " prefix: %pFX\n", &bgp->srv6_locator->prefix); ++ vty_out(vty, " block-length: %d\n", ++ bgp->srv6_locator->block_bits_length); ++ vty_out(vty, " node-length: %d\n", ++ bgp->srv6_locator->node_bits_length); ++ vty_out(vty, " func-length: %d\n", ++ bgp->srv6_locator->function_bits_length); ++ vty_out(vty, " arg-length: %d\n", ++ bgp->srv6_locator->argument_bits_length); ++ } + vty_out(vty, "locator_chunks:\n"); + for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) { + vty_out(vty, "- %pFX\n", &chunk->prefix); +diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c +index d553d3d27..03c0be0bf 100644 +--- a/bgpd/bgp_zebra.c ++++ b/bgpd/bgp_zebra.c +@@ -3359,11 +3359,278 @@ static int bgp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) + return 0; + } + ++/** ++ * Internal function to process an SRv6 locator ++ * ++ * @param locator The locator to be processed ++ */ ++static int bgp_zebra_process_srv6_locator_internal(struct srv6_locator *locator) ++{ ++ struct bgp *bgp = bgp_get_default(); ++ ++ if (!bgp || !bgp->srv6_enabled || !locator) ++ return -1; ++ ++ /* ++ * Check if the main BGP instance is configured to use the received ++ * locator ++ */ ++ if (strcmp(bgp->srv6_locator_name, locator->name) != 0) { ++ zlog_err("%s: SRv6 Locator name unmatch %s:%s", __func__, ++ bgp->srv6_locator_name, locator->name); ++ return 0; ++ } ++ ++ zlog_info("%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); ++ ++ /* Store the locator in the main BGP instance */ ++ bgp->srv6_locator = srv6_locator_alloc(locator->name); ++ srv6_locator_copy(bgp->srv6_locator, locator); ++ ++ /* ++ * Process VPN-to-VRF and VRF-to-VPN leaks to advertise new locator ++ * and SIDs. ++ */ ++ vpn_leak_postchange_all(); ++ ++ return 0; ++} ++ ++static int bgp_zebra_srv6_sid_notify(ZAPI_CALLBACK_ARGS) ++{ ++ struct bgp *bgp = bgp_get_default(); ++ struct srv6_locator *locator; ++ struct srv6_sid_ctx ctx; ++ struct in6_addr sid_addr; ++ enum zapi_srv6_sid_notify note; ++ struct bgp *bgp_vrf; ++ struct vrf *vrf; ++ struct listnode *node, *nnode; ++ char buf[256]; ++ struct in6_addr *tovpn_sid; ++ struct prefix_ipv6 tmp_prefix; ++ uint32_t sid_func; ++ bool found = false; ++ ++ if (!bgp || !bgp->srv6_enabled) ++ return -1; ++ ++ if (!bgp->srv6_locator) { ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("%s: ignoring SRv6 SID notify: locator not set", ++ __func__); ++ return -1; ++ } ++ ++ /* Decode the received notification message */ ++ if (!zapi_srv6_sid_notify_decode(zclient->ibuf, &ctx, &sid_addr, ++ &sid_func, NULL, ¬e, NULL)) { ++ zlog_err("%s : error in msg decode", __func__); ++ return -1; ++ } ++ ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("%s: received SRv6 SID notify: ctx %s sid_value %pI6 %s", ++ __func__, srv6_sid_ctx2str(buf, sizeof(buf), &ctx), ++ &sid_addr, zapi_srv6_sid_notify2str(note)); ++ ++ /* Get the BGP instance for which the SID has been requested, if any */ ++ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp_vrf)) { ++ vrf = vrf_lookup_by_id(bgp_vrf->vrf_id); ++ if (!vrf) ++ continue; ++ ++ if (vrf->vrf_id == ctx.vrf_id) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("%s: ignoring SRv6 SID notify: No VRF suitable for received SID ctx %s sid_value %pI6", ++ __func__, ++ srv6_sid_ctx2str(buf, sizeof(buf), &ctx), ++ &sid_addr); ++ return -1; ++ } ++ ++ /* Handle notification */ ++ switch (note) { ++ case ZAPI_SRV6_SID_ALLOCATED: ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("SRv6 SID %pI6 %s : ALLOCATED", &sid_addr, ++ srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); ++ ++ /* Verify that the received SID belongs to the configured locator */ ++ tmp_prefix.family = AF_INET6; ++ tmp_prefix.prefixlen = IPV6_MAX_BITLEN; ++ tmp_prefix.prefix = sid_addr; ++ ++ if (!prefix_match((struct prefix *)&bgp->srv6_locator->prefix, ++ (struct prefix *)&tmp_prefix)) ++ return -1; ++ ++ /* Get label */ ++ uint8_t func_len = bgp->srv6_locator->function_bits_length; ++ uint8_t shift_len = BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH - ++ func_len; ++ ++ int label = sid_func << shift_len; ++ ++ /* Un-export VPN to VRF routes */ ++ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp, ++ bgp_vrf); ++ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp, ++ bgp_vrf); ++ ++ locator = srv6_locator_alloc(bgp->srv6_locator_name); ++ srv6_locator_copy(locator, bgp->srv6_locator); ++ ++ /* Store SID, locator, and label */ ++ tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); ++ *tovpn_sid = sid_addr; ++ if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT6) { ++ XFREE(MTYPE_BGP_SRV6_SID, ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); ++ srv6_locator_free( ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator); ++ sid_unregister(bgp, ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); ++ ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid = tovpn_sid; ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = locator; ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_transpose_label = ++ label; ++ } else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT4) { ++ XFREE(MTYPE_BGP_SRV6_SID, ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); ++ srv6_locator_free( ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator); ++ sid_unregister(bgp, ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); ++ ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid = tovpn_sid; ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = locator; ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_transpose_label = ++ label; ++ } else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT46) { ++ XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); ++ srv6_locator_free(bgp_vrf->tovpn_sid_locator); ++ sid_unregister(bgp, bgp_vrf->tovpn_sid); ++ ++ bgp_vrf->tovpn_sid = tovpn_sid; ++ bgp_vrf->tovpn_sid_locator = locator; ++ bgp_vrf->tovpn_sid_transpose_label = label; ++ } else { ++ srv6_locator_free(locator); ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("Unsupported behavior. Not assigned SRv6 SID: %s %pI6", ++ srv6_sid_ctx2str(buf, sizeof(buf), ++ &ctx), ++ &sid_addr); ++ return -1; ++ } ++ ++ /* Register the new SID */ ++ sid_register(bgp, tovpn_sid, bgp->srv6_locator_name); ++ ++ /* Export VPN to VRF routes */ ++ vpn_leak_postchange_all(); ++ ++ break; ++ case ZAPI_SRV6_SID_RELEASED: ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("SRv6 SID %pI6 %s: RELEASED", &sid_addr, ++ srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); ++ ++ /* Un-export VPN to VRF routes */ ++ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp, ++ bgp_vrf); ++ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp, ++ bgp_vrf); ++ ++ /* Remove SID, locator, and label */ ++ if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT6) { ++ XFREE(MTYPE_BGP_SRV6_SID, ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); ++ if (bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator) { ++ srv6_locator_free(bgp->vpn_policy[AFI_IP6] ++ .tovpn_sid_locator); ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = ++ NULL; ++ } ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_transpose_label = ++ 0; ++ ++ /* Unregister the SID */ ++ sid_unregister(bgp, ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); ++ } else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT4) { ++ XFREE(MTYPE_BGP_SRV6_SID, ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); ++ if (bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator) { ++ srv6_locator_free(bgp->vpn_policy[AFI_IP] ++ .tovpn_sid_locator); ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = ++ NULL; ++ } ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_transpose_label = ++ 0; ++ ++ /* Unregister the SID */ ++ sid_unregister(bgp, ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); ++ } else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT46) { ++ XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); ++ if (bgp_vrf->tovpn_sid_locator) { ++ srv6_locator_free(bgp_vrf->tovpn_sid_locator); ++ bgp_vrf->tovpn_sid_locator = NULL; ++ } ++ bgp_vrf->tovpn_sid_transpose_label = 0; ++ ++ /* Unregister the SID */ ++ sid_unregister(bgp, bgp_vrf->tovpn_sid); ++ } else { ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("Unsupported behavior. Not assigned SRv6 SID: %s %pI6", ++ srv6_sid_ctx2str(buf, sizeof(buf), ++ &ctx), ++ &sid_addr); ++ return -1; ++ } ++ ++ /* Export VPN to VRF routes*/ ++ vpn_leak_postchange_all(); ++ break; ++ case ZAPI_SRV6_SID_FAIL_ALLOC: ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("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: ++ if (BGP_DEBUG(zebra, ZEBRA)) ++ zlog_debug("%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 int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS) + { + struct srv6_locator loc = {}; + struct bgp *bgp = bgp_get_default(); +- const char *loc_name = bgp->srv6_locator_name; + + if (!bgp || !bgp->srv6_enabled) + return 0; +@@ -3371,10 +3638,7 @@ static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS) + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + +- if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0) +- return -1; +- +- return 0; ++ return bgp_zebra_process_srv6_locator_internal(&loc); + } + + static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) +@@ -3382,7 +3646,8 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) + struct srv6_locator loc = {}; + struct bgp *bgp = bgp_get_default(); + struct listnode *node, *nnode; +- struct srv6_locator_chunk *chunk, *tovpn_sid_locator; ++ struct srv6_locator_chunk *chunk; ++ struct srv6_locator *tovpn_sid_locator; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + struct in6_addr *tovpn_sid; +@@ -3394,6 +3659,12 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + ++ // clear SRv6 locator ++ if (bgp->srv6_locator) { ++ srv6_locator_free(bgp->srv6_locator); ++ bgp->srv6_locator = NULL; ++ } ++ + // refresh chunks + for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) + if (prefix_match((struct prefix *)&loc.prefix, +@@ -3470,10 +3741,12 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) + tmp_prefi.prefixlen = IPV6_MAX_BITLEN; + tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix; + if (prefix_match((struct prefix *)&loc.prefix, +- (struct prefix *)&tmp_prefi)) +- srv6_locator_chunk_free( +- &bgp_vrf->vpn_policy[AFI_IP] +- .tovpn_sid_locator); ++ (struct prefix *)&tmp_prefi)) { ++ srv6_locator_free(bgp_vrf->vpn_policy[AFI_IP] ++ .tovpn_sid_locator); ++ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = ++ NULL; ++ } + } + + /* refresh vpnv6 tovpn_sid_locator */ +@@ -3484,10 +3757,12 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) + tmp_prefi.prefixlen = IPV6_MAX_BITLEN; + tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix; + if (prefix_match((struct prefix *)&loc.prefix, +- (struct prefix *)&tmp_prefi)) +- srv6_locator_chunk_free( +- &bgp_vrf->vpn_policy[AFI_IP6] +- .tovpn_sid_locator); ++ (struct prefix *)&tmp_prefi)) { ++ srv6_locator_free(bgp_vrf->vpn_policy[AFI_IP6] ++ .tovpn_sid_locator); ++ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = ++ NULL; ++ } + } + + /* refresh per-vrf tovpn_sid_locator */ +@@ -3497,9 +3772,10 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) + tmp_prefi.prefixlen = IPV6_MAX_BITLEN; + tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix; + if (prefix_match((struct prefix *)&loc.prefix, +- (struct prefix *)&tmp_prefi)) +- srv6_locator_chunk_free( +- &bgp_vrf->tovpn_sid_locator); ++ (struct prefix *)&tmp_prefi)) { ++ srv6_locator_free(bgp_vrf->tovpn_sid_locator); ++ bgp_vrf->tovpn_sid_locator = NULL; ++ } + } + } + +@@ -3536,6 +3812,7 @@ static zclient_handler *const bgp_handlers[] = { + [ZEBRA_SRV6_LOCATOR_DELETE] = bgp_zebra_process_srv6_locator_delete, + [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] = + bgp_zebra_process_srv6_locator_chunk, ++ [ZEBRA_SRV6_SID_NOTIFY] = bgp_zebra_srv6_sid_notify, + }; + + static int bgp_if_new_hook(struct interface *ifp) +@@ -4068,6 +4345,89 @@ int bgp_zebra_srv6_manager_release_locator_chunk(const char *name) + return srv6_manager_release_locator_chunk(zclient, name); + } + ++/** ++ * Ask the SRv6 Manager (zebra) about a specific locator ++ * ++ * @param name Locator name ++ * @return 0 on success, -1 otherwise ++ */ ++int bgp_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); ++} ++ ++/** ++ * Ask the SRv6 Manager (zebra) to allocate a SID. ++ * ++ * Optionally, it is possible to provide an IPv6 address (sid_value parameter). ++ * ++ * When sid_value is provided, the SRv6 Manager allocates the requested SID ++ * address, if the request can be satisfied (explicit allocation). ++ * ++ * When sid_value is not provided, the SRv6 Manager allocates any available SID ++ * from the provided locator (dynamic allocation). ++ * ++ * @param ctx Context to be associated with the request SID ++ * @param sid_value IPv6 address to be associated with the requested SID (optional) ++ * @param locator_name Name of the locator from which the SID must be allocated ++ * @param sid_func SID Function allocated by the SRv6 Manager. ++ */ ++bool bgp_zebra_request_srv6_sid(const struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, ++ const char *locator_name, uint32_t *sid_func) ++{ ++ int ret; ++ ++ if (!ctx || !locator_name) ++ return false; ++ ++ /* ++ * Send the Get SRv6 SID request to the SRv6 Manager and check the ++ * result ++ */ ++ ret = srv6_manager_get_sid(zclient, ctx, sid_value, locator_name, ++ sid_func); ++ if (ret < 0) { ++ zlog_warn("%s: error getting SRv6 SID!", __func__); ++ return false; ++ } ++ ++ return true; ++} ++ ++/** ++ * Ask the SRv6 Manager (zebra) to release a previously allocated SID. ++ * ++ * This function is used to tell the SRv6 Manager that BGP no longer intends ++ * to use the SID. ++ * ++ * @param ctx Context to be associated with the SID to be released ++ */ ++void bgp_zebra_release_srv6_sid(const struct srv6_sid_ctx *ctx) ++{ ++ int ret; ++ ++ if (!ctx) ++ return; ++ ++ /* ++ * Send the Release SRv6 SID request to the SRv6 Manager and check the ++ * result ++ */ ++ ret = srv6_manager_release_sid(zclient, ctx); ++ if (ret < 0) { ++ zlog_warn("%s: error releasing SRv6 SID!", __func__); ++ return; ++ } ++} ++ + void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label, + ifindex_t ifindex, vrf_id_t vrf_id, + enum lsp_types_t ltype, struct prefix *p, +diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h +index d0b3351ad..1471770dc 100644 +--- a/bgpd/bgp_zebra.h ++++ b/bgpd/bgp_zebra.h +@@ -120,6 +120,13 @@ extern int bgp_zebra_update(struct bgp *bgp, afi_t afi, safi_t safi, + extern int bgp_zebra_stale_timer_update(struct bgp *bgp); + extern int bgp_zebra_srv6_manager_get_locator_chunk(const char *name); + extern int bgp_zebra_srv6_manager_release_locator_chunk(const char *name); ++extern int bgp_zebra_srv6_manager_get_locator(const char *name); ++extern bool bgp_zebra_request_srv6_sid(const struct srv6_sid_ctx *ctx, ++ struct in6_addr *sid_value, ++ const char *locator_name, ++ uint32_t *sid_func); ++extern void bgp_zebra_release_srv6_sid(const struct srv6_sid_ctx *ctx); ++ + extern void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label, + ifindex_t index, vrf_id_t vrfid, + enum lsp_types_t ltype, +diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c +index cf52053c4..06a0e0367 100644 +--- a/bgpd/bgpd.c ++++ b/bgpd/bgpd.c +@@ -1478,9 +1478,11 @@ static void bgp_srv6_init(struct bgp *bgp) + static void bgp_srv6_cleanup(struct bgp *bgp) + { + for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { +- if (bgp->vpn_policy[afi].tovpn_sid_locator != NULL) +- srv6_locator_chunk_free( +- &bgp->vpn_policy[afi].tovpn_sid_locator); ++ if (bgp->vpn_policy[afi].tovpn_sid_locator != NULL) { ++ srv6_locator_free( ++ bgp->vpn_policy[afi].tovpn_sid_locator); ++ bgp->vpn_policy[afi].tovpn_sid_locator = NULL; ++ } + if (bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent != NULL) + XFREE(MTYPE_BGP_SRV6_SID, + bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent); +@@ -1491,8 +1493,10 @@ static void bgp_srv6_cleanup(struct bgp *bgp) + } + } + +- if (bgp->tovpn_sid_locator != NULL) +- srv6_locator_chunk_free(&bgp->tovpn_sid_locator); ++ if (bgp->tovpn_sid_locator != NULL) { ++ srv6_locator_free(bgp->tovpn_sid_locator); ++ bgp->tovpn_sid_locator = NULL; ++ } + if (bgp->tovpn_zebra_vrf_sid_last_sent != NULL) + XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent); + if (bgp->tovpn_sid != NULL) { +@@ -1504,6 +1508,9 @@ static void bgp_srv6_cleanup(struct bgp *bgp) + list_delete(&bgp->srv6_locator_chunks); + if (bgp->srv6_functions) + list_delete(&bgp->srv6_functions); ++ ++ srv6_locator_free(bgp->srv6_locator); ++ bgp->srv6_locator = NULL; + } + + /* Allocate new peer object, implicitely locked. */ +diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h +index 057e26a83..b1feb8bde 100644 +--- a/bgpd/bgpd.h ++++ b/bgpd/bgpd.h +@@ -249,7 +249,7 @@ struct vpn_policy { + */ + uint32_t tovpn_sid_index; /* unset => set to 0 */ + struct in6_addr *tovpn_sid; +- struct srv6_locator_chunk *tovpn_sid_locator; ++ struct srv6_locator *tovpn_sid_locator; + uint32_t tovpn_sid_transpose_label; + struct in6_addr *tovpn_zebra_vrf_sid_last_sent; + }; +@@ -813,11 +813,12 @@ struct bgp { + /* BGP VPN SRv6 backend */ + bool srv6_enabled; + char srv6_locator_name[SRV6_LOCNAME_SIZE]; ++ struct srv6_locator *srv6_locator; + struct list *srv6_locator_chunks; + struct list *srv6_functions; + uint32_t tovpn_sid_index; /* unset => set to 0 */ + struct in6_addr *tovpn_sid; +- struct srv6_locator_chunk *tovpn_sid_locator; ++ struct srv6_locator *tovpn_sid_locator; + uint32_t tovpn_sid_transpose_label; + struct in6_addr *tovpn_zebra_vrf_sid_last_sent; + diff --git a/src/sonic-frr/patch/series b/src/sonic-frr/patch/series index 3c68abe241e8..8381f11a0d7b 100644 --- a/src/sonic-frr/patch/series +++ b/src/sonic-frr/patch/series @@ -43,3 +43,8 @@ 0061-Set-multipath-to-514-and-disable-bgp-vnc-for-optimiz.patch 0062-zebra-lib-use-internal-rbtree-per-ns.patch 0063-Patch-to-send-tag-value-associated-with-route-via-ne.patch +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 +0068-bgpd-Extend-BGP-to-communicate-with-the-SRv6-SID-Manager-to-allocate-release-SRv6-SIDs.patch