summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_addpath.h6
-rw-r--r--bgpd/bgp_aspath.h8
-rw-r--r--bgpd/bgp_bmp.c2
-rw-r--r--bgpd/bgp_ecommunity.c38
-rw-r--r--bgpd/bgp_filter.c20
-rw-r--r--bgpd/bgp_filter.h6
-rw-r--r--bgpd/bgp_fsm.c2
-rw-r--r--bgpd/bgp_nht.c52
-rw-r--r--bgpd/bgp_open.c88
-rw-r--r--bgpd/bgp_open.h2
-rw-r--r--bgpd/bgp_packet.c128
-rw-r--r--bgpd/bgp_routemap.c34
-rw-r--r--bgpd/bgp_routemap_nb.c5
-rw-r--r--bgpd/bgp_routemap_nb.h5
-rw-r--r--bgpd/bgp_routemap_nb_config.c60
-rw-r--r--bgpd/bgp_updgrp.c7
-rw-r--r--bgpd/bgp_updgrp_adv.c34
-rw-r--r--bgpd/bgp_updgrp_packet.c15
-rw-r--r--bgpd/bgp_vty.c203
-rw-r--r--bgpd/bgpd.c5
-rw-r--r--bgpd/bgpd.h13
-rw-r--r--configure.ac2
-rw-r--r--doc/developer/workflow.rst8
-rw-r--r--doc/user/bgp.rst36
-rw-r--r--lib/filter.c3
-rw-r--r--lib/filter.h1
-rw-r--r--lib/filter_nb.c114
-rw-r--r--lib/northbound.c4
-rw-r--r--ospfd/ospf_ri.c15
-rw-r--r--ospfd/ospf_te.c4
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf4
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf8
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json13
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json116
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf32
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json142
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf23
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf11
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json13
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json116
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf31
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf22
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf15
-rw-r--r--tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py179
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/__init__.py0
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/r1/frr.conf13
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/r2/frr.conf37
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/r3/frr.conf16
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/r4/frr.conf16
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/r5/frr.conf16
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/r6/frr.conf16
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/r7/frr.conf13
-rw-r--r--tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py128
-rw-r--r--tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py104
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c11/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c12/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c21/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c22/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf5
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf9
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c31/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c32/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf61
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf32
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf50
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf29
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf50
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf6
-rw-r--r--tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf29
-rwxr-xr-xtests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py169
-rw-r--r--tests/topotests/ospf_single_switch/r1_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/r2_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/r3_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/r4_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/r5_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/r6_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/r7_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/r8_frr.conf18
-rw-r--r--tests/topotests/ospf_single_switch/test_ospf_single_switch.py189
-rwxr-xr-xtools/frr-reload.py9
-rw-r--r--yang/frr-bgp-route-map.yang6
-rw-r--r--zebra/zebra_vxlan.c5
95 files changed, 2596 insertions, 259 deletions
diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h
index d562000e30..b19e63c946 100644
--- a/bgpd/bgp_addpath.h
+++ b/bgpd/bgp_addpath.h
@@ -21,6 +21,12 @@ struct bgp_addpath_capability {
uint8_t flags;
};
+struct bgp_paths_limit_capability {
+ uint16_t afi;
+ uint8_t safi;
+ uint16_t paths_limit;
+};
+
#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1
void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d);
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index ebfc7d087d..2a831c3a55 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -65,6 +65,14 @@ struct aspath {
#define ASPATH_STR_DEFAULT_LEN 32
+/* `set as-path exclude ASn' */
+struct aspath_exclude {
+ struct aspath *aspath;
+ bool exclude_all;
+ char *exclude_aspath_acl_name;
+ struct as_list *exclude_aspath_acl;
+};
+
/* Prototypes. */
extern void aspath_init(void);
extern void aspath_finish(void);
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index 6a55432e14..1de48a072a 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -2766,7 +2766,7 @@ static int bmp_config_write(struct bgp *bgp, struct vty *vty)
afi2str_lower(afi), safi2str(safi));
if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB))
vty_out(vty, " bmp monitor %s %s loc-rib\n",
- afi2str(afi), safi2str(safi));
+ afi2str_lower(afi), safi2str(safi));
}
frr_each (bmp_listeners, &bt->listeners, bl)
vty_out(vty, " \n bmp listener %pSU port %d\n",
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index a8ae177d03..90c0123b93 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -724,15 +724,21 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
memset(buf, 0, INET_ADDRSTRLEN + 1);
memcpy(buf, str, p - str);
- if (dot) {
+ if (dot == 3) {
/* Parsing A.B.C.D in:
* A.B.C.D:MN
*/
ret = inet_aton(buf, &ip);
if (ret == 0)
goto error;
+ } else if (dot == 1) {
+ /* Parsing A.B AS number in:
+ * A.B:MN
+ */
+ if (!asn_str2asn(buf, &as))
+ goto error;
} else {
- /* ASN */
+ /* Parsing A AS number in A:MN */
errno = 0;
tmp_as = strtoul(buf, &endptr, 10);
/* 'unsigned long' is a uint64 on 64-bit
@@ -750,8 +756,11 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
} else if (*p == '.') {
if (separator)
goto error;
+ /* either IP or AS format */
dot++;
- if (dot > 4)
+ if (dot > 1)
+ ecomm_type = ECOMMUNITY_ENCODE_IP;
+ if (dot >= 4)
goto error;
} else {
digit = 1;
@@ -776,19 +785,18 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
if (!digit && (!separator || !val_color_set))
goto error;
- /* Encode result into extended community. */
- if (dot)
- ecomm_type = ECOMMUNITY_ENCODE_IP;
- else if (as > BGP_AS_MAX)
- ecomm_type = ECOMMUNITY_ENCODE_AS4;
- else if (as > 0)
- ecomm_type = ECOMMUNITY_ENCODE_AS;
- else if (val_color) {
- ecomm_type = ECOMMUNITY_ENCODE_OPAQUE;
- sub_type = ECOMMUNITY_COLOR;
- val = val_color;
+ if (ecomm_type != ECOMMUNITY_ENCODE_IP) {
+ /* Encode result into extended community for AS format or color. */
+ if (as > BGP_AS_MAX)
+ ecomm_type = ECOMMUNITY_ENCODE_AS4;
+ else if (as > 0)
+ ecomm_type = ECOMMUNITY_ENCODE_AS;
+ else if (val_color) {
+ ecomm_type = ECOMMUNITY_ENCODE_OPAQUE;
+ sub_type = ECOMMUNITY_COLOR;
+ val = val_color;
+ }
}
-
if (ecommunity_encode(ecomm_type, sub_type, 1, as, ip, val, eval))
goto error;
*token = ecommunity_token_val;
diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c
index ad541b67ad..a85117965a 100644
--- a/bgpd/bgp_filter.c
+++ b/bgpd/bgp_filter.c
@@ -205,8 +205,17 @@ static struct as_list *as_list_new(void)
static void as_list_free(struct as_list *aslist)
{
- XFREE(MTYPE_AS_STR, aslist->name);
- XFREE(MTYPE_AS_LIST, aslist);
+ struct aspath_exclude_list *cur_bp = aslist->exclude_list;
+ struct aspath_exclude_list *next_bp = NULL;
+
+ while (cur_bp) {
+ next_bp = cur_bp->next;
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, cur_bp);
+ cur_bp = next_bp;
+ }
+
+ XFREE (MTYPE_AS_STR, aslist->name);
+ XFREE (MTYPE_AS_LIST, aslist);
}
/* Insert new AS list to list of as_list. Each as_list is sorted by
@@ -290,6 +299,7 @@ static void as_list_delete(struct as_list *aslist)
{
struct as_list_list *list;
struct as_filter *filter, *next;
+ struct aspath_exclude_list *cur_bp;
for (filter = aslist->head; filter; filter = next) {
next = filter->next;
@@ -308,6 +318,12 @@ static void as_list_delete(struct as_list *aslist)
else
list->head = aslist->next;
+ cur_bp = aslist->exclude_list;
+ while (cur_bp) {
+ cur_bp->bp_as_excl->exclude_aspath_acl = NULL;
+ cur_bp = cur_bp->next;
+ }
+
as_list_free(aslist);
}
diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h
index 1890fd3d96..2d9f07ce84 100644
--- a/bgpd/bgp_filter.h
+++ b/bgpd/bgp_filter.h
@@ -25,6 +25,11 @@ struct as_filter {
int64_t seq;
};
+struct aspath_exclude_list {
+ struct aspath_exclude_list *next;
+ struct aspath_exclude *bp_as_excl;
+};
+
/* AS path filter list. */
struct as_list {
char *name;
@@ -34,6 +39,7 @@ struct as_list {
struct as_filter *head;
struct as_filter *tail;
+ struct aspath_exclude_list *exclude_list;
};
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 234dbb0715..657c7e22d7 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -260,6 +260,8 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
peer->afc_recv[afi][safi] = from_peer->afc_recv[afi][safi];
peer->orf_plist[afi][safi] = from_peer->orf_plist[afi][safi];
peer->llgr[afi][safi] = from_peer->llgr[afi][safi];
+ peer->addpath_paths_limit[afi][safi] =
+ from_peer->addpath_paths_limit[afi][safi];
}
if (bgp_getsockname(peer) < 0) {
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 526b5730a0..884fabf077 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -116,24 +116,36 @@ static int bgp_isvalid_nexthop_for_mplsovergre(struct bgp_nexthop_cache *bnc,
static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc,
struct bgp_path_info *path)
{
+ return (bnc && (bnc->nexthop_num > 0 &&
+ (CHECK_FLAG(path->flags, BGP_PATH_ACCEPT_OWN) ||
+ CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) ||
+ bgp_isvalid_nexthop_for_ebgp(bnc, path) ||
+ bgp_isvalid_nexthop_for_mplsovergre(bnc, path))));
+}
+
+static bool bgp_isvalid_nexthop_for_l3vpn(struct bgp_nexthop_cache *bnc,
+ struct bgp_path_info *path)
+{
+ if (bgp_zebra_num_connects() == 0)
+ return 1;
+
+ if (path->attr->srv6_l3vpn || path->attr->srv6_vpn) {
+ /* In the case of SRv6-VPN, we need to track the reachability to the
+ * SID (in other words, IPv6 address). We check that the SID is
+ * available in the BGP update; then if it is available, we check
+ * for the nexthop reachability.
+ */
+ if (bnc && (bnc->nexthop_num > 0 && bgp_isvalid_nexthop(bnc)))
+ return 1;
+ return 0;
+ }
/*
- * - In the case of MPLS-VPN, the label is learned from LDP or other
+ * In the case of MPLS-VPN, the label is learned from LDP or other
* protocols, and nexthop tracking is enabled for the label.
* The value is recorded as BGP_NEXTHOP_LABELED_VALID.
- * - In the case of SRv6-VPN, we need to track the reachability to the
- * SID (in other words, IPv6 address). As in MPLS, we need to record
- * the value as BGP_NEXTHOP_SID_VALID. However, this function is
- * currently not implemented, and this function assumes that all
- * Transit routes for SRv6-VPN are valid.
* - Otherwise check for mpls-gre acceptance
*/
- return (bgp_zebra_num_connects() == 0 ||
- (bnc && (bnc->nexthop_num > 0 &&
- (CHECK_FLAG(path->flags, BGP_PATH_ACCEPT_OWN) ||
- CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) ||
- bnc->bgp->srv6_enabled ||
- bgp_isvalid_nexthop_for_ebgp(bnc, path) ||
- bgp_isvalid_nexthop_for_mplsovergre(bnc, path)))));
+ return bgp_isvalid_nexthop_for_mpls(bnc, path);
}
static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc)
@@ -496,7 +508,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
else if (safi == SAFI_UNICAST && pi &&
pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra &&
pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop)
- return bgp_isvalid_nexthop_for_mpls(bnc, pi);
+ return bgp_isvalid_nexthop_for_l3vpn(bnc, pi);
else if (safi == SAFI_MPLS_VPN && pi &&
pi->sub_type != BGP_ROUTE_IMPORTED)
/* avoid not redistributing mpls vpn routes */
@@ -1045,8 +1057,11 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
break;
case AFI_IP6:
p->family = AF_INET6;
-
- if (is_bgp_static) {
+ if (pi->attr && pi->attr->srv6_l3vpn) {
+ IPV6_ADDR_COPY(&(p->u.prefix6),
+ &(pi->attr->srv6_l3vpn->sid));
+ p->prefixlen = IPV6_MAX_BITLEN;
+ } else if (is_bgp_static) {
p->u.prefix6 = p_orig->u.prefix6;
p->prefixlen = p_orig->prefixlen;
} else {
@@ -1295,8 +1310,9 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc)
&& (path->attr->evpn_overlay.type
!= OVERLAY_INDEX_GATEWAY_IP)) {
bnc_is_valid_nexthop =
- bgp_isvalid_nexthop_for_mpls(bnc, path) ? true
- : false;
+ bgp_isvalid_nexthop_for_l3vpn(bnc, path)
+ ? true
+ : false;
} else if (safi == SAFI_MPLS_VPN &&
path->sub_type != BGP_ROUTE_IMPORTED) {
/* avoid not redistributing mpls vpn routes */
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 43a59e2448..aa1d81362b 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -42,6 +42,7 @@ const struct message capcode_str[] = {
{ CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart" },
{ CAPABILITY_CODE_ROLE, "Role" },
{ CAPABILITY_CODE_SOFT_VERSION, "Software Version" },
+ { CAPABILITY_CODE_PATHS_LIMIT, "Paths-Limit" },
{ 0 }
};
@@ -61,6 +62,7 @@ static const size_t cap_minsizes[] = {
[CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN,
[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,
};
/* value the capability must be a multiple of.
@@ -83,6 +85,7 @@ static const size_t cap_modsizes[] = {
[CAPABILITY_CODE_LLGR] = 1,
[CAPABILITY_CODE_ROLE] = 1,
[CAPABILITY_CODE_SOFT_VERSION] = 1,
+ [CAPABILITY_CODE_PATHS_LIMIT] = 5,
};
/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can
@@ -739,6 +742,62 @@ static int bgp_capability_addpath(struct peer *peer,
return 0;
}
+static int bgp_capability_paths_limit(struct peer *peer,
+ struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + hdr->length;
+
+ if (hdr->length % CAPABILITY_CODE_PATHS_LIMIT_LEN) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Paths-Limit: Received invalid length %d, non-multiple of %d",
+ hdr->length, CAPABILITY_CODE_PATHS_LIMIT_LEN);
+ return -1;
+ }
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV)) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_DATA,
+ "Paths-Limit: Received Paths-Limit capability without Add-Path capability");
+ return -1;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV);
+
+ while (stream_get_getp(s) + CAPABILITY_CODE_PATHS_LIMIT_LEN <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi = stream_getw(s);
+ iana_safi_t pkt_safi = stream_getc(s);
+ uint16_t paths_limit = stream_getw(s);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s limit: %u",
+ peer->host,
+ lookup_msg(capcode_str, hdr->code, NULL),
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), paths_limit);
+
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Paths-Limit capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ } else if (!peer->afc[afi][safi]) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Paths-Limit capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ }
+
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_PATHS_LIMIT_AF_RCV);
+ peer->addpath_paths_limit[afi][safi].receive = paths_limit;
+ }
+
+ return 0;
+}
+
static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr)
{
struct stream *s = BGP_INPUT(peer);
@@ -1012,6 +1071,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_EXT_MESSAGE:
case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
+ case CAPABILITY_CODE_PATHS_LIMIT:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
@@ -1113,6 +1173,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_PATHS_LIMIT:
+ ret = bgp_capability_paths_limit(peer, &caphdr);
+ break;
default:
if (caphdr.code > 128) {
/* We don't send Notification for unknown vendor
@@ -1874,6 +1937,31 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
}
}
+ /* Paths-Limit capability */
+ SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, (CAPABILITY_CODE_PATHS_LIMIT_LEN *
+ afi_safi_count) +
+ 2)
+ : stream_putc(s, (CAPABILITY_CODE_PATHS_LIMIT_LEN *
+ afi_safi_count) +
+ 2);
+ stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT);
+ stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT_LEN * afi_safi_count);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ stream_putw(s, peer->addpath_paths_limit[afi][safi].send);
+
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_PATHS_LIMIT_AF_ADV);
+ }
+
/* ORF capability. */
FOREACH_AFI_SAFI (afi, safi) {
if (CHECK_FLAG(peer->af_flags[afi][safi],
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 34f4b7619e..a01e49ceba 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -53,6 +53,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */
#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 */
/* Capability Length */
#define CAPABILITY_CODE_MP_LEN 4
@@ -61,6 +62,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_RESTART_LEN 2 /* Receiving only case */
#define CAPABILITY_CODE_AS4_LEN 4
#define CAPABILITY_CODE_ADDPATH_LEN 4
+#define CAPABILITY_CODE_PATHS_LIMIT_LEN 5
#define CAPABILITY_CODE_ENHE_LEN 6 /* NRLI AFI = 2, SAFI = 2, Nexthop AFI = 2 */
#define CAPABILITY_CODE_MIN_FQDN_LEN 2
#define CAPABILITY_CODE_ENHANCED_LEN 0
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 9d484d901a..da352a8441 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1224,7 +1224,7 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
if (!peer_established(peer->connection))
return;
- if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) &&
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) ||
!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV))
return;
@@ -1462,6 +1462,49 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
iana_safi2str(pkt_safi));
break;
+ case CAPABILITY_CODE_PATHS_LIMIT:
+ SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_ADV);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ addpath_afi_safi_count++;
+ }
+
+ stream_putc(s, action);
+ stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT);
+ stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT_LEN *
+ addpath_afi_safi_count);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
+ &pkt_safi);
+
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ stream_putw(s,
+ peer->addpath_paths_limit[afi][safi].send);
+
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s, limit: %u",
+ peer,
+ action == CAPABILITY_ACTION_SET
+ ? "Advertising"
+ : "Removing",
+ capability, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi),
+ peer->addpath_paths_limit[afi][safi]
+ .send);
+ }
+
+ break;
case CAPABILITY_CODE_ORF:
/* Convert AFI, SAFI to values for packet. */
bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
@@ -3170,6 +3213,85 @@ ignore:
}
}
+static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action,
+ struct capability_header *hdr,
+ struct peer *peer)
+{
+ uint8_t *data = pnt + 3;
+ uint8_t *end = data + hdr->length;
+ size_t len = end - data;
+ afi_t afi;
+ safi_t safi;
+
+ if (action == CAPABILITY_ACTION_SET) {
+ if (len % CAPABILITY_CODE_PATHS_LIMIT_LEN) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Paths-Limit: Received invalid length %zu, non-multiple of %d",
+ len, CAPABILITY_CODE_PATHS_LIMIT_LEN);
+ return;
+ }
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV)) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_DATA,
+ "Paths-Limit: Received Paths-Limit capability without Add-Path capability");
+ goto ignore;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV);
+
+ while (data + CAPABILITY_CODE_PATHS_LIMIT_LEN <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi;
+ iana_safi_t pkt_safi;
+ struct bgp_paths_limit_capability bpl = {};
+
+ memcpy(&bpl, data, sizeof(bpl));
+ pkt_afi = ntohs(bpl.afi);
+ pkt_safi = safi_int2iana(bpl.safi);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s limit: %u",
+ peer->host,
+ lookup_msg(capcode_str, hdr->code,
+ NULL),
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi),
+ bpl.paths_limit);
+
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi,
+ &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Paths-Limit capability for this AFI/SAFI",
+ peer->host,
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ goto ignore;
+ } else if (!peer->afc[afi][safi]) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Paths-Limit capability for this AFI/SAFI",
+ peer->host,
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ goto ignore;
+ }
+
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV);
+ peer->addpath_paths_limit[afi][safi].receive =
+ bpl.paths_limit;
+ignore:
+ data += CAPABILITY_CODE_PATHS_LIMIT_LEN;
+ }
+ } else {
+ FOREACH_AFI_SAFI (afi, safi)
+ UNSET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV);
+
+ UNSET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV);
+ }
+}
+
static void bgp_dynamic_capability_orf(uint8_t *pnt, int action,
struct capability_header *hdr,
struct peer *peer)
@@ -3723,6 +3845,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
case CAPABILITY_CODE_ADDPATH:
bgp_dynamic_capability_addpath(pnt, action, hdr, peer);
break;
+ case CAPABILITY_CODE_PATHS_LIMIT:
+ bgp_dynamic_capability_paths_limit(pnt, action, hdr,
+ peer);
+ break;
case CAPABILITY_CODE_ORF:
bgp_dynamic_capability_orf(pnt, action, hdr, peer);
break;
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index c1b4fb186b..a091b67680 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -2323,17 +2323,10 @@ static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = {
route_set_aspath_prepend_free,
};
-/* `set as-path exclude ASn' */
-struct aspath_exclude {
- struct aspath *aspath;
- bool exclude_all;
- char *exclude_aspath_acl_name;
- struct as_list *exclude_aspath_acl;
-};
-
static void *route_aspath_exclude_compile(const char *arg)
{
struct aspath_exclude *ase;
+ struct aspath_exclude_list *ael;
const char *str = arg;
static const char asp_acl[] = "as-path-access-list";
@@ -2348,16 +2341,41 @@ static void *route_aspath_exclude_compile(const char *arg)
ase->exclude_aspath_acl = as_list_lookup(str);
} else
ase->aspath = aspath_str2aspath(str, bgp_get_asnotation(NULL));
+
+ if (ase->exclude_aspath_acl) {
+ ael = XCALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct aspath_exclude_list));
+ ael->bp_as_excl = ase;
+ ael->next = ase->exclude_aspath_acl->exclude_list;
+ ase->exclude_aspath_acl->exclude_list = ael;
+ }
+
return ase;
}
static void route_aspath_exclude_free(void *rule)
{
struct aspath_exclude *ase = rule;
+ struct aspath_exclude_list *cur_ael = NULL;
+ struct aspath_exclude_list *prev_ael = NULL;
aspath_free(ase->aspath);
if (ase->exclude_aspath_acl_name)
XFREE(MTYPE_TMP, ase->exclude_aspath_acl_name);
+ if (ase->exclude_aspath_acl)
+ cur_ael = ase->exclude_aspath_acl->exclude_list;
+ while (cur_ael) {
+ if (cur_ael->bp_as_excl == ase) {
+ if (prev_ael)
+ prev_ael->next = cur_ael->next;
+ else
+ ase->exclude_aspath_acl->exclude_list = NULL;
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, cur_ael);
+ break;
+ }
+ prev_ael = cur_ael;
+ cur_ael = cur_ael->next;
+ }
XFREE(MTYPE_ROUTE_MAP_COMPILED, ase);
}
diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c
index 2320d2f908..096502aaa9 100644
--- a/bgpd/bgp_routemap_nb.c
+++ b/bgpd/bgp_routemap_nb.c
@@ -156,7 +156,6 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name",
.cbs = {
.modify = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify,
- .destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy,
}
},
{
@@ -367,14 +366,12 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn",
.cbs = {
.modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify,
- .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy,
}
},
{
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address",
.cbs = {
.modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify,
- .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy,
}
},
{
@@ -403,7 +400,6 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type",
.cbs = {
.modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify,
- .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy,
}
},
{
@@ -424,7 +420,6 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific",
.cbs = {
.modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify,
- .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy,
}
},
{
diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h
index 00c1bde62a..d7f0cea30e 100644
--- a/bgpd/bgp_routemap_nb.h
+++ b/bgpd/bgp_routemap_nb.h
@@ -66,7 +66,6 @@ int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_destroy(
struct nb_cb_destroy_args *args);
void lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish(struct nb_cb_apply_finish_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify(struct nb_cb_modify_args *args);
-int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_modify(
@@ -137,9 +136,7 @@ int lib_route_map_entry_set_action_rmap_set_action_aggregator_destroy(
struct nb_cb_destroy_args *args);
void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish(struct nb_cb_apply_finish_args *args);
int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify(struct nb_cb_modify_args *args);
-int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify(struct nb_cb_modify_args *args);
-int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_extended_modify(struct nb_cb_modify_args *args);
@@ -152,11 +149,9 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_destroy(
struct nb_cb_destroy_args *args);
void lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish(struct nb_cb_apply_finish_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify(struct nb_cb_modify_args *args);
-int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args);
-int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_modify(
struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_destroy(
diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c
index e7b4fa6a7e..c1d6ee12e1 100644
--- a/bgpd/bgp_routemap_nb_config.c
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -1232,22 +1232,6 @@ lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_nam
return NB_OK;
}
-int
-lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy(
- struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- break;
- }
-
- return NB_OK;
-
-}
-
/*
* XPath:
* /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any
@@ -2838,21 +2822,6 @@ lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify(
return NB_OK;
}
-int
-lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy(
- struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- break;
- }
-
- return NB_OK;
-}
-
/*
* XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address
@@ -2872,21 +2841,6 @@ lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_mod
return NB_OK;
}
-int
-lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy(
- struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- break;
- }
-
- return NB_OK;
-}
-
/*
* XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:comm-list-name
@@ -3031,13 +2985,6 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify(
return NB_OK;
}
-int
-lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy(
- struct nb_cb_destroy_args *args)
-{
- return NB_OK;
-}
-
/*
* XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth
@@ -3119,13 +3066,6 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_spec
return NB_OK;
}
-int
-lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(
- struct nb_cb_destroy_args *args)
-{
- return NB_OK;
-}
-
/*
* XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-none
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index d13515af6f..c522865eba 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -145,6 +145,8 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
dst->addpath_type[afi][safi] = src->addpath_type[afi][safi];
dst->addpath_best_selected[afi][safi] =
src->addpath_best_selected[afi][safi];
+ dst->addpath_paths_limit[afi][safi] =
+ src->addpath_paths_limit[afi][safi];
dst->local_as = src->local_as;
dst->change_local_as = src->change_local_as;
dst->shared_network = src->shared_network;
@@ -348,6 +350,8 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word((flags & PEER_UPDGRP_AF_FLAGS), key);
key = jhash_1word((uint32_t)peer->addpath_type[afi][safi], key);
key = jhash_1word(peer->addpath_best_selected[afi][safi], key);
+ key = jhash_1word(peer->addpath_paths_limit[afi][safi].receive, key);
+ key = jhash_1word(peer->addpath_paths_limit[afi][safi].send, key);
key = jhash_1word((peer->cap & PEER_UPDGRP_CAP_FLAGS), key);
key = jhash_1word((peer->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS),
key);
@@ -461,6 +465,9 @@ static unsigned int updgrp_hash_key_make(const void *p)
PEER_UPDGRP_AF_CAP_FLAGS),
peer->v_routeadv, peer->change_local_as,
peer->as_path_loop_detection);
+ zlog_debug("%pBP Update Group Hash: addpath paths-limit: (send %u, receive %u)",
+ peer, peer->addpath_paths_limit[afi][safi].send,
+ peer->addpath_paths_limit[afi][safi].receive);
zlog_debug(
"%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s",
peer, peer->max_packet_size, peer->pmax_out[afi][safi],
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index 7ecebe3020..cc039e3e11 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -97,13 +97,19 @@ subgrp_announce_addpath_best_selected(struct bgp_dest *dest,
enum bgp_path_selection_reason reason;
char pfx_buf[PREFIX2STR_BUFFER] = {};
int paths_eq = 0;
- int best_path_count = 0;
struct list *list = list_new();
struct bgp_path_info *pi = NULL;
+ uint16_t paths_count = 0;
+ uint16_t paths_limit = peer->addpath_paths_limit[afi][safi].receive;
if (peer->addpath_type[afi][safi] == BGP_ADDPATH_BEST_SELECTED) {
- while (best_path_count++ <
- peer->addpath_best_selected[afi][safi]) {
+ paths_limit =
+ paths_limit
+ ? MIN(paths_limit,
+ peer->addpath_best_selected[afi][safi])
+ : peer->addpath_best_selected[afi][safi];
+
+ while (paths_count++ < paths_limit) {
struct bgp_path_info *exist = NULL;
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
@@ -139,8 +145,26 @@ subgrp_announce_addpath_best_selected(struct bgp_dest *dest,
subgroup_process_announce_selected(
subgrp, NULL, dest, afi, safi, id);
} else {
- subgroup_process_announce_selected(subgrp, pi, dest,
- afi, safi, id);
+ /* No Paths-Limit involved */
+ if (!paths_limit) {
+ subgroup_process_announce_selected(subgrp, pi,
+ dest, afi,
+ safi, id);
+ continue;
+ }
+
+ /* If we have Paths-Limit capability, we MUST
+ * not send more than the number of paths expected
+ * by the peer.
+ */
+ if (paths_count++ < paths_limit)
+ subgroup_process_announce_selected(subgrp, pi,
+ dest, afi,
+ safi, id);
+ else
+ subgroup_process_announce_selected(subgrp, NULL,
+ dest, afi,
+ safi, id);
}
}
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index 7502bf2ec6..bff52c80d8 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -523,11 +523,16 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
gnh_modified = 1;
}
- if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) {
- if (peer->nexthop.v4.s_addr != INADDR_ANY) {
- ipv4_to_ipv4_mapped_ipv6(mod_v6nhg,
- peer->nexthop.v4);
- }
+ if (peer->nexthop.v4.s_addr != INADDR_ANY &&
+ (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg) ||
+ (peer->connection->su.sa.sa_family == AF_INET &&
+ paf->afi == AFI_IP6))) {
+ /* set a IPv4 mapped IPv6 address if no global IPv6
+ * address is found or if announcing IPv6 prefix
+ * over an IPv4 BGP session.
+ */
+ ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, peer->nexthop.v4);
+ gnh_modified = 1;
}
if (IS_MAPPED_IPV6(&peer->nexthop.v6_global)) {
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 31524e2221..efc71bc8a7 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -9198,6 +9198,63 @@ DEFPY(
return CMD_SUCCESS;
}
+DEFPY (neighbor_addpath_paths_limit,
+ neighbor_addpath_paths_limit_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor addpath-rx-paths-limit (1-65535)$paths_limit",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Paths Limit for Addpath to receive from the peer\n"
+ "Maximum number of paths\n")
+{
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_af_flag_set_vty(vty, neighbor, afi, safi,
+ PEER_FLAG_ADDPATH_RX_PATHS_LIMIT);
+
+ peer->addpath_paths_limit[afi][safi].send = paths_limit;
+
+ bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_PATHS_LIMIT,
+ CAPABILITY_ACTION_SET);
+
+ return ret;
+}
+
+DEFPY (no_neighbor_addpath_paths_limit,
+ no_neighbor_addpath_paths_limit_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor addpath-rx-paths-limit [(1-65535)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Paths Limit for Addpath to receive from the peer\n"
+ "Maximum number of paths\n")
+{
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_af_flag_unset_vty(vty, neighbor, afi, safi,
+ PEER_FLAG_ADDPATH_RX_PATHS_LIMIT);
+
+ peer->addpath_paths_limit[afi][safi].send = 0;
+
+ bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_PATHS_LIMIT,
+ CAPABILITY_ACTION_UNSET);
+
+ return ret;
+}
+
DEFPY(
no_neighbor_aspath_loop_detection,
no_neighbor_aspath_loop_detection_cmd,
@@ -14146,6 +14203,86 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
json_add);
}
+ /* Paths-Limit */
+ if (CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_ADV)) {
+ json_object *json_add = NULL;
+ const char *print_store;
+
+ json_add = json_object_new_object();
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ json_object *json_sub = NULL;
+
+ json_sub = json_object_new_object();
+ print_store = get_afi_safi_str(afi, safi,
+ true);
+
+ if (CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV) ||
+ CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV)) {
+ if (CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV) &&
+ CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV)) {
+ json_object_boolean_true_add(
+ json_sub,
+ "advertisedAndReceived");
+ json_object_int_add(
+ json_sub,
+ "advertisedPathsLimit",
+ p->addpath_paths_limit
+ [afi][safi]
+ .send);
+ json_object_int_add(
+ json_sub,
+ "receivedPathsLimit",
+ p->addpath_paths_limit
+ [afi][safi]
+ .receive);
+ } else if (CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV)) {
+ json_object_boolean_true_add(
+ json_sub,
+ "advertised");
+ json_object_int_add(
+ json_sub,
+ "advertisedPathsLimit",
+ p->addpath_paths_limit
+ [afi][safi]
+ .send);
+ } else if (CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV)) {
+ json_object_boolean_true_add(
+ json_sub,
+ "received");
+ json_object_int_add(
+ json_sub,
+ "receivedPathsLimit",
+ p->addpath_paths_limit
+ [afi][safi]
+ .receive);
+ }
+ }
+
+ if (CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV) ||
+ CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV))
+ json_object_object_add(json_add,
+ print_store,
+ json_sub);
+ else
+ json_object_free(json_sub);
+ }
+
+ json_object_object_add(json_cap, "pathsLimit",
+ json_add);
+ }
+
/* Dynamic */
if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV)) {
@@ -14599,6 +14736,47 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
}
}
+ /* Paths-Limit */
+ if (CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_ADV)) {
+ vty_out(vty, " Paths-Limit:\n");
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV) ||
+ CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV)) {
+ vty_out(vty, " %s: ",
+ get_afi_safi_str(afi,
+ safi,
+ false));
+
+ if (CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV))
+ vty_out(vty,
+ "advertised (%u)",
+ p->addpath_paths_limit
+ [afi][safi]
+ .send);
+
+ if (CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV))
+ vty_out(vty,
+ "%sreceived (%u)",
+ CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV)
+ ? " and "
+ : "",
+ p->addpath_paths_limit
+ [afi][safi]
+ .receive);
+
+ vty_out(vty, "\n");
+ }
+ }
+ }
+
/* Dynamic */
if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV)) {
@@ -18383,6 +18561,11 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DISABLE_ADDPATH_RX))
vty_out(vty, " neighbor %s disable-addpath-rx\n", addr);
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ADDPATH_RX_PATHS_LIMIT))
+ vty_out(vty, " neighbor %s addpath-rx-paths-limit %u\n", addr,
+ peer->addpath_paths_limit[afi][safi].send);
+
/* ORF capability. */
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM)
|| peergroup_af_flag_check(peer, afi, safi,
@@ -20544,6 +20727,26 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE,
&no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ /* "neighbor addpath-rx-paths-limit" commands.*/
+ install_element(BGP_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_addpath_paths_limit_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_addpath_paths_limit_cmd);
+
/* "neighbor sender-as-path-loop-detection" commands. */
install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd);
install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 8fc52652a2..e7712f0f3e 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1527,6 +1527,8 @@ struct peer *peer_new(struct bgp *bgp)
PEER_FLAG_SEND_LARGE_COMMUNITY);
peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
peer->addpath_best_selected[afi][safi] = 0;
+ peer->addpath_paths_limit[afi][safi].receive = 0;
+ peer->addpath_paths_limit[afi][safi].send = 0;
peer->soo[afi][safi] = NULL;
}
@@ -1620,6 +1622,8 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
peer_dst->weight[afi][safi] = peer_src->weight[afi][safi];
peer_dst->addpath_type[afi][safi] =
peer_src->addpath_type[afi][safi];
+ peer_dst->addpath_paths_limit[afi][safi] =
+ peer_src->addpath_paths_limit[afi][safi];
}
for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
@@ -4614,6 +4618,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
{PEER_FLAG_SOO, 0, peer_change_reset},
{PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset},
{PEER_FLAG_SEND_EXT_COMMUNITY_RPKI, 1, peer_change_reset_out},
+ {PEER_FLAG_ADDPATH_RX_PATHS_LIMIT, 0, peer_change_none},
{0, 0, 0}};
/* Proper action set. */
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 0f69095323..cf333e07cc 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1140,6 +1140,11 @@ struct llgr_info {
uint8_t flags;
};
+struct addpath_paths_limit {
+ uint16_t send;
+ uint16_t receive;
+};
+
struct peer_connection {
struct peer *peer;
@@ -1333,6 +1338,8 @@ struct peer {
#define PEER_CAP_ROLE_RCV (1ULL << 26) /* role received */
#define PEER_CAP_SOFT_VERSION_ADV (1ULL << 27)
#define PEER_CAP_SOFT_VERSION_RCV (1ULL << 28)
+#define PEER_CAP_PATHS_LIMIT_ADV (1U << 29)
+#define PEER_CAP_PATHS_LIMIT_RCV (1U << 30)
/* Capability flags (reset in bgp_stop) */
uint32_t af_cap[AFI_MAX][SAFI_MAX];
@@ -1351,6 +1358,8 @@ struct peer {
#define PEER_CAP_ENHE_AF_NEGO (1U << 14) /* Extended nexthop afi/safi negotiated */
#define PEER_CAP_LLGR_AF_ADV (1U << 15)
#define PEER_CAP_LLGR_AF_RCV (1U << 16)
+#define PEER_CAP_PATHS_LIMIT_AF_ADV (1U << 17)
+#define PEER_CAP_PATHS_LIMIT_AF_RCV (1U << 18)
/* Global configuration flags. */
/*
@@ -1528,6 +1537,7 @@ struct peer {
#define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 27)
#define PEER_FLAG_SOO (1ULL << 28)
#define PEER_FLAG_SEND_EXT_COMMUNITY_RPKI (1ULL << 29)
+#define PEER_FLAG_ADDPATH_RX_PATHS_LIMIT (1ULL << 30)
#define PEER_FLAG_ACCEPT_OWN (1ULL << 63)
enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
@@ -1845,6 +1855,9 @@ struct peer {
/* Add-Path Best selected paths number to advertise */
uint8_t addpath_best_selected[AFI_MAX][SAFI_MAX];
+ /* Add-Path Paths-Limit */
+ struct addpath_paths_limit addpath_paths_limit[AFI_MAX][SAFI_MAX];
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(peer);
diff --git a/configure.ac b/configure.ac
index 4fe28f24ed..c73131751f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2028,7 +2028,7 @@ dnl libyang
dnl ---------------
PKG_CHECK_MODULES([LIBYANG], [libyang >= 2.1.128], , [
AC_MSG_ERROR([m4_normalize([libyang >= 2.1.128 is required, and was not found on your system.
-Pleaes consult doc/developer/building-libyang.rst for instructions on installing or building libyang.])])])
+Please consult doc/developer/building-libyang.rst for instructions on installing or building libyang.])])])
ac_cflags_save="$CFLAGS"
CFLAGS="$CFLAGS $LIBYANG_CFLAGS"
AC_CHECK_MEMBER([struct lyd_node.priv], [], [
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index 68834ed710..f720f6279e 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -167,13 +167,13 @@ as early as possible, i.e. the first 2-week window.
For reference, the expected release schedule according to the above is:
+---------+------------+------------+------------+
-| Release | 2023-11-07 | 2024-03-05 | 2024-07-02 |
+| Release | 2024-03-12 | 2024-07-02 | 2024-11-05 |
+---------+------------+------------+------------+
-| RC | 2023-10-24 | 2024-02-20 | 2024-06-18 |
+| RC | 2024-02-27 | 2024-06-18 | 2024-10-22 |
+---------+------------+------------+------------+
-| dev/X.Y | 2023-10-10 | 2024-02-06 | 2024-06-04 |
+| dev/X.Y | 2024-02-13 | 2024-06-04 | 2024-10-08 |
+---------+------------+------------+------------+
-| freeze | 2023-09-26 | 2024-01-23 | 2024-05-21 |
+| freeze | 2024-01-30 | 2024-05-21 | 2024-09-24 |
+---------+------------+------------+------------+
Here is the hint on how to get the dates easily:
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 53dc551ca3..9ae9508b02 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1780,6 +1780,17 @@ Configuring Peers
Do not accept additional paths from this neighbor.
+.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> addpath-rx-paths-limit (1-65535)
+
+ Limit the maximum number of paths a BGP speaker can receive from a peer, optimizing
+ the transmission of BGP routes by selectively relaying pertinent routes instead of
+ the entire set.
+
+ If this command is configured, the sender will only send the number of paths specified
+ in PATHS-LIMIT capability.
+
+ To exchange this limit, both peers must support the PATHS-LIMIT capability.
+
.. clicmd:: neighbor PEER ttl-security hops NUMBER
This command enforces Generalized TTL Security Mechanism (GTSM), as
@@ -3196,6 +3207,31 @@ L3VPN SRv6
Specify the SRv6 locator to be used for SRv6 L3VPN. The Locator name must
be set in zebra, but user can set it in any order.
+L3VPN SRv6 SID reachability
+---------------------------
+
+In the context of IPv4 L3VPN over SRv6 specific usecase, 2001:db8:12::2
+is the peer IPv6 address of r2, and 2001:db8:2:2:: is the SRv6 SID
+advertised by router r2 for prefix P. On r1, the SID reachability is
+checked in order to install the prefix P. The below output indicates
+that the 2001:db8:2:2:: prefix is valid.
+
+
+.. code-block:: frr
+
+ r1# show bgp nexthop detail
+ Current BGP nexthop cache:
+ 2001:db8:2:2:: valid [IGP metric 0], #paths 4
+ gate 2001:db8:12::2, if eth0
+ Last update: Tue Nov 14 10:36:28 2023
+ Paths:
+ 1/1 192.168.2.0/24 VRF vrf10 flags 0x4018
+ 1/3 192.168.2.0/24 RD 65002:10 VRF default flags 0x418
+ 2001:db8:12::2 valid [IGP metric 0], #paths 0, peer 2001:db8:12::2
+ if eth0
+ Last update: Tue Nov 14 10:36:26 2023
+ Paths:
+
General configuration
^^^^^^^^^^^^^^^^^^^^^
diff --git a/lib/filter.c b/lib/filter.c
index a0adff0e35..5a0790f8bf 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -410,7 +410,10 @@ void access_list_filter_add(struct access_list *access,
filter->prev = access->tail;
access->tail = filter;
}
+}
+void access_list_filter_update(struct access_list *access)
+{
/* Run hook function. */
if (access->master->add_hook)
(*access->master->add_hook)(access);
diff --git a/lib/filter.h b/lib/filter.h
index bd9e22d384..4fa482ba4e 100644
--- a/lib/filter.h
+++ b/lib/filter.h
@@ -128,6 +128,7 @@ struct filter *filter_new(void);
void access_list_filter_add(struct access_list *access, struct filter *filter);
void access_list_filter_delete(struct access_list *access,
struct filter *filter);
+void access_list_filter_update(struct access_list *access);
int64_t filter_new_seq_get(struct access_list *access);
extern const struct frr_yang_module_info frr_filter_info;
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
index ec31f6d395..39042d39ab 100644
--- a/lib/filter_nb.c
+++ b/lib/filter_nb.c
@@ -17,23 +17,6 @@
#include "lib/plist_int.h"
#include "lib/routemap.h"
-/* Helper function. */
-static void acl_notify_route_map(struct access_list *acl, int route_map_event)
-{
- switch (route_map_event) {
- case RMAP_EVENT_FILTER_ADDED:
- if (acl->master->add_hook)
- (*acl->master->add_hook)(acl);
- break;
- case RMAP_EVENT_FILTER_DELETED:
- if (acl->master->delete_hook)
- (*acl->master->delete_hook)(acl);
- break;
- }
-
- route_map_notify_dependencies(acl->name, route_map_event);
-}
-
static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args)
{
int type = yang_dnode_get_enum(args->dnode, "../../type");
@@ -153,9 +136,6 @@ static int lib_prefix_list_entry_prefix_length_greater_or_equal_modify(
ple->ge = yang_dnode_get_uint8(args->dnode, NULL);
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -174,9 +154,6 @@ static int lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(
ple->le = yang_dnode_get_uint8(args->dnode, NULL);
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -195,9 +172,6 @@ static int lib_prefix_list_entry_prefix_length_greater_or_equal_destroy(
ple->ge = 0;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -216,9 +190,6 @@ static int lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy(
ple->le = 0;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -575,6 +546,15 @@ static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+static void
+lib_access_list_entry_apply_finish(struct nb_cb_apply_finish_args *args)
+{
+ struct filter *f;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ access_list_filter_update(f->acl);
+}
+
/*
* XPath: /frr-filter:lib/access-list/entry/action
*/
@@ -594,8 +574,6 @@ lib_access_list_entry_action_modify(struct nb_cb_modify_args *args)
else
f->type = FILTER_DENY;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -629,8 +607,6 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
fz = &f->u.zfilter;
yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -647,8 +623,6 @@ lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
fz = &f->u.zfilter;
memset(&fz->prefix, 0, sizeof(fz->prefix));
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -681,8 +655,6 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
fz = &f->u.zfilter;
fz->exact = yang_dnode_get_bool(args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -699,8 +671,6 @@ lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args)
fz = &f->u.zfilter;
fz->exact = 0;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -733,8 +703,6 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args)
yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL);
fc->addr_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -751,8 +719,6 @@ lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args)
fc = &f->u.cfilter;
cisco_unset_addr_mask(&fc->addr, &fc->addr_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -777,8 +743,6 @@ static int lib_access_list_entry_network_destroy(struct nb_cb_destroy_args *args
fc = &f->u.cfilter;
cisco_unset_addr_mask(&fc->addr, &fc->addr_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -810,8 +774,6 @@ lib_access_list_entry_network_address_modify(struct nb_cb_modify_args *args)
fc = &f->u.cfilter;
yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -843,8 +805,6 @@ lib_access_list_entry_network_mask_modify(struct nb_cb_modify_args *args)
fc = &f->u.cfilter;
yang_dnode_get_ipv4(&fc->addr_mask, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -877,8 +837,6 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args)
fc->addr.s_addr = INADDR_ANY;
fc->addr_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -895,8 +853,6 @@ lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args)
fc = &f->u.cfilter;
cisco_unset_addr_mask(&fc->addr, &fc->addr_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -929,8 +885,6 @@ static int lib_access_list_entry_destination_host_modify(
yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL);
fc->mask_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -948,8 +902,6 @@ static int lib_access_list_entry_destination_host_destroy(
fc->extended = 0;
cisco_unset_addr_mask(&fc->mask, &fc->mask_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -977,8 +929,6 @@ static int lib_access_list_entry_destination_network_destroy(
fc->extended = 0;
cisco_unset_addr_mask(&fc->mask, &fc->mask_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -1010,8 +960,6 @@ static int lib_access_list_entry_destination_network_address_modify(
fc->extended = 1;
yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -1043,8 +991,6 @@ static int lib_access_list_entry_destination_network_mask_modify(
fc->extended = 1;
yang_dnode_get_ipv4(&fc->mask_mask, args->dnode, NULL);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -1077,8 +1023,6 @@ static int lib_access_list_entry_destination_any_create(
fc->mask.s_addr = INADDR_ANY;
fc->mask_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -1096,8 +1040,6 @@ static int lib_access_list_entry_destination_any_destroy(
fc->extended = 0;
cisco_unset_addr_mask(&fc->mask, &fc->mask_mask);
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -1144,8 +1086,6 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args)
break;
}
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED);
-
return NB_OK;
}
@@ -1161,8 +1101,6 @@ static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args)
fz = &f->u.zfilter;
fz->prefix.family = AF_UNSPEC;
- acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED);
-
return NB_OK;
}
@@ -1279,6 +1217,22 @@ static int lib_prefix_list_entry_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+static void
+lib_prefix_list_entry_apply_finish(struct nb_cb_apply_finish_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /*
+ * Finish prefix entry update procedure. The procedure is started in
+ * children callbacks. `prefix_list_entry_update_start` can be called
+ * multiple times if multiple children are modified, but it is actually
+ * executed only once because of the protection by `ple->installed`.
+ */
+ prefix_list_entry_update_finish(ple);
+}
+
/*
* XPath: /frr-filter:lib/prefix-list/entry/action
*/
@@ -1301,9 +1255,6 @@ static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args)
else
ple->type = PREFIX_DENY;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1331,10 +1282,6 @@ static int lib_prefix_list_entry_prefix_modify(struct nb_cb_modify_args *args)
prefix_copy(&ple->prefix, &p);
}
-
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1352,9 +1299,6 @@ static int lib_prefix_list_entry_prefix_destroy(struct nb_cb_destroy_args *args)
memset(&ple->prefix, 0, sizeof(ple->prefix));
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1602,9 +1546,6 @@ static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args)
break;
}
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1622,9 +1563,6 @@ static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args)
ple->any = false;
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
-
return NB_OK;
}
@@ -1652,6 +1590,7 @@ const struct frr_yang_module_info frr_filter_info = {
.cbs = {
.create = lib_access_list_entry_create,
.destroy = lib_access_list_entry_destroy,
+ .apply_finish = lib_access_list_entry_apply_finish,
.cli_cmp = access_list_cmp,
.cli_show = access_list_show,
}
@@ -1790,6 +1729,7 @@ const struct frr_yang_module_info frr_filter_info = {
.cbs = {
.create = lib_prefix_list_entry_create,
.destroy = lib_prefix_list_entry_destroy,
+ .apply_finish = lib_prefix_list_entry_apply_finish,
.cli_cmp = prefix_list_cmp,
.cli_show = prefix_list_show,
}
diff --git a/lib/northbound.c b/lib/northbound.c
index f9f9449ea6..25ea658bc4 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -157,7 +157,7 @@ void nb_nodes_delete(void)
struct nb_node *nb_node_find(const char *path)
{
const struct lysc_node *snode;
- uint32_t llopts;
+ uint32_t llopts = 0;
/*
* Use libyang to find the schema node associated to the path and get
@@ -165,8 +165,6 @@ struct nb_node *nb_node_find(const char *path)
* disable logging temporarily to avoid libyang from logging an error
* message when the node is not found.
*/
- llopts = ly_log_options(LY_LOSTORE);
- llopts &= ~LY_LOLOG;
ly_temp_log_options(&llopts);
snode = yang_find_snode(ly_native_ctx, path, 0);
diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c
index 99326b41b3..dbe44f7be4 100644
--- a/ospfd/ospf_ri.c
+++ b/ospfd/ospf_ri.c
@@ -1784,11 +1784,10 @@ static void ospf_router_info_schedule(enum lsa_opcode opcode)
DEFUN (router_info,
router_info_area_cmd,
- "router-info <as|area [A.B.C.D]>",
+ "router-info <as|area>",
OSPF_RI_STR
"Enable the Router Information functionality with AS flooding scope\n"
- "Enable the Router Information functionality with Area flooding scope\n"
- "OSPF area ID in IP format (deprecated)\n")
+ "Enable the Router Information functionality with Area flooding scope\n")
{
int idx_mode = 1;
uint8_t scope;
@@ -1844,6 +1843,15 @@ DEFUN (router_info,
return CMD_SUCCESS;
}
+#if CONFDATE > 20240809
+CPP_NOTICE("Drop deprecated router_info_area_id_cmd")
+#endif
+ALIAS_HIDDEN (router_info,
+ router_info_area_id_cmd,
+ "router-info area A.B.C.D",
+ OSPF_RI_STR
+ "Enable the Router Information functionality with Area flooding scope\n"
+ "OSPF area ID in IP format (deprecated)\n")
DEFUN (no_router_info,
no_router_info_cmd,
@@ -2239,6 +2247,7 @@ static void ospf_router_info_register_vty(void)
install_element(VIEW_NODE, &show_ip_ospf_router_info_pce_cmd);
install_element(OSPF_NODE, &router_info_area_cmd);
+ install_element(OSPF_NODE, &router_info_area_id_cmd);
install_element(OSPF_NODE, &no_router_info_cmd);
install_element(OSPF_NODE, &pce_address_cmd);
install_element(OSPF_NODE, &no_pce_address_cmd);
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
index a55a37a907..359dc1f5d4 100644
--- a/ospfd/ospf_te.c
+++ b/ospfd/ospf_te.c
@@ -2246,6 +2246,10 @@ static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa)
}
/* Get corresponding Edge from Link State Data Base */
+ if (IPV4_NET0(attr.standard.local.s_addr) && !attr.standard.local_id) {
+ ote_debug(" |- Found no TE Link local address/ID. Abort!");
+ return -1;
+ }
edge = get_edge(ted, attr.adv, attr.standard.local);
old = edge->attributes;
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/__init__.py b/tests/topotests/bgp_6vpe_ebgp_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/__init__.py
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf
new file mode 100644
index 0000000000..06a23bb0fd
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf
@@ -0,0 +1,4 @@
+ipv6 route fd00:200::/64 fd00:100::2
+interface eth-pe1
+ ipv6 address fd00:100::1/64
+!
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf
new file mode 100644
index 0000000000..2dadfc4007
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf
@@ -0,0 +1,8 @@
+ipv6 route fd00:100::/64 fd00:200::5
+interface eth-pe2
+ ipv6 address fd00:200::6/64
+ ipv6 address fd00:201::6/64
+ ipv6 address fd00:300::6/64
+ ipv6 address fd00:400::6/64
+ ipv6 address fd01:200::6/64
+!
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json
new file mode 100644
index 0000000000..c2100add8e
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json
@@ -0,0 +1,13 @@
+{
+ "ipv6Vpn": {
+ "routerId": "198.51.100.2",
+ "as": 65500,
+ "peers": {
+ "192.0.2.5": {
+ "remoteAs": 65501,
+ "state": "Established",
+ "peerState": "OK"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json
new file mode 100644
index 0000000000..c6e776d069
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json
@@ -0,0 +1,116 @@
+{
+ "vrfName": "vrf1",
+ "routerId": "198.51.100.2",
+ "defaultLocPrf": 100,
+ "localAS": 65500,
+ "routes": {
+ "fd00:100::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:100::/64",
+ "metric": 0,
+ "weight": 32768,
+ "path": "",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:200::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:200::/64",
+ "metric": 0,
+ "weight": 0,
+ "path": "65501",
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:201::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:201::/64",
+ "metric": 0,
+ "weight": 0,
+ "path": "65501",
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:300::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:300::/64",
+ "metric": 0,
+ "weight": 0,
+ "path": "65501",
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:400::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:400::/64",
+ "metric": 0,
+ "weight": 0,
+ "path": "65501",
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd01:200::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd01:200::/64",
+ "metric": 0,
+ "weight": 0,
+ "path": "65501",
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf
new file mode 100644
index 0000000000..26e94d4b97
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf
@@ -0,0 +1,32 @@
+!
+!debug bgp zebra
+router bgp 65500
+ bgp router-id 198.51.100.2
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.5 remote-as 65501
+ neighbor 192.0.2.5 capability extended-nexthop
+ address-family ipv4 unicast
+ no neighbor 192.0.2.5 activate
+ exit-address-family
+ address-family ipv6 vpn
+ neighbor 192.0.2.5 activate
+ neighbor 192.0.2.5 route-map rmap in
+ exit-address-family
+exit
+router bgp 65500 vrf vrf1
+ bgp router-id 198.51.100.2
+ address-family ipv6 unicast
+ redistribute connected
+ label vpn export 101
+ rd vpn export 444:1
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+interface eth-pe2
+ mpls bgp forwarding
+!
+route-map rmap permit 1
+ set ipv6 next-hop prefer-global
+!
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json
new file mode 100644
index 0000000000..154574963b
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json
@@ -0,0 +1,142 @@
+{
+ "fd00:100::/64": [
+ {
+ "prefix": "fd00:100::/64",
+ "protocol": "connected",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth-h1",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "fd00:200::/64": [
+ {
+ "prefix": "fd00:200::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "vrf": "default",
+ "active": true,
+ "labels": [
+ 102
+ ],
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "fd00:201::/64": [
+ {
+ "prefix": "fd00:201::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "vrf": "default",
+ "active": true,
+ "labels": [
+ 102
+ ],
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "fd00:300::/64": [
+ {
+ "prefix": "fd00:300::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "vrf": "default",
+ "active": true,
+ "labels": [
+ 102
+ ],
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "fd00:400::/64": [
+ {
+ "prefix": "fd00:400::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "vrf": "default",
+ "active": true,
+ "labels": [
+ 102
+ ],
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "fd01:200::/64": [
+ {
+ "prefix": "fd01:200::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:205",
+ "afi": "ipv6",
+ "vrf": "default",
+ "active": true,
+ "labels": [
+ 102
+ ],
+ "weight": 1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf
new file mode 100644
index 0000000000..61f2fe7def
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf
@@ -0,0 +1,23 @@
+!
+interface lo
+ ip router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 3
+!
+interface eth-pe2
+ ip router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0007.e901.2222.00
+ is-type level-1
+ lsp-gen-interval 1
+ mpls-te on
+ mpls-te router-address 198.51.100.2
+ segment-routing on
+ segment-routing node-msd 8
+ segment-routing global-block 1000 10000 local-block 30000 30999
+ segment-routing prefix 198.51.100.2/32 index 22
+!
+
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf
new file mode 100644
index 0000000000..7ddd98f6e7
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf
@@ -0,0 +1,11 @@
+!
+interface eth-h1
+ ipv6 address fd00:100::2/64
+!
+interface eth-pe2
+ ip address 192.0.2.2/24
+ ipv6 address ::ffff:192.0.2.2/120
+!
+interface lo
+ ip address 198.51.100.2/32
+!
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json
new file mode 100644
index 0000000000..d74079498e
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json
@@ -0,0 +1,13 @@
+{
+ "ipv6Vpn": {
+ "routerId": "198.51.100.5",
+ "as": 65501,
+ "peers": {
+ "192.0.2.2": {
+ "remoteAs": 65500,
+ "state": "Established",
+ "peerState": "OK"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json
new file mode 100644
index 0000000000..ec42999e8a
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json
@@ -0,0 +1,116 @@
+{
+ "vrfName": "vrf1",
+ "routerId": "198.51.100.5",
+ "defaultLocPrf": 100,
+ "localAS": 65501,
+ "routes": {
+ "fd00:100::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:100::/64",
+ "metric": 0,
+ "weight": 0,
+ "path": "65500",
+ "nexthops": [
+ {
+ "ip": "::ffff:c000:202",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:200::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:200::/64",
+ "metric": 0,
+ "weight": 32768,
+ "path": "",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:201::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:201::/64",
+ "metric": 0,
+ "weight": 32768,
+ "path": "",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:300::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:300::/64",
+ "metric": 0,
+ "weight": 32768,
+ "path": "",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd00:400::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd00:400::/64",
+ "metric": 0,
+ "weight": 32768,
+ "path": "",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "fd01:200::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "network": "fd01:200::/64",
+ "metric": 0,
+ "weight": 32768,
+ "path": "",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf
new file mode 100644
index 0000000000..03b63af90f
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf
@@ -0,0 +1,31 @@
+!
+router bgp 65501
+ bgp router-id 198.51.100.5
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.2 remote-as 65500
+ neighbor 192.0.2.2 capability extended-nexthop
+ address-family ipv4 unicast
+ no neighbor 192.0.2.2 activate
+ exit-address-family
+ address-family ipv6 vpn
+ neighbor 192.0.2.2 activate
+ neighbor 192.0.2.2 route-map rmap in
+ exit-address-family
+exit
+router bgp 65501 vrf vrf1
+ bgp router-id 198.51.100.5
+ address-family ipv6 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 444:2
+ rt vpn both 52:100
+ export vpn
+ import vpn
+exit-address-family
+!
+interface eth-pe1
+ mpls bgp forwarding
+!
+route-map rmap permit 1
+ set ipv6 next-hop prefer-global
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf
new file mode 100644
index 0000000000..f210554ff6
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf
@@ -0,0 +1,22 @@
+!
+interface lo
+ ip router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 3
+!
+interface eth-pe1
+ ip router isis 1
+ isis hello-interval 1
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0007.e901.5555.00
+ is-type level-1
+ lsp-gen-interval 1
+ mpls-te on
+ mpls-te router-address 198.51.100.5
+ segment-routing on
+ segment-routing node-msd 8
+ segment-routing global-block 1000 10000 local-block 33000 33999
+ segment-routing prefix 198.51.100.5/32 index 55
+!
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf
new file mode 100644
index 0000000000..bf20638684
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf
@@ -0,0 +1,15 @@
+!
+interface eth-h2
+ ipv6 address fd00:200::5/64
+ ipv6 address fd00:201::5/64
+ ipv6 address fd00:300::5/64
+ ipv6 address fd00:400::5/64
+ ipv6 address fd01:200::5/64
+!
+interface eth-pe1
+ ip address 192.0.2.5/24
+ ipv6 address ::ffff:192.0.2.5/120
+!
+interface lo
+ ip address 198.51.100.5/32
+!
diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py b/tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py
new file mode 100644
index 0000000000..cbed8f0896
--- /dev/null
+++ b/tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by 6WIND
+#
+
+"""
+Test the FRR BGP 6VPE functionality
+"""
+
+import os
+import sys
+import json
+import functools
+from functools import partial
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.checkping import check_ping
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.isisd]
+
+
+def build_topo(tgen):
+ """
+ +---+ +---+ +---+ +---+
+ | h1|----|pe1|----|pe2|----| h2|
+ +---+ +---+ +---+ +---+
+ """
+
+ def connect_routers(tgen, left, right):
+ pe = None
+ host = None
+ for rname in [left, right]:
+ if rname not in tgen.routers().keys():
+ tgen.add_router(rname)
+ if "pe" in rname:
+ pe = tgen.gears[rname]
+ if "h" in rname:
+ host = tgen.gears[rname]
+
+ switch = tgen.add_switch("s-{}-{}".format(left, right))
+ switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right))
+ switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left))
+
+ if pe and host:
+ pe.cmd("ip link add vrf1 type vrf table 10")
+ pe.cmd("ip link set vrf1 up")
+ pe.cmd("ip link set dev eth-{} master vrf1".format(host.name))
+
+ if "p" in left and "p" in right:
+ # PE <-> P or P <-> P
+ tgen.gears[left].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(right))
+ tgen.gears[right].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(left))
+
+ connect_routers(tgen, "h1", "pe1")
+ connect_routers(tgen, "pe1", "pe2")
+ connect_routers(tgen, "pe2", "h2")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+ logger.info("setup_module")
+
+ for rname, router in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if "h" in rname:
+ # hosts
+ continue
+
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_convergence():
+ "Assert that BGP is converging."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp peers to go up")
+
+ router_list = ["pe1", "pe2"]
+
+ for name in router_list:
+ router = tgen.gears[name]
+ ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=90, wait=1)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_ipv6_vpn():
+ "Assert that BGP is exchanging BGP route."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp peers exchanging UPDATES")
+
+ router_list = ["pe1", "pe2"]
+
+ for name in router_list:
+ router = tgen.gears[name]
+ ref_file = "{}/{}/bgp_vrf_ipv6.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp vrf vrf1 ipv6 unicast json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = "{}: BGP UPDATE exchange failure".format(router.name)
+ assert res is None, assertmsg
+
+
+def test_zebra_ipv6_installed():
+ "Assert that routes are installed."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ pe1 = tgen.gears["pe1"]
+ logger.info("check ipv6 routes installed on pe1")
+
+ ref_file = "{}/{}/ipv6_routes_vrf.json".format(CWD, pe1.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, pe1, "show ipv6 route vrf vrf1 json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = "{}: Zebra Installation failure on vrf vrf1".format(pe1.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_ping6_ok():
+ "Check that h1 pings h2"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_ping("h1", "fd00:200::6", True, 5, 1)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_addpath_paths_limit/__init__.py b/tests/topotests/bgp_addpath_paths_limit/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/__init__.py
diff --git a/tests/topotests/bgp_addpath_paths_limit/r1/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r1/frr.conf
new file mode 100644
index 0000000000..65beb7f286
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/r1/frr.conf
@@ -0,0 +1,13 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers connect 5
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 addpath-rx-paths-limit 2
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_addpath_paths_limit/r2/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r2/frr.conf
new file mode 100644
index 0000000000..796b4d0ba7
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/r2/frr.conf
@@ -0,0 +1,37 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+int r2-eth1
+ ip address 192.168.2.2/24
+!
+int r2-eth2
+ ip address 192.168.7.2/24
+!
+router bgp 65002
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.7.7 remote-as external
+ neighbor 192.168.7.7 timers connect 5
+ neighbor 192.168.2.3 remote-as external
+ neighbor 192.168.2.3 timers connect 5
+ neighbor 192.168.2.3 weight 3
+ neighbor 192.168.2.4 remote-as external
+ neighbor 192.168.2.4 timers connect 5
+ neighbor 192.168.2.4 weight 4
+ neighbor 192.168.2.5 remote-as external
+ neighbor 192.168.2.5 timers connect 5
+ neighbor 192.168.2.5 weight 5
+ neighbor 192.168.2.6 remote-as external
+ neighbor 192.168.2.6 timers connect 5
+ neighbor 192.168.2.6 weight 6
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 addpath-tx-all-paths
+ neighbor 192.168.1.1 prefix-list announce out
+ neighbor 192.168.7.7 addpath-tx-all-paths
+ neighbor 192.168.7.7 prefix-list announce out
+ exit-address-family
+!
+ip prefix-list announce seq 5 permit 172.16.16.254/32
+!
diff --git a/tests/topotests/bgp_addpath_paths_limit/r3/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r3/frr.conf
new file mode 100644
index 0000000000..4d834d3113
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/r3/frr.conf
@@ -0,0 +1,16 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r3-eth0
+ ip address 192.168.2.3/24
+!
+router bgp 65003
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers connect 5
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_addpath_paths_limit/r4/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r4/frr.conf
new file mode 100644
index 0000000000..01e0aa99d3
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/r4/frr.conf
@@ -0,0 +1,16 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r4-eth0
+ ip address 192.168.2.4/24
+!
+router bgp 65004
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers connect 5
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_addpath_paths_limit/r5/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r5/frr.conf
new file mode 100644
index 0000000000..02bb847987
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/r5/frr.conf
@@ -0,0 +1,16 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r5-eth0
+ ip address 192.168.2.5/24
+!
+router bgp 65005
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers connect 5
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_addpath_paths_limit/r6/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r6/frr.conf
new file mode 100644
index 0000000000..39fdbcce32
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/r6/frr.conf
@@ -0,0 +1,16 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r6-eth0
+ ip address 192.168.2.6/24
+!
+router bgp 65006
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers connect 5
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_addpath_paths_limit/r7/frr.conf b/tests/topotests/bgp_addpath_paths_limit/r7/frr.conf
new file mode 100644
index 0000000000..8c44566b2f
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/r7/frr.conf
@@ -0,0 +1,13 @@
+!
+int r7-eth0
+ ip address 192.168.7.7/24
+!
+router bgp 65007
+ timers bgp 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.7.2 remote-as external
+ neighbor 192.168.7.2 timers connect 5
+ address-family ipv4 unicast
+ neighbor 192.168.7.2 addpath-rx-paths-limit 3
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py b/tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py
new file mode 100644
index 0000000000..b7a1cfbb27
--- /dev/null
+++ b/tests/topotests/bgp_addpath_paths_limit/test_bgp_addpath_paths_limit.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2024 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if Paths-Limit capability works as expected.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 8):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r5"])
+ switch.add_link(tgen.gears["r6"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r7"])
+ 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_addpath_paths_limit():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r7 = tgen.gears["r7"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.7.7": {
+ "neighborCapabilities": {
+ "pathsLimit": {
+ "ipv4Unicast": {
+ "advertisedAndReceived": True,
+ "advertisedPathsLimit": 0,
+ "receivedPathsLimit": 3,
+ }
+ }
+ }
+ },
+ "192.168.1.1": {
+ "neighborCapabilities": {
+ "pathsLimit": {
+ "ipv4Unicast": {
+ "advertisedAndReceived": True,
+ "advertisedPathsLimit": 0,
+ "receivedPathsLimit": 2,
+ }
+ }
+ }
+ },
+ }
+ 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_routes(router, expected):
+ output = json.loads(
+ router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.254/32 json")
+ )
+
+ if "paths" not in output:
+ return "No paths received"
+
+ return topotest.json_cmp(len(output["paths"]), expected)
+
+ test_func = functools.partial(_bgp_check_received_routes, r1, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Received routes count is not as expected"
+
+ test_func = functools.partial(_bgp_check_received_routes, r7, 3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Received routes count is not as expected"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py
index a0cd89f064..d373a749fe 100644
--- a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py
+++ b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py
@@ -62,23 +62,48 @@ def teardown_module(mod):
tgen.stop_topology()
+expected_1 = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65002"}],
+ "172.16.255.32/32": [{"path": ""}],
+ }
+}
+
+expected_2 = {
+ "routes": {
+ "172.16.255.31/32": [{"path": ""}],
+ "172.16.255.32/32": [{"path": ""}],
+ }
+}
+
+expected_3 = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65003"}],
+ "172.16.255.32/32": [{"path": "65003"}],
+ }
+}
+
+expected_4 = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65002 65003"}],
+ "172.16.255.32/32": [{"path": "65002 65003"}],
+ }
+}
+
+
+def bgp_converge(router, expected):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+
+ return topotest.json_cmp(output, expected)
+
+
def test_bgp_set_aspath_exclude():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- def _bgp_converge(router):
- output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
- expected = {
- "routes": {
- "172.16.255.31/32": [{"path": "65002"}],
- "172.16.255.32/32": [{"path": ""}],
- }
- }
- return topotest.json_cmp(output, expected)
-
- test_func = functools.partial(_bgp_converge, tgen.gears["r1"])
+ test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_1)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert result is None, "Failed overriding incoming AS-PATH with route-map"
@@ -102,19 +127,7 @@ conf
"""
)
- expected = {
- "routes": {
- "172.16.255.31/32": [{"path": ""}],
- "172.16.255.32/32": [{"path": ""}],
- }
- }
-
- def _bgp_regexp_1(router):
- output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
-
- return topotest.json_cmp(output, expected)
-
- test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"])
+ test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_2)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert result is None, "Failed overriding incoming AS-PATH with regex 1 route-map"
@@ -127,19 +140,46 @@ conf
"""
)
- expected = {
- "routes": {
- "172.16.255.31/32": [{"path": "65003"}],
- "172.16.255.32/32": [{"path": "65003"}],
- }
- }
-
- test_func = functools.partial(_bgp_regexp_1, tgen.gears["r1"])
+ # tgen.mininet_cli()
+ test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_3)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert result is None, "Failed overriding incoming AS-PATH with regex 2 route-map"
+def test_no_bgp_set_aspath_exclude_access_list():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ rname = "r1"
+ r1 = tgen.gears[rname]
+
+ r1.vtysh_cmd(
+ """
+conf
+ no bgp as-path access-list SECOND permit 2
+ """
+ )
+
+ test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, "Failed removing bgp as-path access-list"
+
+ r1.vtysh_cmd(
+ """
+clear bgp *
+ """
+ )
+
+ test_func = functools.partial(bgp_converge, tgen.gears["r1"], expected_4)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, "Failed to renegotiate with peers"
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c11/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c11/bgpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c11/bgpd.conf
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf
new file mode 100644
index 0000000000..bcf5a0499f
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c11/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.1.254
+ipv6 route ::/0 2001:1::ffff
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf
new file mode 100644
index 0000000000..0615cf9a95
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c11/zebra.conf
@@ -0,0 +1,6 @@
+hostname c11
+!
+interface eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:1::1/64
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c12/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c12/bgpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c12/bgpd.conf
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf
new file mode 100644
index 0000000000..bcf5a0499f
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c12/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.1.254
+ipv6 route ::/0 2001:1::ffff
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf
new file mode 100644
index 0000000000..18985aa383
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c12/zebra.conf
@@ -0,0 +1,6 @@
+hostname c12
+!
+interface eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:1::1/64
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c21/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c21/bgpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c21/bgpd.conf
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf
new file mode 100644
index 0000000000..608e60060e
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c21/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.2.254
+ipv6 route ::/0 2001:2::ffff
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf
new file mode 100644
index 0000000000..b8b70ac965
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c21/zebra.conf
@@ -0,0 +1,6 @@
+hostname c21
+!
+interface eth0
+ ip address 192.168.2.1/24
+ ipv6 address 2001:2::1/64
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c22/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c22/bgpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c22/bgpd.conf
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf
new file mode 100644
index 0000000000..277aae998c
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c22/staticd.conf
@@ -0,0 +1,5 @@
+
+!
+ip route 0.0.0.0/0 192.168.2.254
+ipv6 route ::/0 2001:2::ffff
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf
new file mode 100644
index 0000000000..cc764cc35c
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c22/zebra.conf
@@ -0,0 +1,9 @@
+hostname c22
+!
+interface eth0
+ ip address 192.168.2.1/24
+ ipv6 address 2001:2::1/64
+!
+ip route 0.0.0.0/0 192.168.2.254
+ipv6 route ::/0 2001:2::ffff
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c31/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c31/bgpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c31/bgpd.conf
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf
new file mode 100644
index 0000000000..0c88575abd
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c31/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.3.254
+ipv6 route ::/0 2001:3::ffff
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf
new file mode 100644
index 0000000000..3f75641ea7
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c31/zebra.conf
@@ -0,0 +1,6 @@
+hostname c31
+!
+interface eth0
+ ip address 192.168.3.1/24
+ ipv6 address 2001:3::1/64
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c32/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/c32/bgpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c32/bgpd.conf
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf
new file mode 100644
index 0000000000..0c88575abd
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c32/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.3.254
+ipv6 route ::/0 2001:3::ffff
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf
new file mode 100644
index 0000000000..c06a7d19f5
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/c32/zebra.conf
@@ -0,0 +1,6 @@
+hostname c32
+!
+interface eth0
+ ip address 192.168.3.1/24
+ ipv6 address 2001:3::1/64
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf
new file mode 100644
index 0000000000..573dbe0951
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r1/bgpd.conf
@@ -0,0 +1,61 @@
+frr defaults traditional
+!
+hostname r1
+password zebra
+!
+log commands
+!
+router bgp 65001
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:12::2 remote-as 65002
+ neighbor 2001:db8:12::2 timers 3 10
+ neighbor 2001:db8:12::2 timers connect 1
+ neighbor 2001:db8:12::2 capability extended-nexthop
+ neighbor 2001:db8:13::3 remote-as 65001
+ neighbor 2001:db8:13::3 timers 3 10
+ neighbor 2001:db8:13::3 timers connect 1
+ neighbor 2001:db8:13::3 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8:12::2 activate
+ neighbor 2001:db8:13::3 activate
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf10
+ bgp router-id 192.0.2.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 1
+ rd vpn export 65001:10
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf20
+ bgp router-id 192.0.2.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 2
+ rd vpn export 65001:20
+ rt vpn both 0:20
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+interface eth0
+ mpls bgp forwarding
+!
+interface eth1
+ mpls bgp forwarding
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf
new file mode 100644
index 0000000000..49b64fd7af
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r1/staticd.conf
@@ -0,0 +1,4 @@
+!
+ipv6 route 2001:db8:2:2::/64 2001:db8:12::2
+ipv6 route 2001:db8:3:3::/64 2001:db8:13::3
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf
new file mode 100644
index 0000000000..79dbf95593
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r1/zebra.conf
@@ -0,0 +1,32 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ ipv6 address 2001:db8:1:1::1/128
+!
+interface eth0
+ ipv6 address 2001:db8:12::1/64
+!
+interface eth1
+ ipv6 address 2001:db8:13::1/64
+!
+interface eth2 vrf vrf10
+ ip address 192.168.1.254/24
+!
+interface eth3 vrf vrf20
+ ip address 192.168.1.254/24
+!
+segment-routing
+ srv6
+ locators
+ locator default
+ prefix 2001:db8:1:1::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf
new file mode 100644
index 0000000000..723d6fca2f
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r2/bgpd.conf
@@ -0,0 +1,50 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+!
+log commands
+!
+router bgp 65002
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:12::1 remote-as 65001
+ neighbor 2001:db8:12::1 timers 3 10
+ neighbor 2001:db8:12::1 timers connect 1
+ neighbor 2001:db8:12::1 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8:12::1 activate
+ exit-address-family
+ !
+!
+router bgp 65002 vrf vrf10
+ bgp router-id 192.0.2.2
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 1
+ rd vpn export 65002:10
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+router bgp 65002 vrf vrf20
+ bgp router-id 192.0.2.2
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 2
+ rd vpn export 65002:20
+ rt vpn both 0:20
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf
new file mode 100644
index 0000000000..8d80c1ead2
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r2/staticd.conf
@@ -0,0 +1,4 @@
+!
+ipv6 route 2001:db8:1:1::/64 2001:db8:12::1
+ipv6 route 2001:db8:3:3::/64 2001:db8:12::1
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf
new file mode 100644
index 0000000000..09a65b989c
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r2/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ipv6 address 2001:db8:2:2::1/128
+!
+interface eth0
+ ipv6 address 2001:db8:12::2/64
+!
+interface eth1 vrf vrf10
+ ip address 192.168.2.254/24
+!
+interface eth2 vrf vrf20
+ ip address 192.168.2.254/24
+!
+segment-routing
+ srv6
+ locators
+ locator default
+ prefix 2001:db8:2:2::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf b/tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf
new file mode 100644
index 0000000000..c412cb6d29
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r3/bgpd.conf
@@ -0,0 +1,50 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+!
+log commands
+!
+router bgp 65001
+ bgp router-id 192.0.2.3
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8:13::1 remote-as 65001
+ neighbor 2001:db8:13::1 timers 3 10
+ neighbor 2001:db8:13::1 timers connect 1
+ neighbor 2001:db8:13::1 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8:13::1 activate
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf10
+ bgp router-id 192.0.2.3
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 1
+ rd vpn export 65001:10
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf20
+ bgp router-id 192.0.2.2
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 2
+ rd vpn export 65001:20
+ rt vpn both 0:20
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf b/tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf
new file mode 100644
index 0000000000..9d672d51ba
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r3/staticd.conf
@@ -0,0 +1,6 @@
+!
+ipv6 route 2001:db8:12::/64 2001:db8:13::1
+!
+ipv6 route 2001:db8:1:1::/64 2001:db8:13::1
+ipv6 route 2001:db8:2:2::/64 2001:db8:13::1
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf b/tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf
new file mode 100644
index 0000000000..a20cb76a74
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/r3/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ipv6 address 2001:db8:3:3::1/128
+!
+interface eth0
+ ipv6 address 2001:db8:13::3/64
+!
+interface eth1 vrf vrf10
+ ip address 192.168.3.254/24
+!
+interface eth2 vrf vrf20
+ ip address 192.168.3.254/24
+!
+segment-routing
+ srv6
+ locators
+ locator default
+ prefix 2001:db8:3:3::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py
new file mode 100755
index 0000000000..92315bce04
--- /dev/null
+++ b/tests/topotests/bgp_srv6_sid_reachability/test_bgp_srv6_sid_reachability.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by 6WIND
+#
+
+import os
+import re
+import sys
+import json
+import functools
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import required_linux_kernel_version
+from lib.checkping import check_ping
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r3")
+
+ tgen.add_router("c11")
+ tgen.add_router("c12")
+ tgen.add_router("c21")
+ tgen.add_router("c22")
+ tgen.add_router("c31")
+ tgen.add_router("c32")
+
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r3"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["c11"], "eth2", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["c12"], "eth3", "eth0")
+ tgen.add_link(tgen.gears["r2"], tgen.gears["c21"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r2"], tgen.gears["c22"], "eth2", "eth0")
+ tgen.add_link(tgen.gears["r3"], tgen.gears["c31"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r3"], tgen.gears["c32"], "eth2", "eth0")
+
+
+def setup_module(mod):
+ result = required_linux_kernel_version("5.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ for rname, router in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r1"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r1"].run("ip link set vrf10 up")
+ tgen.gears["r1"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r1"].run("ip link set vrf20 up")
+ tgen.gears["r1"].run("ip link set eth2 master vrf10")
+ tgen.gears["r1"].run("ip link set eth3 master vrf20")
+
+ tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r2"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r2"].run("ip link set vrf10 up")
+ tgen.gears["r2"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r2"].run("ip link set vrf20 up")
+ tgen.gears["r2"].run("ip link set eth1 master vrf10")
+ tgen.gears["r2"].run("ip link set eth2 master vrf20")
+
+ tgen.gears["r3"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r3"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r3"].run("ip link set vrf10 up")
+ tgen.gears["r3"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r3"].run("ip link set vrf20 up")
+ tgen.gears["r3"].run("ip link set eth1 master vrf10")
+ tgen.gears["r3"].run("ip link set eth2 master vrf20")
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ping():
+ tgen = get_topogen()
+
+ check_ping("c11", "192.168.2.1", True, 10, 1)
+ check_ping("c11", "192.168.3.1", True, 10, 1)
+ check_ping("c12", "192.168.2.1", True, 10, 1)
+ check_ping("c12", "192.168.3.1", True, 10, 1)
+ check_ping("c21", "192.168.3.1", True, 10, 1)
+ check_ping("c22", "192.168.3.1", True, 10, 1)
+
+
+def test_sid_unreachable_nht():
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ no ipv6 route 2001:db8:2:2::/64 2001:db8:12::2
+ """
+ )
+ check_ping("c11", "192.168.2.1", False, 10, 1)
+
+
+def test_sid_reachable_again_nht():
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ ipv6 route 2001:db8:2:2::/64 2001:db8:12::2
+ """
+ )
+ check_ping("c11", "192.168.2.1", True, 10, 1)
+
+
+def test_sid_unreachable_bgp_update():
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65002
+ no segment-routing srv6
+ exit
+ router bgp 65002 vrf vrf10
+ address-family ipv4 unicast
+ no sid vpn export 1
+ """
+ )
+ check_ping("c11", "192.168.2.1", False, 10, 1)
+
+
+def test_sid_reachable_again_bgp_update():
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65002
+ segment-routing srv6
+ locator default
+ exit
+ exit
+ router bgp 65002 vrf vrf10
+ address-family ipv4 unicast
+ sid vpn export 1
+ """
+ )
+ check_ping("c11", "192.168.2.1", True, 10, 1)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_single_switch/r1_frr.conf b/tests/topotests/ospf_single_switch/r1_frr.conf
new file mode 100644
index 0000000000..8fbb24133d
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r1_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r1
+ip forwarding
+!
+interface r1-eth0
+ ip address 203.0.113.1/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r1-eth1
+ ip address 10.0.1.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.1
+!
diff --git a/tests/topotests/ospf_single_switch/r2_frr.conf b/tests/topotests/ospf_single_switch/r2_frr.conf
new file mode 100644
index 0000000000..ea75ad6f0d
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r2_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r2
+ip forwarding
+!
+interface r2-eth0
+ ip address 203.0.113.2/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r2-eth1
+ ip address 10.0.2.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.2
+!
diff --git a/tests/topotests/ospf_single_switch/r3_frr.conf b/tests/topotests/ospf_single_switch/r3_frr.conf
new file mode 100644
index 0000000000..e04e76edbb
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r3_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r3
+ip forwarding
+!
+interface r3-eth0
+ ip address 203.0.113.3/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r3-eth1
+ ip address 10.0.3.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.3
+!
diff --git a/tests/topotests/ospf_single_switch/r4_frr.conf b/tests/topotests/ospf_single_switch/r4_frr.conf
new file mode 100644
index 0000000000..9f01158539
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r4_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r4
+ip forwarding
+!
+interface r4-eth0
+ ip address 203.0.113.4/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r4-eth1
+ ip address 10.0.4.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.4
+!
diff --git a/tests/topotests/ospf_single_switch/r5_frr.conf b/tests/topotests/ospf_single_switch/r5_frr.conf
new file mode 100644
index 0000000000..c9f34974c2
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r5_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r5
+ip forwarding
+!
+interface r5-eth0
+ ip address 203.0.113.5/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r5-eth1
+ ip address 10.0.5.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.5
+!
diff --git a/tests/topotests/ospf_single_switch/r6_frr.conf b/tests/topotests/ospf_single_switch/r6_frr.conf
new file mode 100644
index 0000000000..ba4b0bd286
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r6_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r6
+ip forwarding
+!
+interface r6-eth0
+ ip address 203.0.113.6/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r6-eth1
+ ip address 10.0.6.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.6
+!
diff --git a/tests/topotests/ospf_single_switch/r7_frr.conf b/tests/topotests/ospf_single_switch/r7_frr.conf
new file mode 100644
index 0000000000..3b28381ee8
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r7_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r7
+ip forwarding
+!
+interface r7-eth0
+ ip address 203.0.113.7/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r7-eth1
+ ip address 10.0.7.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.7
+!
diff --git a/tests/topotests/ospf_single_switch/r8_frr.conf b/tests/topotests/ospf_single_switch/r8_frr.conf
new file mode 100644
index 0000000000..3b1b0e5f64
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/r8_frr.conf
@@ -0,0 +1,18 @@
+!
+hostname r8
+ip forwarding
+!
+interface r8-eth0
+ ip address 203.0.113.8/24
+ ip ospf network point-to-multipoint
+ ip ospf area 0.0.0.0
+ !ip ospf prefix-suppression
+!
+interface r8-eth1
+ ip address 10.0.8.1/24
+ ip ospf passive
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.0.8
+!
diff --git a/tests/topotests/ospf_single_switch/test_ospf_single_switch.py b/tests/topotests/ospf_single_switch/test_ospf_single_switch.py
new file mode 100644
index 0000000000..65c918283f
--- /dev/null
+++ b/tests/topotests/ospf_single_switch/test_ospf_single_switch.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Adriano Marto Reis <adrianomarto@gmail.com>
+#
+
+import os
+import sys
+import json
+import subprocess
+from functools import partial
+import pytest
+
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+from lib.common_config import verify_rib
+from lib.ospf import verify_ospf_rib
+from _ast import Try
+
+pytestmark = pytest.mark.ospfd
+
+"""
+A large set of routers are connected to the same switch. Each router shares a
+single network. All shared networks must be reachable from all routers.
+"""
+
+TOPOLOGY = """
+ net1 net2 netN
+ --- --- ---
+ | | OSPF-passive |
+ | | |
+ +---+---+ +---+---+ +---+---+
+ | | | | | |
+ | r1 | | r2 | (...) | rN |
+ | | | | | |
+ +---+---+ +---+---+ +---+---+
+ | | OSPF-active |
+ | | |
+ +----------------+--------------------------------+
+ switch
+
+
+"""
+
+N_ROUTERS = 8
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+
+def build_topo(tgen):
+ "Build the topology"
+
+ # Create a single switch to connect all the routers
+ switch = tgen.add_switch("switch")
+
+ # Create routers
+ for router_id in range(1, N_ROUTERS + 1):
+ router = tgen.add_router(f"r{router_id}")
+ switch.add_link(router)
+
+ # The shared network needs to be connected to something
+ dummy = tgen.add_switch(f"s{router_id}")
+ dummy.add_link(router)
+
+
+def setup_module(mod):
+ logger.info("OSPF single switch:\n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_frr_config(os.path.join(CWD, "{}_frr.conf".format(rname)))
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Tear-down the test environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def is_iproute2_json_supported():
+ """
+ Checks if the command 'ip -j route' is supported.
+ """
+ try:
+ output = subprocess.run(['ip', '-j', 'route', 'get', '0.0.0.0'], stdout=subprocess.PIPE).stdout.decode()
+ json.loads(output)
+ return True
+ except json.decoder.JSONDecodeError:
+ return False
+
+
+@pytest.mark.skipif(not is_iproute2_json_supported(), reason="'ip -j route' not supported")
+def test_all_routes_advertised():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ # networks advertised by each router and the expected next hops
+ networks = {
+ "r1": ("10.0.1.0/24", "203.0.113.1"),
+ "r2": ("10.0.2.0/24", "203.0.113.2"),
+ "r3": ("10.0.3.0/24", "203.0.113.3"),
+ "r4": ("10.0.4.0/24", "203.0.113.4"),
+ "r5": ("10.0.5.0/24", "203.0.113.5"),
+ "r6": ("10.0.6.0/24", "203.0.113.6"),
+ "r7": ("10.0.7.0/24", "203.0.113.7"),
+ "r8": ("10.0.8.0/24", "203.0.113.8"),
+ }
+
+ for router_orig in tgen.routers().keys():
+ for router_dest, network in networks.items():
+ if router_orig != router_dest:
+ input_dict = {
+ router_orig: {
+ "static_routes": [
+ {
+ "network": network[0],
+ }
+ ]
+ }
+ }
+ result = verify_ospf_rib(tgen, router_orig, input_dict, next_hop=network[1])
+ assert result is True, "Error: {}".format(result)
+ result = verify_rib(tgen, "ipv4", router_orig, input_dict, next_hop=network[1])
+ assert result is True, "Error: {}".format(result)
+
+ check_route(router_orig, network[0], network[1])
+
+
+def check_route(router_name, network, expected_nexthop):
+ """
+ Checks if the given network is present on the given router and has the
+ expected next hop.
+ """
+ tgen = get_topogen()
+ router = tgen.gears[router_name]
+
+ expected_response = {
+ network: [
+ {
+ "prefix": network,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": expected_nexthop,
+ "active": True,
+ },
+ ],
+ },
+ ],
+ }
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ f"show ip route {network} json",
+ expected_response,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), f"{router_name} (ospfd): no route {network} via {expected_nexthop}"
+
+ address = network.split("/")[0]
+ output = router.cmd(f"ip -j route get {address}")
+ logger.info(output)
+ routes = json.loads(output)
+ assert routes[0]["gateway"] == expected_nexthop, f"{router_name} (kernel): no route {address} via {expected_nexthop}"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 73479c634b..ef92e8b59f 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -992,16 +992,17 @@ def bgp_delete_move_lines(lines_to_add, lines_to_del):
del_dict[ctx_keys[0]][re_pg.group(1)] = list()
found_pg_del_cmd = True
+ # move neighbor remote-as lines at the end
+ for ctx_keys, line in lines_to_del_to_app:
+ lines_to_del.remove((ctx_keys, line))
+ lines_to_del.append((ctx_keys, line))
+
if found_pg_del_cmd == False:
bgp_delete_inst_move_line(lines_to_del)
if del_nbr_dict:
bgp_remove_neighbor_cfg(lines_to_del, del_nbr_dict)
return (lines_to_add, lines_to_del)
- for ctx_keys, line in lines_to_del_to_app:
- lines_to_del.remove((ctx_keys, line))
- lines_to_del.append((ctx_keys, line))
-
# {'router bgp 65001': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']},
# 'router bgp 65001 vrf vrf1': {'PG': ['10.1.1.2'], 'PG1': ['10.1.1.21']}}
for ctx_keys, line in lines_to_del:
diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang
index c50c51389e..c679f3b911 100644
--- a/yang/frr-bgp-route-map.yang
+++ b/yang/frr-bgp-route-map.yang
@@ -379,6 +379,7 @@ identity set-extcommunity-color {
grouping extcommunity-non-transitive-types {
leaf two-octet-as-specific {
type boolean;
+ default false;
description
"Non-Transitive Two-Octet AS-Specific Extended Community";
}
@@ -769,6 +770,7 @@ identity set-extcommunity-color {
+ "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:match-extcommunity')";
container comm-list {
leaf comm-list-name {
+ mandatory true;
type bgp-filter:bgp-list-name;
}
@@ -872,11 +874,13 @@ identity set-extcommunity-color {
description
"Value of the ext-community.";
leaf lb-type {
+ mandatory true;
type frr-bgp-route-map:extcommunity-lb-type;
}
leaf bandwidth {
when "../lb-type = 'explicit-bandwidth'";
+ mandatory true;
type uint16 {
range "1..25600";
}
@@ -1108,12 +1112,14 @@ identity set-extcommunity-color {
container aggregator {
leaf aggregator-asn {
type asn-type;
+ mandatory true;
description
"ASN of the aggregator";
}
leaf aggregator-address {
type inet:ipv4-address;
+ mandatory true;
description
"IPv4 address of the aggregator";
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 361a300491..cc12cb4221 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -5153,6 +5153,11 @@ void zebra_vxlan_macvlan_up(struct interface *ifp)
zif = ifp->info;
assert(zif);
+
+ if (zif->link_nsid)
+ /* the link interface is another namespace */
+ return;
+
link_ifp = zif->link;
link_zif = link_ifp->info;
assert(link_zif);