Merge pull request #9486 from slankdev/slankdev-srv6-no-cli-1

CLI to delete SRv6 locator
This commit is contained in:
Igor Ryzhov 2021-09-14 19:04:03 +03:00 committed by GitHub
commit b8c01bba53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1137 additions and 87 deletions

View File

@ -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 "),

View File

@ -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)) {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;

View File

@ -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,

View File

@ -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
}
]
}
]
}
}
}
}

View File

@ -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
}
]
}
]
}
}
}
}

View File

@ -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
!

View File

@ -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
}
]
}
]
}
}
}
}

View File

@ -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
}
]
}
]
}
}
}
}

View File

@ -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
!

View File

@ -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__":

View File

@ -1,6 +1,2 @@
[
{
"name": "loc3",
"chunks": []
}
]

View File

@ -1,8 +1,2 @@
[
{
"name": "loc3",
"chunks": [
"2001:db8:3:3::/64"
]
}
]

View File

@ -0,0 +1,2 @@
[
]

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -0,0 +1,5 @@
{
"locators":[
]
}

View File

@ -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:]

View File

@ -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[] = {

View File

@ -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)) {

View File

@ -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);