diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index e9050c5aec..89aed1ba69 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -4507,7 +4507,9 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, stream_put(s, &attr->srv6_l3vpn->sid, sizeof(attr->srv6_l3vpn->sid)); /* sid */ stream_putc(s, 0); /* sid_flags */ - stream_putw(s, 0xffff); /* endpoint */ + stream_putw(s, + attr->srv6_l3vpn + ->endpoint_behavior); /* endpoint */ stream_putc(s, 0); /* reserved */ stream_putc( s, diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 9a25450afa..18cb90763c 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1554,13 +1554,22 @@ 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 = + from_bgp->vpn_policy[afi].tovpn_sid_locator; encode_label( from_bgp->vpn_policy[afi].tovpn_sid_transpose_label, &label); static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, sizeof(struct bgp_attr_srv6_l3vpn)); static_attr.srv6_l3vpn->sid_flags = 0x00; - static_attr.srv6_l3vpn->endpoint_behavior = 0xffff; + static_attr.srv6_l3vpn->endpoint_behavior = + afi == AFI_IP + ? (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT4) + : (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT6); static_attr.srv6_l3vpn->loc_block_len = from_bgp->vpn_policy[afi] .tovpn_sid_locator->block_bits_length; @@ -1587,12 +1596,17 @@ 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; encode_label(from_bgp->tovpn_sid_transpose_label, &label); static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, sizeof(struct bgp_attr_srv6_l3vpn)); static_attr.srv6_l3vpn->sid_flags = 0x00; - static_attr.srv6_l3vpn->endpoint_behavior = 0xffff; + static_attr.srv6_l3vpn->endpoint_behavior = + CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID) + ? SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID + : SRV6_ENDPOINT_BEHAVIOR_END_DT46; static_attr.srv6_l3vpn->loc_block_len = from_bgp->tovpn_sid_locator->block_bits_length; static_attr.srv6_l3vpn->loc_node_len = diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 0a843b9283..db43266d68 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -810,6 +810,36 @@ and this section also helps that case. ! ... +.. clicmd:: behavior usid + + Specify the SRv6 locator as a Micro-segment (uSID) locator. When a locator is + specified as a uSID locator, all the SRv6 SIDs allocated from the locator by the routing + protocols are bound to the SRv6 uSID behaviors. For example, if you configure BGP to use + a locator specified as a uSID locator, BGP instantiates and advertises SRv6 uSID behaviors + (e.g., ``uDT4`` / ``uDT6`` / ``uDT46``) instead of classic SRv6 behaviors + (e.g., ``End.DT4`` / ``End.DT6`` / ``End.DT46``). + +:: + + 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 block-len 32 node-len 16 func-bits 16 + router(config-srv6-locator)# behavior usid + + router(config-srv6-locator)# show run + ... + segment-routing + srv6 + locators + locator loc1 + prefix fc00:0:1::/48 + behavior usid + ! + ... + .. _multicast-rib-commands: Multicast RIB Commands diff --git a/lib/srv6.c b/lib/srv6.c index 1c2c8913d5..5cd82080f5 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -241,6 +241,10 @@ json_object *srv6_locator_json(const struct srv6_locator *loc) 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", loc->status_up); @@ -286,6 +290,10 @@ json_object *srv6_locator_detailed_json(const struct srv6_locator *loc) 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 18d5bdebc2..acfb0631cc 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -92,6 +92,9 @@ struct srv6_locator { bool status_up; struct list *chunks; + uint8_t flags; +#define SRV6_LOCATOR_USID (1 << 0) /* The SRv6 Locator is a uSID Locator */ + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(srv6_locator); @@ -116,6 +119,23 @@ struct srv6_locator_chunk { uint8_t proto; uint16_t instance; uint32_t session_id; + + uint8_t flags; +}; + +/* + * SRv6 Endpoint Behavior codepoints, as defined by IANA in + * https://www.iana.org/assignments/segment-routing/segment-routing.xhtml + */ +enum srv6_endpoint_behavior_codepoint { + SRV6_ENDPOINT_BEHAVIOR_RESERVED = 0x0000, + SRV6_ENDPOINT_BEHAVIOR_END_DT6 = 0x0012, + SRV6_ENDPOINT_BEHAVIOR_END_DT4 = 0x0013, + SRV6_ENDPOINT_BEHAVIOR_END_DT46 = 0x0014, + SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID = 0x003E, + SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID = 0x003F, + SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID = 0x0040, + SRV6_ENDPOINT_BEHAVIOR_OPAQUE = 0xFFFF, }; struct nexthop_srv6 { diff --git a/lib/zclient.c b/lib/zclient.c index 2517773dc4..fd6eb7db0d 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1088,6 +1088,7 @@ int zapi_srv6_locator_chunk_encode(struct stream *s, stream_putc(s, c->node_bits_length); stream_putc(s, c->function_bits_length); stream_putc(s, c->argument_bits_length); + stream_putc(s, c->flags); return 0; } @@ -1109,6 +1110,7 @@ int zapi_srv6_locator_chunk_decode(struct stream *s, STREAM_GETC(s, c->node_bits_length); STREAM_GETC(s, c->function_bits_length); STREAM_GETC(s, c->argument_bits_length); + STREAM_GETC(s, c->flags); return 0; stream_failure: diff --git a/tests/topotests/srv6_locator_usid/__init__.py b/tests/topotests/srv6_locator_usid/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_1.json b/tests/topotests/srv6_locator_usid/expected_chunks_1.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_1.json @@ -0,0 +1 @@ +[] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_2.json b/tests/topotests/srv6_locator_usid/expected_chunks_2.json new file mode 100644 index 0000000000..304d73807c --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_2.json @@ -0,0 +1,8 @@ +[ + { + "name": "loc1", + "chunks": [ + "fc00:0:1::/48" + ] + } +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_3.json b/tests/topotests/srv6_locator_usid/expected_chunks_3.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_3.json @@ -0,0 +1 @@ +[] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_4.json b/tests/topotests/srv6_locator_usid/expected_chunks_4.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_4.json @@ -0,0 +1 @@ +[] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_5.json b/tests/topotests/srv6_locator_usid/expected_chunks_5.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_5.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_6.json b/tests/topotests/srv6_locator_usid/expected_chunks_6.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_6.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_7.json b/tests/topotests/srv6_locator_usid/expected_chunks_7.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_7.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_chunks_8.json b/tests/topotests/srv6_locator_usid/expected_chunks_8.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_chunks_8.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator_usid/expected_locators_1.json b/tests/topotests/srv6_locator_usid/expected_locators_1.json new file mode 100644 index 0000000000..c0eeacc09a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_1.json @@ -0,0 +1,20 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_2.json b/tests/topotests/srv6_locator_usid/expected_locators_2.json new file mode 100644 index 0000000000..38a6739d64 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_2.json @@ -0,0 +1,20 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "sharp" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_3.json b/tests/topotests/srv6_locator_usid/expected_locators_3.json new file mode 100644 index 0000000000..c0eeacc09a --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_3.json @@ -0,0 +1,20 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_4.json b/tests/topotests/srv6_locator_usid/expected_locators_4.json new file mode 100644 index 0000000000..b1528ff111 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_4.json @@ -0,0 +1,35 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + }, + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_5.json b/tests/topotests/srv6_locator_usid/expected_locators_5.json new file mode 100644 index 0000000000..b6acc238a7 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_5.json @@ -0,0 +1,36 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + }, + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_6.json b/tests/topotests/srv6_locator_usid/expected_locators_6.json new file mode 100644 index 0000000000..b1528ff111 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_6.json @@ -0,0 +1,35 @@ +{ + "locators":[ + { + "name": "loc1", + "prefix": "fc00:0:1::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "behavior": "usid", + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:1::/48", + "proto": "system" + } + ] + }, + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "statusUp": true, + "chunks": [ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_7.json b/tests/topotests/srv6_locator_usid/expected_locators_7.json new file mode 100644 index 0000000000..e965e02170 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_7.json @@ -0,0 +1,19 @@ +{ + "locators":[ + { + "name": "loc2", + "prefix": "fc00:0:2::/48", + "statusUp": true, + "blockBitsLength": 32, + "nodeBitsLength": 16, + "functionBitsLength": 16, + "argumentBitsLength": 0, + "chunks":[ + { + "prefix": "fc00:0:2::/48", + "proto": "system" + } + ] + } + ] +} diff --git a/tests/topotests/srv6_locator_usid/expected_locators_8.json b/tests/topotests/srv6_locator_usid/expected_locators_8.json new file mode 100644 index 0000000000..6e1b993ca8 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/expected_locators_8.json @@ -0,0 +1,4 @@ +{ + "locators":[ + ] +} diff --git a/tests/topotests/srv6_locator_usid/r1/setup.sh b/tests/topotests/srv6_locator_usid/r1/setup.sh new file mode 100644 index 0000000000..36ed713f24 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/r1/setup.sh @@ -0,0 +1,2 @@ +ip link add dummy0 type dummy +ip link set dummy0 up diff --git a/tests/topotests/srv6_locator_usid/r1/sharpd.conf b/tests/topotests/srv6_locator_usid/r1/sharpd.conf new file mode 100644 index 0000000000..d46085935c --- /dev/null +++ b/tests/topotests/srv6_locator_usid/r1/sharpd.conf @@ -0,0 +1,7 @@ +hostname r1 +! +log stdout notifications +log monitor notifications +log commands +log file sharpd.log debugging +! diff --git a/tests/topotests/srv6_locator_usid/r1/zebra.conf b/tests/topotests/srv6_locator_usid/r1/zebra.conf new file mode 100644 index 0000000000..78ef1e9d40 --- /dev/null +++ b/tests/topotests/srv6_locator_usid/r1/zebra.conf @@ -0,0 +1,20 @@ +hostname r1 +! +! debug zebra events +! debug zebra rib detailed +! +log stdout notifications +log monitor notifications +log commands +log file zebra.log debugging +! +segment-routing + srv6 + locators + locator loc1 + prefix fc00:0:1::/48 func-bits 16 block-len 32 node-len 16 + behavior usid + ! + ! + ! +! diff --git a/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py b/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py new file mode 100755 index 0000000000..37fd736d2b --- /dev/null +++ b/tests/topotests/srv6_locator_usid/test_srv6_locator_usid.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python + +# Copyright (c) 2022, University of Rome Tor Vergata +# Authored by Carmine Scarpitta +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_srv6_locator_usid.py: +Test for SRv6 Locator uSID on zebra +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def setup_module(mod): + tgen = Topogen({None: "r1"}, mod.__name__) + tgen.start_topology() + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join( + CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_SHARP, os.path.join( + CWD, "{}/sharpd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def _check_srv6_locator(router, expected_locator_file): + logger.info("checking zebra locator status") + output = json.loads( + router.vtysh_cmd("show segment-routing srv6 locator json") + ) + expected = open_json_file("{}/{}".format(CWD, expected_locator_file)) + return topotest.json_cmp(output, expected) + + +def _check_sharpd_chunk(router, expected_chunk_file): + logger.info("checking sharpd locator chunk status") + output = json.loads( + router.vtysh_cmd("show sharp segment-routing srv6 json") + ) + expected = open_json_file("{}/{}".format(CWD, expected_chunk_file)) + return topotest.json_cmp(output, expected) + + +def check_srv6_locator(router, expected_file): + func = functools.partial(_check_srv6_locator, router, expected_file) + success, result = topotest.run_and_expect(func, None, count=5, wait=3) + assert result is None, "Failed" + + +def check_sharpd_chunk(router, expected_file): + func = functools.partial(_check_sharpd_chunk, router, expected_file) + success, result = topotest.run_and_expect(func, None, count=5, wait=3) + assert result is None, "Failed" + + +def test_srv6_usid_locator_configuration(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Verify SRv6 Locators instantiated from config file") + check_srv6_locator(router, "expected_locators_1.json") + check_sharpd_chunk(router, "expected_chunks_1.json") + + +def test_srv6_usid_locator_get_chunk(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Get chunk for the locator loc1") + router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc1") + check_srv6_locator(router, "expected_locators_2.json") + check_sharpd_chunk(router, "expected_chunks_2.json") + + +def test_srv6_usid_locator_release_chunk(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Release chunk for the locator loc1") + router.vtysh_cmd("sharp srv6-manager release-locator-chunk loc1") + check_srv6_locator(router, "expected_locators_3.json") + check_sharpd_chunk(router, "expected_chunks_3.json") + + +def test_srv6_usid_locator_create_locator(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Create an additional SRv6 Locator") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc2 + prefix fc00:0:2::/48 func-bits 16 block-len 32 node-len 16 + """ + ) + check_srv6_locator(router, "expected_locators_4.json") + check_sharpd_chunk(router, "expected_chunks_4.json") + + +def test_srv6_usid_locator_set_behavior_usid(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info( + "Specify the SRv6 Locator loc2 as a Micro-segment (uSID) Locator" + ) + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc2 + behavior usid + """ + ) + check_srv6_locator(router, "expected_locators_5.json") + check_sharpd_chunk(router, "expected_chunks_5.json") + + +def test_srv6_usid_locator_unset_behavior_usid(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Clear Micro-segment (uSID) Locator flag for loc2") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc2 + no behavior usid + """ + ) + check_srv6_locator(router, "expected_locators_6.json") + check_sharpd_chunk(router, "expected_chunks_6.json") + + +def test_srv6_usid_locator_delete(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info( + "Delete locator loc1 and verify that the chunk is released automatically" + ) + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_srv6_locator(router, "expected_locators_7.json") + check_sharpd_chunk(router, "expected_chunks_7.json") + + +def test_srv6_usid_locator_delete_all(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r1"] + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + + logger.info("Delete all the SRv6 configuration") + router.vtysh_cmd( + """ + configure terminal + segment-routing + no srv6 + """ + ) + check_srv6_locator(router, "expected_locators_8.json") + check_sharpd_chunk(router, "expected_chunks_8.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index a3db53f296..85eb6b3451 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2710,6 +2710,7 @@ int zsend_srv6_manager_get_locator_chunk_response(struct zserv *client, chunk.keep = 0; chunk.proto = client->proto; chunk.instance = client->instance; + chunk.flags = loc->flags; zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK, vrf_id); zapi_srv6_locator_chunk_encode(s, &chunk); diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 36506cacc7..d61e4f8045 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -177,6 +177,58 @@ struct srv6_locator *zebra_srv6_locator_lookup(const char *name) return NULL; } +void zebra_notify_srv6_locator_add(struct srv6_locator *locator) +{ + struct listnode *node; + struct zserv *client; + + /* + * Notify new locator info to zclients. + * + * The srv6 locators and their prefixes are managed by zserv(zebra). + * And an actual configuration the srv6 sid in the srv6 locator is done + * by zclient(bgpd, isisd, etc). The configuration of each locator + * allocation and specify it by zserv and zclient should be + * asynchronous. For that, zclient should be received the event via + * ZAPI when a srv6 locator is added on zebra. + * Basically, in SRv6, adding/removing SRv6 locators is performed less + * frequently than adding rib entries, so a broad to all zclients will + * not degrade the overall performance of FRRouting. + */ + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) + zsend_zebra_srv6_locator_add(client, locator); +} + +void zebra_notify_srv6_locator_delete(struct srv6_locator *locator) +{ + struct listnode *n; + struct srv6_locator_chunk *c; + struct zserv *client; + + /* + * Notify deleted locator info to zclients if needed. + * + * zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and + * uses it for its own purpose. For example, in the case of BGP L3VPN, + * the SID assigned to vpn unicast rib will be given. + * And when the locator is deleted by zserv(zebra), those SIDs need to + * be withdrawn. The zclient must initiate the withdrawal of the SIDs + * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the + * owner of each chunk. + */ + 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; + } + zsend_zebra_srv6_locator_delete(client, locator); + } +} + struct zebra_srv6 *zebra_srv6_get_default(void) { static struct zebra_srv6 srv6; diff --git a/zebra/zebra_srv6.h b/zebra/zebra_srv6.h index 84fcc305bc..f320b9ca0f 100644 --- a/zebra/zebra_srv6.h +++ b/zebra/zebra_srv6.h @@ -61,6 +61,9 @@ extern void zebra_srv6_locator_add(struct srv6_locator *locator); extern void zebra_srv6_locator_delete(struct srv6_locator *locator); extern struct srv6_locator *zebra_srv6_locator_lookup(const char *name); +void zebra_notify_srv6_locator_add(struct srv6_locator *locator); +void zebra_notify_srv6_locator_delete(struct srv6_locator *locator); + extern void zebra_srv6_init(void); extern struct zebra_srv6 *zebra_srv6_get_default(void); extern bool zebra_srv6_is_enable(void); diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index e6810bdc56..1221365d4d 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -172,6 +172,9 @@ DEFUN (show_srv6_locator_detail, 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"); + vty_out(vty, "Chunks:\n"); for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, node, chunk)) { @@ -369,6 +372,38 @@ DEFPY (locator_prefix, return CMD_SUCCESS; } +DEFPY (locator_behavior, + locator_behavior_cmd, + "[no] behavior usid", + NO_STR + "Configure SRv6 behavior\n" + "Specify SRv6 behavior uSID\n") +{ + VTY_DECLVAR_CONTEXT(srv6_locator, locator); + + if (no && !CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + /* SRv6 locator uSID flag already unset, nothing to do */ + return CMD_SUCCESS; + + if (!no && CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + /* SRv6 locator uSID flag already set, nothing to do */ + return CMD_SUCCESS; + + /* Remove old locator from zclients */ + zebra_notify_srv6_locator_delete(locator); + + /* Set/Unset the SRV6_LOCATOR_USID */ + if (no) + UNSET_FLAG(locator->flags, SRV6_LOCATOR_USID); + else + SET_FLAG(locator->flags, SRV6_LOCATOR_USID); + + /* Notify the new locator to zclients */ + zebra_notify_srv6_locator_add(locator); + + return CMD_SUCCESS; +} + static int zebra_sr_config(struct vty *vty) { struct zebra_srv6 *srv6 = zebra_srv6_get_default(); @@ -399,6 +434,8 @@ static int zebra_sr_config(struct vty *vty) if (locator->argument_bits_length) vty_out(vty, " arg-len %u", locator->argument_bits_length); + if (CHECK_FLAG(locator->flags, SRV6_LOCATOR_USID)) + vty_out(vty, " behavior usid"); vty_out(vty, "\n"); vty_out(vty, " exit\n"); vty_out(vty, " !\n"); @@ -435,6 +472,7 @@ void zebra_srv6_vty_init(void) /* Command for configuration */ install_element(SRV6_LOC_NODE, &locator_prefix_cmd); + install_element(SRV6_LOC_NODE, &locator_behavior_cmd); /* Command for operation */ install_element(VIEW_NODE, &show_srv6_locator_cmd);