diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index ac29b1c7d4ca..492359c04f7b 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -894,6 +894,9 @@ and this section also helps that case. function development holds the owner of the chunk of this locator, and no other routing protocol will use this area. + Additionally, if a static local SID is configured, + it will also be displayed in this section. + :: router# show segment-routing srv6 locator loc1 detail @@ -901,12 +904,17 @@ and this section also helps that case. Prefix: 2001:db8:1:1::/64 Chunks: - prefix: 2001:db8:1:1::/64, owner: system + sids: + -sid 2001:db8:1:1:fff1:11::/128 + behavior End.uDT46 + vrf Vrf1 router# show segment-routing srv6 locator loc2 detail Name: loc2 Prefix: 2001:db8:2:2::/64 Chunks: - prefix: 2001:db8:2:2::/64, owner: sharp + sids: .. clicmd:: segment-routing @@ -991,6 +999,43 @@ and this section also helps that case. ! ... +.. clicmd:: sid WORD behavior [vrf VRF] + + Specify the locator sid manually. Configuring a local sid in a purely static mode + by specifying the sid value would generate a unique mySID table entry. + This feature will support the configuration of static SRv6 decapsulation on the system. + + It supports four parameter options, corresponding to the following functions: + End.uN, End.uDT4, End.uDT6, End.uDT46 + + When configuring the local sid, if the action is set to 'uN', no vrf should be set. + While for any other action, it is necessary to specify a specific vrf. + +:: + + router# configure terminal + router(config)# segment-routing + router(config-sr)# srv6 + router(config-srv6)# locators + router(config-srv6-locators)# locator loc1 + router(config-srv6-locator)# prefix fcbb:bbbb:1::/48 block-len 32 node-len 16 func-bits 16 + router(config-srv6-locator)# sid fcbb:bbbb:1:fe01:: behavior uDT6 vrf Vrf1 + router(config-srv6-locator)# sid fcbb:bbbb:1:fe02:abcd:: behavior uDT4 vrf Vrf1 + router(config-srv6-locator)# sid fcbb:bbbb:1:fe03:abcd:abcd:: behavior uDT46 vrf Vrf2 + + router(config-srv6-locator)# show run + ... + segment-routing + srv6 + locators + locator loc1 + prefix fcbb:bbbb:1::/48 block-len 32 node-len 16 func-bits 16 + sid fcbb:bbbb:1:fe01:: behavior uDT6 vrf Vrf1 + sid fcbb:bbbb:1:fe02:abcd:: behavior uDT4 vrf Vrf1 + sid fcbb:bbbb:1:fe03:abcd:abcd:: behavior uDT46 vrf Vrf2 + ! + ... + .. clicmd:: behavior usid Specify the SRv6 locator as a Micro-segment (uSID) locator. When a locator is diff --git a/lib/command.h b/lib/command.h index c60751789f66..c97739f935dd 100644 --- a/lib/command.h +++ b/lib/command.h @@ -156,6 +156,7 @@ enum node_type { SRV6_NODE, /* SRv6 node */ SRV6_LOCS_NODE, /* SRv6 locators node */ SRV6_LOC_NODE, /* SRv6 locator node */ + SRV6_PREFIX_NODE, /* SRv6 locator prefix 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 */ diff --git a/lib/srv6.c b/lib/srv6.c index e6fc375fbb11..224ae9cb2fa9 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -49,6 +49,14 @@ const char *seg6local_action2str(uint32_t action) return "End.AM"; case ZEBRA_SEG6_LOCAL_ACTION_END_DT46: return "End.DT46"; + case ZEBRA_SEG6_LOCAL_ACTION_END_UN: + return "End.uN"; + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT4: + return "End.uDT4"; + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT6: + return "End.uDT6"; + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT46: + return "End.uDT46"; case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: return "unspec"; default: @@ -167,6 +175,8 @@ struct srv6_locator *srv6_locator_alloc(const char *name) locator->chunks = list_new(); locator->chunks->del = srv6_locator_chunk_list_free; + locator->sids = list_new(); + QOBJ_REG(locator, srv6_locator); return locator; } @@ -205,6 +215,20 @@ void srv6_locator_free(struct srv6_locator *locator) } } +struct seg6_sid *srv6_locator_sid_alloc(void) +{ + struct seg6_sid *sid = NULL; + + sid = XCALLOC(MTYPE_SRV6_LOCATOR_CHUNK, sizeof(struct seg6_sid)); + strlcpy(sid->vrfName, "Default", sizeof(sid->vrfName)); + return sid; +} + +void srv6_locator_sid_free(struct seg6_sid *sid) +{ + XFREE(MTYPE_SRV6_LOCATOR_CHUNK, sid); +} + void srv6_locator_chunk_free(struct srv6_locator_chunk **chunk) { XFREE(MTYPE_SRV6_LOCATOR_CHUNK, *chunk); @@ -316,6 +340,27 @@ srv6_locator_chunk_detailed_json(const struct srv6_locator_chunk *chunk) return jo_root; } +json_object *srv6_locator_sid_detailed_json(const struct srv6_locator *locator, + const struct seg6_sid *sid) +{ + json_object *jo_root = NULL; + char buf[256]; + + jo_root = json_object_new_object(); + + /* set sid */ + prefix2str(&sid->ipv6Addr, buf, sizeof(buf)); + json_object_string_add(jo_root, "sid", buf); + + /* set behavior */ + json_object_string_add(jo_root, "behavior", seg6local_action2str(sid->sidaction)); + + /* set vrf */ + json_object_string_add(jo_root, "vrf", sid->vrfName); + + return jo_root; +} + json_object *srv6_locator_json(const struct srv6_locator *loc) { struct listnode *node; @@ -392,10 +437,14 @@ json_object *srv6_locator_json(const struct srv6_locator *loc) json_object *srv6_locator_detailed_json(const struct srv6_locator *loc) { struct listnode *node; + struct listnode *sidnode; struct srv6_locator_chunk *chunk; + struct seg6_sid *sid = NULL; json_object *jo_root = NULL; json_object *jo_chunk = NULL; json_object *jo_chunks = NULL; + json_object *jo_sid = NULL; + json_object *jo_sids = NULL; jo_root = json_object_new_object(); @@ -461,5 +510,13 @@ json_object *srv6_locator_detailed_json(const struct srv6_locator *loc) json_object_array_add(jo_chunks, jo_chunk); } + /* set sids */ + jo_sids = json_object_new_array(); + json_object_object_add(jo_root, "sids", jo_sids); + for (ALL_LIST_ELEMENTS_RO(loc->sids, sidnode, sid)) { + jo_sid = srv6_locator_sid_detailed_json(loc, sid); + json_object_array_add(jo_sids, jo_sid); + } + return jo_root; } diff --git a/lib/srv6.h b/lib/srv6.h index 9a041e3d85b2..ca46a4c7c60e 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -10,6 +10,7 @@ #include #include "prefix.h" #include "json.h" +#include "vrf.h" #include #include @@ -63,6 +64,11 @@ enum seg6local_action_t { ZEBRA_SEG6_LOCAL_ACTION_END_AM = 14, ZEBRA_SEG6_LOCAL_ACTION_END_BPF = 15, ZEBRA_SEG6_LOCAL_ACTION_END_DT46 = 16, + + ZEBRA_SEG6_LOCAL_ACTION_END_UDT6 = 19, + ZEBRA_SEG6_LOCAL_ACTION_END_UDT4 = 20, + ZEBRA_SEG6_LOCAL_ACTION_END_UDT46 = 21, + ZEBRA_SEG6_LOCAL_ACTION_END_UN = 22, }; /* Flavor operations for SRv6 End* Behaviors */ @@ -129,6 +135,7 @@ struct srv6_locator { uint64_t current; bool status_up; struct list *chunks; + struct list *sids; uint8_t flags; #define SRV6_LOCATOR_USID (1 << 0) /* The SRv6 Locator is a uSID Locator */ @@ -167,6 +174,13 @@ struct srv6_locator_chunk { uint8_t flags; }; +struct seg6_sid { + enum seg6local_action_t sidaction; + char vrfName[VRF_NAMSIZ + 1]; + struct prefix_ipv6 ipv6Addr; + char sidstr[PREFIX_STRLEN]; +}; + /* * SRv6 Endpoint Behavior codepoints, as defined by IANA in * https://www.iana.org/assignments/segment-routing/segment-routing.xhtml @@ -366,6 +380,10 @@ static inline const char *srv6_sid_ctx2str(char *str, size_t size, case ZEBRA_SEG6_LOCAL_ACTION_END_AS: case ZEBRA_SEG6_LOCAL_ACTION_END_AM: case ZEBRA_SEG6_LOCAL_ACTION_END_BPF: + case ZEBRA_SEG6_LOCAL_ACTION_END_UN: + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT4: + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT6: + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT46: default: snprintf(str + len, size - len, " unknown(%s)", __func__); } @@ -378,6 +396,8 @@ int snprintf_seg6_segs(char *str, extern struct srv6_locator *srv6_locator_alloc(const char *name); extern struct srv6_locator_chunk *srv6_locator_chunk_alloc(void); +extern struct seg6_sid *srv6_locator_sid_alloc(void); +extern void srv6_locator_sid_free(struct seg6_sid *sid); 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); @@ -388,6 +408,8 @@ 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); +json_object *srv6_locator_sid_detailed_json(const struct srv6_locator *locator, + const struct seg6_sid *sid); extern struct srv6_sid_format *srv6_sid_format_alloc(const char *name); extern void srv6_sid_format_free(struct srv6_sid_format *format); diff --git a/lib/zclient.c b/lib/zclient.c index 063944fd3b23..79ef33063c71 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1156,6 +1156,33 @@ int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l) return -1; } +int zapi_srv6_locator_sid_encode(struct stream *s, struct srv6_locator *loc) +{ + struct seg6_sid *sidtmp = NULL; + struct listnode *node = NULL; + + stream_putw(s, strlen(loc->name)); + stream_put(s, loc->name, strlen(loc->name)); + + stream_putw(s, loc->prefix.prefixlen); + stream_put(s, &loc->prefix.prefix, sizeof(loc->prefix.prefix)); + stream_putc(s, loc->block_bits_length); + stream_putc(s, loc->node_bits_length); + stream_putc(s, loc->function_bits_length); + stream_putc(s, loc->argument_bits_length); + + stream_putl(s, loc->sids->count); + for (ALL_LIST_ELEMENTS_RO(loc->sids, node, sidtmp)) { + stream_putw(s, sidtmp->ipv6Addr.prefixlen); + stream_put(s, &sidtmp->ipv6Addr.prefix, sizeof(sidtmp->ipv6Addr.prefix)); + stream_putl(s, sidtmp->sidaction); + stream_putw(s, strlen(sidtmp->vrfName)); + stream_put(s, sidtmp->vrfName, strlen(sidtmp->vrfName)); + } + + return 0; +} + static int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) { int i; diff --git a/lib/zclient.h b/lib/zclient.h index 2385a8a2197b..1982578d7261 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -211,6 +211,9 @@ typedef enum { ZEBRA_SRV6_MANAGER_GET_LOCATOR, ZEBRA_SRV6_MANAGER_GET_SRV6_SID, ZEBRA_SRV6_MANAGER_RELEASE_SRV6_SID, + ZEBRA_SRV6_MANAGER_GET_LOCATOR_SID, + ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_SID, + ZEBRA_SRV6_MANAGER_GET_LOCATOR_ALL, ZEBRA_ERROR, ZEBRA_CLIENT_CAPABILITIES, ZEBRA_OPAQUE_MESSAGE, @@ -1064,6 +1067,8 @@ extern struct interface *zebra_interface_link_params_read(struct stream *s, bool *changed); extern size_t zebra_interface_link_params_write(struct stream *, struct interface *); + +extern int zapi_srv6_locator_sid_encode(struct stream *s, struct srv6_locator *loc); extern enum zclient_send_status zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t chunk_size, uint32_t base); diff --git a/tests/topotests/zebra_static_locator_sid/__init__.py b/tests/topotests/zebra_static_locator_sid/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/zebra_static_locator_sid/r1/bgpd.conf b/tests/topotests/zebra_static_locator_sid/r1/bgpd.conf new file mode 100644 index 000000000000..d87e4d97b1ac --- /dev/null +++ b/tests/topotests/zebra_static_locator_sid/r1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + no bgp ebgp-requires-policy + timers bgp start-timer 0 + neighbor aaa peer-group + neighbor aaa remote-as 65001 + neighbor 192.168.254.2 peer-group aaa + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/zebra_static_locator_sid/r1/zebra.conf b/tests/topotests/zebra_static_locator_sid/r1/zebra.conf new file mode 100644 index 000000000000..57958c442006 --- /dev/null +++ b/tests/topotests/zebra_static_locator_sid/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +interface r1-eth1 + ip address 192.168.254.1/24 +! +ip forwarding +! diff --git a/tests/topotests/zebra_static_locator_sid/r2/bgpd.conf b/tests/topotests/zebra_static_locator_sid/r2/bgpd.conf new file mode 100644 index 000000000000..35094f35c170 --- /dev/null +++ b/tests/topotests/zebra_static_locator_sid/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65001 + no bgp ebgp-requires-policy + timers bgp start-timer 0 + neighbor aaa peer-group + neighbor aaa remote-as 65000 + neighbor 192.168.254.1 peer-group aaa + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + exit-address-family +! diff --git a/tests/topotests/zebra_static_locator_sid/r2/zebra.conf b/tests/topotests/zebra_static_locator_sid/r2/zebra.conf new file mode 100644 index 000000000000..f2daa523acdd --- /dev/null +++ b/tests/topotests/zebra_static_locator_sid/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +interface r2-eth1 + ip address 192.168.254.2/24 +! +ip forwarding +! diff --git a/tests/topotests/zebra_static_locator_sid/test_zebra_static_locator_sid.py b/tests/topotests/zebra_static_locator_sid/test_zebra_static_locator_sid.py new file mode 100644 index 000000000000..cc19291d6f26 --- /dev/null +++ b/tests/topotests/zebra_static_locator_sid/test_zebra_static_locator_sid.py @@ -0,0 +1,208 @@ +""" +test_zebra_static_locator_sid.py + +Test if works the following commands: +segment-routing + srv6 + locators + locator loc1 + prefix fcbb:bbbb:1::/48 block-len 32 node-len 16 func-bits 16 + sid fcbb:bbbb:1:fe01:: behavior uDT6 vrf Vrf1 + sid fcbb:bbbb:1:fe02:abcd:: behavior uDT4 vrf Vrf1 + sid fcbb:bbbb:1:fe03:abcd:abcd:: behavior uDT46 vrf Vrf2 + locator loc2 + prefix fcdd:dddd:2::/48 block-len 32 node-len 16 func-bits 16 + sid fcdd:dddd:2:fe11:abcd:: behavior uDT6 vrf Vrf2 + sid fcdd:dddd:2:fe12:: behavior uDT46 vrf Vrf3 + +Test contains two parts: +- Verify that the static sid is configured correctly. +- Ensure that the static sid can be removed properly. +""" + +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 +from lib.common_config import step + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_zebra_static_locator_sid(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + r1 = tgen.gears['r1'] + + def _zebra_conf_static_sids(router, locator_conf_args): + router.vtysh_cmd(locator_conf_args) + + + def _zebra_check_static_sids(router, cmd_args, expected_args): + output = json.loads(router.vtysh_cmd(cmd_args)) + return topotest.json_cmp(output, expected_args) + + + locator_conf_args = """ + configure terminal + segment-routing + srv6 + locators + locator loc1 + prefix fcbb:bbbb:1::/48 block-len 32 node-len 16 func-bits 16 + sid fcbb:bbbb:1:fe01:: behavior uDT6 vrf Vrf1 + sid fcbb:bbbb:1:fe02:abcd:: behavior uDT4 vrf Vrf1 + sid fcbb:bbbb:1:fe03:abcd:abcd:: behavior uDT46 vrf Vrf2 + exit + exit + locator loc2 + prefix fcdd:dddd:2::/48 block-len 32 node-len 16 func-bits 16 + sid fcdd:dddd:2:fe11:abcd:: behavior uDT6 vrf Vrf2 + sid fcdd:dddd:2:fe12:: behavior uDT46 vrf Vrf3 + """ + locator_rm_conf_args = """ + configure terminal + segment-routing + srv6 + locators + locator loc1 + prefix fcbb:bbbb:1::/48 block-len 32 node-len 16 func-bits 16 + no sid fcbb:bbbb:1:fe01:: behavior uDT6 vrf Vrf1 + no sid fcbb:bbbb:1:fe02:abcd:: behavior uDT4 vrf Vrf1 + no sid fcbb:bbbb:1:fe03:abcd:abcd:: behavior uDT46 vrf Vrf2 + """ + cmd_args = "show segment-routing srv6 locator loc1 detail json" + expected_sids_args = { + "name":"loc1", + "prefix":"fcbb:bbbb:1::/48", + "blockBitsLength":32, + "nodeBitsLength":16, + "functionBitsLength":16, + "argumentBitsLength":0, + "algoNum":0, + "statusUp":True, + "chunks":[ + { + "prefix":"fcbb:bbbb:1::/48", + "blockBitsLength":0, + "nodeBitsLength":0, + "functionBitsLength":0, + "argumentBitsLength":0, + "keep":0, + "proto":"system", + "instance":0, + "sessionId":0 + } + ], + "sids":[ + { + "sid":"fcbb:bbbb:1:fe01::/128", + "behavior":"End.uDT6", + "vrf":"Vrf1" + }, + { + "sid":"fcbb:bbbb:1:fe02:abcd::/128", + "behavior":"End.uDT4", + "vrf":"Vrf1" + }, + { + "sid":"fcbb:bbbb:1:fe03:abcd:abcd::/128", + "behavior":"End.uDT46", + "vrf":"Vrf2" + } + ] + } + expected_no_sids_args = { + "name":"loc1", + "prefix":"fcbb:bbbb:1::/48", + "blockBitsLength":32, + "nodeBitsLength":16, + "functionBitsLength":16, + "argumentBitsLength":0, + "algoNum":0, + "statusUp":True, + "chunks":[ + { + "prefix":"fcbb:bbbb:1::/48", + "blockBitsLength":0, + "nodeBitsLength":0, + "functionBitsLength":0, + "argumentBitsLength":0, + "keep":0, + "proto":"system", + "instance":0, + "sessionId":0 + } + ], + "sids":[ + ] + } + + + step("Configure the static sids for locator loc1 and loc2 on router1") + _zebra_conf_static_sids(r1, locator_conf_args) + + step("Check mySID (ADD)") + test_func = functools.partial(_zebra_check_static_sids, r1, + cmd_args, expected_sids_args) + _, result = topotest.run_and_expect(test_func, None, count=5, wait=5) + + assert result is None, 'Failed to add static sids' + + step("Remove the static sids for locator loc1 on router1") + _zebra_conf_static_sids(r1, locator_rm_conf_args) + + step("Check mySID (DEL)") + test_func = functools.partial(_zebra_check_static_sids, r1, + cmd_args, expected_no_sids_args) + _, result = topotest.run_and_expect(test_func, None, count=5, wait=5) + + assert result is None, 'Failed to remove static sids' + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) \ No newline at end of file diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c460dea70cda..43567586e327 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1326,6 +1326,13 @@ static struct cmd_node srv6_loc_node = { .prompt = "%s(config-srv6-locator)# ", }; +static struct cmd_node srv6_prefix_node = { + .name = "srv6-locator-prefix", + .node = SRV6_PREFIX_NODE, + .parent_node = SRV6_LOC_NODE, + .prompt = "%s(config-srv6-locator-prefix)# ", +}; + static struct cmd_node srv6_encap_node = { .name = "srv6-encap", .node = SRV6_ENCAP_NODE, @@ -1710,6 +1717,22 @@ DEFUNSH(VTYSH_ZEBRA, srv6_locator, srv6_locator_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_ZEBRA, srv6_prefix, srv6_prefix_cmd, + "prefix X:X::X:X/M$prefix \ + [block-len (16-64)$block_bit_len] [node-len (16-64)$node_bit_len] [func-bits (16-80)$func_bit_len]", + "Configure SRv6 locator prefix\n" + "Specify SRv6 locator prefix\n" + "Configure SRv6 locator block length in bits\n" + "Specify SRv6 locator block length in bits\n" + "Configure SRv6 locator node length in bits\n" + "Specify SRv6 locator node length in bits\n" + "Configure SRv6 locator function length in bits\n" + "Specify SRv6 locator function length in bits\n") +{ + vty->node = SRV6_PREFIX_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ZEBRA, srv6_encap, srv6_encap_cmd, "encapsulation", "Segment Routing SRv6 encapsulation\n") @@ -2559,6 +2582,14 @@ DEFUNSH(VTYSH_ZEBRA, exit_srv6_loc_config, exit_srv6_loc_config_cmd, "exit", return CMD_SUCCESS; } +DEFUNSH(VTYSH_ZEBRA, exit_srv6_prefix_config, exit_srv6_prefix_config_cmd, + "exit", "Exit from SRv6-locators prefix configuration mode\n") +{ + if (vty->node == SRV6_PREFIX_NODE) + vty->node = SRV6_LOC_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ZEBRA, exit_srv6_encap, exit_srv6_encap_cmd, "exit", "Exit from SRv6-encapsulation configuration mode\n") { @@ -5001,6 +5032,7 @@ void vtysh_init_vty(void) install_node(&srv6_node); install_node(&srv6_locs_node); install_node(&srv6_loc_node); + install_node(&srv6_prefix_node); install_node(&srv6_encap_node); install_node(&srv6_sid_formats_node); install_node(&srv6_sid_format_usid_f3216_node); @@ -5447,9 +5479,13 @@ void vtysh_init_vty(void) install_element(SRV6_LOCS_NODE, &exit_srv6_locs_config_cmd); install_element(SRV6_LOCS_NODE, &vtysh_end_all_cmd); + install_element(SRV6_LOC_NODE, &srv6_prefix_cmd); install_element(SRV6_LOC_NODE, &exit_srv6_loc_config_cmd); install_element(SRV6_LOC_NODE, &vtysh_end_all_cmd); + install_element(SRV6_PREFIX_NODE, &exit_srv6_prefix_config_cmd); + install_element(SRV6_PREFIX_NODE, &vtysh_end_all_cmd); + install_element(SRV6_ENCAP_NODE, &exit_srv6_encap_cmd); install_element(SRV6_ENCAP_NODE, &vtysh_end_all_cmd); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 492fe528894e..6095a29bcc55 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1726,6 +1726,10 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, case ZEBRA_SEG6_LOCAL_ACTION_END_AS: case ZEBRA_SEG6_LOCAL_ACTION_END_AM: case ZEBRA_SEG6_LOCAL_ACTION_END_BPF: + case ZEBRA_SEG6_LOCAL_ACTION_END_UN: + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT4: + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT6: + case ZEBRA_SEG6_LOCAL_ACTION_END_UDT46: case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: zlog_err("%s: unsupport seg6local behaviour action=%u", __func__, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ab55998af046..4f6485c6e076 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2771,6 +2771,37 @@ int zsend_client_close_notify(struct zserv *client, struct zserv *closed_client) return zserv_send_message(client, s); } +int zsend_srv6_manager_get_locator_sid_response(struct zserv *client, vrf_id_t vrf_id, + struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_MANAGER_GET_LOCATOR_SID, vrf_id); + zapi_srv6_locator_sid_encode(s, loc); + stream_putw_at(s, 0, stream_get_endp(s)); + return zserv_send_message(client, s); +} + +int zsend_srv6_manager_del_sid(struct zserv *client, vrf_id_t vrf_id, struct srv6_locator *loc, + struct seg6_sid *sid) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_MANAGER_RELEASE_LOCATOR_SID, vrf_id); + + stream_putw(s, strlen(loc->name)); + stream_put(s, loc->name, strlen(loc->name)); + + stream_putl(s, 1); + stream_putw(s, sid->ipv6Addr.prefixlen); + stream_put(s, &sid->ipv6Addr.prefix, sizeof(sid->ipv6Addr.prefix)); + stream_putl(s, sid->sidaction); + stream_putw(s, strlen(sid->vrfName)); + stream_put(s, sid->vrfName, strlen(sid->vrfName)); + stream_putw_at(s, 0, stream_get_endp(s)); + return zserv_send_message(client, s); +} + int zsend_srv6_manager_get_locator_chunk_response(struct zserv *client, vrf_id_t vrf_id, struct srv6_locator *loc) diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index a59ccc838b97..a9caf9023712 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -118,6 +118,11 @@ extern int zsend_srv6_manager_get_locator_chunk_response(struct zserv *client, extern int zsend_srv6_manager_get_locator_response(struct zserv *client, struct srv6_locator *locator); +extern int zsend_srv6_manager_get_locator_sid_response(struct zserv *client, vrf_id_t vrf_id, + struct srv6_locator *loc); +extern int zsend_srv6_manager_del_sid(struct zserv *client, vrf_id_t vrf_id, + struct srv6_locator *loc, struct seg6_sid *sid); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 624f60e815af..49a37808a938 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -628,10 +628,16 @@ void zebra_srv6_locator_add(struct srv6_locator *locator) void zebra_srv6_locator_delete(struct srv6_locator *locator) { - struct listnode *n; + struct listnode *n, *nnode; struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct seg6_sid *sid = NULL; struct zserv *client; + struct listnode *client_node; + for (ALL_LIST_ELEMENTS(locator->sids, n, nnode, sid)) { + listnode_delete(locator->sids, sid); + srv6_locator_sid_free(sid); + } /* * Notify deleted locator info to zclients if needed. * @@ -643,7 +649,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(zrouter.client_list, n, client)) + + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, client_node, client)) zsend_zebra_srv6_locator_delete(client, locator); listnode_delete(srv6->locators, locator); diff --git a/zebra/zebra_srv6.h b/zebra/zebra_srv6.h index 1599fd7adfbd..3479bb2b8e07 100644 --- a/zebra/zebra_srv6.h +++ b/zebra/zebra_srv6.h @@ -245,6 +245,7 @@ DECLARE_HOOK(srv6_manager_get_locator, extern void zebra_srv6_locator_add(struct srv6_locator *locator); extern void zebra_srv6_locator_delete(struct srv6_locator *locator); +extern void zebra_srv6_prefix_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); diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index 6867b1bbb6c4..04f18ccd8519 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -26,6 +26,7 @@ #include "zebra/redistribute.h" #include "zebra/zebra_routemap.h" #include "zebra/zebra_dplane.h" +#include "zebra/zapi_msg.h" #include "zebra/zebra_srv6_vty_clippy.c" @@ -61,6 +62,28 @@ static struct cmd_node srv6_loc_node = { .prompt = "%s(config-srv6-locator)# " }; +static struct cmd_node srv6_prefix_node = { .name = "srv6-locator-prefix", + .node = SRV6_PREFIX_NODE, + .parent_node = SRV6_LOC_NODE, + .prompt = "%s(config-srv6-locator-prefix)# " }; + +static struct seg6_sid *sid_lookup_by_vrf_action(struct srv6_locator *loc, const char *vrfname, + enum seg6local_action_t sidaction) + +{ + struct seg6_sid *sid = NULL; + struct listnode *node, *nnode; + + if (!vrfname) + return NULL; + + for (ALL_LIST_ELEMENTS(loc->sids, node, nnode, sid)) { + if (strcmp(sid->vrfName, vrfname) == 0 && (sid->sidaction == sidaction)) + return sid; + } + return NULL; +} + static struct cmd_node srv6_encap_node = { .name = "srv6-encap", .node = SRV6_ENCAP_NODE, @@ -195,7 +218,10 @@ DEFUN (show_srv6_locator_detail, struct zebra_srv6 *srv6 = zebra_srv6_get_default(); struct srv6_locator *locator; struct listnode *node; + struct listnode *sidnode; + struct seg6_sid *sid = NULL; char str[256]; + char buf[256]; const char *locator_name = argv[4]->arg; json_object *json_locator = NULL; @@ -253,6 +279,13 @@ DEFUN (show_srv6_locator_detail, vty_out(vty, "- prefix: %s, owner: %s\n", str, zebra_route_string(chunk->proto)); } + vty_out(vty, " sids:\n"); + for (ALL_LIST_ELEMENTS_RO(locator->sids, sidnode, sid)) { + prefix2str(&sid->ipv6Addr, buf, sizeof(buf)); + vty_out(vty, " -sid %s\n", buf); + vty_out(vty, " behavior %s\n", seg6local_action2str(sid->sidaction)); + vty_out(vty, " vrf %s\n", sid->vrfName); + } } @@ -334,6 +367,7 @@ DEFUN_NOSH (srv6_locator, if (locator) { VTY_PUSH_CONTEXT(SRV6_LOC_NODE, locator); locator->status_up = true; + vty->node = SRV6_LOC_NODE; return CMD_SUCCESS; } @@ -387,7 +421,7 @@ DEFUN (no_srv6_locator, return CMD_SUCCESS; } -DEFPY (locator_prefix, +DEFUN_NOSH (locator_prefix, locator_prefix_cmd, "prefix X:X::X:X/M$prefix [block-len (16-64)$block_bit_len] \ [node-len (16-64)$node_bit_len] [func-bits (0-64)$func_bit_len]", @@ -405,11 +439,33 @@ DEFPY (locator_prefix, struct listnode *node = NULL; uint8_t expected_prefixlen; struct srv6_sid_format *format; + char *prefixstr = NULL; + struct prefix_ipv6 prefix; + int ret = 0; + int idx = 0; + int block_bit_len = 0; + int node_bit_len = 0; + int func_bit_len = 0; + + prefixstr = argv[1]->arg; + ret = str2prefix_ipv6(prefixstr, &prefix); + apply_mask_ipv6(&prefix); + if (!ret) { + vty_out(vty, "Malformed IPv6 prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv_find(argv, argc, "block-len", &idx)) + block_bit_len = strtoul(argv[idx + 1]->arg, NULL, 10); + if (argv_find(argv, argc, "node-len", &idx)) + node_bit_len = strtoul(argv[idx + 1]->arg, NULL, 10); + if (argv_find(argv, argc, "func-bits", &idx)) + func_bit_len = strtoul(argv[idx + 1]->arg, NULL, 10); - locator->prefix = *prefix; + locator->prefix = prefix; func_bit_len = func_bit_len ?: ZEBRA_SRV6_FUNCTION_LENGTH; - expected_prefixlen = prefix->prefixlen; + expected_prefixlen = prefix.prefixlen; format = locator->sid_format; if (format) { if (strmatch(format->name, SRV6_SID_FORMAT_USID_F3216_NAME)) @@ -423,35 +479,34 @@ DEFPY (locator_prefix, SRV6_SID_FORMAT_UNCOMPRESSED_F4024_NODE_LEN; } - if (prefix->prefixlen != expected_prefixlen) { + 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); + 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; + block_bit_len = prefix.prefixlen - node_bit_len; } else if (node_bit_len == 0) { - node_bit_len = prefix->prefixlen - block_bit_len; + node_bit_len = prefix.prefixlen - block_bit_len; } else { - if (block_bit_len + node_bit_len != prefix->prefixlen) { + if (block_bit_len + node_bit_len != prefix.prefixlen) { vty_out(vty, "%% block-len + node-len must be equal to the selected prefix length %d\n", - prefix->prefixlen); + prefix.prefixlen); return CMD_WARNING_CONFIG_FAILED; } } - if (prefix->prefixlen + func_bit_len + 0 > 128) { + if (prefix.prefixlen + func_bit_len + 0 > 128) { vty_out(vty, - "%% prefix-len + function-len + arg-len (%ld) cannot be greater than 128\n", - prefix->prefixlen + func_bit_len + 0); + "%% prefix-len + function-len + arg-len (%d) cannot be greater than 128\n", + prefix.prefixlen + func_bit_len + 0); return CMD_WARNING_CONFIG_FAILED; } @@ -475,7 +530,7 @@ DEFPY (locator_prefix, if (list_isempty(locator->chunks)) { chunk = srv6_locator_chunk_alloc(); - chunk->prefix = *prefix; + chunk->prefix = prefix; chunk->proto = 0; listnode_add(locator->chunks, chunk); } else { @@ -486,7 +541,7 @@ DEFPY (locator_prefix, struct zserv *client; struct listnode *client_node; - chunk->prefix = *prefix; + chunk->prefix = prefix; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, client_node, client)) { @@ -506,6 +561,8 @@ DEFPY (locator_prefix, zebra_srv6_locator_format_set(locator, locator->sid_format); + vty->node = SRV6_PREFIX_NODE; + return CMD_SUCCESS; } @@ -941,11 +998,150 @@ DEFPY(no_srv6_sid_format_explicit, return CMD_SUCCESS; } + +DEFPY(locator_sid, locator_sid_cmd, + "sid WORD behavior ", + "Configure static local sid\n" + "Specify SRv6 locator hex sid\n" + "Configure behavior\n" + "Apply the code to an End.uN SID\n" + "Apply the code to an End.uDT4 SID\n" + "vrf\n" + "vrf\n" + "Apply the code to an End.uDT6 SID\n" + "vrf\n" + "vrf\n" + "Apply the code to an End.uDT46 SID\n" + "vrf\n" + "vrf\n") +{ + VTY_DECLVAR_CONTEXT(srv6_locator, locator); + struct seg6_sid *srv6_sid = NULL; + struct listnode *node = NULL; + enum seg6local_action_t sidaction = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; + int idx = 0; + char *vrfName = NULL; + char *prefix = NULL; + int ret = 0; + struct prefix_ipv6 ipv6prefix = { 0 }; + struct zserv *client; + struct listnode *client_node; + struct zebra_vrf *zvrf = NULL; + + if (!locator->status_up) { + vty_out(vty, "Missing valid prefix.\n"); + return CMD_WARNING; + } + if (argv_find(argv, argc, "uN", &idx)) + sidaction = ZEBRA_SEG6_LOCAL_ACTION_END_UN; + else if (argv_find(argv, argc, "uDT4", &idx)) { + sidaction = ZEBRA_SEG6_LOCAL_ACTION_END_UDT4; + vrfName = argv[idx + 2]->arg; + } else if (argv_find(argv, argc, "uDT6", &idx)) { + sidaction = ZEBRA_SEG6_LOCAL_ACTION_END_UDT6; + vrfName = argv[idx + 2]->arg; + } else if (argv_find(argv, argc, "uDT46", &idx)) { + sidaction = ZEBRA_SEG6_LOCAL_ACTION_END_UDT46; + vrfName = argv[idx + 2]->arg; + } + prefix = argv[1]->arg; + ret = str2prefix_ipv6(prefix, &ipv6prefix); + if (!ret) { + vty_out(vty, "Malformed IPv6 prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!prefix_match((struct prefix *)&locator->prefix, (struct prefix *)&ipv6prefix)) { + vty_out(vty, "SID prefix must match locator prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (vrfName) { + zvrf = zebra_vrf_lookup_by_name(vrfName); + if (!zvrf) { + vty_out(vty, "Vrf does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + for (ALL_LIST_ELEMENTS_RO(locator->sids, node, srv6_sid)) { + if (IPV6_ADDR_SAME(&srv6_sid->ipv6Addr.prefix, &ipv6prefix.prefix)) { + vty_out(vty, "Prefix %s is already exist, please delete it first.\n", + argv[1]->arg); + return CMD_WARNING; + } + } + srv6_sid = sid_lookup_by_vrf_action(locator, vrfName, sidaction); + if (srv6_sid) { + vty_out(vty, "VRF %s is already exist, please delete it first.\n", vrfName); + return CMD_WARNING; + } + srv6_sid = srv6_locator_sid_alloc(); + srv6_sid->sidaction = sidaction; + if (vrfName != NULL) + strlcpy(srv6_sid->vrfName, vrfName, VRF_NAMSIZ); + else + strlcpy(srv6_sid->vrfName, VRF_DEFAULT_NAME, VRF_NAMSIZ); + + srv6_sid->ipv6Addr = ipv6prefix; + strlcpy(srv6_sid->sidstr, prefix, PREFIX_STRLEN); + + listnode_add(locator->sids, srv6_sid); + + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, client_node, client)) + zsend_srv6_manager_get_locator_sid_response(client, VRF_DEFAULT, locator); + return CMD_SUCCESS; +} + +DEFPY(no_locator_sid, + no_locator_sid_cmd, + "no sid WORD behavior ", + NO_STR + "Configure SRv6 locator sid\n" + "Specify SRv6 locator hex sid\n" + "Configure behavior\n" + "Apply the code to an End.uN SID\n" + "Apply the code to an End.uDT4 SID\n" + "vrf\n" + "vrf\n" + "Apply the code to an End.uDT6 SID\n" + "vrf\n" + "vrf\n" + "Apply the code to an End.uDT46 SID\n" + "vrf\n" + "vrf\n") +{ + VTY_DECLVAR_CONTEXT(srv6_locator, locator); + struct seg6_sid *srv6_sid = NULL; + struct listnode *node, *next; + char *prefix = NULL; + int ret = 0; + struct prefix_ipv6 ipv6prefix = { 0 }; + struct zserv *client; + struct listnode *client_node; + + prefix = argv[2]->arg; + ret = str2prefix_ipv6(prefix, &ipv6prefix); + if (!ret) { + vty_out(vty, "Malformed IPv6 prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + for (ALL_LIST_ELEMENTS(locator->sids, node, next, srv6_sid)) { + if (IPV6_ADDR_SAME(&srv6_sid->ipv6Addr.prefix, &ipv6prefix.prefix)) { + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, client_node, client)) + zsend_srv6_manager_del_sid(client, VRF_DEFAULT, locator, srv6_sid); + listnode_delete(locator->sids, srv6_sid); + srv6_locator_sid_free(srv6_sid); + return CMD_SUCCESS; + } + } + return CMD_SUCCESS; +} + static int zebra_sr_config(struct vty *vty) { struct zebra_srv6 *srv6 = zebra_srv6_get_default(); - struct listnode *node; + struct listnode *node, *sidnode; struct srv6_locator *locator; + struct seg6_sid *srv6_sid; struct srv6_sid_format *format; char str[256]; bool display_source_srv6 = false; @@ -988,6 +1184,26 @@ 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"); + for (ALL_LIST_ELEMENTS_RO(locator->sids, sidnode, srv6_sid)) { + vty_out(vty, " sid %s", srv6_sid->sidstr); + vty_out(vty, " behavior"); + if (srv6_sid->sidaction == ZEBRA_SEG6_LOCAL_ACTION_END_UN) + vty_out(vty, " uN"); + else if (srv6_sid->sidaction == ZEBRA_SEG6_LOCAL_ACTION_END_UDT4) { + vty_out(vty, " uDT4"); + vty_out(vty, " vrf %s", srv6_sid->vrfName); + } else if (srv6_sid->sidaction == ZEBRA_SEG6_LOCAL_ACTION_END_UDT6) { + vty_out(vty, " uDT6"); + vty_out(vty, " vrf %s", srv6_sid->vrfName); + } else if (srv6_sid->sidaction == ZEBRA_SEG6_LOCAL_ACTION_END_UDT46) { + vty_out(vty, " uDT46"); + vty_out(vty, " vrf %s", srv6_sid->vrfName); + } + vty_out(vty, "\n"); + } + vty_out(vty, "\n"); + vty_out(vty, " exit\n"); + vty_out(vty, " !\n"); if (locator->sid_format) { format = locator->sid_format; vty_out(vty, " format %s\n", format->name); @@ -1058,6 +1274,7 @@ void zebra_srv6_vty_init(void) install_node(&srv6_node); install_node(&srv6_locs_node); install_node(&srv6_loc_node); + install_node(&srv6_prefix_node); install_node(&srv6_encap_node); install_node(&srv6_sid_formats_node); install_node(&srv6_sid_format_usid_f3216_node); @@ -1066,6 +1283,7 @@ void zebra_srv6_vty_init(void) install_default(SRV6_NODE); install_default(SRV6_LOCS_NODE); install_default(SRV6_LOC_NODE); + install_default(SRV6_PREFIX_NODE); install_default(SRV6_ENCAP_NODE); install_default(SRV6_SID_FORMATS_NODE); install_default(SRV6_SID_FORMAT_USID_F3216_NODE); @@ -1090,6 +1308,8 @@ void zebra_srv6_vty_init(void) /* Command for configuration */ install_element(SRV6_LOC_NODE, &locator_prefix_cmd); + install_element(SRV6_PREFIX_NODE, &locator_sid_cmd); + install_element(SRV6_PREFIX_NODE, &no_locator_sid_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);