diff options
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); |
