summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--babeld/babeld.c14
-rw-r--r--bgpd/bgp_attr.c4
-rw-r--r--bgpd/bgp_debug.c1
-rw-r--r--bgpd/bgp_evpn_vty.c17
-rw-r--r--bgpd/bgp_mplsvpn.c46
-rw-r--r--bgpd/bgp_nht.c80
-rw-r--r--bgpd/bgp_open.c16
-rw-r--r--bgpd/bgp_open.h2
-rw-r--r--bgpd/bgp_packet.c13
-rw-r--r--bgpd/bgp_route.c129
-rw-r--r--bgpd/bgp_route.h3
-rw-r--r--bgpd/bgp_vty.c97
-rw-r--r--bgpd/bgpd.c37
-rw-r--r--bgpd/bgpd.h5
-rw-r--r--bgpd/rfapi/rfapi_import.c12
-rw-r--r--doc/user/bgp.rst14
-rw-r--r--isisd/isis_nb.c12
-rw-r--r--isisd/isis_nb_config.c40
-rw-r--r--lib/zlog_5424.c12
-rw-r--r--tests/topotests/bgp_bmp/test_bgp_bmp_1.py7
-rw-r--r--tests/topotests/bgp_ipv6_link_local_capability/__init__.py0
-rw-r--r--tests/topotests/bgp_ipv6_link_local_capability/r1/frr.conf15
-rw-r--r--tests/topotests/bgp_ipv6_link_local_capability/r2/frr.conf13
-rw-r--r--tests/topotests/bgp_ipv6_link_local_capability/test_bgp_ipv6_link_local_capability.py110
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf10
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json155
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json103
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json120
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json120
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json137
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py234
-rw-r--r--tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py75
32 files changed, 1347 insertions, 306 deletions
diff --git a/babeld/babeld.c b/babeld/babeld.c
index 1d2f60e3ad..4e68f05df4 100644
--- a/babeld/babeld.c
+++ b/babeld/babeld.c
@@ -538,7 +538,7 @@ resize_receive_buffer(int size)
}
static void
-babel_distribute_update (struct distribute_ctx *ctx, struct distribute *dist)
+babel_distribute_update (struct distribute_ctx *ctx __attribute__((__unused__)), struct distribute *dist)
{
struct interface *ifp;
babel_interface_nfo *babel_ifp;
@@ -593,7 +593,7 @@ babel_distribute_update_all (struct prefix_list *notused)
}
static void
-babel_distribute_update_all_wrapper (struct access_list *notused)
+babel_distribute_update_all_wrapper (struct access_list *notused __attribute__((__unused__)))
{
babel_distribute_update_all(NULL);
}
@@ -872,16 +872,18 @@ babeld_quagga_init(void)
/* Stubs to adapt Babel's filtering calls to Quagga's infrastructure. */
int
-input_filter(const unsigned char *id,
+input_filter(const unsigned char *id __attribute__((__unused__)),
const unsigned char *prefix, unsigned short plen,
- const unsigned char *neigh, unsigned int ifindex)
+ const unsigned char *neigh __attribute__((__unused__)),
+ unsigned int ifindex)
{
return babel_filter(0, prefix, plen, ifindex);
}
int
-output_filter(const unsigned char *id, const unsigned char *prefix,
- unsigned short plen, unsigned int ifindex)
+output_filter(const unsigned char *id __attribute__((__unused__)),
+ const unsigned char *prefix, unsigned short plen,
+ unsigned int ifindex)
{
return babel_filter(1, prefix, plen, ifindex);
}
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index d349922c52..c15dada9c1 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -2450,6 +2450,10 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
if (!peer->nexthop.ifp) {
zlog_warn("%s sent a v6 global attribute but address is a V6 LL and there's no peer interface information. Hence, withdrawing",
peer->host);
+ if (CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
+ CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP);
return BGP_ATTR_PARSE_WITHDRAW;
}
attr->nh_ifindex = peer->nexthop.ifp->ifindex;
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 319638e412..dfebc00e0a 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -149,6 +149,7 @@ static const struct message bgp_notify_update_msg[] = {
{BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
{BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
{BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
+ {BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP, "/Unreachable Link-Local Address"},
{0}};
static const struct message bgp_notify_cease_msg[] = {
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index dc6e0d33c2..8a0c6e10d6 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -766,7 +766,7 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
if (detail)
route_vty_out_detail(vty, bgp, bd, bgp_dest_get_prefix(bd), pi,
AFI_L2VPN, SAFI_EVPN, RPKI_NOT_BEING_USED,
- json_path, NULL);
+ json_path, NULL, 0);
else
route_vty_out(vty, &bd->rn->p, pi, 0, SAFI_EVPN,
json_path, false);
@@ -893,7 +893,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn,
if (detail)
route_vty_out_detail(vty, bgp, dest, &tmp_p, pi, AFI_L2VPN,
SAFI_EVPN, RPKI_NOT_BEING_USED, json_path,
- NULL);
+ NULL, 0);
else
route_vty_out(vty, &tmp_p, pi, 0, SAFI_EVPN,
@@ -2569,7 +2569,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), pi, afi, safi,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2697,7 +2697,7 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
}
route_vty_out_detail(vty, bgp, dest, (struct prefix *)&tmp_p, pi, afi, safi,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2807,7 +2807,7 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), pi, afi, safi,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2919,7 +2919,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), pi, afi,
- safi, RPKI_NOT_BEING_USED, json_path, NULL);
+ safi, RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -3055,7 +3055,7 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, p, pi, AFI_L2VPN, SAFI_EVPN,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -3227,7 +3227,8 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
route_vty_out_detail(vty, bgp, dest,
bgp_dest_get_prefix(dest), pi,
AFI_L2VPN, SAFI_EVPN,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL,
+ 0);
} else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
json_path, false);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 46e529f03d..6a8df3b68e 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -1052,6 +1052,7 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn,
struct bgp_path_info *bpi_ultimate;
struct bgp *bgp_nexthop;
struct bgp_table *table;
+ struct interface *ifp;
bool nh_valid;
bpi_ultimate = bgp_get_imported_bpi_ultimate(source_bpi);
@@ -1062,6 +1063,15 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn,
else
bgp_nexthop = bgp_orig;
+ /* The nexthop is invalid if its VRF does not exist */
+ if (bgp_nexthop->vrf_id == VRF_UNKNOWN)
+ return false;
+
+ /* The nexthop is invalid if its VRF interface is down*/
+ ifp = if_get_vrf_loopback(bgp_nexthop->vrf_id);
+ if (ifp && !if_is_up(ifp))
+ return false;
+
/*
* No nexthop tracking for redistributed routes, for
* EVPN-imported routes that get leaked, or for routes
@@ -1126,8 +1136,8 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
struct bgp_path_info *bpi;
struct bgp_path_info *new;
struct bgp_path_info_extra *extra;
- struct bgp_path_info *parent = source_bpi;
struct bgp_labels bgp_labels = {};
+ struct bgp *bgp_nexthop;
bool labelssame;
uint8_t i;
@@ -1159,8 +1169,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
* match parent
*/
for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) {
- if (bpi->extra && bpi->extra->vrfleak &&
- bpi->extra->vrfleak->parent == parent)
+ if (bpi->extra && bpi->extra->vrfleak && bpi->extra->vrfleak->parent == source_bpi)
break;
}
@@ -1174,6 +1183,16 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
labelssame = bgp_path_info_labels_same(bpi, bgp_labels.label,
bgp_labels.num_labels);
+ bgp_nexthop = bpi->extra->vrfleak->bgp_orig ?: bgp_orig;
+ if (bgp_nexthop->vrf_id == VRF_UNKNOWN) {
+ if (debug) {
+ zlog_debug("%s: ->%s(s_flags: 0x%x b_flags: 0x%x): %pFX: Found route, origin VRF does not exist, not leaking",
+ __func__, to_bgp->name_pretty, source_bpi->flags,
+ bpi->flags, p);
+ }
+ return NULL;
+ }
+
if (CHECK_FLAG(source_bpi->flags, BGP_PATH_REMOVED)
&& CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
if (debug) {
@@ -1185,9 +1204,11 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
return NULL;
}
- if (attrhash_cmp(bpi->attr, new_attr) && labelssame
- && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
-
+ if (attrhash_cmp(bpi->attr, new_attr) && labelssame &&
+ !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) &&
+ leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, source_bpi, bpi,
+ bgp_orig, p,
+ debug) == !!CHECK_FLAG(bpi->flags, BGP_PATH_VALID)) {
bgp_attr_unintern(&new_attr);
if (debug)
zlog_debug(
@@ -1274,6 +1295,14 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
return NULL;
}
+ if (bgp_orig->vrf_id == VRF_UNKNOWN) {
+ if (debug) {
+ zlog_debug("%s: ->%s(s_flags: 0x%x): %pFX: New route, origin VRF does not exist, not leaking",
+ __func__, to_bgp->name_pretty, source_bpi->flags, p);
+ }
+ return NULL;
+ }
+
new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, 0,
to_bgp->peer_self, new_attr, bn);
@@ -1297,9 +1326,8 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
if (bgp_labels.num_labels)
new->extra->labels = bgp_labels_intern(&bgp_labels);
- new->extra->vrfleak->parent = bgp_path_info_lock(parent);
- bgp_dest_lock_node(
- (struct bgp_dest *)parent->net);
+ new->extra->vrfleak->parent = bgp_path_info_lock(source_bpi);
+ bgp_dest_lock_node((struct bgp_dest *)source_bpi->net);
new->extra->vrfleak->bgp_orig = bgp_lock(bgp_orig);
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 2ef7ec97e3..c8e15372b6 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -38,7 +38,7 @@ extern struct zclient *zclient;
static void register_zebra_rnh(struct bgp_nexthop_cache *bnc);
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc);
-static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
+static bool make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
static void bgp_nht_ifp_initial(struct event *thread);
DEFINE_HOOK(bgp_nht_path_update, (struct bgp *bgp, struct bgp_path_info *pi, bool valid),
@@ -330,7 +330,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
/* This will return true if the global IPv6 NH is a link local
* addr */
- if (make_prefix(afi, pi, &p) < 0)
+ if (!make_prefix(afi, pi, &p))
return 1;
/*
@@ -1026,7 +1026,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp)
* make_prefix - make a prefix structure from the path (essentially
* path's node.
*/
-static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
+static bool make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
{
int is_bgp_static = ((pi->type == ZEBRA_ROUTE_BGP)
@@ -1036,12 +1036,13 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
struct bgp_dest *net = pi->net;
const struct prefix *p_orig = bgp_dest_get_prefix(net);
struct in_addr ipv4;
+ struct peer *peer = pi->peer;
+ struct attr *attr = pi->attr;
if (p_orig->family == AF_FLOWSPEC) {
- if (!pi->peer)
- return -1;
- return bgp_flowspec_get_first_nh(pi->peer->bgp,
- pi, p, afi);
+ if (!peer)
+ return false;
+ return bgp_flowspec_get_first_nh(peer->bgp, pi, p, afi);
}
memset(p, 0, sizeof(struct prefix));
switch (afi) {
@@ -1051,34 +1052,32 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
p->u.prefix4 = p_orig->u.prefix4;
p->prefixlen = p_orig->prefixlen;
} else {
- if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
- ipv4_mapped_ipv6_to_ipv4(
- &pi->attr->mp_nexthop_global, &ipv4);
+ if (IS_MAPPED_IPV6(&attr->mp_nexthop_global)) {
+ ipv4_mapped_ipv6_to_ipv4(&attr->mp_nexthop_global, &ipv4);
p->u.prefix4 = ipv4;
p->prefixlen = IPV4_MAX_BITLEN;
} else {
if (p_orig->family == AF_EVPN)
- p->u.prefix4 =
- pi->attr->mp_nexthop_global_in;
+ p->u.prefix4 = attr->mp_nexthop_global_in;
else
- p->u.prefix4 = pi->attr->nexthop;
+ p->u.prefix4 = attr->nexthop;
p->prefixlen = IPV4_MAX_BITLEN;
}
}
break;
case AFI_IP6:
p->family = AF_INET6;
- if (pi->attr->srv6_l3vpn) {
+ if (attr->srv6_l3vpn) {
p->prefixlen = IPV6_MAX_BITLEN;
- if (pi->attr->srv6_l3vpn->transposition_len != 0 &&
+ if (attr->srv6_l3vpn->transposition_len != 0 &&
BGP_PATH_INFO_NUM_LABELS(pi)) {
- IPV6_ADDR_COPY(&p->u.prefix6, &pi->attr->srv6_l3vpn->sid);
+ IPV6_ADDR_COPY(&p->u.prefix6, &attr->srv6_l3vpn->sid);
transpose_sid(&p->u.prefix6,
decode_label(&pi->extra->labels->label[0]),
- pi->attr->srv6_l3vpn->transposition_offset,
- pi->attr->srv6_l3vpn->transposition_len);
+ attr->srv6_l3vpn->transposition_offset,
+ attr->srv6_l3vpn->transposition_len);
} else
- IPV6_ADDR_COPY(&(p->u.prefix6), &(pi->attr->srv6_l3vpn->sid));
+ IPV6_ADDR_COPY(&(p->u.prefix6), &(attr->srv6_l3vpn->sid));
} else if (is_bgp_static) {
p->u.prefix6 = p_orig->u.prefix6;
p->prefixlen = p_orig->prefixlen;
@@ -1086,28 +1085,35 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
/* If we receive MP_REACH nexthop with ::(LL)
* or LL(LL), use LL address as nexthop cache.
*/
- if (pi->attr &&
- pi->attr->mp_nexthop_len ==
- BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
- (IN6_IS_ADDR_UNSPECIFIED(
- &pi->attr->mp_nexthop_global) ||
- IN6_IS_ADDR_LINKLOCAL(&pi->attr->mp_nexthop_global)))
- p->u.prefix6 = pi->attr->mp_nexthop_local;
+ if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
+ (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) ||
+ IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)))
+ p->u.prefix6 = attr->mp_nexthop_local;
/* If we receive MR_REACH with (GA)::(LL)
* then check for route-map to choose GA or LL
*/
- else if (pi->attr &&
- pi->attr->mp_nexthop_len ==
- BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
- if (CHECK_FLAG(pi->attr->nh_flags,
- BGP_ATTR_NH_MP_PREFER_GLOBAL))
- p->u.prefix6 =
- pi->attr->mp_nexthop_global;
+ else if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ if (CHECK_FLAG(attr->nh_flags, BGP_ATTR_NH_MP_PREFER_GLOBAL))
+ p->u.prefix6 = attr->mp_nexthop_global;
else
- p->u.prefix6 =
- pi->attr->mp_nexthop_local;
+ p->u.prefix6 = attr->mp_nexthop_local;
+ } else if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL &&
+ IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
+ /* If we receive MP_REACH with GUA as LL, we should
+ * check if we have Link-Local Next Hop capability also.
+ */
+ if (!(CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
+ CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV))) {
+ zlog_warn("%s: received IPv6 global next-hop as Link-Local, but no capability exchanged",
+ __func__);
+ p->u.prefix6 = attr->mp_nexthop_global;
+ } else {
+ p->u.prefix6 = attr->mp_nexthop_global;
+ p->prefixlen = IPV6_MAX_BITLEN;
+ return false;
+ }
} else
- p->u.prefix6 = pi->attr->mp_nexthop_global;
+ p->u.prefix6 = attr->mp_nexthop_global;
p->prefixlen = IPV6_MAX_BITLEN;
}
break;
@@ -1119,7 +1125,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
}
break;
}
- return 0;
+ return true;
}
/**
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 6451c7cf38..be04d87b74 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -43,6 +43,7 @@ const struct message capcode_str[] = {
{ CAPABILITY_CODE_ROLE, "Role" },
{ CAPABILITY_CODE_SOFT_VERSION, "Software Version" },
{ CAPABILITY_CODE_PATHS_LIMIT, "Paths-Limit" },
+ { CAPABILITY_CODE_LINK_LOCAL, "Link-Local Next Hop" },
{ 0 }
};
@@ -63,6 +64,7 @@ const size_t cap_minsizes[] = {
[CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
[CAPABILITY_CODE_SOFT_VERSION] = CAPABILITY_CODE_SOFT_VERSION_LEN,
[CAPABILITY_CODE_PATHS_LIMIT] = CAPABILITY_CODE_PATHS_LIMIT_LEN,
+ [CAPABILITY_CODE_LINK_LOCAL] = CAPABILITY_CODE_LINK_LOCAL_LEN,
};
/* value the capability must be a multiple of.
@@ -1067,6 +1069,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
case CAPABILITY_CODE_PATHS_LIMIT:
+ case CAPABILITY_CODE_LINK_LOCAL:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
@@ -1168,6 +1171,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_SOFT_VERSION:
ret = bgp_capability_software_version(peer, &caphdr);
break;
+ case CAPABILITY_CODE_LINK_LOCAL:
+ SET_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV);
+ break;
case CAPABILITY_CODE_PATHS_LIMIT:
ret = bgp_capability_paths_limit(peer, &caphdr);
break;
@@ -1968,6 +1974,16 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN);
}
+ /* Link-Local Next Hop capability. */
+ if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL)) {
+ SET_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_LINK_LOCAL_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_LINK_LOCAL_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_LINK_LOCAL);
+ stream_putc(s, CAPABILITY_CODE_LINK_LOCAL_LEN);
+ }
+
/* FQDN capability */
if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN)
&& cmd_hostname_get()) {
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 3a8cba9b7d..abe3b51f5d 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -54,6 +54,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */
#define CAPABILITY_CODE_ROLE 9 /* Role Capability */
#define CAPABILITY_CODE_PATHS_LIMIT 76 /* Paths Limit Capability */
+#define CAPABILITY_CODE_LINK_LOCAL 77 /* draft-white-linklocal-capability */
/* Capability Length */
#define CAPABILITY_CODE_MP_LEN 4
@@ -71,6 +72,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
#define CAPABILITY_CODE_ROLE_LEN 1
#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
+#define CAPABILITY_CODE_LINK_LOCAL_LEN 0
/* Cooperative Route Filtering Capability. */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index f8726ffff9..3e90d7881c 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1245,6 +1245,18 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
/* Encode MP_EXT capability. */
switch (capability_code) {
+ case CAPABILITY_CODE_LINK_LOCAL:
+ stream_putc(s, action);
+ stream_putc(s, CAPABILITY_CODE_LINK_LOCAL);
+ stream_putc(s, 0);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", peer,
+ action == CAPABILITY_ACTION_SET ? "Advertising" : "Removing",
+ capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
+
+ COND_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV, action == CAPABILITY_ACTION_SET);
+ break;
case CAPABILITY_CODE_SOFT_VERSION:
stream_putc(s, action);
stream_putc(s, CAPABILITY_CODE_SOFT_VERSION);
@@ -3769,6 +3781,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
case CAPABILITY_CODE_PATHS_LIMIT:
+ case CAPABILITY_CODE_LINK_LOCAL:
if (hdr->length < cap_minsizes[hdr->code]) {
zlog_info("%pBP: %s Capability length error: got %u, expected at least %u",
peer, capability, hdr->length,
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index f46977af69..36690d55f4 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2506,8 +2506,16 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
} else if (!ibgp_to_ibgp && !transparent &&
!CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT) &&
IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) && peer->shared_network &&
- (from == bgp->peer_self || peer->sort == BGP_PEER_EBGP))
- global_and_ll = true;
+ (from == bgp->peer_self || peer->sort == BGP_PEER_EBGP)) {
+ /* If an implementation intends to send a single link-local forwarding
+ * address in the Next Hop field of the MP_REACH_NLRI, it MUST set the
+ * length of the Next Hop field to 16 and include only the IPv6 link-local
+ * address in the Next Hop field.
+ */
+ if (!(CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
+ CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV)))
+ global_and_ll = true;
+ }
if (global_and_ll) {
if (safi == SAFI_MPLS_VPN)
@@ -9946,6 +9954,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
"ipv6");
json_object_string_add(json_nexthop_global, "scope",
"global");
+ json_object_int_add(json_nexthop_global, "length", attr->mp_nexthop_len);
/* We display both LL & GL if both have been
* received */
@@ -9969,6 +9978,8 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
"ipv6");
json_object_string_add(json_nexthop_ll, "scope",
"link-local");
+ json_object_int_add(json_nexthop_global, "length",
+ attr->mp_nexthop_len);
if ((IPV6_ADDR_CMP(&attr->mp_nexthop_global,
&attr->mp_nexthop_local) !=
@@ -10784,7 +10795,7 @@ static void route_vty_out_detail_es_info(struct vty *vty,
void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
const struct prefix *p, struct bgp_path_info *path, afi_t afi,
safi_t safi, enum rpki_states rpki_curr_state, json_object *json_paths,
- struct attr *pattr)
+ struct attr *pattr, uint16_t show_opts)
{
char buf[INET6_ADDRSTRLEN];
char vni_buf[30] = {};
@@ -11090,6 +11101,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
"ipv6");
json_object_string_add(json_nexthop_global, "scope",
"global");
+ json_object_int_add(json_nexthop_global, "length", attr->mp_nexthop_len);
} else {
if (nexthop_hostname)
vty_out(vty, " %pI6(%s)",
@@ -11277,6 +11289,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
json_object_string_add(json_nexthop_ll, "afi", "ipv6");
json_object_string_add(json_nexthop_ll, "scope",
"link-local");
+ json_object_int_add(json_nexthop_ll, "length", attr->mp_nexthop_len);
json_object_boolean_true_add(json_nexthop_ll,
"accessible");
@@ -11811,6 +11824,16 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
llgr_remaining);
}
+ /* Display internal data if asked */
+ if (CHECK_FLAG(show_opts, BGP_SHOW_OPT_INTERNAL_DATA)) {
+ if (!json_paths) {
+ vty_out(vty, " net: %p, path: %p, pathext: %p, attr: %p\n", path->net,
+ path, path->extra, attr);
+ vty_out(vty, " flags net: 0x%u, path: 0x%u, attr: 0x%" PRIu64 "\n",
+ path->net->flags, path->flags, attr->flag);
+ }
+ }
+
/* Output some debug about internal state of the dest flags */
if (json_paths) {
if (CHECK_FLAG(bn->flags, BGP_NODE_PROCESS_SCHEDULED))
@@ -12242,7 +12265,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t sa
route_vty_out_detail(vty, bgp, dest, dest_p, pi,
family2afi(dest_p->family), safi,
- rpki_curr_state, json_paths, NULL);
+ rpki_curr_state, json_paths, NULL,
+ show_flags);
} else {
route_vty_out(vty, dest_p, pi, display,
safi, json_paths, wide);
@@ -12760,7 +12784,8 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
static void bgp_show_path_info(const struct prefix_rd *pfx_rd, struct bgp_dest *bgp_node,
struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
json_object *json, enum bgp_path_type pathtype, int *display,
- enum rpki_states rpki_target_state, struct attr *attr)
+ enum rpki_states rpki_target_state, struct attr *attr,
+ uint16_t show_opts)
{
struct bgp_path_info *pi;
int header = 1;
@@ -12804,7 +12829,8 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd, struct bgp_dest *
&& (CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH)
|| CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))))
route_vty_out_detail(vty, bgp, bgp_node, bgp_dest_get_prefix(bgp_node), pi,
- afi, safi, rpki_curr_state, json_paths, attr);
+ afi, safi, rpki_curr_state, json_paths, attr,
+ show_opts);
}
if (json && json_paths) {
@@ -12841,12 +12867,11 @@ const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
}
/* Display specified route of BGP table. */
-static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
- struct bgp_table *rib, const char *ip_str,
- afi_t afi, safi_t safi,
- enum rpki_states rpki_target_state,
- struct prefix_rd *prd, int prefix_check,
- enum bgp_path_type pathtype, bool use_json)
+static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, struct bgp_table *rib,
+ const char *ip_str, afi_t afi, safi_t safi,
+ enum rpki_states rpki_target_state, struct prefix_rd *prd,
+ int prefix_check, enum bgp_path_type pathtype, bool use_json,
+ uint16_t show_opts)
{
int ret;
int display = 0;
@@ -12891,8 +12916,8 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
continue;
}
- bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi,
- json, pathtype, &display, rpki_target_state, NULL);
+ bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json,
+ pathtype, &display, rpki_target_state, NULL, show_opts);
bgp_dest_unlock_node(rm);
}
@@ -12951,8 +12976,8 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
rm = longest_pfx;
bgp_dest_lock_node(rm);
- bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi,
- json, pathtype, &display, rpki_target_state, NULL);
+ bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json,
+ pathtype, &display, rpki_target_state, NULL, show_opts);
bgp_dest_unlock_node(rm);
}
@@ -12979,7 +13004,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
if (!prefix_check
|| dest_p->prefixlen == match.prefixlen) {
bgp_show_path_info(NULL, dest, vty, bgp, afi, safi, json, pathtype,
- &display, rpki_target_state, NULL);
+ &display, rpki_target_state, NULL, show_opts);
}
bgp_dest_unlock_node(dest);
@@ -12999,10 +13024,10 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
}
/* Display specified route of Main RIB */
-static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str,
- afi_t afi, safi_t safi, struct prefix_rd *prd,
- int prefix_check, enum bgp_path_type pathtype,
- enum rpki_states rpki_target_state, bool use_json)
+static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str, afi_t afi,
+ safi_t safi, struct prefix_rd *prd, int prefix_check,
+ enum bgp_path_type pathtype, enum rpki_states rpki_target_state,
+ bool use_json, uint16_t show_opts)
{
if (!bgp) {
bgp = bgp_get_default();
@@ -13019,9 +13044,9 @@ static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str,
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
- return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str,
- afi, safi, rpki_target_state, prd,
- prefix_check, pathtype, use_json);
+ return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str, afi, safi,
+ rpki_target_state, prd, prefix_check, pathtype, use_json,
+ show_opts);
}
static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc,
@@ -13383,7 +13408,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
|A.B.C.D/M longer-prefixes\
|X:X::X:X/M longer-prefixes\
|"BGP_SELF_ORIG_CMD_STR"\
- |detail-routes$detail_routes\
+ |detail-routes$detail_routes [internal$internal]\
] [json$uj [detail$detail_json] | wide$wide]",
SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
BGP_SAFI_WITH_LABEL_HELP_STR
@@ -13434,6 +13459,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
"Display route and more specific routes\n"
BGP_SELF_ORIG_HELP_STR
"Display detailed version of all routes\n"
+ "Display detailed version of all routes including internal data\n"
JSON_STR
"Display detailed version of JSON output\n"
"Increase table width for longer prefixes\n")
@@ -13462,6 +13488,9 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
if (detail_routes)
SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+ if (internal)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_INTERNAL_DATA);
+
/* [<ipv4|ipv6> [all]] */
if (all) {
SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL);
@@ -13750,7 +13779,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
DEFUN (show_ip_bgp_route,
show_ip_bgp_route_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [internal] [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -13761,6 +13790,7 @@ DEFUN (show_ip_bgp_route,
"IPv4 prefix\n"
"Network in the BGP routing table to display\n"
"IPv6 prefix\n"
+ "Display internal data additionally\n"
"Display only the bestpath\n"
"Display only multipaths\n"
"Display only paths that match the specified rpki state\n"
@@ -13777,6 +13807,7 @@ DEFUN (show_ip_bgp_route,
struct bgp *bgp = NULL;
enum bgp_path_type path_type;
bool uj = use_json(argc, argv);
+ uint16_t show_opts = 0;
int idx = 0;
@@ -13814,6 +13845,10 @@ DEFUN (show_ip_bgp_route,
prefix = argv[idx]->arg;
+ /* Display internal data also */
+ if (argv_find(argv, argc, "internal", &idx))
+ SET_FLAG(show_opts, BGP_SHOW_OPT_INTERNAL_DATA);
+
/* [<bestpath|multipath>] */
if (argv_find(argv, argc, "bestpath", &idx))
path_type = BGP_PATH_SHOW_BESTPATH;
@@ -13822,8 +13857,8 @@ DEFUN (show_ip_bgp_route,
else
path_type = BGP_PATH_SHOW_ALL;
- return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check,
- path_type, RPKI_NOT_BEING_USED, uj);
+ return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check, path_type,
+ RPKI_NOT_BEING_USED, uj, show_opts);
}
DEFUN (show_ip_bgp_regexp,
@@ -14681,9 +14716,8 @@ DEFUN (show_ip_bgp_vpn_all_route_prefix,
return CMD_WARNING;
}
- return bgp_show_route(vty, bgp, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0,
- BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED,
- use_json(argc, argv));
+ return bgp_show_route(vty, bgp, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_SHOW_ALL,
+ RPKI_NOT_BEING_USED, use_json(argc, argv), 0);
}
#endif /* KEEP_OLD_VPN_COMMANDS */
@@ -14715,9 +14749,8 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix,
vty_out(vty, "Unable to figure out Network\n");
return CMD_WARNING;
}
- return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL,
- prefix_check, BGP_PATH_SHOW_ALL,
- RPKI_NOT_BEING_USED, use_json(argc, argv));
+ return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL, prefix_check,
+ BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, use_json(argc, argv), 0);
}
static void show_adj_route_header(struct vty *vty, struct peer *peer,
@@ -14873,8 +14906,9 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
if (use_json)
json_net = json_object_new_object();
- bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp, afi, safi, json_net,
- BGP_PATH_SHOW_ALL, &display, RPKI_NOT_BEING_USED, NULL);
+ bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp, afi, safi,
+ json_net, BGP_PATH_SHOW_ALL, &display,
+ RPKI_NOT_BEING_USED, NULL, show_flags);
if (use_json)
json_object_object_addf(json_ar, json_net,
"%pFX", rn_p);
@@ -15010,7 +15044,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
pass_in = dest;
bgp_show_path_info(NULL, pass_in, vty, bgp, afi, safi,
json_net, BGP_PATH_SHOW_ALL, &display,
- RPKI_NOT_BEING_USED, NULL);
+ RPKI_NOT_BEING_USED, NULL, show_flags);
if (use_json)
json_object_object_addf(
json_ar, json_net,
@@ -15062,7 +15096,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
bgp_show_path_info(NULL, dest, vty, bgp, afi, safi,
json_net, BGP_PATH_SHOW_ALL,
&display, RPKI_NOT_BEING_USED,
- adj->attr);
+ adj->attr, show_flags);
if (use_json)
json_object_object_addf(json_ar, json_net,
"%pFX", rn_p);
@@ -15108,9 +15142,10 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
if (use_json)
json_net =
json_object_new_object();
- bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp, afi,
- safi, json_net, BGP_PATH_SHOW_BESTPATH,
- &display, RPKI_NOT_BEING_USED, NULL);
+ bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp,
+ afi, safi, json_net,
+ BGP_PATH_SHOW_BESTPATH, &display,
+ RPKI_NOT_BEING_USED, NULL, show_flags);
if (use_json)
json_object_object_addf(
json_ar, json_net,
@@ -15754,10 +15789,9 @@ DEFUN (show_bgp_afi_vpn_rd_route,
}
if (!strcmp(argv[5]->arg, "all"))
- return bgp_show_route(vty, NULL, argv[6]->arg, afi,
- SAFI_MPLS_VPN, NULL, 0, BGP_PATH_SHOW_ALL,
- RPKI_NOT_BEING_USED,
- use_json(argc, argv));
+ return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, NULL, 0,
+ BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, use_json(argc, argv),
+ 0);
ret = str2prefix_rd(argv[5]->arg, &prd);
if (!ret) {
@@ -15765,9 +15799,8 @@ DEFUN (show_bgp_afi_vpn_rd_route,
return CMD_WARNING;
}
- return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd,
- 0, BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED,
- use_json(argc, argv));
+ return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd, 0,
+ BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, use_json(argc, argv), 0);
}
static struct bgp_distance *bgp_distance_new(void)
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index c4cbbee0c7..1f854cad19 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -752,6 +752,7 @@ DECLARE_HOOK(bgp_route_update,
#define BGP_SHOW_OPT_JSON_DETAIL (1 << 7)
#define BGP_SHOW_OPT_TERSE (1 << 8)
#define BGP_SHOW_OPT_ROUTES_DETAIL (1 << 9)
+#define BGP_SHOW_OPT_INTERNAL_DATA (1 << 10)
/* Prototypes. */
extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi,
@@ -969,7 +970,7 @@ extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
const struct prefix *p, struct bgp_path_info *path, afi_t afi,
safi_t safi, enum rpki_states, json_object *json_paths,
- struct attr *attr);
+ struct attr *attr, uint16_t show_opts);
extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
struct bgp_table *table, struct prefix_rd *prd,
enum bgp_show_type type, void *output_arg,
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 98021f7ef4..fd4fd8afd6 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -114,6 +114,10 @@ FRR_CFG_DEFAULT_BOOL(BGP_SOFT_VERSION_CAPABILITY,
{ .val_bool = true, .match_profile = "datacenter", },
{ .val_bool = false },
);
+FRR_CFG_DEFAULT_BOOL(BGP_LINK_LOCAL_CAPABILITY,
+ { .val_bool = true, .match_profile = "datacenter", },
+ { .val_bool = false },
+);
FRR_CFG_DEFAULT_BOOL(BGP_DYNAMIC_CAPABILITY,
{ .val_bool = true, .match_profile = "datacenter", },
{ .val_bool = false },
@@ -623,6 +627,8 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
if (DFLT_BGP_SOFT_VERSION_CAPABILITY)
SET_FLAG((*bgp)->flags,
BGP_FLAG_SOFT_VERSION_CAPABILITY);
+ if (DFLT_BGP_LINK_LOCAL_CAPABILITY)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY);
if (DFLT_BGP_DYNAMIC_CAPABILITY)
SET_FLAG((*bgp)->flags,
BGP_FLAG_DYNAMIC_CAPABILITY);
@@ -4436,6 +4442,24 @@ DEFPY (bgp_default_software_version_capability,
return CMD_SUCCESS;
}
+DEFPY (bgp_default_link_local_capability,
+ bgp_default_link_local_capability_cmd,
+ "[no] bgp default link-local-capability",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Advertise Link-Local Next Hop capability for all neighbors\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (no)
+ UNSET_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY);
+ else
+ SET_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY);
+
+ return CMD_SUCCESS;
+}
+
DEFPY (bgp_default_dynamic_capability,
bgp_default_dynamic_capability_cmd,
"[no] bgp default dynamic-capability",
@@ -6065,6 +6089,34 @@ DEFPY(neighbor_capability_software_version,
return ret;
}
+/* neighbor capability link-local */
+DEFPY(neighbor_capability_link_local,
+ neighbor_capability_link_local_cmd,
+ "[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor capability link-local",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise Link-Local Next Hop capability to the peer\n")
+{
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (no)
+ ret = peer_flag_unset_vty(vty, neighbor, PEER_FLAG_CAPABILITY_LINK_LOCAL);
+ else
+ ret = peer_flag_set_vty(vty, neighbor, PEER_FLAG_CAPABILITY_LINK_LOCAL);
+
+ bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, CAPABILITY_CODE_LINK_LOCAL,
+ no ? CAPABILITY_ACTION_UNSET : CAPABILITY_ACTION_SET);
+
+ return ret;
+}
+
static int peer_af_flag_modify_vty(struct vty *vty, const char *peer_str,
afi_t afi, safi_t safi, uint64_t flag,
int set)
@@ -14942,6 +14994,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
json_object_object_add(json_cap, "softwareVersion",
json_soft_version);
+ /* Link-Local Next Hop capability */
+ json_object *json_link_local = NULL;
+
+ json_link_local = json_object_new_object();
+ json_object_boolean_add(json_link_local, "advertised",
+ !!CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_ADV));
+ json_object_boolean_add(json_link_local, "received",
+ !!CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_RCV));
+ json_object_object_add(json_cap, "linkLocalNextHop", json_link_local);
+
/* Graceful Restart */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
@@ -15369,6 +15431,21 @@ CPP_NOTICE("Remove `gracefulRestartCapability` JSON field")
vty_out(vty, "\n");
+ /* Link-Local Next Hop capability */
+ vty_out(vty, " Link-Local Next Hop Capability:");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_ADV))
+ vty_out(vty, " advertised link-local");
+ else
+ vty_out(vty, " not advertised");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_RCV))
+ vty_out(vty, " received link-local");
+ else
+ vty_out(vty, " not received");
+
+ vty_out(vty, "\n");
+
/* Graceful Restart */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
@@ -18913,6 +18990,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
addr);
}
+ /* capability link-local */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY)) {
+ if (!peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL))
+ vty_out(vty, " no neighbor %s capability link-local\n", addr);
+ } else {
+ if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL))
+ vty_out(vty, " neighbor %s capability link-local\n", addr);
+ }
+
/* dont-capability-negotiation */
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
@@ -19622,6 +19708,11 @@ int bgp_config_write(struct vty *vty)
? ""
: "no ");
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY) !=
+ SAVE_BGP_LINK_LOCAL_CAPABILITY)
+ vty_out(vty, " %sbgp default link-local-capability\n",
+ CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY) ? "" : "no ");
+
if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY) !=
SAVE_BGP_DYNAMIC_CAPABILITY)
vty_out(vty,
@@ -20728,6 +20819,9 @@ void bgp_vty_init(void)
/* bgp default software-version-capability */
install_element(BGP_NODE, &bgp_default_software_version_capability_cmd);
+ /* bgp default link-local-capability */
+ install_element(BGP_NODE, &bgp_default_link_local_capability_cmd);
+
/* bgp default dynamic-capability */
install_element(BGP_NODE, &bgp_default_dynamic_capability_cmd);
@@ -21383,6 +21477,9 @@ void bgp_vty_init(void)
/* "neighbor capability software-version" commands.*/
install_element(BGP_NODE, &neighbor_capability_software_version_cmd);
+ /* "neighbor capability link-local" commands.*/
+ install_element(BGP_NODE, &neighbor_capability_link_local_cmd);
+
/* "neighbor capability orf prefix-list" commands.*/
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
install_element(BGP_NODE,
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index efb2c00fa5..2672f38291 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1575,6 +1575,9 @@ struct peer *peer_new(struct bgp *bgp)
if (CHECK_FLAG(bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY))
peer_flag_set(peer, PEER_FLAG_CAPABILITY_SOFT_VERSION);
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY))
+ peer_flag_set(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL);
+
if (CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY))
peer_flag_set(peer, PEER_FLAG_DYNAMIC_CAPABILITY);
@@ -2964,6 +2967,11 @@ static void peer_group2peer_config_copy(struct peer_group *group,
SET_FLAG(peer->flags,
PEER_FLAG_DYNAMIC_CAPABILITY);
+ /* capability link-local apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_CAPABILITY_LINK_LOCAL))
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_LINK_LOCAL))
+ SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_LINK_LOCAL);
+
/* password apply */
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
PEER_STR_ATTR_INHERIT(peer, group, password,
@@ -3410,17 +3418,6 @@ static struct bgp *bgp_create(as_t *as, const char *name,
}
bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp));
- bgp->as = *as;
- if (as_pretty)
- bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_pretty);
- else
- bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, asn_asn2asplain(*as));
-
- if (asnotation != ASNOTATION_UNDEFINED) {
- bgp->asnotation = asnotation;
- SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION);
- } else
- asn_str2asn_notation(bgp->as_pretty, NULL, &bgp->asnotation);
if (BGP_DEBUG(zebra, ZEBRA)) {
if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
@@ -3464,6 +3461,18 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bgp->peer = list_new();
peer_init:
+ bgp->as = *as;
+ if (as_pretty)
+ bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_pretty);
+ else
+ bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, asn_asn2asplain(*as));
+
+ if (asnotation != ASNOTATION_UNDEFINED) {
+ bgp->asnotation = asnotation;
+ SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION);
+ } else
+ asn_str2asn_notation(bgp->as_pretty, NULL, &bgp->asnotation);
+
bgp->peer->cmp = (int (*)(void *, void *))peer_cmp;
bgp->peerhash = hash_create(peer_hash_key_make, peer_hash_same,
"BGP Peer Hash");
@@ -4834,6 +4843,7 @@ static const struct peer_flag_action peer_flag_action_list[] = {
{PEER_FLAG_EXTENDED_LINK_BANDWIDTH, 0, peer_change_none},
{PEER_FLAG_LONESOUL, 0, peer_change_reset_out},
{PEER_FLAG_TCP_MSS, 0, peer_change_none},
+ {PEER_FLAG_CAPABILITY_LINK_LOCAL, 0, peer_change_none},
{0, 0, 0}};
static const struct peer_flag_action peer_af_flag_action_list[] = {
@@ -4921,7 +4931,10 @@ static int peer_flag_action_set(const struct peer_flag_action *action_list,
static void peer_flag_modify_action(struct peer *peer, uint64_t flag)
{
- if (flag == PEER_FLAG_DYNAMIC_CAPABILITY)
+ if (flag == PEER_FLAG_DYNAMIC_CAPABILITY || flag == PEER_FLAG_CAPABILITY_ENHE ||
+ flag == PEER_FLAG_CAPABILITY_FQDN || flag == PEER_FLAG_CAPABILITY_SOFT_VERSION ||
+ flag == PEER_FLAG_DONT_CAPABILITY || flag == PEER_FLAG_OVERRIDE_CAPABILITY ||
+ flag == PEER_FLAG_STRICT_CAP_MATCH || flag == PEER_FLAG_CAPABILITY_LINK_LOCAL)
peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
else if (flag == PEER_FLAG_PASSIVE)
peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index ee904391c1..d02c1c924d 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -566,6 +566,7 @@ struct bgp {
#define BGP_FLAG_IPV6_NO_AUTO_RA (1ULL << 40)
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_INSTALL (1ULL << 41)
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_DELETE (1ULL << 42)
+#define BGP_FLAG_LINK_LOCAL_CAPABILITY (1ULL << 43)
/* BGP default address-families.
* New peers inherit enabled afi/safis from bgp instance.
@@ -1410,6 +1411,8 @@ struct peer {
#define PEER_CAP_SOFT_VERSION_RCV (1ULL << 28)
#define PEER_CAP_PATHS_LIMIT_ADV (1U << 29)
#define PEER_CAP_PATHS_LIMIT_RCV (1U << 30)
+#define PEER_CAP_LINK_LOCAL_ADV (1ULL << 31)
+#define PEER_CAP_LINK_LOCAL_RCV (1ULL << 32)
/* Capability flags (reset in bgp_stop) */
uint32_t af_cap[AFI_MAX][SAFI_MAX];
@@ -1543,6 +1546,7 @@ struct peer {
#define PEER_FLAG_AS_LOOP_DETECTION (1ULL << 38) /* as path loop detection */
#define PEER_FLAG_EXTENDED_LINK_BANDWIDTH (1ULL << 39)
#define PEER_FLAG_DUAL_AS (1ULL << 40)
+#define PEER_FLAG_CAPABILITY_LINK_LOCAL (1ULL << 41)
/*
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
@@ -2100,6 +2104,7 @@ struct bgp_nlri {
#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9
#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10
#define BGP_NOTIFY_UPDATE_MAL_AS_PATH 11
+#define BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP 12 /* draft-white-linklocal-capability */
/* BGP_NOTIFY_CEASE sub codes (RFC 4486). */
#define BGP_NOTIFY_CEASE_MAX_PREFIX 1
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
index 99d8bcfce4..d9f63700f0 100644
--- a/bgpd/rfapi/rfapi_import.c
+++ b/bgpd/rfapi/rfapi_import.c
@@ -4067,9 +4067,15 @@ static void rfapiProcessPeerDownRt(struct peer *peer,
bpi, import_table, afi, -1);
import_table->holddown_count[afi] += 1;
}
- rfapiBiStartWithdrawTimer(import_table, rn, bpi,
- afi, safi,
- timer_service_func);
+ if (bm->terminating) {
+ if (safi == SAFI_MPLS_VPN)
+ rfapiExpireVpnNow(import_table, rn, bpi, 1);
+ else
+ rfapiExpireEncapNow(import_table, rn, bpi);
+
+ } else
+ rfapiBiStartWithdrawTimer(import_table, rn, bpi, afi, safi,
+ timer_service_func);
}
}
}
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 5add30b6f4..a1dd799392 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1953,6 +1953,15 @@ Configuring Peers
are not supporting this capability or supporting BGP Capabilities
Negotiation RFC 2842.
+.. clicmd:: neighbor PEER capability link-local
+
+ Send the Link-Local Next Hop capability in the BGP OPEN message to the neighbor.
+ This is useful in data center environments where point-to-point (unnumbered) links
+ are utilized. This capability standardizes the operation of BGP over a
+ point-to-point links using link-local IPv6 addressing only.
+
+ Enabled by default for the ``datacenter`` profile.
+
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> accept-own
Enable handling of self-originated VPN routes containing ``accept-own`` community.
@@ -4638,7 +4647,7 @@ incoming/outgoing directions.
If ``json`` option is specified, output is displayed in JSON format.
-.. clicmd:: show [ip] bgp [afi] [safi] [all] detail-routes
+.. clicmd:: show [ip] bgp [afi] [safi] [all] detail-routes [internal]
Display the detailed version of all routes. The same format as using
``show [ip] bgp [afi] [safi] PREFIX``, but for the whole BGP table.
@@ -4646,6 +4655,9 @@ incoming/outgoing directions.
If ``all`` option is specified, ``ip`` keyword is ignored and,
routes displayed for all AFIs and SAFIs.
+ ``internal`` option is used to display internal data additionally. JSON
+ output is not supported with this option.
+
If ``afi`` is specified, with ``all`` option, routes will be displayed for
each SAFI in the selected AFI.
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 3024bb57ea..5f95fe48a4 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -561,12 +561,6 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
- .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker/type",
- .cbs = {
- .modify = isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify,
- }
- },
- {
.xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list",
.cbs = {
.cli_show = cli_show_isis_frr_remote_lfa_plist,
@@ -598,12 +592,6 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
- .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker/type",
- .cbs = {
- .modify = isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify,
- }
- },
- {
.xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list",
.cbs = {
.cli_show = cli_show_isis_frr_remote_lfa_plist,
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index fb391534e2..6bd3b39bc7 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -1763,26 +1763,6 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_destroy(
}
/*
- * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker/type
- */
-int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
- struct nb_cb_modify_args *args)
-{
- struct lfa_tiebreaker *tie_b;
- struct isis_area *area;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- tie_b = nb_running_get_entry(args->dnode, NULL, true);
- area = tie_b->area;
- tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
- lsp_regenerate_schedule(area, area->is_type, 0);
-
- return NB_OK;
-}
-
-/*
* XPath: /frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list
*/
int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
@@ -1912,26 +1892,6 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_destroy(
}
/*
- * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker/type
- */
-int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
- struct nb_cb_modify_args *args)
-{
- struct lfa_tiebreaker *tie_b;
- struct isis_area *area;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- tie_b = nb_running_get_entry(args->dnode, NULL, true);
- area = tie_b->area;
- tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
- lsp_regenerate_schedule(area, area->is_type, 0);
-
- return NB_OK;
-}
-
-/*
* XPath: /frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list
*/
int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
diff --git a/lib/zlog_5424.c b/lib/zlog_5424.c
index 4c60d4b405..6265ce3b1f 100644
--- a/lib/zlog_5424.c
+++ b/lib/zlog_5424.c
@@ -782,7 +782,7 @@ static void zlog_5424_cycle(struct zlog_cfg_5424 *zcf, int fd)
}
old = zcf->active ? &zcf->active->zt : NULL;
- old = zlog_target_replace(old, &zlt->zt);
+ old = zlog_target_replace(old, zlt ? &zlt->zt : NULL);
zcf->active = zlt;
/* oldt->fd == fd happens for zlog_5424_apply_meta() */
@@ -1076,9 +1076,17 @@ bool zlog_5424_apply_dst(struct zlog_cfg_5424 *zcf)
bool zlog_5424_apply_meta(struct zlog_cfg_5424 *zcf)
{
+ int fd;
+
frr_with_mutex (&zcf->cfg_mtx) {
if (zcf->active)
- zlog_5424_cycle(zcf, zcf->active->fd);
+ fd = zcf->active->fd;
+ else if (zcf->prio_min != ZLOG_DISABLED)
+ fd = zlog_5424_open(zcf, -1);
+ else
+ fd = -1;
+ if (fd >= 0)
+ zlog_5424_cycle(zcf, fd);
}
return true;
diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp_1.py b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py
index 1d7aa97473..c454c9c4a4 100644
--- a/tests/topotests/bgp_bmp/test_bgp_bmp_1.py
+++ b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py
@@ -78,6 +78,13 @@ def setup_module(mod):
"tcpdump -nni r1-eth0 -s 0 -w {} &".format(pcap_file), stdout=None
)
+ tgen.net["r2"].cmd(
+ """
+ip link add vrf1 type vrf table 10
+ip link set vrf1 up
+"""
+ )
+
for _, (rname, router) in enumerate(tgen.routers().items(), 1):
logger.info("Loading router %s" % rname)
router.load_frr_config(
diff --git a/tests/topotests/bgp_ipv6_link_local_capability/__init__.py b/tests/topotests/bgp_ipv6_link_local_capability/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_link_local_capability/__init__.py
diff --git a/tests/topotests/bgp_ipv6_link_local_capability/r1/frr.conf b/tests/topotests/bgp_ipv6_link_local_capability/r1/frr.conf
new file mode 100644
index 0000000000..1cf7f3b913
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_link_local_capability/r1/frr.conf
@@ -0,0 +1,15 @@
+!
+int lo
+ ip address 10.0.0.1/32
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ bgp default link-local-capability
+ neighbor r1-eth0 interface remote-as auto
+ address-family ipv6 unicast
+ network 2001:db8::1/128
+ neighbor r1-eth0 activate
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_ipv6_link_local_capability/r2/frr.conf b/tests/topotests/bgp_ipv6_link_local_capability/r2/frr.conf
new file mode 100644
index 0000000000..4af053dcf6
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_link_local_capability/r2/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.0.0.2/32
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ bgp default link-local-capability
+ neighbor r2-eth0 interface remote-as auto
+ address-family ipv6 unicast
+ neighbor r2-eth0 activate
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_ipv6_link_local_capability/test_bgp_ipv6_link_local_capability.py b/tests/topotests/bgp_ipv6_link_local_capability/test_bgp_ipv6_link_local_capability.py
new file mode 100644
index 0000000000..1822f17ee4
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_link_local_capability/test_bgp_ipv6_link_local_capability.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2024 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_ipv6_link_local_capability():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "r2-eth0": {
+ "neighborCapabilities": {
+ "linkLocalNextHop": {
+ "advertised": True,
+ "received": True,
+ }
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't converge initially"
+
+ def _bgp_check_received_nexthops():
+ output = json.loads(r2.vtysh_cmd("show bgp 2001:db8::1/128 json"))
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "nexthops": [
+ {
+ "hostname": "r1",
+ "afi": "ipv6",
+ "scope": "global",
+ "length": 16,
+ "accessible": True,
+ }
+ ],
+ "peer": {
+ "routerId": "10.0.0.1",
+ "hostname": "r1",
+ "interface": "r2-eth0",
+ "type": "external",
+ },
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_received_nexthops)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Can't see 2001:db8::1/128"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf
index d3ababde3a..e3f8b242a1 100644
--- a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf
@@ -1,3 +1,7 @@
+vrf DONNA
+ ip route 172.16.3.0/24 10.0.0.254
+exit-vrf
+!
int dummy0
ip address 10.0.4.1/24
no shut
@@ -28,6 +32,9 @@ ip router-id 10.0.4.1
!
router bgp 99
no bgp ebgp-requires-policy
+ ! 10.0.4.254 peer session will not be established
+ ! it is there just to activate the ipv4 vpn table
+ neighbor 10.0.4.254 remote-as external
address-family ipv4 unicast
redistribute connected
rd vpn export 10.0.4.1:1
@@ -36,11 +43,14 @@ router bgp 99
export vpn
import vpn
!
+ address-family ipv4 vpn
+ neighbor 10.0.4.254 activate
!
router bgp 99 vrf DONNA
no bgp ebgp-requires-policy
address-family ipv4 unicast
redistribute connected
+ network 172.16.3.0/24
label vpn export 101
rd vpn export 10.0.4.1:1
rt vpn export 10.0.4.1:101
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json
new file mode 100644
index 0000000000..c7ff2f4f80
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json
@@ -0,0 +1,155 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": [
+ {
+ "valid": null,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "ZITA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json
new file mode 100644
index 0000000000..1797d78582
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json
@@ -0,0 +1,103 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": null,
+ "172.16.101.0/24": null
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json
new file mode 100644
index 0000000000..4b0eaaa052
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json
@@ -0,0 +1,120 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": null,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": null,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": null
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json
new file mode 100644
index 0000000000..de18bc8463
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json
@@ -0,0 +1,120 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": null
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json
new file mode 100644
index 0000000000..0c249e241e
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json
@@ -0,0 +1,137 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "ZITA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py b/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py
index a44f07b560..67e53cb0cc 100644
--- a/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py
@@ -13,6 +13,7 @@
Test basic VPNv4 route leaking
"""
+import json
import os
import sys
from functools import partial
@@ -60,6 +61,22 @@ def teardown_module(mod):
tgen.stop_topology()
+def test_bgp_convergence():
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
+
def test_vrf_route_leak_donna():
logger.info("Ensure that routes are leaked back and forth")
tgen = get_topogen()
@@ -119,20 +136,20 @@ def test_vrf_route_leak_donna():
],
},
],
- "172.16.101.0/24": [
+ "172.16.3.0/24": [
{
- "protocol": "bgp",
- "selected": None,
+ "protocol": "static",
+ "selected": True,
"nexthops": [
{
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
+ "fib": True,
+ "interfaceName": "dummy1",
+ "active": True,
+ }
],
},
],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -191,20 +208,21 @@ def test_vrf_route_leak_eva():
"protocol": "connected",
}
],
- "172.16.101.0/24": [
+ "172.16.3.0/24": [
{
"protocol": "bgp",
- "selected": None,
+ "selected": True,
"nexthops": [
{
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
+ "fib": True,
+ "interfaceName": "DONNA",
+ "vrf": "DONNA",
+ "active": True,
+ }
],
},
],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -258,6 +276,20 @@ def test_vrf_route_leak_default():
"protocol": "connected",
}
],
+ "172.16.3.0/24": [
+ {
+ "protocol": "bgp",
+ "selected": True,
+ "nexthops": [
+ {
+ "fib": True,
+ "interfaceName": "DONNA",
+ "vrf": "DONNA",
+ "active": True,
+ }
+ ],
+ },
+ ],
}
test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect)
@@ -298,34 +330,8 @@ interface EVA
# Test DONNA VRF.
expect = {
- "10.0.1.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
- "10.0.3.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
+ "10.0.1.0/24": None,
+ "10.0.3.0/24": None,
}
test_func = partial(
@@ -349,6 +355,14 @@ interface EVA
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_eva_down.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_after_eva_up():
logger.info("Ensure that route states change after EVA interface goes up")
@@ -404,6 +418,14 @@ interface EVA
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_add_vrf_zita():
logger.info("Add VRF ZITA and ensure that the route from VRF ZITA is updated")
@@ -417,20 +439,7 @@ def test_vrf_route_leak_donna_add_vrf_zita():
# Test DONNA VRF.
expect = {
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "ZITA",
- "vrf": "ZITA",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -439,6 +448,14 @@ def test_vrf_route_leak_donna_add_vrf_zita():
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_add_zita.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_set_zita_up():
logger.info("Set VRF ZITA up and ensure that the route from VRF ZITA is updated")
@@ -480,6 +497,14 @@ interface ZITA
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_zita_up.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_delete_vrf_zita():
logger.info("Delete VRF ZITA and ensure that the route from VRF ZITA is deleted")
@@ -502,6 +527,101 @@ def test_vrf_route_leak_donna_delete_vrf_zita():
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
+
+def test_vrf_route_leak_default_delete_prefix():
+ logger.info(
+ "Remove BGP static prefix 172.16.3.0/24 from VRF DONNA and ensure that the route is deleted on default"
+ )
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd(
+ """
+configure
+router bgp 99 vrf DONNA
+ address-family ipv4 unicast
+ no network 172.16.3.0/24
+"""
+ )
+
+ # Test default VRF.
+ expect = {
+ "172.16.3.0/24": None,
+ }
+
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP VRF default check failed:\n{}".format(diff)
+
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_del_donna_prefix.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
+
+def test_vrf_route_leak_default_prefix_back():
+ logger.info(
+ "Set back BGP static prefix 172.16.3.0/24 to VRF DONNA and ensure that the route is set on default"
+ )
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd(
+ """
+configure
+router bgp 99 vrf DONNA
+ address-family ipv4 unicast
+ network 172.16.3.0/24
+"""
+ )
+
+ # Test default VRF.
+ expect = {
+ "172.16.3.0/24": [
+ {
+ "protocol": "bgp",
+ "selected": True,
+ "nexthops": [
+ {
+ "fib": True,
+ "interfaceName": "DONNA",
+ "vrf": "DONNA",
+ "active": True,
+ }
+ ],
+ },
+ ],
+ }
+
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP VRF default check failed:\n{}".format(diff)
+
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_memory_leak():
"Run the memory leak test and report results."
diff --git a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
index 6d4b436bcc..3bc36862bf 100644
--- a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
+++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
@@ -123,20 +123,7 @@ def test_vrf_route_leak_donna():
],
},
],
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -195,20 +182,7 @@ def test_vrf_route_leak_eva():
"protocol": "connected",
}
],
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -302,34 +276,8 @@ interface EVA
# Test DONNA VRF.
expect = {
- "10.0.1.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
- "10.0.3.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
+ "10.0.1.0/24": None,
+ "10.0.3.0/24": None,
}
test_func = partial(
@@ -421,20 +369,7 @@ def test_vrf_route_leak_donna_add_vrf_zita():
# Test DONNA VRF.
expect = {
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "ZITA",
- "vrf": "ZITA",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(