mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-06 00:41:20 +00:00
Merge pull request #9486 from slankdev/slankdev-srv6-no-cli-1
CLI to delete SRv6 locator
This commit is contained in:
commit
b8c01bba53
@ -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 "),
|
||||
|
@ -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)) {
|
||||
|
@ -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)
|
||||
{
|
||||
@ -9096,6 +9147,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",
|
||||
@ -9121,6 +9189,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",
|
||||
@ -18915,7 +19009,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);
|
||||
}
|
||||
|
@ -3088,6 +3088,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;
|
||||
@ -3130,6 +3212,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;
|
||||
}
|
||||
@ -3541,3 +3625,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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
!
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
!
|
||||
|
@ -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,131 @@ 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"
|
||||
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)
|
||||
|
||||
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")
|
||||
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)
|
||||
|
||||
|
||||
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__":
|
||||
|
@ -1,6 +1,2 @@
|
||||
[
|
||||
{
|
||||
"name": "loc3",
|
||||
"chunks": []
|
||||
}
|
||||
]
|
||||
|
@ -1,8 +1,2 @@
|
||||
[
|
||||
{
|
||||
"name": "loc3",
|
||||
"chunks": [
|
||||
"2001:db8:3:3::/64"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
2
tests/topotests/srv6_locator/expected_chunks6.json
Normal file
2
tests/topotests/srv6_locator/expected_chunks6.json
Normal file
@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
5
tests/topotests/srv6_locator/expected_locators6.json
Normal file
5
tests/topotests/srv6_locator/expected_locators6.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"locators":[
|
||||
]
|
||||
}
|
||||
|
@ -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:]
|
||||
|
@ -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[] = {
|
||||
|
@ -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);
|
||||
}
|
||||
@ -171,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)) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user