From 15588d7b63b4847282332f4f9da99c4b5b8ce179 Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sun, 5 Sep 2021 02:22:21 +0000 Subject: [PATCH 01/10] lib: add zapi encode/decode func for srv6 locator Signed-off-by: Hiroki Shirokura --- lib/zclient.c | 27 +++++++++++++++++++++++++++ lib/zclient.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/lib/zclient.c b/lib/zclient.c index a1e7194890..dde60a6c90 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1105,6 +1105,33 @@ stream_failure: return -1; } +int zapi_srv6_locator_encode(struct stream *s, const struct srv6_locator *l) +{ + stream_putw(s, strlen(l->name)); + stream_put(s, l->name, strlen(l->name)); + stream_putw(s, l->prefix.prefixlen); + stream_put(s, &l->prefix.prefix, sizeof(l->prefix.prefix)); + return 0; +} + +int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l) +{ + uint16_t len = 0; + + STREAM_GETW(s, len); + if (len > SRV6_LOCNAME_SIZE) + goto stream_failure; + + STREAM_GET(l->name, s, len); + STREAM_GETW(s, l->prefix.prefixlen); + STREAM_GET(&l->prefix.prefix, s, sizeof(l->prefix.prefix)); + l->prefix.family = AF_INET6; + return 0; + +stream_failure: + return -1; +} + 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 71187ccae7..f9438d5db7 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -1090,6 +1090,9 @@ extern int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl); extern int zapi_labels_decode(struct stream *s, struct zapi_labels *zl); +extern int zapi_srv6_locator_encode(struct stream *s, + const struct srv6_locator *l); +extern int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l); extern int zapi_srv6_locator_chunk_encode(struct stream *s, const struct srv6_locator_chunk *c); extern int zapi_srv6_locator_chunk_decode(struct stream *s, From f5ca329b2d5a2f19b583b7732950256c788db34c Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sun, 5 Sep 2021 10:05:53 +0000 Subject: [PATCH 02/10] zebra: implement srv6 locator add/delete notification via ZAPI Signed-off-by: Hiroki Shirokura --- zebra/zapi_msg.c | 25 +++++++++++++++++++++++++ zebra/zebra_srv6.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 6666b3525e..07d171b58f 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1137,6 +1137,31 @@ static int zsend_table_manager_connect_response(struct zserv *client, return zserv_send_message(client, s); } +/* SRv6 locator add notification from zebra daemon. */ +int zsend_zebra_srv6_locator_add(struct zserv *client, struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_LOCATOR_ADD, VRF_DEFAULT); + zapi_srv6_locator_encode(s, loc); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + +/* SRv6 locator delete notification from zebra daemon. */ +int zsend_zebra_srv6_locator_delete(struct zserv *client, + struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_LOCATOR_DELETE, VRF_DEFAULT); + zapi_srv6_locator_encode(s, loc); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* Inbound message handling ------------------------------------------------ */ const int cmd2type[] = { diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index b11331a180..ad99ae1786 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -106,15 +106,60 @@ void zebra_srv6_locator_add(struct srv6_locator *locator) { struct zebra_srv6 *srv6 = zebra_srv6_get_default(); struct srv6_locator *tmp; + struct listnode *node; + struct zserv *client; tmp = zebra_srv6_locator_lookup(locator->name); if (!tmp) listnode_add(srv6->locators, locator); + + /* + * 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_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; + + /* + * 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( + "%s: Not found zclient(proto=%u, instance=%u).", + __func__, c->proto, c->instance); + continue; + } + zsend_zebra_srv6_locator_delete(client, locator); + } listnode_delete(srv6->locators, locator); } From f6e52a81dc310e7a3a97154a2a6edca6e06fc130 Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sun, 5 Sep 2021 10:06:29 +0000 Subject: [PATCH 03/10] zebra: elliminate srv6 locator auto allocation by zlicnet request Signed-off-by: Hiroki Shirokura --- zebra/zebra_srv6.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index ad99ae1786..219d047694 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -216,19 +216,7 @@ assign_srv6_locator_chunk(uint8_t proto, if (!loc) { zlog_info("%s: locator %s was not found", __func__, locator_name); - - loc = srv6_locator_alloc(locator_name); - if (!loc) { - zlog_info("%s: locator %s can't allocated", - __func__, locator_name); - return NULL; - } - - loc->status_up = false; - chunk = srv6_locator_chunk_alloc(); - chunk->proto = NO_PROTO; - listnode_add(loc->chunks, chunk); - zebra_srv6_locator_add(loc); + return NULL; } for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { From 0a735cd52361db3fe3e16c2de5aa1d132c75258e Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sun, 5 Sep 2021 10:07:56 +0000 Subject: [PATCH 04/10] zebra: add srv6's no commands CURRENT_CONFIGURATION: configure terminal segment-routing srv6 locators locator loc1 locator loc2 locator loc3 CMD1: delete single locator configure terminal segment-routing srv6 locators no locator loc1 CMD2: delete srv6 whole config (== delete all locators) configure terminal segment-routing no srv6 Signed-off-by: Hiroki Shirokura --- zebra/zebra_srv6_vty.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index d2b91b6c07..cb1e6c4228 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -197,6 +197,21 @@ DEFUN_NOSH (srv6, return CMD_SUCCESS; } +DEFUN (no_srv6, + no_srv6_cmd, + "no srv6", + NO_STR + "Segment Routing SRv6\n") +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct srv6_locator *locator; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(srv6->locators, node, nnode, locator)) + zebra_srv6_locator_delete(locator); + return CMD_SUCCESS; +} + DEFUN_NOSH (srv6_locators, srv6_locators_cmd, "locators", @@ -233,6 +248,23 @@ DEFUN_NOSH (srv6_locator, return CMD_SUCCESS; } +DEFUN (no_srv6_locator, + no_srv6_locator_cmd, + "no locator WORD", + NO_STR + "Segment Routing SRv6 locator\n" + "Specify locator-name\n") +{ + 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; + } + + zebra_srv6_locator_delete(locator); + return CMD_SUCCESS; +} + DEFPY (locator_prefix, locator_prefix_cmd, "prefix X:X::X:X/M$prefix [func-bits (16-64)$func_bit_len]", @@ -348,8 +380,10 @@ void zebra_srv6_vty_init(void) /* Command for change node */ install_element(CONFIG_NODE, &segment_routing_cmd); install_element(SEGMENT_ROUTING_NODE, &srv6_cmd); + install_element(SEGMENT_ROUTING_NODE, &no_srv6_cmd); install_element(SRV6_NODE, &srv6_locators_cmd); install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); + install_element(SRV6_LOCS_NODE, &no_srv6_locator_cmd); /* Command for configuration */ install_element(SRV6_LOC_NODE, &locator_prefix_cmd); From d79ff732cdcd9583b01d5c22cee05812e7354aa9 Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sun, 5 Sep 2021 02:24:23 +0000 Subject: [PATCH 05/10] bgpd: handle srv6 locator notification and update vpn-rib Signed-off-by: Hiroki Shirokura --- bgpd/bgp_mplsvpn.c | 37 ++++++++++++++++++-- bgpd/bgp_mplsvpn.h | 4 +++ bgpd/bgp_zebra.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 1af2ab384f..b0bab02fb8 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -714,6 +714,15 @@ static void setsids(struct bgp_path_info *bpi, extra->num_sids = num_sids; } +static void unsetsids(struct bgp_path_info *bpi) +{ + struct bgp_path_info_extra *extra; + + extra = bgp_path_info_extra_get(bpi); + extra->num_sids = 0; + memset(extra->sid, 0, sizeof(extra->sid)); +} + /* * returns pointer to new bgp_path_info upon success */ @@ -821,7 +830,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ else if (new_attr->srv6_vpn) setsids(bpi, &new_attr->srv6_vpn->sid, num_sids); - } + } else + unsetsids(bpi); if (nexthop_self_flag) bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF); @@ -847,6 +857,17 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ nh_valid = bgp_find_or_add_nexthop( bgp, bgp_nexthop, afi, safi, bpi, NULL, 0, p); + /* + * If you are using SRv6 VPN instead of MPLS, it need to check + * the SID allocation. If the sid is not allocated, the rib + * will be invalid. + */ + if (bgp->srv6_enabled + && (!new_attr->srv6_l3vpn && !new_attr->srv6_vpn)) { + bgp_path_info_unset_flag(bn, bpi, BGP_PATH_VALID); + nh_valid = false; + } + if (debug) zlog_debug("%s: nexthop is %svalid (in vrf %s)", __func__, (nh_valid ? "" : "not "), @@ -893,7 +914,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ setsids(new, &new_attr->srv6_l3vpn->sid, num_sids); else if (new_attr->srv6_vpn) setsids(new, &new_attr->srv6_vpn->sid, num_sids); - } + } else + unsetsids(new); if (num_labels) setlabels(new, label, num_labels); @@ -933,6 +955,17 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ nh_valid = bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi, new, NULL, 0, p); + /* + * If you are using SRv6 VPN instead of MPLS, it need to check + * the SID allocation. If the sid is not allocated, the rib + * will be invalid. + */ + if (bgp->srv6_enabled + && (!new->attr->srv6_l3vpn && !new->attr->srv6_vpn)) { + bgp_path_info_unset_flag(bn, new, BGP_PATH_VALID); + nh_valid = false; + } + if (debug) zlog_debug("%s: nexthop is %svalid (in vrf %s)", __func__, (nh_valid ? "" : "not "), diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 38193721b3..b0e0f74f77 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -243,6 +243,10 @@ static inline void vpn_leak_postchange(vpn_policy_direction_t direction, if (!bgp_vrf->vpn_policy[afi].tovpn_sid) ensure_vrf_tovpn_sid(bgp_vpn, bgp_vrf, afi); + if (!bgp_vrf->vpn_policy[afi].tovpn_sid + && bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) + vpn_leak_zebra_vrf_sid_withdraw(bgp_vrf, afi); + if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid, bgp_vrf->vpn_policy[afi] .tovpn_zebra_vrf_sid_last_sent)) { diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 5ef49e5108..ac34692fe8 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -3077,6 +3077,88 @@ static void bgp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) vpn_leak_postchange_all(); } +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 (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + if (!bgp || !bgp->srv6_enabled) + return 0; + + if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0) + return -1; + + return 0; +} + +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 prefix_ipv6 *chunk; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + struct in6_addr *tovpn_sid; + struct prefix_ipv6 tmp_prefi; + + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + // refresh chunks + for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)chunk)) + listnode_delete(bgp->srv6_locator_chunks, chunk); + + // refresh functions + for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = func->sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + listnode_delete(bgp->srv6_functions, func); + } + + // refresh tovpn_sid + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + // refresh vpnv4 tovpn_sid + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; + if (tovpn_sid) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = *tovpn_sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + } + + // refresh vpnv6 tovpn_sid + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; + if (tovpn_sid) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = *tovpn_sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + } + } + + vpn_leak_postchange_all(); + return 0; +} + void bgp_zebra_init(struct thread_master *master, unsigned short instance) { zclient_num_connects = 0; @@ -3119,6 +3201,8 @@ void bgp_zebra_init(struct thread_master *master, unsigned short instance) zclient->iptable_notify_owner = iptable_notify_owner; zclient->route_notify_owner = bgp_zebra_route_notify_owner; zclient->instance = instance; + zclient->srv6_locator_add = bgp_zebra_process_srv6_locator_add; + zclient->srv6_locator_delete = bgp_zebra_process_srv6_locator_delete; zclient->process_srv6_locator_chunk = bgp_zebra_process_srv6_locator_chunk; } From e08c56634c9ea31541f7671879376e77b1cedd2d Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Wed, 25 Aug 2021 03:24:19 +0000 Subject: [PATCH 06/10] topotests: update test cases about srv6 locator deletion Before this PR, in case of get locator chunk zapi from zclient, zebra precreated a down state locator and set the chunk ownership. After this PR, this is no longer done, and chunks are no longer automatically generated. In this commit, we will make a test update to check the corresponding detailed behavior. Signed-off-by: Hiroki Shirokura --- .../srv6_locator/expected_chunks4.json | 4 --- .../srv6_locator/expected_chunks5.json | 6 ---- .../srv6_locator/expected_chunks6.json | 2 ++ .../srv6_locator/expected_locators4.json | 10 +++--- .../srv6_locator/expected_locators5.json | 13 +------ .../srv6_locator/expected_locators6.json | 5 +++ .../srv6_locator/test_srv6_locator.py | 35 +++++++++++++++---- 7 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 tests/topotests/srv6_locator/expected_chunks6.json create mode 100644 tests/topotests/srv6_locator/expected_locators6.json diff --git a/tests/topotests/srv6_locator/expected_chunks4.json b/tests/topotests/srv6_locator/expected_chunks4.json index 6e49738f37..0d4f101c7a 100644 --- a/tests/topotests/srv6_locator/expected_chunks4.json +++ b/tests/topotests/srv6_locator/expected_chunks4.json @@ -1,6 +1,2 @@ [ - { - "name": "loc3", - "chunks": [] - } ] diff --git a/tests/topotests/srv6_locator/expected_chunks5.json b/tests/topotests/srv6_locator/expected_chunks5.json index a18221859e..0d4f101c7a 100644 --- a/tests/topotests/srv6_locator/expected_chunks5.json +++ b/tests/topotests/srv6_locator/expected_chunks5.json @@ -1,8 +1,2 @@ [ - { - "name": "loc3", - "chunks": [ - "2001:db8:3:3::/64" - ] - } ] diff --git a/tests/topotests/srv6_locator/expected_chunks6.json b/tests/topotests/srv6_locator/expected_chunks6.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator/expected_chunks6.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator/expected_locators4.json b/tests/topotests/srv6_locator/expected_locators4.json index 7989f9021b..4b0f95f7be 100644 --- a/tests/topotests/srv6_locator/expected_locators4.json +++ b/tests/topotests/srv6_locator/expected_locators4.json @@ -23,11 +23,13 @@ ] }, { - "name":"loc3", - "statusUp":false, - "chunks":[ + "name": "loc3", + "prefix": "2001:db8:3:3::/64", + "statusUp": true, + "chunks": [ { - "proto":"sharp" + "prefix": "2001:db8:3:3::/64", + "proto": "system" } ] } diff --git a/tests/topotests/srv6_locator/expected_locators5.json b/tests/topotests/srv6_locator/expected_locators5.json index 8c512ebc46..bcffa004bd 100644 --- a/tests/topotests/srv6_locator/expected_locators5.json +++ b/tests/topotests/srv6_locator/expected_locators5.json @@ -1,16 +1,5 @@ { "locators":[ - { - "name": "loc1", - "prefix": "2001:db8:1:1::/64", - "statusUp": true, - "chunks": [ - { - "prefix": "2001:db8:1:1::/64", - "proto": "system" - } - ] - }, { "name": "loc2", "prefix": "2001:db8:2:2::/64", @@ -29,7 +18,7 @@ "chunks":[ { "prefix": "2001:db8:3:3::/64", - "proto": "sharp" + "proto": "system" } ] } diff --git a/tests/topotests/srv6_locator/expected_locators6.json b/tests/topotests/srv6_locator/expected_locators6.json new file mode 100644 index 0000000000..66d23d5556 --- /dev/null +++ b/tests/topotests/srv6_locator/expected_locators6.json @@ -0,0 +1,5 @@ +{ + "locators":[ + ] +} + diff --git a/tests/topotests/srv6_locator/test_srv6_locator.py b/tests/topotests/srv6_locator/test_srv6_locator.py index b48cd09bf9..bc5fa409d2 100755 --- a/tests/topotests/srv6_locator/test_srv6_locator.py +++ b/tests/topotests/srv6_locator/test_srv6_locator.py @@ -102,6 +102,10 @@ def test_srv6(): success, result = topotest.run_and_expect(func, None, count=5, wait=0.5) assert result is None, "Failed" + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + logger.info("Test1 for Locator Configuration") check_srv6_locator(router, "expected_locators1.json") check_sharpd_chunk(router, "expected_chunks1.json") @@ -116,12 +120,7 @@ def test_srv6(): check_srv6_locator(router, "expected_locators3.json") check_sharpd_chunk(router, "expected_chunks3.json") - logger.info("Test4 get chunk for non-exist locator by zclient") - router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc3") - check_srv6_locator(router, "expected_locators4.json") - check_sharpd_chunk(router, "expected_chunks4.json") - - logger.info("Test5 Test for Zclient. after locator loc3 was configured") + logger.info("Test4 additional locator loc3") router.vtysh_cmd( """ configure terminal @@ -132,9 +131,33 @@ def test_srv6(): prefix 2001:db8:3:3::/64 """ ) + check_srv6_locator(router, "expected_locators4.json") + check_sharpd_chunk(router, "expected_chunks4.json") + + logger.info("Test5 delete locator and chunk is released automatically") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) check_srv6_locator(router, "expected_locators5.json") check_sharpd_chunk(router, "expected_chunks5.json") + logger.info("Test6 delete srv6 all configuration") + router.vtysh_cmd( + """ + configure terminal + segment-routing + no srv6 + """ + ) + check_srv6_locator(router, "expected_locators6.json") + check_sharpd_chunk(router, "expected_chunks6.json") + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] From 9529336a226750652544e860bafaf61411021dff Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Mon, 6 Sep 2021 23:57:05 +0000 Subject: [PATCH 07/10] topotests: refactor bgp_srv6l3vpn_to_bgp_vrf Signed-off-by: Hiroki Shirokura --- .../test_bgp_srv6l3vpn_to_bgp_vrf.py | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py index 2d544c1ccf..d4aee0d266 100755 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py @@ -129,6 +129,10 @@ def setup_module(mod): tgen.gears["r2"].run("ip link set eth3 master vrf20") tgen.start_router() + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + def teardown_module(mod): tgen = get_topogen() @@ -143,7 +147,22 @@ def open_json_file(filename): assert False, "Could not read file {}".format(filename) -def test_rib(): +def check_ping(name, dest_addr, expect_connected): + def _check(name, dest_addr, match): + tgen = get_topogen() + output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr)) + logger.info(output) + assert match in output, "ping fail" + + match = "{} packet loss".format("0%" if expect_connected else "100%") + logger.info("[+] check {} {} {}".format(name, dest_addr, match)) + tgen = get_topogen() + func = functools.partial(_check, name, dest_addr, match) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, 'Failed' + + +def check_rib(name, cmd, expected_file): def _check(name, cmd, expected_file): logger.info("polling") tgen = get_topogen() @@ -152,51 +171,39 @@ def test_rib(): expected = open_json_file("{}/{}".format(CWD, expected_file)) return topotest.json_cmp(output, expected) - def check(name, cmd, expected_file): - logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) - tgen = get_topogen() - func = functools.partial(_check, name, cmd, expected_file) - success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, "Failed" + logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, 'Failed' - check("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") - check("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") - check("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") - check("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") - check("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") - check("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") - check("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") - check("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") - check("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") - check("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") - check("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") - check("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + +def test_rib(): + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") + check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") + check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") + check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") + check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") + check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") + check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") + check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") + check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") + check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") def test_ping(): - def _check(name, dest_addr, match): - tgen = get_topogen() - output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr)) - logger.info(output) - assert match in output, "ping fail" - - def check(name, dest_addr, match): - logger.info("[+] check {} {} {}".format(name, dest_addr, match)) - tgen = get_topogen() - func = functools.partial(_check, name, dest_addr, match) - success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, "Failed" - - check("ce1", "2001:2::2", " 0% packet loss") - check("ce1", "2001:3::2", " 0% packet loss") - check("ce1", "2001:4::2", " 100% packet loss") - check("ce1", "2001:5::2", " 100% packet loss") - check("ce1", "2001:6::2", " 100% packet loss") - check("ce4", "2001:1::2", " 100% packet loss") - check("ce4", "2001:2::2", " 100% packet loss") - check("ce4", "2001:3::2", " 100% packet loss") - check("ce4", "2001:5::2", " 0% packet loss") - check("ce4", "2001:6::2", " 0% packet loss") + check_ping("ce1", "2001:2::2", True) + check_ping("ce1", "2001:3::2", True) + check_ping("ce1", "2001:4::2", False) + check_ping("ce1", "2001:5::2", False) + check_ping("ce1", "2001:6::2", False) + check_ping("ce4", "2001:1::2", False) + check_ping("ce4", "2001:2::2", False) + check_ping("ce4", "2001:3::2", False) + check_ping("ce4", "2001:5::2", True) + check_ping("ce4", "2001:6::2", True) if __name__ == "__main__": From 6d39d7c0dd7650969c0a87c6f56ca22a268f2bcd Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Sun, 5 Sep 2021 09:48:28 +0000 Subject: [PATCH 08/10] topotests: bgp_srv6l3vpn_to_bgp_vrf locator update test case If the SRv6 locator is deleted in zebra, zclient(bgpd) which allocates SIDs from the locator will update the RIBs which use those SIDs and make them invalid. This will cause the VPNv6 route to be withdrawn and the VPN to stop. If the SRv6 locator is added again, zclient(bgpd) will allocate the SIDs from the locator again, and VPNv6 will be re-established. This commit add a test case to confirm this. Signed-off-by: Hiroki Shirokura --- .../r1/vpnv6_rib_locator_deleted.json | 160 +++++++++++++++++ .../r1/vpnv6_rib_locator_recreated.json | 169 ++++++++++++++++++ .../bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf | 2 + .../r2/vpnv6_rib_locator_deleted.json | 93 ++++++++++ .../r2/vpnv6_rib_locator_recreated.json | 169 ++++++++++++++++++ .../bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf | 2 + .../test_bgp_srv6l3vpn_to_bgp_vrf.py | 37 +++- 7 files changed, 630 insertions(+), 2 deletions(-) create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json create mode 100644 tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000000..f2df9be49d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json @@ -0,0 +1,160 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000000..0fdd3d6dc0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf index ec36870369..68b5730a63 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf @@ -34,7 +34,9 @@ segment-routing ip forwarding ipv6 forwarding ! +ipv6 route 2001:db8:2:1::/64 2001::2 ipv6 route 2001:db8:2:2::/64 2001::2 +ipv6 route 2001:db8:2:3::/64 2001::2 ! line vty ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000000..25cdf031c3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json @@ -0,0 +1,93 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000000..03bbcc008d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf index f3e025d23a..91fd92d422 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf @@ -35,6 +35,8 @@ ip forwarding ipv6 forwarding ! ipv6 route 2001:db8:1:1::/64 2001::1 +ipv6 route 2001:db8:1:2::/64 2001::1 +ipv6 route 2001:db8:1:3::/64 2001::1 ! line vty ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py index d4aee0d266..d5019f6680 100755 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py @@ -159,7 +159,7 @@ def check_ping(name, dest_addr, expect_connected): tgen = get_topogen() func = functools.partial(_check, name, dest_addr, match) success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, 'Failed' + assert result is None, "Failed" def check_rib(name, cmd, expected_file): @@ -175,7 +175,7 @@ def check_rib(name, cmd, expected_file): tgen = get_topogen() func = functools.partial(_check, name, cmd, expected_file) success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) - assert result is None, 'Failed' + assert result is None, "Failed" def test_rib(): @@ -206,6 +206,39 @@ def test_ping(): check_ping("ce4", "2001:6::2", True) +def test_locator_delete(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_locator_recreate(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) From 0249b8b61206dcee9baacee9db9c0d69cf263948 Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Mon, 13 Sep 2021 22:11:11 +0000 Subject: [PATCH 09/10] bgpd: add no-cli for srv6 on bgpd-side (1) Implement zapi wrapper func to release srv6-locator-chunk (2) Implement no locator NAME command router bgp 1 segment-routing srv6 no locator loc1 (3) Implement no segment-routing srv6 command router bgp 1 no segment-routing srv6 Signed-off-by: Hiroki Shirokura --- bgpd/bgp_vty.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_zebra.c | 5 +++ bgpd/bgp_zebra.h | 1 + 3 files changed, 102 insertions(+) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 4a1153651e..8c5956bae6 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -282,6 +282,57 @@ static const char *get_afi_safi_json_str(afi_t afi, safi_t safi) return "Unknown"; } +/* unset srv6 locator */ +static int bgp_srv6_locator_unset(struct bgp *bgp) +{ + int ret; + struct listnode *node, *nnode; + struct prefix_ipv6 *chunk; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + struct in6_addr *tovpn_sid; + + /* 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); + + /* refresh functions */ + for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) + listnode_delete(bgp->srv6_functions, func); + + /* refresh tovpn_sid */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + /* refresh vpnv4 tovpn_sid */ + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; + if (tovpn_sid) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + + /* refresh vpnv6 tovpn_sid */ + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; + if (tovpn_sid) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + } + + /* update vpn bgp processes */ + vpn_leak_postchange_all(); + + /* clear locator name */ + memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name)); + + return 0; +} + /* Utility function to get address family from current node. */ afi_t bgp_node_afi(struct vty *vty) { @@ -9094,6 +9145,23 @@ DEFUN_NOSH (bgp_segment_routing_srv6, return CMD_SUCCESS; } +DEFUN (no_bgp_segment_routing_srv6, + no_bgp_segment_routing_srv6_cmd, + "no segment-routing srv6", + NO_STR + "Segment-Routing configuration\n" + "Segment-Routing SRv6 configuration\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (strlen(bgp->srv6_locator_name) > 0) + if (bgp_srv6_locator_unset(bgp) < 0) + return CMD_WARNING_CONFIG_FAILED; + + bgp->srv6_enabled = false; + return CMD_SUCCESS; +} + DEFPY (bgp_srv6_locator, bgp_srv6_locator_cmd, "locator NAME$name", @@ -9119,6 +9187,32 @@ DEFPY (bgp_srv6_locator, return CMD_SUCCESS; } +DEFPY (no_bgp_srv6_locator, + no_bgp_srv6_locator_cmd, + "no locator NAME$name", + NO_STR + "Specify SRv6 locator\n" + "Specify SRv6 locator\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* when locator isn't configured, do nothing */ + if (strlen(bgp->srv6_locator_name) < 1) + return CMD_SUCCESS; + + /* name validation */ + if (strcmp(name, bgp->srv6_locator_name) != 0) { + vty_out(vty, "%% No srv6 locator is configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* unset locator */ + if (bgp_srv6_locator_unset(bgp) < 0) + return CMD_WARNING_CONFIG_FAILED; + + return CMD_SUCCESS; +} + DEFPY (show_bgp_srv6, show_bgp_srv6_cmd, "show bgp segment-routing srv6", @@ -18910,7 +19004,9 @@ void bgp_vty_init(void) /* srv6 commands */ install_element(VIEW_NODE, &show_bgp_srv6_cmd); install_element(BGP_NODE, &bgp_segment_routing_srv6_cmd); + install_element(BGP_NODE, &no_bgp_segment_routing_srv6_cmd); install_element(BGP_SRV6_NODE, &bgp_srv6_locator_cmd); + install_element(BGP_SRV6_NODE, &no_bgp_srv6_locator_cmd); install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd); } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index ac34692fe8..00ff3483ab 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -3614,3 +3614,8 @@ int bgp_zebra_srv6_manager_get_locator_chunk(const char *name) { return srv6_manager_get_locator_chunk(zclient, name); } + +int bgp_zebra_srv6_manager_release_locator_chunk(const char *name) +{ + return srv6_manager_release_locator_chunk(zclient, name); +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 02b6484943..9c0a1d8f1f 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -114,4 +114,5 @@ extern int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable); extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type); 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); #endif /* _QUAGGA_BGP_ZEBRA_H */ From d4a94e93e5ce2dbe61fb2898ea8fec9dd6da15d2 Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Mon, 13 Sep 2021 23:32:09 +0000 Subject: [PATCH 10/10] topotests: bgp_srv6l3vpn_to_bgp_vrf bgp locator unset test case Signed-off-by: Hiroki Shirokura --- .../test_bgp_srv6l3vpn_to_bgp_vrf.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py index d5019f6680..e0cf8c88e6 100755 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py @@ -239,6 +239,65 @@ def test_locator_recreate(): check_ping("ce1", "2001:2::2", True) +def test_bgp_locator_unset(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_bgp_locator_reset(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) + + +def test_bgp_srv6_unset(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + no segment-routing srv6 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_bgp_srv6_reset(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args))