diff options
145 files changed, 11412 insertions, 1570 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 584f524f05..2cc7e46573 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -287,9 +287,13 @@ static struct assegment *assegment_normalise(struct assegment *head) return head; } -static struct aspath *aspath_new(void) +static struct aspath *aspath_new(enum asnotation_mode asnotation) { - return XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); + struct aspath *as; + + as = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); + as->asnotation = asnotation; + return as; } /* Free AS path structure. */ @@ -537,8 +541,10 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) * * This was changed to 10 after the well-known BGP assertion, which * had hit some parts of the Internet in May of 2009. + * plain format : '4294967295 ' : 10 + 1 + * astod format : '65535.65535 ': 11 + 1 */ -#define ASN_STR_LEN (10 + 1) +#define ASN_STR_LEN (11 + 1) str_size = MAX(assegment_count_asns(seg, 0) * ASN_STR_LEN + 2 + 1, ASPATH_STR_DEFAULT_LEN); str_buf = XMALLOC(MTYPE_AS_STR, str_size); @@ -569,7 +575,7 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) /* We might need to increase str_buf, particularly if path has * differing segments types, our initial guesstimate above will - * have been wrong. Need 10 chars for ASN, a separator each and + * have been wrong. Need 11 chars for ASN, a separator each and * potentially two segment delimiters, plus a space between each * segment and trailing zero. * @@ -595,12 +601,11 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) /* write out the ASNs, with their separators, bar the last one*/ for (i = 0; i < seg->length; i++) { if (make_json) - json_object_array_add( - jseg_list, - json_object_new_int64(seg->as[i])); - - len += snprintf(str_buf + len, str_size - len, "%u", - seg->as[i]); + asn_asn2json_array(jseg_list, seg->as[i], + as->asnotation); + len += snprintfrr(str_buf + len, str_size - len, + ASN_FORMAT(as->asnotation), + &seg->as[i]); if (i < (seg->length - 1)) len += snprintf(str_buf + len, str_size - len, @@ -691,6 +696,7 @@ struct aspath *aspath_dup(struct aspath *aspath) new->str = XMALLOC(MTYPE_AS_STR, buflen); new->str_len = aspath->str_len; + new->asnotation = aspath->asnotation; /* copy the string data */ if (aspath->str_len > 0) @@ -718,6 +724,7 @@ static void *aspath_hash_alloc(void *arg) new->str = aspath->str; new->str_len = aspath->str_len; new->json = aspath->json; + new->asnotation = aspath->asnotation; return new; } @@ -825,7 +832,8 @@ static int assegments_parse(struct stream *s, size_t length, On error NULL is returned. */ -struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit) +struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit, + enum asnotation_mode asnotation) { struct aspath as; struct aspath *find; @@ -840,6 +848,7 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit) return NULL; memset(&as, 0, sizeof(as)); + as.asnotation = asnotation; if (assegments_parse(s, length, &as.segments, use32bit) < 0) return NULL; @@ -1057,7 +1066,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) seg = assegment_append_asns(seg, seg1->as, match); if (!aspath) { - aspath = aspath_new(); + aspath = aspath_new(as1->asnotation); aspath->segments = seg; } else prevseg->next = seg; @@ -1077,7 +1086,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) } if (!aspath) - aspath = aspath_new(); + aspath = aspath_new(as1->asnotation); /* Make as-set using rest of all information. */ from = match; @@ -1521,7 +1530,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source, struct assegment *srcseg, *exclseg, *lastseg; struct aspath *newpath; - newpath = aspath_new(); + newpath = aspath_new(source->asnotation); lastseg = NULL; for (srcseg = source->segments; srcseg; srcseg = srcseg->next) { @@ -1751,7 +1760,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, newseg = assegment_append_asns(newseg, seg->as, cpasns); if (!newpath) { - newpath = aspath_new(); + newpath = aspath_new(aspath->asnotation); newpath->segments = newseg; } else prevseg->next = newseg; @@ -1880,16 +1889,16 @@ static void aspath_segment_add(struct aspath *as, int type) as->segments = new; } -struct aspath *aspath_empty(void) +struct aspath *aspath_empty(enum asnotation_mode asnotation) { - return aspath_parse(NULL, 0, 1); /* 32Bit ;-) */ + return aspath_parse(NULL, 0, 1, asnotation); /* 32Bit ;-) */ } struct aspath *aspath_empty_get(void) { struct aspath *aspath; - aspath = aspath_new(); + aspath = aspath_new(bgp_get_asnotation(NULL)); aspath_make_str_count(aspath, false); return aspath; } @@ -1925,6 +1934,8 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token, unsigned long *asno) { const char *p = buf; + as_t asval; + bool found = false; /* Skip separators (space for sequences, ',' for sets). */ while (isspace((unsigned char)*p) || *p == ',') @@ -1961,30 +1972,18 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token, return p; } - /* Check actual AS value. */ - if (isdigit((unsigned char)*p)) { - as_t asval; - - *token = as_token_asval; - asval = (*p - '0'); - p++; - - while (isdigit((unsigned char)*p)) { - asval *= 10; - asval += (*p - '0'); - p++; - } + asval = 0; + p = asn_str2asn_parse(p, &asval, &found); + if (found) { *asno = asval; - return p; - } - - /* There is no match then return unknown token. */ - *token = as_token_unknown; - p++; + *token = as_token_asval; + } else + *token = as_token_unknown; return p; } -struct aspath *aspath_str2aspath(const char *str) +struct aspath *aspath_str2aspath(const char *str, + enum asnotation_mode asnotation) { enum as_token token = as_token_unknown; unsigned short as_type; @@ -1992,7 +1991,7 @@ struct aspath *aspath_str2aspath(const char *str) struct aspath *aspath; int needtype; - aspath = aspath_new(); + aspath = aspath_new(asnotation); /* We start default type as AS_SEQUENCE. */ as_type = AS_SEQUENCE; @@ -2066,6 +2065,10 @@ bool aspath_cmp(const void *arg1, const void *arg2) const struct assegment *seg1 = ((const struct aspath *)arg1)->segments; const struct assegment *seg2 = ((const struct aspath *)arg2)->segments; + if (((const struct aspath *)arg1)->asnotation != + ((const struct aspath *)arg2)->asnotation) + return false; + while (seg1 || seg2) { int i; if ((!seg1 && seg2) || (seg1 && !seg2)) diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index dd65b423a7..18af375c13 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -57,6 +57,9 @@ struct aspath { and AS path regular expression match. */ char *str; unsigned short str_len; + + /* AS notation used by string expression of AS path */ + enum asnotation_mode asnotation; }; #define ASPATH_STR_DEFAULT_LEN 32 @@ -65,7 +68,9 @@ struct aspath { extern void aspath_init(void); extern void aspath_finish(void); extern struct aspath *aspath_parse(struct stream *s, size_t length, - int use32bit); + int use32bit, + enum asnotation_mode asnotation); + extern struct aspath *aspath_dup(struct aspath *aspath); extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2); extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2); @@ -81,9 +86,10 @@ extern bool aspath_cmp_left(const struct aspath *aspath1, extern bool aspath_cmp_left_confed(const struct aspath *as1, const struct aspath *as2); extern struct aspath *aspath_delete_confed_seq(struct aspath *aspath); -extern struct aspath *aspath_empty(void); +extern struct aspath *aspath_empty(enum asnotation_mode asnotation); extern struct aspath *aspath_empty_get(void); -extern struct aspath *aspath_str2aspath(const char *str); +extern struct aspath *aspath_str2aspath(const char *str, + enum asnotation_mode asnotation); extern void aspath_str_update(struct aspath *as, bool make_json); extern void aspath_free(struct aspath *aspath); extern struct aspath *aspath_intern(struct aspath *aspath); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index fe918a3e03..1e2698cd92 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1054,7 +1054,7 @@ struct attr *bgp_attr_default_set(struct attr *attr, struct bgp *bgp, attr->origin = origin; attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN); - attr->aspath = aspath_empty(); + attr->aspath = aspath_empty(bgp->asnotation); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH); attr->weight = BGP_ATTR_DEFAULT_WEIGHT; attr->tag = 0; @@ -1092,7 +1092,7 @@ struct attr *bgp_attr_aggregate_intern( if (aspath) attr.aspath = aspath_intern(aspath); else - attr.aspath = aspath_empty(); + attr.aspath = aspath_empty(bgp->asnotation); attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH); /* Next hop attribute. */ @@ -1590,15 +1590,19 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; struct peer *const peer = args->peer; const bgp_size_t length = args->length; + enum asnotation_mode asnotation; + asnotation = bgp_get_asnotation( + args->peer && args->peer->bgp ? args->peer->bgp : NULL); /* * peer with AS4 => will get 4Byte ASnums * otherwise, will get 16 Bit */ - attr->aspath = aspath_parse( - peer->curr, length, - CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) - && CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV)); + attr->aspath = + aspath_parse(peer->curr, length, + CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) && + CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV), + asnotation); /* In case of IBGP, length will be zero. */ if (!attr->aspath) { @@ -1614,7 +1618,8 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args) * such messages, conformant BGP speakers SHOULD use the "Treat-as- * withdraw" error handling behavior as per [RFC7606]. */ - if (peer->bgp->reject_as_sets && aspath_check_as_sets(attr->aspath)) { + if (peer->bgp && peer->bgp->reject_as_sets && + aspath_check_as_sets(attr->aspath)) { flog_err(EC_BGP_ATTR_MAL_AS_PATH, "AS_SET and AS_CONFED_SET are deprecated from %pBP", peer); @@ -1640,6 +1645,14 @@ static enum bgp_attr_parse_ret bgp_attr_aspath_check(struct peer *const peer, */ struct aspath *aspath; + /* Refresh peer's type. If we set e.g.: AS_EXTERNAL/AS_INTERNAL, + * then peer->sort remains BGP_PEER_EBGP/IBGP, hence we need to + * have an actual type before checking. + * This is especially a case for BGP confederation peers, to avoid + * receiving and treating AS_PATH as malformed. + */ + (void)peer_sort(peer); + /* Confederation sanity check. */ if ((peer->sort == BGP_PEER_CONFED && !aspath_left_confed_check(attr->aspath)) @@ -1690,8 +1703,11 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args, struct peer *const peer = args->peer; struct attr *const attr = args->attr; const bgp_size_t length = args->length; + enum asnotation_mode asnotation; - *as4_path = aspath_parse(peer->curr, length, 1); + asnotation = bgp_get_asnotation(peer->bgp); + + *as4_path = aspath_parse(peer->curr, length, 1, asnotation); /* In case of IBGP, length will be zero. */ if (!*as4_path) { @@ -3969,22 +3985,20 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, } break; case SAFI_MPLS_VPN: { if (attr->mp_nexthop_len == + BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) + stream_putc(s, attr->mp_nexthop_len); + else + stream_putc(s, BGP_ATTR_NHLEN_VPNV6_GLOBAL); + stream_putl(s, 0); /* RD = 0, per RFC */ + stream_putl(s, 0); + stream_put(s, &attr->mp_nexthop_global, + IPV6_MAX_BYTELEN); + if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) { - stream_putc(s, 48); - stream_putl(s, 0); /* RD = 0, per RFC */ - stream_putl(s, 0); - stream_put(s, &attr->mp_nexthop_global, - IPV6_MAX_BYTELEN); stream_putl(s, 0); /* RD = 0, per RFC */ stream_putl(s, 0); stream_put(s, &attr->mp_nexthop_local, IPV6_MAX_BYTELEN); - } else { - stream_putc(s, 24); - stream_putl(s, 0); /* RD = 0, per RFC */ - stream_putl(s, 0); - stream_put(s, &attr->mp_nexthop_global, - IPV6_MAX_BYTELEN); } } break; case SAFI_ENCAP: @@ -4289,8 +4303,22 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, aspath = aspath_delete_confed_seq(aspath); if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { - /* Stuff our path CONFED_ID on the front */ - aspath = aspath_add_seq(aspath, bgp->confed_id); + /* A confed member, so we need to do the + * AS_CONFED_SEQUENCE thing if it's outside a common + * administration. + * Configured confederation peers MUST be validated + * under BGP_PEER_CONFED, but if we have configured + * remote-as as AS_EXTERNAL, we need to check again + * if the peer belongs to us. + */ + if (bgp_confederation_peers_check(bgp, peer->as)) { + aspath = aspath_dup(attr->aspath); + aspath = aspath_add_confed_seq(aspath, + peer->local_as); + } else { + /* Stuff our path CONFED_ID on the front */ + aspath = aspath_add_seq(aspath, bgp->confed_id); + } } else { if (peer->change_local_as) { /* If replace-as is specified, we only use the diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index d89f6de649..92d92ada2b 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -2498,14 +2498,13 @@ static int bmp_config_write(struct bgp *bgp, struct vty *vty) vty_out(vty, " bmp mirror\n"); FOREACH_AFI_SAFI (afi, safi) { - const char *afi_str = (afi == AFI_IP) ? "ipv4" : "ipv6"; - if (bt->afimon[afi][safi] & BMP_MON_PREPOLICY) vty_out(vty, " bmp monitor %s %s pre-policy\n", - afi_str, safi2str(safi)); + afi2str_lower(afi), safi2str(safi)); if (bt->afimon[afi][safi] & BMP_MON_POSTPOLICY) - vty_out(vty, " bmp monitor %s %s post-policy\n", - afi_str, safi2str(safi)); + vty_out(vty, + " bmp monitor %s %s post-policy\n", + 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_btoa.c b/bgpd/bgp_btoa.c index b410a4b896..fc3363b098 100644 --- a/bgpd/bgp_btoa.c +++ b/bgpd/bgp_btoa.c @@ -86,7 +86,8 @@ static void attr_parse(struct stream *s, uint16_t len) case BGP_ATTR_AS_PATH: { struct aspath *aspath; - aspath = aspath_parse(s, length, 1); + aspath = aspath_parse(s, length, 1, + bgp_get_asnotation(NULL)); printf("ASPATH: %s\n", aspath->str); aspath_free(aspath); } break; diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 2cbbe0a5a9..1d2ba3bf58 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -449,8 +449,12 @@ static char *community_str_get(struct community *com, int i) comval = ntohl(comval); switch (comval) { +#if CONFDATE > 20230801 +CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community") +#endif case COMMUNITY_INTERNET: str = XSTRDUP(MTYPE_COMMUNITY_STR, "internet"); + zlog_warn("`internet` community is deprecated"); break; case COMMUNITY_GSHUT: str = XSTRDUP(MTYPE_COMMUNITY_STR, "graceful-shutdown"); diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index e2bce9cbce..ff6e477355 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -168,7 +168,6 @@ struct community *community_uniq_sort(struct community *com) For Well-known communities value, below keyword is used. - 0x0 "internet" 0xFFFF0000 "graceful-shutdown" 0xFFFF0001 "accept-own" 0xFFFF0002 "route-filter-translated-v4" @@ -229,8 +228,12 @@ static void set_community_string(struct community *com, bool make_json, comval = ntohl(comval); switch (comval) { +#if CONFDATE > 20230801 +CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community") +#endif case COMMUNITY_INTERNET: len += strlen(" internet"); + zlog_warn("`internet` community is deprecated"); break; case COMMUNITY_GSHUT: len += strlen(" graceful-shutdown"); @@ -295,6 +298,9 @@ static void set_community_string(struct community *com, bool make_json, strlcat(str, " ", len); switch (comval) { +#if CONFDATE > 20230801 +CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community") +#endif case COMMUNITY_INTERNET: strlcat(str, "internet", len); if (make_json) { @@ -303,6 +309,7 @@ static void set_community_string(struct community *com, bool make_json, json_object_array_add(json_community_list, json_string); } + zlog_warn("`internet` community is deprecated"); break; case COMMUNITY_GSHUT: strlcat(str, "graceful-shutdown", len); @@ -673,10 +680,14 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val) /* Well known community string check. */ if (isalpha((unsigned char)*p)) { +#if CONFDATE > 20230801 +CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community") +#endif if (strncmp(p, "internet", strlen("internet")) == 0) { *val = COMMUNITY_INTERNET; *token = community_token_no_export; p += strlen("internet"); + zlog_warn("`internet` community is deprecated"); return p; } if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown")) diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 35909a638d..e7af362ea8 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -30,6 +30,9 @@ struct community { }; /* Well-known communities value. */ +#if CONFDATE > 20230801 +CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community") +#endif #define COMMUNITY_INTERNET 0x0 #define COMMUNITY_GSHUT 0xFFFF0000 #define COMMUNITY_ACCEPT_OWN 0xFFFF0001 diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 2dec1ca46d..782245e512 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2635,6 +2635,7 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, char tag_buf[30]; char overlay_index_buf[INET6_ADDRSTRLEN + 14]; const struct prefix_evpn *evp; + int len = 0; /* ' with addpath ID ' 17 * max strlen of uint32 + 10 @@ -2688,11 +2689,15 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, } } - if (prd) - snprintfrr(str, size, "RD %pRD %pFX%s%s%s %s %s", prd, pu.p, + if (prd) { + len += snprintfrr(str + len, size - len, "RD "); + len += snprintfrr(str + len, size - len, + BGP_RD_AS_FORMAT(bgp_get_asnotation(NULL)), + prd); + snprintfrr(str + len, size - len, " %pFX%s%s%s %s %s", pu.p, overlay_index_buf, tag_buf, pathid_buf, afi2str(afi), safi2str(safi)); - else if (safi == SAFI_FLOWSPEC) { + } else if (safi == SAFI_FLOWSPEC) { char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; const struct prefix_fs *fs = pu.fs; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 4758cd98ec..a8560ab539 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -4517,9 +4517,9 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label[0], num_labels, 0, &evpn); else - bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, - safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, - &label[0], num_labels, &evpn); + bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label[0], + num_labels, &evpn); goto done; fail: @@ -4608,9 +4608,9 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, 0, NULL); else - bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, - safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, - NULL, 0, NULL); + bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, + NULL); return 0; } @@ -4751,9 +4751,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, peer->hostname, peer->bgp->vrf_id, &p, attr_str); } - bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, - safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, - &label, 1, &evpn); + bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, + &evpn); } return 0; @@ -5923,6 +5923,8 @@ void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn) vpn->prd.prefixlen = 64; snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, vpn->rd_id); (void)str2prefix_rd(buf, &vpn->prd); + if (vpn->prd_pretty) + XFREE(MTYPE_BGP, vpn->prd_pretty); UNSET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD); } @@ -6027,6 +6029,8 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) bf_release_index(bm->rd_idspace, vpn->rd_id); hash_release(bgp->vni_svi_hash, vpn); hash_release(bgp->vnihash, vpn); + if (vpn->prd_pretty) + XFREE(MTYPE_BGP, vpn->prd_pretty); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); } @@ -6238,13 +6242,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id), vrf_id == VRF_DEFAULT - ? BGP_INSTANCE_TYPE_DEFAULT - : BGP_INSTANCE_TYPE_VRF); + ? BGP_INSTANCE_TYPE_DEFAULT + : BGP_INSTANCE_TYPE_VRF, + NULL, ASNOTATION_UNDEFINED); switch (ret) { case BGP_ERR_AS_MISMATCH: flog_err(EC_BGP_EVPN_AS_MISMATCH, - "BGP instance is already running; AS is %u", - as); + "BGP instance is already running; AS is %s", + bgp_vrf->as_pretty); return -1; case BGP_ERR_INSTANCE_MISMATCH: flog_err(EC_BGP_EVPN_INSTANCE_MISMATCH, @@ -6660,6 +6665,9 @@ void bgp_evpn_cleanup(struct bgp *bgp) list_delete(&bgp->vrf_import_rtl); list_delete(&bgp->vrf_export_rtl); list_delete(&bgp->l2vnis); + + if (bgp->vrf_prd_pretty) + XFREE(MTYPE_BGP, bgp->vrf_prd_pretty); } /* diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index fa913016fb..2f95023aa9 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -742,9 +742,9 @@ int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, 0, NULL); } else { - bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, - safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, - NULL, 0, NULL); + bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, + NULL); } return 0; } @@ -1210,9 +1210,9 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, 0, NULL); } else { - bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, - safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, - NULL, 0, NULL); + bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, + NULL); } return 0; } @@ -2395,7 +2395,8 @@ static void bgp_evpn_es_json_frag_fill(json_object *json_frags, for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { json_frag = json_object_new_object(); - json_object_string_addf(json_frag, "rd", "%pRD", &es_frag->prd); + json_object_string_addf(json_frag, "rd", "%pRDP", + &es_frag->prd); json_object_int_add(json_frag, "eviCount", listcount(es_frag->es_evi_frag_list)); @@ -2410,7 +2411,7 @@ static void bgp_evpn_es_frag_show_detail(struct vty *vty, struct bgp_evpn_es_frag *es_frag; for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { - vty_out(vty, " %pRD EVIs: %d\n", &es_frag->prd, + vty_out(vty, " %pRDP EVIs: %d\n", &es_frag->prd, listcount(es_frag->es_evi_frag_list)); } } @@ -2524,7 +2525,7 @@ static void bgp_evpn_es_show_entry(struct vty *vty, json_object_string_add(json, "esi", es->esi_str); if (es->es_base_frag) - json_object_string_addf(json, "rd", "%pRD", + json_object_string_addf(json, "rd", "%pRDP", &es->es_base_frag->prd); if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) { @@ -2562,7 +2563,7 @@ static void bgp_evpn_es_show_entry(struct vty *vty, bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str)); - vty_out(vty, "%-30s %-5s %-21pRD %-8d %s\n", es->esi_str, + vty_out(vty, "%-30s %-5s %-21pRDP %-8d %s\n", es->esi_str, type_str, &es->es_base_frag->prd, listcount(es->es_evi_list), vtep_str); } @@ -2639,7 +2640,7 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, "ESI: %s\n", es->esi_str); vty_out(vty, " Type: %s\n", type_str); - vty_out(vty, " RD: %pRD\n", &es->es_base_frag->prd); + vty_out(vty, " RD: %pRDP\n", &es->es_base_frag->prd); vty_out(vty, " Originator-IP: %pI4\n", &es->originator_ip); if (es->flags & BGP_EVPNES_LOCAL) vty_out(vty, " Local ES DF preference: %u\n", @@ -3958,7 +3959,8 @@ static void bgp_evpn_es_evi_show_entry(struct vty *vty, json_object *json_types; json_object_string_add(json, "esi", es_evi->es->esi_str); - json_object_int_add(json, "vni", es_evi->vpn->vni); + if (es_evi->vpn) + json_object_int_add(json, "vni", es_evi->vpn->vni); if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE)) { @@ -4002,13 +4004,18 @@ static void bgp_evpn_es_evi_show_entry(struct vty *vty, static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty, struct bgp_evpn_es_evi *es_evi, json_object *json) { + enum asnotation_mode mode; + + mode = bgp_get_asnotation(es_evi->vpn->bgp_vrf); + if (json) { json_object *json_flags; /* Add the "brief" info first */ bgp_evpn_es_evi_show_entry(vty, es_evi, json); if (es_evi->es_frag) - json_object_string_addf(json, "esFragmentRd", "%pRD", + json_object_string_addf(json, "esFragmentRd", + BGP_RD_AS_FORMAT(mode), &es_evi->es_frag->prd); if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) { json_flags = json_object_new_array(); @@ -4032,9 +4039,12 @@ static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty, vty_out(vty, "VNI: %d ESI: %s\n", es_evi->vpn->vni, es_evi->es->esi_str); vty_out(vty, " Type: %s\n", type_str); - if (es_evi->es_frag) - vty_out(vty, " ES fragment RD: %pRD\n", + if (es_evi->es_frag) { + vty_out(vty, " ES fragment RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(mode), &es_evi->es_frag->prd); + vty_out(vty, "\n"); + } vty_out(vty, " Inconsistencies: %s\n", (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) ? "es-vtep-mismatch":"-"); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index d63e7a1a16..fbf3b19c37 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -76,6 +76,7 @@ struct bgpevpn { /* RD for this VNI. */ struct prefix_rd prd; + char *prd_pretty; /* Route type 3 field */ struct in_addr originator_ip; diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 33c202c3d7..e28a8c8057 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -374,7 +374,9 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, json_object_int_add(json, "vni", bgp_vrf->l3vni); json_object_string_add(json, "type", "L3"); json_object_string_add(json, "inKernel", "True"); - json_object_string_addf(json, "rd", "%pRD", &bgp_vrf->vrf_prd); + json_object_string_addf(json, "rd", + BGP_RD_AS_FORMAT(bgp_vrf->asnotation), + &bgp_vrf->vrf_prd); json_object_string_addf(json, "originatorIp", "%pI4", &bgp_vrf->originator_ip); json_object_string_add(json, "advertiseGatewayMacip", "n/a"); @@ -398,7 +400,10 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, vty_out(vty, " Type: %s\n", "L3"); vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(bgp_vrf->vrf_id)); - vty_out(vty, " RD: %pRD\n", &bgp_vrf->vrf_prd); + vty_out(vty, " RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp_vrf->asnotation), + &bgp_vrf->vrf_prd); + vty_out(vty, "\n"); vty_out(vty, " Originator IP: %pI4\n", &bgp_vrf->originator_ip); vty_out(vty, " Advertise-gw-macip : %s\n", "n/a"); @@ -461,8 +466,10 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object *json_import_rtl = NULL; json_object *json_export_rtl = NULL; struct bgp *bgp_evpn; + enum asnotation_mode asnotation; bgp_evpn = bgp_get_evpn(); + asnotation = bgp_get_asnotation(bgp_evpn); if (json) { json_import_rtl = json_object_new_array(); @@ -471,7 +478,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_string_add(json, "type", "L2"); json_object_string_add(json, "inKernel", is_vni_live(vpn) ? "True" : "False"); - json_object_string_addf(json, "rd", "%pRD", &vpn->prd); + json_object_string_addf( + json, "rd", BGP_RD_AS_FORMAT(asnotation), &vpn->prd); json_object_string_addf(json, "originatorIp", "%pI4", &vpn->originator_ip); json_object_string_addf(json, "mcastGroup", "%pI4", @@ -512,7 +520,9 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) vty_out(vty, " Type: %s\n", "L2"); vty_out(vty, " Tenant-Vrf: %s\n", vrf_id_to_name(vpn->tenant_vrf_id)); - vty_out(vty, " RD: %pRD\n", &vpn->prd); + vty_out(vty, " RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(asnotation), &vpn->prd); + vty_out(vty, "\n"); vty_out(vty, " Originator IP: %pI4\n", &vpn->originator_ip); vty_out(vty, " Mcast group: %pI4\n", &vpn->mcast_grp); if (!vpn->advertise_gw_macip && @@ -991,7 +1001,9 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_object_string_add(json_vni, "inKernel", "True"); json_object_string_addf(json_vni, "originatorIp", "%pI4", &bgp->originator_ip); - json_object_string_addf(json_vni, "rd", "%pRD", &bgp->vrf_prd); + json_object_string_addf(json_vni, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), + &bgp->vrf_prd); json_object_string_add(json_vni, "advertiseGatewayMacip", "n/a"); json_object_string_add(json_vni, "advertiseSviMacIp", "n/a"); @@ -1007,7 +1019,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_vni, "rmac", prefix_mac2str(&bgp->rmac, buf2, sizeof(buf2))); } else { - vty_out(vty, "%-1s %-10u %-4s %-21pRD", buf1, bgp->l3vni, "L3", + vty_out(vty, "%-1s %-10u %-4s ", buf1, bgp->l3vni, "L3"); + vty_out(vty, BGP_RD_AS_FORMAT_SPACE(bgp->asnotation), &bgp->vrf_prd); } @@ -1091,11 +1104,13 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) struct listnode *node, *nnode; struct ecommunity *ecom; struct bgp *bgp_evpn; + enum asnotation_mode asnotation; vty = args[0]; json = args[1]; bgp_evpn = bgp_get_evpn(); + asnotation = bgp_get_asnotation(bgp_evpn); if (json) { json_vni = json_object_new_object(); @@ -1112,7 +1127,9 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) json_object_string_add(json_vni, "type", "L2"); json_object_string_add(json_vni, "inKernel", is_vni_live(vpn) ? "True" : "False"); - json_object_string_addf(json_vni, "rd", "%pRD", &vpn->prd); + json_object_string_addf(json_vni, "rd", + BGP_RD_AS_FORMAT(asnotation), + &vpn->prd); json_object_string_addf(json_vni, "originatorIp", "%pI4", &vpn->originator_ip); json_object_string_addf(json_vni, "mcastGroup", "%pI4", @@ -1142,8 +1159,8 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) json_object_string_add(json_vni, "advertiseSviMacIp", "Disabled"); } else { - vty_out(vty, "%-1s %-10u %-4s %-21pRD", buf1, vpn->vni, "L2", - &vpn->prd); + vty_out(vty, "%-1s %-10u %-4s ", buf1, vpn->vni, "L2"); + vty_out(vty, BGP_RD_AS_FORMAT_SPACE(asnotation), &vpn->prd); } for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { @@ -1332,9 +1349,9 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, json, "defaultLocPrf", bgp->default_local_pref); - json_object_int_add( - json, "localAS", - bgp->as); + asn_asn2json(json, "localAS", + bgp->as, + bgp->asnotation); } else { if (option == SHOW_DISPLAY_TAGS) vty_out(vty, @@ -2203,7 +2220,8 @@ static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn, /* * Configure RD for VRF */ -static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd) +static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd, + const char *rd_pretty) { /* If we have already advertise type-5 routes with a diffrent RD, we * have to delete and withdraw them firs @@ -2212,6 +2230,7 @@ static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd) /* update RD */ memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd)); + bgp_vrf->vrf_prd_pretty = XSTRDUP(MTYPE_BGP, rd_pretty); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); /* We have a new RD for VRF. @@ -2233,7 +2252,8 @@ static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf) /* fall back to default RD */ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); - + if (bgp_vrf->vrf_prd_pretty) + XFREE(MTYPE_BGP, bgp_vrf->vrf_prd_pretty); /* We have a new RD for VRF. * Advertise all type-5 routes again with the new RD */ @@ -2244,7 +2264,7 @@ static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf) * Configure RD for a VNI (vty handler) */ static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_rd *rd) + struct prefix_rd *rd, const char *rd_pretty) { /* If the VNI is "live", we need to delete and withdraw this VNI's * local routes with the prior RD first. Then, after updating RD, @@ -2255,6 +2275,7 @@ static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn, /* update RD */ memcpy(&vpn->prd, rd, sizeof(struct prefix_rd)); + vpn->prd_pretty = XSTRDUP(MTYPE_BGP, rd_pretty); SET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD); if (is_vni_live(vpn)) @@ -2778,7 +2799,8 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) { json_rd = json_object_new_object(); - json_object_string_addf(json_rd, "rd", "%pRD", prd); + json_object_string_addf(json_rd, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), prd); } bgp_dest_unlock_node(rd_dest); @@ -2861,7 +2883,9 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) { if (add_rd_to_json) - json_object_object_addf(json, json_rd, "%pRD", prd); + json_object_object_addf( + json, json_rd, + BGP_RD_AS_FORMAT(bgp->asnotation), prd); else { json_object_free(json_rd); json_rd = NULL; @@ -2916,7 +2940,7 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, continue; prefix_rd2str((struct prefix_rd *)rd_destp, rd_str, - sizeof(rd_str)); + sizeof(rd_str), bgp->asnotation); /* Construct an RT-2 from the user-supplied mac(ip), * then search the l2vpn evpn table for it. @@ -3010,7 +3034,7 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, * If 'type' is non-zero, only routes matching that type are shown. */ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, - json_object *json, int detail) + json_object *json, int detail, bool self_orig) { struct bgp_dest *rd_dest; struct bgp_table *table; @@ -3043,7 +3067,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, tbl_ver = table->version; prefix_rd2str((struct prefix_rd *)rd_destp, rd_str, - sizeof(rd_str)); + sizeof(rd_str), bgp->asnotation); if (json) json_rd = json_object_new_object(); @@ -3068,6 +3092,9 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, pi = bgp_dest_get_bgp_path_info(dest); if (pi) { + if (self_orig && (pi->peer != bgp->peer_self)) + continue; + /* Overall header/legend displayed once. */ if (header) { bgp_evpn_show_route_header(vty, bgp, @@ -3187,7 +3214,7 @@ int bgp_evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, if (use_json) json = json_object_new_object(); - evpn_show_all_routes(vty, bgp, type, json, detail); + evpn_show_all_routes(vty, bgp, type, json, detail, false); if (use_json) vty_json(vty, json); @@ -3460,7 +3487,7 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) if (is_vni_configured(vpn)) { vty_out(vty, " vni %u\n", vpn->vni); if (is_rd_configured(vpn)) - vty_out(vty, " rd %pRD\n", &vpn->prd); + vty_out(vty, " rd %s\n", vpn->prd_pretty); if (is_import_rt_configured(vpn)) { for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, @@ -4760,7 +4787,7 @@ int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc) */ DEFUN(show_bgp_l2vpn_evpn_route, show_bgp_l2vpn_evpn_route_cmd, - "show bgp l2vpn evpn route [detail] [type "EVPN_TYPE_ALL_LIST"] [json]", + "show bgp l2vpn evpn route [detail] [type "EVPN_TYPE_ALL_LIST"] ["BGP_SELF_ORIG_CMD_STR"] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -4769,12 +4796,15 @@ DEFUN(show_bgp_l2vpn_evpn_route, "Display Detailed Information\n" EVPN_TYPE_HELP_STR EVPN_TYPE_ALL_LIST_HELP_STR + BGP_SELF_ORIG_HELP_STR JSON_STR) { struct bgp *bgp; int detail = 0; int type = 0; bool uj = false; + int arg_idx = 0; + bool self_orig = false; json_object *json = NULL; uj = use_json(argc, argv); @@ -4792,7 +4822,10 @@ DEFUN(show_bgp_l2vpn_evpn_route, if (argv_find(argv, argc, "detail", &detail)) detail = 1; - evpn_show_all_routes(vty, bgp, type, json, detail); + if (argv_find(argv, argc, BGP_SELF_ORIG_CMD_STR, &arg_idx)) + self_orig = true; + + evpn_show_all_routes(vty, bgp, type, json, detail, self_orig); /* * This is an extremely expensive operation at scale @@ -4858,7 +4891,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, return CMD_WARNING; if (rd_all) - evpn_show_all_routes(vty, bgp, type, json, 1); + evpn_show_all_routes(vty, bgp, type, json, 1, false); else evpn_show_route_rd(vty, bgp, &prd, type, json); @@ -6111,7 +6144,7 @@ DEFUN (bgp_evpn_vrf_rd, return CMD_SUCCESS; /* Configure or update the RD. */ - evpn_configure_vrf_rd(bgp_vrf, &prd); + evpn_configure_vrf_rd(bgp_vrf, &prd, argv[1]->arg); return CMD_SUCCESS; } @@ -6203,7 +6236,7 @@ DEFUN (bgp_evpn_vni_rd, return CMD_SUCCESS; /* Configure or update the RD. */ - evpn_configure_rd(bgp, vpn, &prd); + evpn_configure_rd(bgp, vpn, &prd, argv[1]->arg); return CMD_SUCCESS; } @@ -6388,7 +6421,9 @@ DEFUN (show_bgp_vrf_l3vni_info, for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt)) vty_out(vty, "%s ", ecommunity_str(l3rt->ecom)); vty_out(vty, "\n"); - vty_out(vty, " RD: %pRD\n", &bgp->vrf_prd); + vty_out(vty, " RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), &bgp->vrf_prd); + vty_out(vty, "\n"); } else { json_object_string_add(json, "vrf", name); json_object_string_addf(json, "local-ip", "%pI4", @@ -6424,7 +6459,9 @@ DEFUN (show_bgp_vrf_l3vni_info, json_object_new_string( ecommunity_str(l3rt->ecom))); json_object_object_add(json, "import-rts", json_import_rts); - json_object_string_addf(json, "rd", "%pRD", &bgp->vrf_prd); + json_object_string_addf(json, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), + &bgp->vrf_prd); } if (uj) @@ -7251,7 +7288,7 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, } } if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD)) - vty_out(vty, " rd %pRD\n", &bgp->vrf_prd); + vty_out(vty, " rd %s\n", bgp->vrf_prd_pretty); /* import route-target */ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 6b35f7155f..f9debe43cd 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -127,6 +127,13 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, psize); return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } + + if (psize == 0) { + flog_err(EC_BGP_FLOWSPEC_PACKET, + "Flowspec NLRI length 0 which makes no sense"); + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; + } + if (bgp_fs_nlri_validate(pnt, psize, afi) < 0) { flog_err( EC_BGP_FLOWSPEC_PACKET, @@ -180,9 +187,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0, 0, NULL); else - bgp_withdraw(peer, &p, 0, attr, afi, safi, - ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, - NULL, 0, NULL); + bgp_withdraw(peer, &p, 0, afi, safi, ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, NULL, NULL, 0, NULL); } return BGP_NLRI_PARSE_OK; } diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index 28fac0bedd..0cad119af1 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -442,7 +442,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, &label, 1, 0, NULL); } else { - bgp_withdraw(peer, &p, addpath_id, attr, packet->afi, + bgp_withdraw(peer, &p, addpath_id, packet->afi, SAFI_UNICAST, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, &label, 1, NULL); } diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index bd8ce54775..551da69249 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -240,7 +240,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, 0, NULL); } else { - bgp_withdraw(peer, &p, addpath_id, attr, packet->afi, + bgp_withdraw(peer, &p, addpath_id, packet->afi, SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, NULL); } @@ -1853,7 +1853,7 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ int origin_local = 0; struct bgp *src_vrf; struct interface *ifp; - + char rd_buf[RD_ADDRSTRLEN]; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) { @@ -1892,6 +1892,10 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ return false; } + rd_buf[0] = '\0'; + if (debug && prd) + prefix_rd2str(prd, rd_buf, sizeof(rd_buf), to_bgp->asnotation); + /* A route MUST NOT ever be accepted back into its source VRF, even if * it carries one or more RTs that match that VRF. */ @@ -1900,15 +1904,14 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ ECOMMUNITY_SIZE) == 0) { if (debug) zlog_debug( - "%s: skipping import, match RD (%pRD) of src VRF (%s) and the prefix (%pFX)", - __func__, prd, to_bgp->name_pretty, p); - + "%s: skipping import, match RD (%s) of src VRF (%s) and the prefix (%pFX)", + __func__, rd_buf, to_bgp->name_pretty, p); return false; } if (debug) - zlog_debug("%s: updating RD %pRD, %pFX to %s", __func__, prd, p, - to_bgp->name_pretty); + zlog_debug("%s: updating RD %s, %pFX to %s", __func__, rd_buf, + p, to_bgp->name_pretty); /* shallow copy */ static_attr = *path_vpn->attr; @@ -2403,7 +2406,7 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, &bgp->vrf_prd_auto); bgp->vpn_policy[afi].tovpn_rd = bgp->vrf_prd_auto; prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf, - sizeof(buf)); + sizeof(buf), bgp->asnotation); /* free up pre-existing memory if any and allocate * the ecommunity attribute with new RD/RT @@ -2538,8 +2541,8 @@ void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, from_bgp->vpn_policy[afi].tovpn_rd = from_bgp->vrf_prd_auto; SET_FLAG(from_bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); - prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd, - buf, sizeof(buf)); + prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd, buf, + sizeof(buf), from_bgp->asnotation); from_bgp->vpn_policy[afi].rtlist[edir] = ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0); SET_FLAG(from_bgp->af_flags[afi][safi], diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c index 95a7bb8117..9b2ab66806 100644 --- a/bgpd/bgp_mplsvpn_snmp.c +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -942,11 +942,13 @@ static uint8_t *mplsL3vpnVrfTable(struct variable *v, oid name[], if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP].tovpn_rd, - rd_buf, sizeof(rd_buf)); + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(l3vpn_bgp)); else if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP6].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP6].tovpn_rd, - rd_buf, sizeof(rd_buf)); + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(l3vpn_bgp)); *var_len = strnlen(rd_buf, RD_ADDRSTRLEN); return (uint8_t *)rd_buf; diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index da89c594c8..1c2e686e1c 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -506,10 +506,16 @@ static void bgp_accept(struct thread *thread) * is shutdown. */ if (BGP_PEER_START_SUPPRESSED(peer1)) { - if (bgp_debug_neighbor_events(peer1)) - zlog_debug( - "[Event] Incoming BGP connection rejected from %s due to maximum-prefix or shutdown", - peer1->host); + if (bgp_debug_neighbor_events(peer1)) { + if (peer1->shut_during_cfg) + zlog_debug( + "[Event] Incoming BGP connection rejected from %s due to configuration being currently read in", + peer1->host); + else + zlog_debug( + "[Event] Incoming BGP connection rejected from %s due to maximum-prefix or shutdown", + peer1->host); + } close(bgp_sock); return; } @@ -538,7 +544,7 @@ static void bgp_accept(struct thread *thread) peer1->host); peer = peer_create(&su, peer1->conf_if, peer1->bgp, peer1->local_as, - peer1->as, peer1->as_type, NULL, false); + peer1->as, peer1->as_type, NULL, false, NULL); peer_xfer_config(peer, peer1); bgp_peer_gr_flags_update(peer); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index a3014c0c6d..00a0bc8402 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -802,6 +802,7 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, safi = table->safi; bgp_path = table->bgp; + if (json) { json_path = json_object_new_object(); json_object_string_add(json_path, "afi", afi2str(afi)); @@ -811,7 +812,8 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, dest); if (dest->pdest) json_object_string_addf( - json_path, "rd", "%pRD", + json_path, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( dest->pdest)); json_object_string_add( @@ -821,13 +823,14 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, json_object_array_add(paths, json_path); continue; } - if (dest->pdest) - vty_out(vty, " %d/%d %pBD RD %pRD %s flags 0x%x\n", - afi, safi, dest, + if (dest->pdest) { + vty_out(vty, " %d/%d %pBD RD ", afi, safi, dest); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( - dest->pdest), - bgp_path->name_pretty, path->flags); - else + dest->pdest)); + vty_out(vty, " %s flags 0x%x\n", bgp_path->name_pretty, + path->flags); + } else vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", afi, safi, dest, bgp_path->name_pretty, path->flags); } diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 4f42766b9f..25b458a8e5 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -1190,14 +1190,20 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) } if (BGP_DEBUG(nht, NHT)) { - if (dest->pdest) - zlog_debug( - "... eval path %d/%d %pBD RD %pRD %s flags 0x%x", - afi, safi, dest, + + if (dest->pdest) { + char rd_buf[RD_ADDRSTRLEN]; + + prefix_rd2str( (struct prefix_rd *)bgp_dest_get_prefix( dest->pdest), + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(bnc->bgp)); + zlog_debug( + "... eval path %d/%d %pBD RD %s %s flags 0x%x", + afi, safi, dest, rd_buf, bgp_path->name_pretty, path->flags); - else + } else zlog_debug( "... eval path %d/%d %pBD %s flags 0x%x", afi, safi, dest, bgp_path->name_pretty, diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c index 6e73ab8d1e..bb9f76b8a3 100644 --- a/bgpd/bgp_rd.c +++ b/bgpd/bgp_rd.c @@ -83,12 +83,12 @@ void decode_rd_vnc_eth(const uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth) int str2prefix_rd(const char *str, struct prefix_rd *prd) { - int ret = 0; - char *p; - char *p2; + int ret = 0, type = RD_TYPE_UNDEFINED; + char *p, *p2; struct stream *s = NULL; char *half = NULL; struct in_addr addr; + as_t as_val; prd->family = AF_UNSPEC; prd->prefixlen = 64; @@ -97,41 +97,55 @@ int str2prefix_rd(const char *str, struct prefix_rd *prd) if (!p) goto out; + /* a second ':' is accepted */ + p2 = strchr(p + 1, ':'); + if (p2) { + /* type is in first part */ + half = XMALLOC(MTYPE_TMP, (p - str) + 1); + memcpy(half, str, (p - str)); + half[p - str] = '\0'; + type = atoi(half); + if (type != RD_TYPE_AS && type != RD_TYPE_IP && + type != RD_TYPE_AS4) + goto out; + XFREE(MTYPE_TMP, half); + half = XMALLOC(MTYPE_TMP, (p2 - p)); + memcpy(half, p + 1, (p2 - p - 1)); + half[p2 - p - 1] = '\0'; + p = p2 + 1; + } else { + half = XMALLOC(MTYPE_TMP, (p - str) + 1); + memcpy(half, str, (p - str)); + half[p - str] = '\0'; + } if (!all_digit(p + 1)) goto out; s = stream_new(RD_BYTES); - half = XMALLOC(MTYPE_TMP, (p - str) + 1); - memcpy(half, str, (p - str)); - half[p - str] = '\0'; - - p2 = strchr(str, '.'); - - if (!p2) { - unsigned long as_val; - - if (!all_digit(half)) - goto out; - - as_val = atol(half); - if (as_val > 0xffff) { + /* if it is an AS format or an IP */ + if (asn_str2asn(half, &as_val)) { + if (as_val > UINT16_MAX) { stream_putw(s, RD_TYPE_AS4); stream_putl(s, as_val); stream_putw(s, atol(p + 1)); + if (type != RD_TYPE_UNDEFINED && type != RD_TYPE_AS4) + goto out; } else { stream_putw(s, RD_TYPE_AS); stream_putw(s, as_val); stream_putl(s, atol(p + 1)); + if (type != RD_TYPE_UNDEFINED && type != RD_TYPE_AS) + goto out; } - } else { - if (!inet_aton(half, &addr)) - goto out; - + } else if (inet_aton(half, &addr)) { stream_putw(s, RD_TYPE_IP); stream_put_in_addr(s, &addr); stream_putw(s, atol(p + 1)); - } + if (type != RD_TYPE_UNDEFINED && type != RD_TYPE_IP) + goto out; + } else + goto out; memcpy(prd->val, s->data, 8); ret = 1; @@ -142,12 +156,14 @@ out: return ret; } -char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size) +char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size, + enum asnotation_mode asnotation) { const uint8_t *pnt; uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; + int len = 0; assert(size >= RD_ADDRSTRLEN); @@ -157,11 +173,15 @@ char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size) if (type == RD_TYPE_AS) { decode_rd_as(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val); + len += snprintfrr(buf + len, size - len, ASN_FORMAT(asnotation), + &rd_as.as); + snprintfrr(buf + len, size - len, ":%u", rd_as.val); return buf; } else if (type == RD_TYPE_AS4) { decode_rd_as4(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val); + len += snprintfrr(buf + len, size - len, ASN_FORMAT(asnotation), + &rd_as.as); + snprintfrr(buf + len, size - len, ":%u", rd_as.val); return buf; } else if (type == RD_TYPE_IP) { decode_rd_ip(pnt + 2, &rd_ip); @@ -196,16 +216,38 @@ void form_auto_rd(struct in_addr router_id, (void)str2prefix_rd(buf, prd); } -printfrr_ext_autoreg_p("RD", printfrr_prd); -static ssize_t printfrr_prd(struct fbuf *buf, struct printfrr_eargs *ea, - const void *ptr) +static ssize_t printfrr_prd_asnotation(struct fbuf *buf, + struct printfrr_eargs *ea, + const void *ptr, + enum asnotation_mode asnotation) { char rd_buf[RD_ADDRSTRLEN]; if (!ptr) return bputs(buf, "(null)"); - prefix_rd2str(ptr, rd_buf, sizeof(rd_buf)); + prefix_rd2str(ptr, rd_buf, sizeof(rd_buf), asnotation); return bputs(buf, rd_buf); } + +printfrr_ext_autoreg_p("RDP", printfrr_prd); +static ssize_t printfrr_prd(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_prd_asnotation(buf, ea, ptr, ASNOTATION_PLAIN); +} + +printfrr_ext_autoreg_p("RDD", printfrr_prd_dot); +static ssize_t printfrr_prd_dot(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_prd_asnotation(buf, ea, ptr, ASNOTATION_DOT); +} + +printfrr_ext_autoreg_p("RDE", printfrr_prd_dotplus); +static ssize_t printfrr_prd_dotplus(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_prd_asnotation(buf, ea, ptr, ASNOTATION_DOTPLUS); +} diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h index 44173b7a24..e38c2fad3b 100644 --- a/bgpd/bgp_rd.h +++ b/bgpd/bgp_rd.h @@ -7,7 +7,11 @@ #ifndef _QUAGGA_BGP_RD_H #define _QUAGGA_BGP_RD_H +#include "asn.h" +#include "prefix.h" + /* RD types */ +#define RD_TYPE_UNDEFINED (-1) #define RD_TYPE_AS 0 #define RD_TYPE_IP 1 #define RD_TYPE_AS4 2 @@ -19,6 +23,16 @@ #define RD_ADDRSTRLEN 28 #define RD_BYTES 8 +#define BGP_RD_AS_FORMAT(mode) \ + ((mode == ASNOTATION_DOT) \ + ? "%pRDD" \ + : ((mode == ASNOTATION_DOTPLUS) ? "%pRDE" : "%pRDP")) + +#define BGP_RD_AS_FORMAT_SPACE(mode) \ + ((mode == ASNOTATION_DOT) \ + ? "%-21pRDD" \ + : ((mode == ASNOTATION_DOTPLUS) ? "%-21pRDE" : "%-21pRDP")) + struct rd_as { uint16_t type; as_t as; @@ -51,7 +65,8 @@ extern void decode_rd_vnc_eth(const uint8_t *pnt, #endif extern int str2prefix_rd(const char *, struct prefix_rd *); -extern char *prefix_rd2str(const struct prefix_rd *, char *, size_t); +extern char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size, + enum asnotation_mode asnotation); extern void form_auto_rd(struct in_addr router_id, uint16_t rd_id, struct prefix_rd *prd); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 0d93abc12f..0beef97a04 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4897,10 +4897,9 @@ filtered: } void bgp_withdraw(struct peer *peer, const struct prefix *p, - uint32_t addpath_id, struct attr *attr, afi_t afi, - safi_t safi, int type, int sub_type, struct prefix_rd *prd, - mpls_label_t *label, uint32_t num_labels, - struct bgp_route_evpn *evpn) + uint32_t addpath_id, afi_t afi, safi_t safi, int type, + int sub_type, struct prefix_rd *prd, mpls_label_t *label, + uint32_t num_labels, struct bgp_route_evpn *evpn) { struct bgp *bgp; char pfx_buf[BGP_PRD_PATH_STRLEN]; @@ -6046,7 +6045,7 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0, 0, NULL); else - bgp_withdraw(peer, &p, addpath_id, attr, afi, safi, + bgp_withdraw(peer, &p, addpath_id, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0, NULL); @@ -6078,6 +6077,8 @@ static void bgp_static_free(struct bgp_static *bgp_static) XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name); route_map_counter_decrement(bgp_static->rmap.map); + if (bgp_static->prd_pretty) + XFREE(MTYPE_BGP, bgp_static->prd_pretty); XFREE(MTYPE_ATTR, bgp_static->eth_s_id); XFREE(MTYPE_BGP_STATIC, bgp_static); } @@ -6981,6 +6982,8 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, bgp_static->label = label; bgp_static->prd = prd; + if (rd_str) + bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str); if (rmap_str) { XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name); route_map_counter_decrement(bgp_static->rmap.map); @@ -7243,7 +7246,7 @@ static bool aggr_suppress_map_test(struct bgp *bgp, return false; /* Call route map matching and return result. */ - attr.aspath = aspath_empty(); + attr.aspath = aspath_empty(bgp->asnotation); rmap_path.peer = bgp->peer_self; rmap_path.attr = &attr; @@ -7337,9 +7340,12 @@ static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, struct lcommunity *lcomm) { static struct aspath *ae = NULL; + enum asnotation_mode asnotation; + + asnotation = bgp_get_asnotation(NULL); if (!ae) - ae = aspath_empty(); + ae = aspath_empty(asnotation); if (!pi) return false; @@ -10254,10 +10260,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, if (dest && dest->pdest) { pdest = dest->pdest; if (is_pi_family_evpn(parent_ri)) { - vty_out(vty, - " Imported from %pRD:%pFX, VNI %s", + vty_out(vty, " Imported from "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( - pdest), + pdest)); + vty_out(vty, ":%pFX, VNI %s", (struct prefix_evpn *) bgp_dest_get_prefix(dest), tag_buf); @@ -10270,12 +10277,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, : "inactive"); vty_out(vty, "\n"); - } else - vty_out(vty, " Imported from %pRD:%pFX\n", + } else { + vty_out(vty, " Imported from "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( - pdest), + pdest)); + vty_out(vty, ":%pFX\n", (struct prefix_evpn *) bgp_dest_get_prefix(dest)); + } } } @@ -11184,17 +11194,26 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, "{\n"); *json_header_depth = 2; } - vty_out(vty, " \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64 ",\n \"routerId\": \"%pI4\",\n \"defaultLocPrf\": %u,\n" - " \"localAS\": %u,\n \"routes\": { ", + " \"localAS\": ", bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id, bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT ? VRF_DEFAULT_NAME : bgp->name, table->version, &bgp->router_id, - bgp->default_local_pref, bgp->as); + bgp->default_local_pref); + if ((bgp->asnotation == ASNOTATION_PLAIN) || + ((bgp->asnotation == ASNOTATION_DOT) && + (bgp->as < UINT16_MAX))) + vty_out(vty, "%u", bgp->as); + else { + vty_out(vty, "\""); + vty_out(vty, ASN_FORMAT(bgp->asnotation), &bgp->as); + vty_out(vty, "\""); + } + vty_out(vty, ",\n \"routes\": { "); if (rd) { vty_out(vty, " \"routeDistinguishers\" : {"); ++*json_header_depth; @@ -11455,6 +11474,10 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, || CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) continue; } + if (type == bgp_show_type_self_originated) { + if (pi->peer != bgp->peer_self) + continue; + } if (!use_json && header) { vty_out(vty, @@ -11468,7 +11491,10 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, "\n"); vty_out(vty, "Default local pref %u, ", bgp->default_local_pref); - vty_out(vty, "local AS %u\n", bgp->as); + vty_out(vty, "local AS "); + vty_out(vty, ASN_FORMAT(bgp->asnotation), + &bgp->as); + vty_out(vty, "\n"); if (!detail_routes) { vty_out(vty, BGP_SHOW_SCODE_HEADER); vty_out(vty, BGP_SHOW_NCODE_HEADER); @@ -11665,7 +11691,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, char rd[RD_ADDRSTRLEN]; memcpy(&prd, dest_p, sizeof(struct prefix_rd)); - prefix_rd2str(&prd, rd, sizeof(rd)); + prefix_rd2str(&prd, rd, sizeof(rd), bgp->asnotation); bgp_show_table(vty, bgp, safi, itable, type, output_arg, rd, next == NULL, &output_cum, &total_cum, &json_header_depth, @@ -11816,13 +11842,16 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, if (safi == SAFI_EVPN) { if (!json) { vty_out(vty, "BGP routing table entry for %s%s%pFX\n", - prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) + prd ? prefix_rd2str(prd, buf1, sizeof(buf1), + bgp->asnotation) : "", prd ? ":" : "", (struct prefix_evpn *)p); } else { - json_object_string_add(json, "rd", - prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) : - ""); + json_object_string_add( + json, "rd", + prd ? prefix_rd2str(prd, buf1, sizeof(buf1), + bgp->asnotation) + : ""); bgp_evpn_route2json((struct prefix_evpn *)p, json); } } else { @@ -11832,7 +11861,8 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, "\n", ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) ? prefix_rd2str(prd, buf1, - sizeof(buf1)) + sizeof(buf1), + bgp->asnotation) : ""), safi == SAFI_MPLS_VPN ? ":" : "", p, dest->version); @@ -12055,8 +12085,9 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd, json_object_object_add(json_header, "paths", json_paths); if (pfx_rd) - json_object_object_addf(json, json_header, "%pRD", - pfx_rd); + json_object_object_addf( + json, json_header, + BGP_RD_AS_FORMAT(bgp->asnotation), pfx_rd); } } @@ -12623,6 +12654,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, |alias ALIAS_NAME\ |A.B.C.D/M longer-prefixes\ |X:X::X:X/M longer-prefixes\ + |"BGP_SELF_ORIG_CMD_STR"\ |detail-routes$detail_routes\ ] [json$uj [detail$detail_json] | wide$wide]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR @@ -12672,6 +12704,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, "Display route and more specific routes\n" "IPv6 prefix\n" "Display route and more specific routes\n" + BGP_SELF_ORIG_HELP_STR "Display detailed version of all routes\n" JSON_STR "Display detailed version of JSON output\n" @@ -12866,6 +12899,10 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, output_arg = &p; } + /* self originated only */ + if (argv_find(argv, argc, BGP_SELF_ORIG_CMD_STR, &idx)) + sh_type = bgp_show_type_self_originated; + if (!all) { /* show bgp: AFI_IP6, show ip bgp: AFI_IP */ if (community) @@ -14465,7 +14502,8 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") prd = (const struct prefix_rd *)bgp_dest_get_prefix( dest); - prefix_rd2str(prd, rd_str, sizeof(rd_str)); + prefix_rd2str(prd, rd_str, sizeof(rd_str), + bgp->asnotation); show_adj_route( vty, peer, table, afi, safi, type, rmap_name, @@ -15594,7 +15632,6 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest; struct bgp_table *table; const struct prefix *p; - const struct prefix_rd *prd; struct bgp_static *bgp_static; mpls_label_t label; @@ -15612,13 +15649,12 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, continue; p = bgp_dest_get_prefix(dest); - prd = (const struct prefix_rd *)bgp_dest_get_prefix( - pdest); /* "network" configuration display. */ label = decode_label(&bgp_static->label); - vty_out(vty, " network %pFX rd %pRD", p, prd); + vty_out(vty, " network %pFX rd %s", p, + bgp_static->prd_pretty); if (safi == SAFI_MPLS_VPN) vty_out(vty, " label %u", label); @@ -15641,7 +15677,6 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest; struct bgp_table *table; const struct prefix *p; - const struct prefix_rd *prd; struct bgp_static *bgp_static; char buf[PREFIX_STRLEN * 2]; char buf2[SU_ADDRSTRLEN]; @@ -15669,7 +15704,6 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, esi_to_str(bgp_static->eth_s_id, esi_buf, sizeof(esi_buf)); p = bgp_dest_get_prefix(dest); - prd = (struct prefix_rd *)bgp_dest_get_prefix(pdest); /* "network" configuration display. */ if (p->u.prefix_evpn.route_type == 5) { @@ -15696,8 +15730,9 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, &bgp_static->gatewayIp.u.prefix, buf2, sizeof(buf2)); vty_out(vty, - " network %s rd %pRD ethtag %u label %u esi %s gwip %s routermac %s\n", - buf, prd, p->u.prefix_evpn.prefix_addr.eth_tag, + " network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n", + buf, bgp_static->prd_pretty, + p->u.prefix_evpn.prefix_addr.eth_tag, decode_label(&bgp_static->label), esi_buf, buf2, macrouter); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index d449152c21..66cc62ab09 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -46,6 +46,7 @@ enum bgp_show_type { bgp_show_type_detail, bgp_show_type_rpki, bgp_show_type_prefix_version, + bgp_show_type_self_originated, }; enum bgp_show_adj_route_type { @@ -66,7 +67,7 @@ enum bgp_show_adj_route_type { #define BGP_SHOW_RPKI_HEADER \ "RPKI validation codes: V valid, I invalid, N Not found\n\n" #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n" -#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n" +#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n" /* Maximum number of labels we can process or send with a prefix. We * really do only 1 for MPLS (BGP-LU) but we can do 2 for EVPN-VxLAN. @@ -355,6 +356,7 @@ struct bgp_static { /* Route Distinguisher */ struct prefix_rd prd; + char *prd_pretty; /* MPLS label. */ mpls_label_t label; @@ -742,10 +744,10 @@ extern void bgp_update(struct peer *peer, const struct prefix *p, uint32_t num_labels, int soft_reconfig, struct bgp_route_evpn *evpn); extern void bgp_withdraw(struct peer *peer, const struct prefix *p, - uint32_t addpath_id, struct attr *attr, afi_t afi, - safi_t safi, int type, int sub_type, - struct prefix_rd *prd, mpls_label_t *label, - uint32_t num_labels, struct bgp_route_evpn *evpn); + uint32_t addpath_id, afi_t afi, safi_t safi, int type, + int sub_type, struct prefix_rd *prd, + mpls_label_t *label, uint32_t num_labels, + struct bgp_route_evpn *evpn); /* for bgp_nexthop and bgp_damp */ extern void bgp_process(struct bgp *, struct bgp_dest *, afi_t, safi_t); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index f9def03693..8fd46d00af 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -213,7 +213,7 @@ static void *route_aspath_compile(const char *arg) { struct aspath *aspath; - aspath = aspath_str2aspath(arg); + aspath = aspath_str2aspath(arg, bgp_get_asnotation(NULL)); if (!aspath) return NULL; return aspath; @@ -3024,11 +3024,11 @@ static void *route_set_origin_compile(const char *arg) origin = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t)); if (strcmp(arg, "igp") == 0) - *origin = 0; + *origin = BGP_ORIGIN_IGP; else if (strcmp(arg, "egp") == 0) - *origin = 1; + *origin = BGP_ORIGIN_EGP; else - *origin = 2; + *origin = BGP_ORIGIN_INCOMPLETE; return origin; } @@ -3631,17 +3631,29 @@ route_set_ipv6_nexthop_local(void *rule, const struct prefix *p, void *object) { struct in6_addr *address; struct bgp_path_info *path; + struct bgp_dest *dest; + struct bgp_table *table = NULL; /* Fetch routemap's rule information. */ address = rule; path = object; + dest = path->net; + + if (!dest) + return RMAP_OKAY; + + table = bgp_dest_table(dest); + if (!table) + return RMAP_OKAY; /* Set next hop value. */ path->attr->mp_nexthop_local = *address; /* Set nexthop length. */ - if (path->attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL && - path->attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) + if (table->safi == SAFI_MPLS_VPN || table->safi == SAFI_ENCAP || + table->safi == SAFI_EVPN) + path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL; + else path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL; SET_FLAG(path->attr->rmap_change_flags, @@ -5663,15 +5675,16 @@ DEFUN_YANG (no_set_label_index, DEFUN_YANG (set_aspath_prepend_asn, set_aspath_prepend_asn_cmd, - "set as-path prepend (1-4294967295)...", + "set as-path prepend ASNUM...", SET_STR "Transform BGP AS_PATH attribute\n" "Prepend to the as-path\n" - "AS number\n") + AS_STR) { int idx_asn = 3; int ret; char *str; + struct aspath *aspath; str = argv_concat(argv, argc, idx_asn); @@ -5679,6 +5692,12 @@ DEFUN_YANG (set_aspath_prepend_asn, "./set-action[action='frr-bgp-route-map:as-path-prepend']"; char xpath_value[XPATH_MAXLEN]; + aspath = route_aspath_compile(str); + if (!aspath) { + vty_out(vty, "%% Invalid AS path value %s\n", str); + return CMD_WARNING_CONFIG_FAILED; + } + route_aspath_free(aspath); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/frr-bgp-route-map:prepend-as-path", xpath); @@ -5713,16 +5732,22 @@ DEFUN_YANG (set_aspath_prepend_lastas, DEFPY_YANG (set_aspath_replace_asn, set_aspath_replace_asn_cmd, - "set as-path replace <any|(1-4294967295)>$replace", + "set as-path replace <any|ASNUM>$replace", SET_STR "Transform BGP AS_PATH attribute\n" "Replace AS number to local AS number\n" "Replace any AS number to local AS number\n" - "Replace a specific AS number to local AS number\n") + "Replace a specific AS number in plain or dotted format to local AS number\n") { const char *xpath = "./set-action[action='frr-bgp-route-map:as-path-replace']"; char xpath_value[XPATH_MAXLEN]; + as_t as_value; + + if (!strmatch(replace, "any") && !asn_str2asn(replace, &as_value)) { + vty_out(vty, "%% Invalid AS value %s\n", replace); + return CMD_WARNING_CONFIG_FAILED; + } nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), @@ -5733,13 +5758,13 @@ DEFPY_YANG (set_aspath_replace_asn, DEFPY_YANG (no_set_aspath_replace_asn, no_set_aspath_replace_asn_cmd, - "no set as-path replace [<any|(1-4294967295)>]", + "no set as-path replace [<any|ASNUM>]", NO_STR SET_STR "Transform BGP AS_PATH attribute\n" "Replace AS number to local AS number\n" "Replace any AS number to local AS number\n" - "Replace a specific AS number to local AS number\n") + "Replace a specific AS number in plain or dotted format to local AS number\n") { const char *xpath = "./set-action[action='frr-bgp-route-map:as-path-replace']"; @@ -5750,12 +5775,12 @@ DEFPY_YANG (no_set_aspath_replace_asn, DEFUN_YANG (no_set_aspath_prepend, no_set_aspath_prepend_cmd, - "no set as-path prepend [(1-4294967295)]", + "no set as-path prepend [ASNUM]", NO_STR SET_STR "Transform BGP AS_PATH attribute\n" "Prepend to the as-path\n" - "AS number\n") + AS_STR) { const char *xpath = "./set-action[action='frr-bgp-route-map:as-path-prepend']"; @@ -5783,15 +5808,16 @@ DEFUN_YANG (no_set_aspath_prepend_lastas, DEFUN_YANG (set_aspath_exclude, set_aspath_exclude_cmd, - "set as-path exclude (1-4294967295)...", + "set as-path exclude ASNUM...", SET_STR "Transform BGP AS-path attribute\n" "Exclude from the as-path\n" - "AS number\n") + AS_STR) { int idx_asn = 3; int ret; char *str; + struct aspath *aspath; str = argv_concat(argv, argc, idx_asn); @@ -5799,6 +5825,12 @@ DEFUN_YANG (set_aspath_exclude, "./set-action[action='frr-bgp-route-map:as-path-exclude']"; char xpath_value[XPATH_MAXLEN]; + aspath = route_aspath_compile(str); + if (!aspath) { + vty_out(vty, "%% Invalid AS path value %s\n", str); + return CMD_WARNING_CONFIG_FAILED; + } + route_aspath_free(aspath); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/frr-bgp-route-map:exclude-as-path", xpath); @@ -5810,7 +5842,7 @@ DEFUN_YANG (set_aspath_exclude, DEFUN_YANG (no_set_aspath_exclude, no_set_aspath_exclude_cmd, - "no set as-path exclude (1-4294967295)...", + "no set as-path exclude ASNUM...", NO_STR SET_STR "Transform BGP AS_PATH attribute\n" @@ -5870,9 +5902,14 @@ DEFUN_YANG (set_community, else first = 1; +#if CONFDATE > 20230801 +CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community") +#endif if (strncmp(argv[i]->arg, "internet", strlen(argv[i]->arg)) == 0) { buffer_putstr(b, "internet"); + vty_out(vty, "%% `internet` community is deprecated\n"); + zlog_warn("`internet` community is deprecated"); continue; } if (strncmp(argv[i]->arg, "local-AS", strlen(argv[i]->arg)) @@ -6496,11 +6533,11 @@ DEFPY_YANG (no_set_aigp_metric, DEFUN_YANG (set_aggregator_as, set_aggregator_as_cmd, - "set aggregator as (1-4294967295) A.B.C.D", + "set aggregator as ASNUM A.B.C.D", SET_STR "BGP aggregator attribute\n" "AS number of aggregator\n" - "AS number\n" + AS_STR "IP address of aggregator\n") { int idx_number = 3; @@ -6509,6 +6546,12 @@ DEFUN_YANG (set_aggregator_as, char xpath_addr[XPATH_MAXLEN]; const char *xpath = "./set-action[action='frr-bgp-route-map:aggregator']"; + as_t as_value; + + if (!asn_str2asn(argv[idx_number]->arg, &as_value)) { + vty_out(vty, "%% Invalid AS value %s\n", argv[idx_number]->arg); + return CMD_WARNING_CONFIG_FAILED; + } nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); @@ -6531,12 +6574,12 @@ DEFUN_YANG (set_aggregator_as, DEFUN_YANG (no_set_aggregator_as, no_set_aggregator_as_cmd, - "no set aggregator as [(1-4294967295) A.B.C.D]", + "no set aggregator as [ASNUM A.B.C.D]", NO_STR SET_STR "BGP aggregator attribute\n" "AS number of aggregator\n" - "AS number\n" + AS_STR "IP address of aggregator\n") { const char *xpath = diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 61b8b4edce..32ca909fe6 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -81,6 +81,7 @@ struct rpki_for_each_record_arg { unsigned int *prefix_amount; as_t as; json_object *json; + enum asnotation_mode asnotation; }; static int start(void); @@ -105,7 +106,7 @@ static void rpki_delete_all_cache_nodes(void); static int add_tcp_cache(const char *host, const char *port, const uint8_t preference, const char *bindaddr); static void print_record(const struct pfx_record *record, struct vty *vty, - json_object *json); + json_object *json, enum asnotation_mode asnotation); static bool is_synchronized(void); static bool is_running(void); static bool is_stopping(void); @@ -270,7 +271,7 @@ static void rpki_delete_all_cache_nodes(void) } static void print_record(const struct pfx_record *record, struct vty *vty, - json_object *json) + json_object *json, enum asnotation_mode asnotation) { char ip[INET6_ADDRSTRLEN]; json_object *json_record = NULL; @@ -278,8 +279,10 @@ static void print_record(const struct pfx_record *record, struct vty *vty, lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip)); if (!json) { - vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len, - record->max_len, record->asn); + vty_out(vty, "%-40s %3u - %3u ", ip, record->min_len, + record->max_len); + vty_out(vty, ASN_FORMAT(asnotation), (as_t *)&record->asn); + vty_out(vty, "\n"); } else { json_record = json_object_new_object(); json_object_string_add(json_record, "prefix", ip); @@ -287,7 +290,7 @@ static void print_record(const struct pfx_record *record, struct vty *vty, record->min_len); json_object_int_add(json_record, "prefixLenMax", record->max_len); - json_object_int_add(json_record, "asn", record->asn); + asn_asn2json(json_record, "asn", record->asn, asnotation); json_object_array_add(json, json_record); } } @@ -299,7 +302,7 @@ static void print_record_by_asn(const struct pfx_record *record, void *data) if (record->asn == arg->as) { (*arg->prefix_amount)++; - print_record(record, vty, arg->json); + print_record(record, vty, arg->json, arg->asnotation); } } @@ -310,7 +313,7 @@ static void print_record_cb(const struct pfx_record *record, void *data) (*arg->prefix_amount)++; - print_record(record, vty, arg->json); + print_record(record, vty, arg->json, arg->asnotation); } static struct rtr_mgr_group *get_groups(void) @@ -728,6 +731,7 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as, arg.vty = vty; arg.as = as; arg.json = NULL; + arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); if (!group) { if (!json) @@ -780,6 +784,7 @@ static void print_prefix_table(struct vty *vty, json_object *json) arg.vty = vty; arg.json = NULL; + arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); if (!group) { if (!json) @@ -1337,7 +1342,7 @@ DEFPY (show_rpki_prefix_table, DEFPY (show_rpki_as_number, show_rpki_as_number_cmd, - "show rpki as-number (1-4294967295)$by_asn [json$uj]", + "show rpki as-number ASNUM$by_asn [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup by ASN in prefix table\n" @@ -1361,7 +1366,7 @@ DEFPY (show_rpki_as_number, DEFPY (show_rpki_prefix, show_rpki_prefix_cmd, - "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn] [json$uj]", + "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [ASNUM$asn] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup IP prefix and optionally ASN in prefix table\n" @@ -1372,6 +1377,7 @@ DEFPY (show_rpki_prefix, { json_object *json = NULL; json_object *json_records = NULL; + enum asnotation_mode asnotation; if (!is_synchronized()) { if (!uj) @@ -1397,8 +1403,8 @@ DEFPY (show_rpki_prefix, enum pfxv_state result; if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count, - asn, &addr, prefix->prefixlen, &result) - != PFX_SUCCESS) { + asn, &addr, prefix->prefixlen, + &result) != PFX_SUCCESS) { if (!json) vty_out(vty, "Prefix lookup failed\n"); return CMD_WARNING; @@ -1415,13 +1421,14 @@ DEFPY (show_rpki_prefix, json_object_object_add(json, "prefixes", json_records); } + asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); for (size_t i = 0; i < match_count; ++i) { const struct pfx_record *record = &matches[i]; - if (record->max_len >= prefix->prefixlen - && ((asn != 0 && (uint32_t)asn == record->asn) - || asn == 0)) { - print_record(&matches[i], vty, json_records); + if (record->max_len >= prefix->prefixlen && + ((asn != 0 && (uint32_t)asn == record->asn) || asn == 0)) { + print_record(&matches[i], vty, json_records, + asnotation); } } diff --git a/bgpd/bgp_script.c b/bgpd/bgp_script.c index 6cc75494ed..f4ab233524 100644 --- a/bgpd/bgp_script.c +++ b/bgpd/bgp_script.c @@ -149,7 +149,8 @@ void lua_decode_attr(lua_State *L, int idx, struct attr *attr) attr->nh_ifindex = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, idx, "aspath"); - attr->aspath = aspath_str2aspath(lua_tostring(L, -1)); + attr->aspath = aspath_str2aspath(lua_tostring(L, -1), + bgp_get_asnotation(NULL)); lua_pop(L, 1); lua_getfield(L, idx, "localpref"); attr->local_pref = lua_tointeger(L, -1); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index b858d9c4b4..8ef18a34d0 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -24,6 +24,7 @@ #include "queue.h" #include "filter.h" #include "frrstr.h" +#include "asn.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr_evpn.h" @@ -579,9 +580,10 @@ static const char *get_bgp_default_af_flag(afi_t afi, safi_t safi) } int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, - enum bgp_instance_type inst_type) + enum bgp_instance_type inst_type, const char *as_pretty, + enum asnotation_mode asnotation) { - int ret = bgp_get(bgp, as, name, inst_type); + int ret = bgp_get(bgp, as, name, inst_type, as_pretty, asnotation); if (ret == BGP_CREATED) { bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, @@ -1222,7 +1224,12 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, /* Clear all neighbors belonging to a specific AS. */ if (sort == clear_as) { - as_t as = strtoul(arg, NULL, 10); + as_t as; + + if (!asn_str2asn(arg, &as)) { + vty_out(vty, "%% BGP: No such AS %s\n", arg); + return CMD_WARNING; + } for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (peer->as != as) @@ -1460,16 +1467,23 @@ DEFUN (no_auto_summary, /* "router bgp" commands. */ DEFUN_NOSH (router_bgp, router_bgp_cmd, - "router bgp [(1-4294967295)$instasn [<view|vrf> VIEWVRFNAME]]", + "router bgp [ASNUM$instasn [<view|vrf> VIEWVRFNAME] [as-notation <dot|dot+|plain>]]", ROUTER_STR BGP_STR AS_STR - BGP_INSTANCE_HELP_STR) + BGP_INSTANCE_HELP_STR + "Force the AS notation output\n" + "use 'AA.BB' format for AS 4 byte values\n" + "use 'AA.BB' format for all AS values\n" + "use plain format for all AS values\n") { int idx_asn = 2; int idx_view_vrf = 3; int idx_vrf = 4; int is_new_bgp = 0; + int idx_asnotation = 3; + int idx_asnotation_kind = 4; + enum asnotation_mode asnotation = ASNOTATION_UNDEFINED; int ret; as_t as; struct bgp *bgp; @@ -1494,39 +1508,62 @@ DEFUN_NOSH (router_bgp, // "router bgp X" else { - as = strtoul(argv[idx_asn]->arg, NULL, 10); + if (!asn_str2asn(argv[idx_asn]->arg, &as)) { + vty_out(vty, "%% BGP: No such AS %s\n", + argv[idx_asn]->arg); + return CMD_WARNING_CONFIG_FAILED; + } if (as == BGP_PRIVATE_AS_MAX || as == BGP_AS4_MAX) vty_out(vty, "Reserved AS used (%u|%u); AS is %u\n", BGP_PRIVATE_AS_MAX, BGP_AS4_MAX, as); inst_type = BGP_INSTANCE_TYPE_DEFAULT; - if (argc > 3) { - name = argv[idx_vrf]->arg; - if (!strcmp(argv[idx_view_vrf]->text, "vrf")) { - if (strmatch(name, VRF_DEFAULT_NAME)) - name = NULL; - else - inst_type = BGP_INSTANCE_TYPE_VRF; - } else if (!strcmp(argv[idx_view_vrf]->text, "view")) - inst_type = BGP_INSTANCE_TYPE_VIEW; + if (argv_find(argv, argc, "VIEWVRFNAME", &idx_vrf)) { + idx_view_vrf = idx_vrf - 1; + if (argv[idx_view_vrf]->text) { + name = argv[idx_vrf]->arg; + + if (!strcmp(argv[idx_view_vrf]->text, "vrf")) { + if (strmatch(name, VRF_DEFAULT_NAME)) + name = NULL; + else + inst_type = + BGP_INSTANCE_TYPE_VRF; + } else if (!strcmp(argv[idx_view_vrf]->text, + "view")) + inst_type = BGP_INSTANCE_TYPE_VIEW; + } + } + if (argv_find(argv, argc, "as-notation", &idx_asnotation)) { + idx_asnotation_kind = idx_asnotation + 1; + if (strmatch(argv[idx_asnotation_kind]->text, "dot+")) + asnotation = ASNOTATION_DOTPLUS; + else if (strmatch(argv[idx_asnotation_kind]->text, + "dot")) + asnotation = ASNOTATION_DOT; + else if (strmatch(argv[idx_asnotation_kind]->text, + "plain")) + asnotation = ASNOTATION_PLAIN; } if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) is_new_bgp = (bgp_lookup(as, name) == NULL); - ret = bgp_get_vty(&bgp, &as, name, inst_type); + ret = bgp_get_vty(&bgp, &as, name, inst_type, + argv[idx_asn]->arg, asnotation); switch (ret) { case BGP_ERR_AS_MISMATCH: - vty_out(vty, "BGP is already running; AS is %u\n", as); + vty_out(vty, "BGP is already running; AS is %s\n", + bgp->as_pretty); return CMD_WARNING_CONFIG_FAILED; case BGP_ERR_INSTANCE_MISMATCH: vty_out(vty, "BGP instance name and AS number mismatch\n"); vty_out(vty, - "BGP instance is already running; AS is %u\n", - as); + "BGP instance is already running; AS is %s\n", + bgp->as_pretty); return CMD_WARNING_CONFIG_FAILED; } @@ -1542,6 +1579,19 @@ DEFUN_NOSH (router_bgp, bgp_vpn_leak_export(bgp); /* Pending: handle when user tries to change a view to vrf n vv. */ + /* for pre-existing bgp instance, + * - update as_pretty + * - update asnotation if explicitly mentioned + */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) { + XFREE(MTYPE_BGP, bgp->as_pretty); + bgp->as_pretty = XSTRDUP(MTYPE_BGP, argv[idx_asn]->arg); + if (!CHECK_FLAG(bgp->config, BGP_CONFIG_ASNOTATION) && + asnotation != ASNOTATION_UNDEFINED) { + SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION); + bgp->asnotation = asnotation; + } + } } /* unset the auto created flag as the user config is now present */ @@ -1554,12 +1604,16 @@ DEFUN_NOSH (router_bgp, /* "no router bgp" commands. */ DEFUN (no_router_bgp, no_router_bgp_cmd, - "no router bgp [(1-4294967295)$instasn [<view|vrf> VIEWVRFNAME]]", + "no router bgp [ASNUM$instasn [<view|vrf> VIEWVRFNAME] [as-notation <dot|dot+|plain>]]", NO_STR ROUTER_STR BGP_STR AS_STR - BGP_INSTANCE_HELP_STR) + BGP_INSTANCE_HELP_STR + "Force the AS notation output\n" + "use 'AA.BB' format for AS 4 byte values\n" + "use 'AA.BB' format for all AS values\n" + "use plain format for all AS values\n") { int idx_asn = 3; int idx_vrf = 5; @@ -1588,8 +1642,11 @@ DEFUN (no_router_bgp, return CMD_WARNING_CONFIG_FAILED; } } else { - as = strtoul(argv[idx_asn]->arg, NULL, 10); - + if (!asn_str2asn(argv[idx_asn]->arg, &as)) { + vty_out(vty, "%% BGP: No such AS %s\n", + argv[idx_asn]->arg); + return CMD_WARNING_CONFIG_FAILED; + } if (argc > 4) { name = argv[idx_vrf]->arg; if (strmatch(argv[idx_vrf - 1]->text, "vrf") @@ -1821,7 +1878,6 @@ DEFPY (bgp_suppress_fib_pending, return CMD_SUCCESS; } - /* BGP Cluster ID. */ DEFUN (bgp_cluster_id, bgp_cluster_id_cmd, @@ -1917,30 +1973,33 @@ DEFPY (no_bgp_send_extra_data, DEFUN (bgp_confederation_identifier, bgp_confederation_identifier_cmd, - "bgp confederation identifier (1-4294967295)", + "bgp confederation identifier ASNUM", BGP_STR "AS confederation parameters\n" - "AS number\n" + AS_STR "Set routing domain confederation AS\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_number = 3; as_t as; - as = strtoul(argv[idx_number]->arg, NULL, 10); + if (!asn_str2asn(argv[idx_number]->arg, &as)) { + vty_out(vty, "%% BGP: No such AS %s\n", argv[idx_number]->arg); + return CMD_WARNING_CONFIG_FAILED; + } - bgp_confederation_id_set(bgp, as); + bgp_confederation_id_set(bgp, as, argv[idx_number]->arg); return CMD_SUCCESS; } DEFUN (no_bgp_confederation_identifier, no_bgp_confederation_identifier_cmd, - "no bgp confederation identifier [(1-4294967295)]", + "no bgp confederation identifier [ASNUM]", NO_STR BGP_STR "AS confederation parameters\n" - "AS number\n" + AS_STR "Set routing domain confederation AS\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -1951,7 +2010,7 @@ DEFUN (no_bgp_confederation_identifier, DEFUN (bgp_confederation_peers, bgp_confederation_peers_cmd, - "bgp confederation peers (1-4294967295)...", + "bgp confederation peers ASNUM...", BGP_STR "AS confederation parameters\n" "Peer ASs in BGP confederation\n" @@ -1963,15 +2022,20 @@ DEFUN (bgp_confederation_peers, int i; for (i = idx_asn; i < argc; i++) { - as = strtoul(argv[i]->arg, NULL, 10); - bgp_confederation_peers_add(bgp, as); + if (!asn_str2asn(argv[i]->arg, &as)) { + vty_out(vty, "%% Invalid confed peer AS value: %s\n", + argv[i]->arg); + continue; + } + + bgp_confederation_peers_add(bgp, as, argv[i]->arg); } return CMD_SUCCESS; } DEFUN (no_bgp_confederation_peers, no_bgp_confederation_peers_cmd, - "no bgp confederation peers (1-4294967295)...", + "no bgp confederation peers ASNUM...", NO_STR BGP_STR "AS confederation parameters\n" @@ -1984,8 +2048,11 @@ DEFUN (no_bgp_confederation_peers, int i; for (i = idx_asn; i < argc; i++) { - as = strtoul(argv[i]->arg, NULL, 10); - + if (!asn_str2asn(argv[i]->arg, &as)) { + vty_out(vty, "%% Invalid confed peer AS value: %s\n", + argv[i]->arg); + continue; + } bgp_confederation_peers_remove(bgp, as); } return CMD_SUCCESS; @@ -4489,11 +4556,13 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str, } else if (as_str[0] == 'e') { as = 0; as_type = AS_EXTERNAL; - } else { - /* Get AS number. */ - as = strtoul(as_str, NULL, 10); - } + } else if (!asn_str2asn(as_str, &as)) + as_type = AS_UNSPECIFIED; + if (as_type == AS_UNSPECIFIED) { + vty_out(vty, "%% Invalid peer AS: %s\n", as_str); + return CMD_WARNING_CONFIG_FAILED; + } /* If peer is peer group or interface peer, call proper function. */ ret = str2sockunion(peer_str, &su); if (ret < 0) { @@ -4502,11 +4571,12 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str, /* Check if existing interface peer */ peer = peer_lookup_by_conf_if(bgp, peer_str); - ret = peer_remote_as(bgp, NULL, peer_str, &as, as_type); + ret = peer_remote_as(bgp, NULL, peer_str, &as, as_type, as_str); /* if not interface peer, check peer-group settings */ if (ret < 0 && !peer) { - ret = peer_group_remote_as(bgp, peer_str, &as, as_type); + ret = peer_group_remote_as(bgp, peer_str, &as, as_type, + as_str); if (ret < 0) { vty_out(vty, "%% Create the peer-group or interface first\n"); @@ -4520,7 +4590,7 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str, "%% Can not configure the local system as neighbor\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = peer_remote_as(bgp, &su, NULL, &as, as_type); + ret = peer_remote_as(bgp, &su, NULL, &as, as_type, as_str); } return bgp_vty_return(vty, ret); @@ -4591,7 +4661,7 @@ ALIAS(no_bgp_shutdown, no_bgp_shutdown_msg_cmd, DEFUN (neighbor_remote_as, neighbor_remote_as_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <(1-4294967295)|internal|external>", + "neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <ASNUM|internal|external>", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a BGP neighbor\n" @@ -4671,18 +4741,19 @@ static int peer_conf_interface_get(struct vty *vty, const char *conf_if, as_type = AS_EXTERNAL; } else { /* Get AS number. */ - as = strtoul(as_str, NULL, 10); - as_type = AS_SPECIFIED; + if (asn_str2asn(as_str, &as)) + as_type = AS_SPECIFIED; } } peer = peer_lookup_by_conf_if(bgp, conf_if); if (peer) { if (as_str) - ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type); + ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type, + as_str); } else { peer = peer_create(NULL, conf_if, bgp, bgp->as, as, as_type, - NULL, true); + NULL, true, as_str); if (!peer) { vty_out(vty, "%% BGP failed to create peer\n"); @@ -4784,7 +4855,7 @@ DEFUN (neighbor_interface_config_v6only, DEFUN (neighbor_interface_config_remote_as, neighbor_interface_config_remote_as_cmd, - "neighbor WORD interface remote-as <(1-4294967295)|internal|external>", + "neighbor WORD interface remote-as <ASNUM|internal|external>", NEIGHBOR_STR "Interface name or neighbor tag\n" "Enable BGP on interface\n" @@ -4801,7 +4872,7 @@ DEFUN (neighbor_interface_config_remote_as, DEFUN (neighbor_interface_v6only_config_remote_as, neighbor_interface_v6only_config_remote_as_cmd, - "neighbor WORD interface v6only remote-as <(1-4294967295)|internal|external>", + "neighbor WORD interface v6only remote-as <ASNUM|internal|external>", NEIGHBOR_STR "Interface name or neighbor tag\n" "Enable BGP with v6 link-local only\n" @@ -4970,7 +5041,7 @@ DEFUN (no_neighbor_peer_group, DEFUN (no_neighbor_interface_peer_group_remote_as, no_neighbor_interface_peer_group_remote_as_cmd, - "no neighbor WORD remote-as <(1-4294967295)|internal|external>", + "no neighbor WORD remote-as <ASNUM|internal|external>", NO_STR NEIGHBOR_STR "Interface name or neighbor tag\n" @@ -4987,7 +5058,7 @@ DEFUN (no_neighbor_interface_peer_group_remote_as, /* look up for neighbor by interface name config. */ peer = peer_lookup_by_conf_if(bgp, argv[idx_word]->arg); if (peer) { - peer_as_change(peer, 0, AS_UNSPECIFIED); + peer_as_change(peer, 0, AS_UNSPECIFIED, NULL); return CMD_SUCCESS; } @@ -5003,11 +5074,11 @@ DEFUN (no_neighbor_interface_peer_group_remote_as, DEFUN (neighbor_local_as, neighbor_local_as_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295)", + "neighbor <A.B.C.D|X:X::X:X|WORD> local-as ASNUM", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a local-as number\n" - "AS number used as local AS\n") + "AS number expressed in dotted or plain format used as local AS\n") { int idx_peer = 1; int idx_number = 3; @@ -5019,18 +5090,23 @@ DEFUN (neighbor_local_as, if (!peer) return CMD_WARNING_CONFIG_FAILED; - as = strtoul(argv[idx_number]->arg, NULL, 10); - ret = peer_local_as_set(peer, as, 0, 0); + if (!asn_str2asn(argv[idx_number]->arg, &as)) { + vty_out(vty, "%% Invalid neighbor local-as value: %s\n", + argv[idx_number]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = peer_local_as_set(peer, as, 0, 0, argv[idx_number]->arg); return bgp_vty_return(vty, ret); } DEFUN (neighbor_local_as_no_prepend, neighbor_local_as_no_prepend_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295) no-prepend", + "neighbor <A.B.C.D|X:X::X:X|WORD> local-as ASNUM no-prepend", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a local-as number\n" - "AS number used as local AS\n" + "AS number expressed in dotted or plain format used as local AS\n" "Do not prepend local-as to updates from ebgp peers\n") { int idx_peer = 1; @@ -5043,18 +5119,23 @@ DEFUN (neighbor_local_as_no_prepend, if (!peer) return CMD_WARNING_CONFIG_FAILED; - as = strtoul(argv[idx_number]->arg, NULL, 10); - ret = peer_local_as_set(peer, as, 1, 0); + if (!asn_str2asn(argv[idx_number]->arg, &as)) { + vty_out(vty, "%% Invalid neighbor local-as value: %s\n", + argv[idx_number]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = peer_local_as_set(peer, as, 1, 0, argv[idx_number]->arg); return bgp_vty_return(vty, ret); } DEFUN (neighbor_local_as_no_prepend_replace_as, neighbor_local_as_no_prepend_replace_as_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295) no-prepend replace-as", + "neighbor <A.B.C.D|X:X::X:X|WORD> local-as ASNUM no-prepend replace-as", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a local-as number\n" - "AS number used as local AS\n" + "AS number expressed in dotted or plain format used as local AS\n" "Do not prepend local-as to updates from ebgp peers\n" "Do not prepend local-as to updates from ibgp peers\n") { @@ -5068,19 +5149,24 @@ DEFUN (neighbor_local_as_no_prepend_replace_as, if (!peer) return CMD_WARNING_CONFIG_FAILED; - as = strtoul(argv[idx_number]->arg, NULL, 10); - ret = peer_local_as_set(peer, as, 1, 1); + if (!asn_str2asn(argv[idx_number]->arg, &as)) { + vty_out(vty, "%% Invalid neighbor local-as value: %s\n", + argv[idx_number]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = peer_local_as_set(peer, as, 1, 1, argv[idx_number]->arg); return bgp_vty_return(vty, ret); } DEFUN (no_neighbor_local_as, no_neighbor_local_as_cmd, - "no neighbor <A.B.C.D|X:X::X:X|WORD> local-as [(1-4294967295) [no-prepend [replace-as]]]", + "no neighbor <A.B.C.D|X:X::X:X|WORD> local-as [ASNUM [no-prepend [replace-as]]]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Specify a local-as number\n" - "AS number used as local AS\n" + "AS number expressed in dotted or plain format used as local AS\n" "Do not prepend local-as to updates from ebgp peers\n" "Do not prepend local-as to updates from ibgp peers\n") { @@ -9020,10 +9106,13 @@ DEFPY (af_rd_vpn_export, bgp_get_default(), bgp); if (yes) { + bgp->vpn_policy[afi].tovpn_rd_pretty = + XSTRDUP(MTYPE_BGP, rd_str); bgp->vpn_policy[afi].tovpn_rd = prd; SET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); } else { + XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty); UNSET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); } @@ -9548,7 +9637,8 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, /* Auto-create assuming the same AS */ ret = bgp_get_vty(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_UNDEFINED); if (ret) { vty_out(vty, @@ -9660,7 +9750,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, if (!bgp_default) { /* Auto-create assuming the same AS */ ret = bgp_get_vty(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_UNDEFINED); if (ret) { vty_out(vty, @@ -9675,8 +9766,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, vrf_bgp = bgp_default; else /* Auto-create assuming the same AS */ - ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type); - + ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type, + NULL, ASNOTATION_UNDEFINED); if (ret) { vty_out(vty, "VRF %s is not configured as a bgp instance\n", @@ -10144,7 +10235,7 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, /* one clear bgp command to rule them all */ DEFUN (clear_ip_bgp_all, clear_ip_bgp_all_cmd, - "clear [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6|l2vpn> [<unicast|multicast|vpn|labeled-unicast|flowspec|evpn>]] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|(1-4294967295)|external|peer-group PGNAME> [<soft [<in|out>]|in [prefix-filter]|out|message-stats>]", + "clear [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6|l2vpn> [<unicast|multicast|vpn|labeled-unicast|flowspec|evpn>]] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|ASNUM|external|peer-group PGNAME> [<soft [<in|out>]|in [prefix-filter]|out|message-stats>]", CLEAR_STR IP_STR BGP_STR @@ -10157,7 +10248,7 @@ DEFUN (clear_ip_bgp_all, "BGP IPv4 neighbor to clear\n" "BGP IPv6 neighbor to clear\n" "BGP neighbor on interface to clear\n" - "Clear peers with the AS number\n" + "Clear peers with the AS number in plain or dotted format\n" "Clear all external peers\n" "Clear all members of peer-group\n" "BGP peer-group name\n" @@ -10198,7 +10289,7 @@ DEFUN (clear_ip_bgp_all, if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) argv_find_and_parse_safi(argv, argc, &idx, &safi); - /* <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group PGNAME> */ + /* <*|A.B.C.D|X:X::X:X|WORD|ASNUM|external|peer-group PGNAME> */ if (argv_find(argv, argc, "*", &idx)) { clr_sort = clear_all; } else if (argv_find(argv, argc, "A.B.C.D", &idx)) { @@ -10217,7 +10308,7 @@ DEFUN (clear_ip_bgp_all, } else if (argv_find(argv, argc, "WORD", &idx)) { clr_sort = clear_peer; clr_arg = argv[idx]->arg; - } else if (argv_find(argv, argc, "(1-4294967295)", &idx)) { + } else if (argv_find(argv, argc, "ASNUM", &idx)) { clr_sort = clear_as; clr_arg = argv[idx]->arg; } else if (argv_find(argv, argc, "external", &idx)) { @@ -10355,8 +10446,8 @@ DEFUN (show_bgp_views, /* Skip VRFs. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) continue; - vty_out(vty, "\t%s (AS%u)\n", bgp->name ? bgp->name : "(null)", - bgp->as); + vty_out(vty, "\t%s (AS%s)\n", bgp->name ? bgp->name : "(null)", + bgp->as_pretty); } return CMD_SUCCESS; @@ -11142,7 +11233,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_string_addf(json, "routerId", "%pI4", &bgp->router_id); - json_object_int_add(json, "as", bgp->as); + asn_asn2json(json, "as", bgp->as, + bgp->asnotation); json_object_int_add(json, "vrfId", vrf_id_ui); json_object_string_add( json, "vrfName", @@ -11152,8 +11244,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, : bgp->name); } else { vty_out(vty, - "BGP router identifier %pI4, local AS number %u vrf-id %d", - &bgp->router_id, bgp->as, + "BGP router identifier %pI4, local AS number %s vrf-id %d", + &bgp->router_id, bgp->as_pretty, bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id); @@ -11387,12 +11479,13 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_string_add(json_peer, "domainname", peer->domainname); - json_object_int_add(json_peer, "remoteAs", peer->as); - json_object_int_add( - json_peer, "localAs", - peer->change_local_as - ? peer->change_local_as - : peer->local_as); + asn_asn2json(json_peer, "remoteAs", peer->as, + bgp->asnotation); + asn_asn2json(json_peer, "localAs", + peer->change_local_as + ? peer->change_local_as + : peer->local_as, + bgp->asnotation); json_object_int_add(json_peer, "version", 4); json_object_int_add(json_peer, "msgRcvd", PEER_TOTAL_RX(peer)); @@ -11572,14 +11665,19 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, &peer->ibuf->count, memory_order_relaxed); - if (show_wide) + vty_out(vty, "4 "); + vty_out(vty, ASN_FORMAT_SPACE(bgp->asnotation), + &peer->as); + if (show_wide) { vty_out(vty, - "4 %10u %10u %9u %9u %8" PRIu64 - " %4zu %4zu %8s", - peer->as, + ASN_FORMAT_SPACE( + bgp->asnotation), peer->change_local_as - ? peer->change_local_as - : peer->local_as, + ? &peer->change_local_as + : &peer->local_as); + vty_out(vty, + " %9u %9u %8" PRIu64 + " %4zu %4zu %8s", PEER_TOTAL_RX(peer), PEER_TOTAL_TX(peer), peer->version[afi][safi], @@ -11588,10 +11686,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, timebuf, BGP_UPTIME_LEN, 0, NULL)); - else - vty_out(vty, "4 %10u %9u %9u %8" PRIu64 - " %4zu %4zu %8s", - peer->as, PEER_TOTAL_RX(peer), + } else { + vty_out(vty, + " %9u %9u %8" PRIu64 + " %4zu %4zu %8s", + PEER_TOTAL_RX(peer), PEER_TOTAL_TX(peer), peer->version[afi][safi], inq_count, outq_count, @@ -11599,7 +11698,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, timebuf, BGP_UPTIME_LEN, 0, NULL)); - + } if (peer_established(peer)) { if (peer->afc_recv[afi][safi]) { if (CHECK_FLAG( @@ -11892,7 +11991,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd, "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR " [" BGP_SAFI_WITH_LABEL_CMD_STR - "]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json$uj]", + "]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <ASNUM|internal|external>>] [terse] [wide] [json$uj]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR "Display the entries for all address families\n" @@ -11903,8 +12002,7 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd, "Neighbor to display information about\n" "Neighbor to display information about\n" "Neighbor on BGP configured interface\n" - "Show only the specified remote AS sessions\n" - "AS number\n" + "Show only the specified remote AS sessions\n" AS_STR "Internal (iBGP) AS sessions\n" "External (eBGP) AS sessions\n" "Shorten the information on BGP instances\n" @@ -11946,8 +12044,12 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd, as_type = AS_INTERNAL; else if (argv[idx + 1]->arg[0] == 'e') as_type = AS_EXTERNAL; - else - as = (as_t)atoi(argv[idx + 1]->arg); + else if (!asn_str2asn(argv[idx + 1]->arg, &as)) { + vty_out(vty, + "%% Invalid neighbor remote-as value: %s\n", + argv[idx + 1]->arg); + return CMD_SUCCESS; + } } if (argv_find(argv, argc, "terse", &idx)) @@ -13082,13 +13184,14 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_string_addf(json_neigh, "bgpNeighborAddr", "%pSU", &p->su); - json_object_int_add(json_neigh, "remoteAs", p->as); + asn_asn2json(json_neigh, "remoteAs", p->as, bgp->asnotation); if (p->change_local_as) - json_object_int_add(json_neigh, "localAs", - p->change_local_as); + asn_asn2json(json_neigh, "localAs", p->change_local_as, + bgp->asnotation); else - json_object_int_add(json_neigh, "localAs", p->local_as); + asn_asn2json(json_neigh, "localAs", p->local_as, + bgp->asnotation); if (CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) json_object_boolean_true_add(json_neigh, @@ -13098,13 +13201,19 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_boolean_true_add(json_neigh, "localAsReplaceAs"); } else { - if ((p->as_type == AS_SPECIFIED) || (p->as_type == AS_EXTERNAL) - || (p->as_type == AS_INTERNAL)) - vty_out(vty, "remote AS %u, ", p->as); - else + if ((p->as_type == AS_SPECIFIED) || + (p->as_type == AS_EXTERNAL) || + (p->as_type == AS_INTERNAL)) { + vty_out(vty, "remote AS "); + vty_out(vty, ASN_FORMAT(bgp->asnotation), &p->as); + vty_out(vty, ", "); + } else vty_out(vty, "remote AS Unspecified, "); - vty_out(vty, "local AS %u%s%s, ", - p->change_local_as ? p->change_local_as : p->local_as, + vty_out(vty, "local AS "); + vty_out(vty, ASN_FORMAT(bgp->asnotation), + p->change_local_as ? &p->change_local_as + : &p->local_as); + vty_out(vty, "%s%s, ", CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? " no-prepend" : "", @@ -15577,10 +15686,9 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, json_object_new_string(vname)); json_object_object_add(json, "exportToVrfs", json_export_vrfs); - json_object_string_addf(json, "routeDistinguisher", - "%pRD", - &bgp->vpn_policy[afi].tovpn_rd); - + json_object_string_addf( + json, "routeDistinguisher", "%s", + bgp->vpn_policy[afi].tovpn_rd_pretty); dir = BGP_VPN_POLICY_DIR_TOVPN; if (bgp->vpn_policy[afi].rtlist[dir]) { ecom_str = ecommunity_ecom2str( @@ -15648,8 +15756,10 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, node, vname)) vty_out(vty, " %s\n", vname); - vty_out(vty, "RD: %pRD\n", + vty_out(vty, "RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), &bgp->vpn_policy[afi].tovpn_rd); + vty_out(vty, "\n"); dir = BGP_VPN_POLICY_DIR_TOVPN; if (bgp->vpn_policy[afi].rtlist[dir]) { @@ -16014,18 +16124,22 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group, if (conf->as_type == AS_SPECIFIED || conf->as_type == AS_EXTERNAL) { if (json) - json_object_int_add(json_peer_group, "remoteAs", - conf->as); - else - vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", - group->name, conf->as); + asn_asn2json(json_peer_group, "remoteAs", conf->as, + bgp_get_asnotation(conf->bgp)); + else { + vty_out(vty, "\nBGP peer-group %s, remote AS ", + group->name); + vty_out(vty, ASN_FORMAT(bgp_get_asnotation(conf->bgp)), + &conf->as); + vty_out(vty, "\n"); + } } else if (conf->as_type == AS_INTERNAL) { if (json) - json_object_int_add(json_peer_group, "remoteAs", - group->bgp->as); + asn_asn2json(json, "remoteAs", group->bgp->as, + group->bgp->asnotation); else - vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", - group->name, group->bgp->as); + vty_out(vty, "\nBGP peer-group %s, remote AS %s\n", + group->name, group->bgp->as_pretty); } else { if (!json) vty_out(vty, "\nBGP peer-group %s\n", group->name); @@ -17148,8 +17262,8 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, } if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) - vty_out(vty, "%*srd vpn export %pRD\n", indent, "", - &bgp->vpn_policy[afi].tovpn_rd); + vty_out(vty, "%*srd vpn export %s\n", indent, "", + bgp->vpn_policy[afi].tovpn_rd_pretty); if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { @@ -17319,7 +17433,7 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, vty_out(vty, " peer-group %s", peer->group->name); if_pg_printed = true; } else if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " remote-as %u", peer->as); + vty_out(vty, " remote-as %s", peer->as_pretty); if_ras_printed = true; } else if (peer->as_type == AS_INTERNAL) { vty_out(vty, " remote-as internal"); @@ -17339,8 +17453,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); + vty_out(vty, " neighbor %s remote-as %s\n", + addr, peer->as_pretty); } else if (peer->as_type == AS_INTERNAL) { vty_out(vty, " neighbor %s remote-as internal\n", @@ -17368,8 +17482,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, if (!if_ras_printed) { if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); + vty_out(vty, " neighbor %s remote-as %s\n", + addr, peer->as_pretty); } else if (peer->as_type == AS_INTERNAL) { vty_out(vty, " neighbor %s remote-as internal\n", @@ -17384,8 +17498,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, /* local-as */ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { - vty_out(vty, " neighbor %s local-as %u", addr, - peer->change_local_as); + vty_out(vty, " neighbor %s local-as %s", addr, + peer->change_local_as_pretty); if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) vty_out(vty, " no-prepend"); if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) @@ -18066,12 +18180,16 @@ int bgp_config_write(struct vty *vty) continue; /* Router bgp ASN */ - vty_out(vty, "router bgp %u", bgp->as); + vty_out(vty, "router bgp %s", bgp->as_pretty); if (bgp->name) vty_out(vty, " %s %s", (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) ? "view" : "vrf", bgp->name); + if (CHECK_FLAG(bgp->config, BGP_CONFIG_ASNOTATION)) + vty_out(vty, " as-notation %s", + asn_mode2str(bgp->asnotation)); + vty_out(vty, "\n"); /* BGP fast-external-failover. */ @@ -18189,8 +18307,8 @@ int bgp_config_write(struct vty *vty) /* Confederation identifier*/ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) - vty_out(vty, " bgp confederation identifier %u\n", - bgp->confed_id); + vty_out(vty, " bgp confederation identifier %s\n", + bgp->confed_id_pretty); /* Confederation peer */ if (bgp->confed_peers_cnt > 0) { @@ -18199,7 +18317,8 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp confederation peers"); for (i = 0; i < bgp->confed_peers_cnt; i++) - vty_out(vty, " %u", bgp->confed_peers[i]); + vty_out(vty, " %s", + bgp->confed_peers[i].as_pretty); vty_out(vty, "\n"); } diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 56485035e8..826723b92d 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -28,6 +28,9 @@ struct bgp; BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR \ BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR +#define BGP_SELF_ORIG_CMD_STR "self-originate" +#define BGP_SELF_ORIG_HELP_STR "Display only self-originated routes\n" + #define SHOW_GR_HEADER \ "Codes: GR - Graceful Restart," \ " * - Inheriting Global GR Config,\n" \ @@ -135,7 +138,8 @@ extern void bgp_vty_init(void); extern void community_alias_vty(void); extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, - enum bgp_instance_type inst_type); + enum bgp_instance_type inst_type, const char *as_pretty, + enum asnotation_mode asnotation); extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9ea5a92adc..0fd0dafa22 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -553,7 +553,7 @@ void bgp_tcp_keepalive_unset(struct bgp *bgp) } /* BGP confederation configuration. */ -void bgp_confederation_id_set(struct bgp *bgp, as_t as) +void bgp_confederation_id_set(struct bgp *bgp, as_t as, const char *as_str) { struct peer *peer; struct listnode *node, *nnode; @@ -565,6 +565,9 @@ void bgp_confederation_id_set(struct bgp *bgp, as_t as) /* Remember - were we doing confederation before? */ already_confed = bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION); bgp->confed_id = as; + if (bgp->confed_id_pretty) + XFREE(MTYPE_BGP, bgp->confed_id_pretty); + bgp->confed_id_pretty = XSTRDUP(MTYPE_BGP, as_str); bgp_config_set(bgp, BGP_CONFIG_CONFEDERATION); /* If we were doing confederation already, this is just an external @@ -617,6 +620,7 @@ void bgp_confederation_id_unset(struct bgp *bgp) struct listnode *node, *nnode; bgp->confed_id = 0; + XFREE(MTYPE_BGP, bgp->confed_id_pretty); bgp_config_unset(bgp, BGP_CONFIG_CONFEDERATION); for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { @@ -644,14 +648,14 @@ bool bgp_confederation_peers_check(struct bgp *bgp, as_t as) return false; for (i = 0; i < bgp->confed_peers_cnt; i++) - if (bgp->confed_peers[i] == as) + if (bgp->confed_peers[i].as == as) return true; return false; } /* Add an AS to the confederation set. */ -void bgp_confederation_peers_add(struct bgp *bgp, as_t as) +void bgp_confederation_peers_add(struct bgp *bgp, as_t as, const char *as_str) { struct peer *peer; struct listnode *node, *nnode; @@ -662,11 +666,13 @@ void bgp_confederation_peers_add(struct bgp *bgp, as_t as) if (bgp_confederation_peers_check(bgp, as)) return; - bgp->confed_peers = - XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers, - (bgp->confed_peers_cnt + 1) * sizeof(as_t)); + bgp->confed_peers = XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers, + (bgp->confed_peers_cnt + 1) * + sizeof(struct as_confed)); - bgp->confed_peers[bgp->confed_peers_cnt] = as; + bgp->confed_peers[bgp->confed_peers_cnt].as = as; + bgp->confed_peers[bgp->confed_peers_cnt].as_pretty = + XSTRDUP(MTYPE_BGP, as_str); bgp->confed_peers_cnt++; if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)) { @@ -703,9 +709,15 @@ void bgp_confederation_peers_remove(struct bgp *bgp, as_t as) return; for (i = 0; i < bgp->confed_peers_cnt; i++) - if (bgp->confed_peers[i] == as) - for (j = i + 1; j < bgp->confed_peers_cnt; j++) - bgp->confed_peers[j - 1] = bgp->confed_peers[j]; + if (bgp->confed_peers[i].as == as) { + XFREE(MTYPE_BGP, bgp->confed_peers[i].as_pretty); + for (j = i + 1; j < bgp->confed_peers_cnt; j++) { + bgp->confed_peers[j - 1].as = + bgp->confed_peers[j].as; + bgp->confed_peers[j - 1].as_pretty = + bgp->confed_peers[j].as_pretty; + } + } bgp->confed_peers_cnt--; @@ -714,9 +726,9 @@ void bgp_confederation_peers_remove(struct bgp *bgp, as_t as) XFREE(MTYPE_BGP_CONFED_LIST, bgp->confed_peers); bgp->confed_peers = NULL; } else - bgp->confed_peers = - XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers, - bgp->confed_peers_cnt * sizeof(as_t)); + bgp->confed_peers = XREALLOC( + MTYPE_BGP_CONFED_LIST, bgp->confed_peers, + bgp->confed_peers_cnt * sizeof(struct as_confed)); /* Now reset any peer who's remote AS has just been removed from the CONFED */ @@ -1172,6 +1184,11 @@ static void peer_free(struct peer *peer) FOREACH_AFI_SAFI (afi, safi) bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE); + if (peer->change_local_as_pretty) + XFREE(MTYPE_BGP, peer->change_local_as_pretty); + if (peer->as_pretty) + XFREE(MTYPE_BGP, peer->as_pretty); + bgp_unlock(peer->bgp); memset(peer, 0, sizeof(struct peer)); @@ -1753,7 +1770,7 @@ void bgp_recalculate_all_bestpaths(struct bgp *bgp) struct peer *peer_create(union sockunion *su, const char *conf_if, struct bgp *bgp, as_t local_as, as_t remote_as, int as_type, struct peer_group *group, - bool config_node) + bool config_node, const char *as_str) { int active; struct peer *peer; @@ -1778,6 +1795,9 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, } peer->local_as = local_as; peer->as = remote_as; + /* internal and external values do not use as_pretty */ + if (as_str && asn_str2asn(as_str, NULL)) + peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str); peer->as_type = as_type; peer->local_id = bgp->router_id; peer->v_holdtime = bgp->default_holdtime; @@ -1881,7 +1901,8 @@ bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi) } /* Change peer's AS number. */ -void peer_as_change(struct peer *peer, as_t as, int as_specified) +void peer_as_change(struct peer *peer, as_t as, int as_specified, + const char *as_str) { enum bgp_peer_sort origtype, newtype; @@ -1896,6 +1917,12 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) } origtype = peer_sort_lookup(peer); peer->as = as; + if (as_specified == AS_SPECIFIED && as_str) { + if (peer->as_pretty) + XFREE(MTYPE_BGP, peer->as_pretty); + peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str); + } else if (peer->as_type == AS_UNSPECIFIED && peer->as_pretty) + XFREE(MTYPE_BGP, peer->as_pretty); peer->as_type = as_specified; if (bgp_config_check(peer->bgp, BGP_CONFIG_CONFEDERATION) @@ -1953,7 +1980,7 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) /* If peer does not exist, create new one. If peer already exists, set AS number to the peer. */ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, - as_t *as, int as_type) + as_t *as, int as_type, const char *as_str) { struct peer *peer; as_t local_as; @@ -2007,22 +2034,22 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, /* Existing peer's AS number change. */ if (((peer->as_type == AS_SPECIFIED) && peer->as != *as) || (peer->as_type != as_type)) - peer_as_change(peer, *as, as_type); + peer_as_change(peer, *as, as_type, as_str); } else { if (conf_if) return BGP_ERR_NO_INTERFACE_CONFIG; /* If the peer is not part of our confederation, and its not an iBGP peer then spoof the source AS */ - if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION) - && !bgp_confederation_peers_check(bgp, *as) - && bgp->as != *as) + if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION) && + !bgp_confederation_peers_check(bgp, *as) && *as && + bgp->as != *as) local_as = bgp->confed_id; else local_as = bgp->as; peer_create(su, conf_if, bgp, local_as, *as, as_type, NULL, - true); + true, as_str); } return 0; @@ -2047,6 +2074,13 @@ const char *bgp_get_name_by_role(uint8_t role) return "unknown"; } +enum asnotation_mode bgp_get_asnotation(struct bgp *bgp) +{ + if (!bgp) + return ASNOTATION_PLAIN; + return bgp->asnotation; +} + static void peer_group2peer_config_copy_af(struct peer_group *group, struct peer *peer, afi_t afi, safi_t safi) @@ -2804,7 +2838,7 @@ static void peer_group2peer_config_copy(struct peer_group *group, /* Peer group's remote AS configuration. */ int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as, - int as_type) + int as_type, const char *as_str) { struct peer_group *group; struct peer *peer; @@ -2820,12 +2854,12 @@ int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as, /* When we setup peer-group AS number all peer group member's AS number must be updated to same number. */ - peer_as_change(group->conf, *as, as_type); + peer_as_change(group->conf, *as, as_type, as_str); for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { if (((peer->as_type == AS_SPECIFIED) && peer->as != *as) || (peer->as_type != as_type)) - peer_as_change(peer, *as, as_type); + peer_as_change(peer, *as, as_type, as_str); } return 0; @@ -3132,7 +3166,7 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, } peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as, - group->conf->as_type, group, true); + group->conf->as_type, group, true, NULL); peer = peer_lock(peer); /* group->peer list reference */ listnode_add(group->peer, peer); @@ -3184,23 +3218,37 @@ static void bgp_vrf_string_name_delete(void *data) /* BGP instance creation by `router bgp' commands. */ static struct bgp *bgp_create(as_t *as, const char *name, - enum bgp_instance_type inst_type) + enum bgp_instance_type inst_type, + const char *as_pretty, + enum asnotation_mode asnotation) { struct bgp *bgp; afi_t afi; safi_t safi; bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp)); + bgp->as = *as; + if (as_pretty) + bgp->as_pretty = XSTRDUP(MTYPE_BGP, as_pretty); + else + bgp->as_pretty = XSTRDUP(MTYPE_BGP, asn_asn2asplain(*as)); + + if (asnotation != ASNOTATION_UNDEFINED) { + bgp->asnotation = asnotation; + SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION); + } else + asn_str2asn_notation(bgp->as_pretty, NULL, &bgp->asnotation); if (BGP_DEBUG(zebra, ZEBRA)) { if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) - zlog_debug("Creating Default VRF, AS %u", *as); + zlog_debug("Creating Default VRF, AS %s", + bgp->as_pretty); else - zlog_debug("Creating %s %s, AS %u", + zlog_debug("Creating %s %s, AS %s", (inst_type == BGP_INSTANCE_TYPE_VRF) ? "VRF" : "VIEW", - name, *as); + name, bgp->as_pretty); } /* Default the EVPN VRF to the default one */ @@ -3277,7 +3325,6 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->condition_check_period = DEFAULT_CONDITIONAL_ROUTES_POLL_TIME; bgp_addpath_init_bgp_data(&bgp->tx_addpath); bgp->fast_convergence = false; - bgp->as = *as; bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME; #ifdef ENABLE_BGP_VNC @@ -3514,7 +3561,8 @@ int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, const char *name, /* Called from VTY commands. */ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, - enum bgp_instance_type inst_type) + enum bgp_instance_type inst_type, const char *as_pretty, + enum asnotation_mode asnotation) { struct bgp *bgp; struct vrf *vrf = NULL; @@ -3524,7 +3572,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, if (ret || *bgp_val) return ret; - bgp = bgp_create(as, name, inst_type); + bgp = bgp_create(as, name, inst_type, as_pretty, asnotation); /* * view instances will never work inside of a vrf @@ -3918,8 +3966,13 @@ void bgp_free(struct bgp *bgp) dir = BGP_VPN_POLICY_DIR_TOVPN; if (bgp->vpn_policy[afi].rtlist[dir]) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); + if (bgp->vpn_policy[afi].tovpn_rd_pretty) + XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty); } + bgp_confederation_id_unset(bgp); + + XFREE(MTYPE_BGP, bgp->as_pretty); XFREE(MTYPE_BGP, bgp->name); XFREE(MTYPE_BGP, bgp->name_pretty); XFREE(MTYPE_BGP, bgp->snmp_stats); @@ -4024,7 +4077,7 @@ struct peer *peer_create_bind_dynamic_neighbor(struct bgp *bgp, /* Create peer first; we've already checked group config is valid. */ peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as, - group->conf->as_type, group, true); + group->conf->as_type, group, true, NULL); if (!peer) return NULL; @@ -4583,6 +4636,9 @@ void bgp_shutdown_enable(struct bgp *bgp, const char *msg) /* Disable global administrative shutdown of all peers of BGP instance */ void bgp_shutdown_disable(struct bgp *bgp) { + const struct listnode *node; + struct peer *peer; + /* do nothing if not shut down. */ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) return; @@ -4593,6 +4649,9 @@ void bgp_shutdown_disable(struct bgp *bgp) /* clear the BGP instances shutdown flag */ UNSET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN); + + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) + bgp_timer_set(peer); } /* Change specified peer flag. */ @@ -6228,7 +6287,7 @@ int peer_allowas_in_unset(struct peer *peer, afi_t afi, safi_t safi) } int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, - bool replace_as) + bool replace_as, const char *as_str) { bool old_no_prepend, old_replace_as; struct bgp *bgp = peer->bgp; @@ -6253,6 +6312,9 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, && old_replace_as == replace_as) return 0; peer->change_local_as = as; + if (as_str) + peer->change_local_as_pretty = XSTRDUP(MTYPE_BGP, as_str); + (void)peer_sort(peer); /* Check if handling a regular peer. */ @@ -6286,6 +6348,9 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, COND_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS, replace_as); member->change_local_as = as; + if (as_str) + member->change_local_as_pretty = + XSTRDUP(MTYPE_BGP, as_str); } return 0; @@ -6311,6 +6376,7 @@ int peer_local_as_unset(struct peer *peer) peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND); peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS); peer->change_local_as = 0; + XFREE(MTYPE_BGP, peer->change_local_as_pretty); } /* Check if handling a regular peer. */ @@ -6341,6 +6407,7 @@ int peer_local_as_unset(struct peer *peer) UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); member->change_local_as = 0; + XFREE(MTYPE_BGP, member->change_local_as_pretty); /* Send notification or stop peer depending on state. */ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status)) { @@ -8024,7 +8091,7 @@ static void bgp_instasn_autocomplete(vector comps, struct cmd_token *token) { struct listnode *next, *next2; struct bgp *bgp, *bgp2; - char buf[11]; + char buf[ASN_STRING_MAX_SIZE]; for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) { /* deduplicate */ @@ -8037,7 +8104,7 @@ static void bgp_instasn_autocomplete(vector comps, struct cmd_token *token) if (bgp2 != bgp) continue; - snprintf(buf, sizeof(buf), "%u", bgp->as); + snprintf(buf, sizeof(buf), "%s", bgp->as_pretty); vector_set(comps, XSTRDUP(MTYPE_COMPLETION, buf)); } } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b5faa4d353..2a7c7a3143 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -16,6 +16,7 @@ #include "vty.h" #include "srv6.h" #include "iana_afi.h" +#include "asn.h" /* For union sockunion. */ #include "queue.h" @@ -62,7 +63,6 @@ enum zebra_gr_mode { }; /* Typedef BGP specific types. */ -typedef uint32_t as_t; typedef uint16_t as16_t; /* we may still encounter 16 Bit asnums */ typedef uint16_t bgp_size_t; @@ -203,6 +203,7 @@ struct vpn_policy { /* should be mpls_label_t? */ uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */ uint32_t tovpn_zebra_vrf_label_last_sent; + char *tovpn_rd_pretty; struct prefix_rd tovpn_rd; struct prefix tovpn_nexthop; /* unset => set to 0 */ uint32_t flags; @@ -324,10 +325,16 @@ struct bgp_srv6_function { char locator_name[SRV6_LOCNAME_SIZE]; }; +struct as_confed { + as_t as; + char *as_pretty; +}; + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ as_t as; + char *as_pretty; /* Name of this BGP instance. */ char *name; @@ -383,6 +390,7 @@ struct bgp { uint16_t config; #define BGP_CONFIG_CLUSTER_ID (1 << 0) #define BGP_CONFIG_CONFEDERATION (1 << 1) +#define BGP_CONFIG_ASNOTATION (1 << 2) /* BGP router identifier. */ struct in_addr router_id; @@ -394,7 +402,8 @@ struct bgp { /* BGP confederation information. */ as_t confed_id; - as_t *confed_peers; + char *confed_id_pretty; + struct as_confed *confed_peers; int confed_peers_cnt; struct thread @@ -728,6 +737,7 @@ struct bgp { /* RD for this VRF */ struct prefix_rd vrf_prd; + char *vrf_prd_pretty; /* import rt list for the vrf instance */ struct list *vrf_import_rtl; @@ -782,6 +792,8 @@ struct bgp { bool allow_martian; + enum asnotation_mode asnotation; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(bgp); @@ -1105,6 +1117,8 @@ struct peer { /* Peer's remote AS number. */ int as_type; as_t as; + /* for vty as format */ + char *as_pretty; /* Peer's local AS number. */ as_t local_as; @@ -1113,6 +1127,8 @@ struct peer { /* Peer's Change local AS number. */ as_t change_local_as; + /* for vty as format */ + char *change_local_as_pretty; /* Remote router ID. */ struct in_addr remote_id; @@ -1270,7 +1286,7 @@ struct peer { * so if a flag is unset, the corresponding override flag is unset too. * However if a flag is set, the corresponding override flag is set. */ - uint32_t flags_override; + uint64_t flags_override; /* * Parallel array to flags that indicates whether the default behavior * of *flags_override* should be inverted. If a flag is unset and the @@ -1308,11 +1324,13 @@ struct peer { * inversion state of the flag differs between peer and peer-group, the * newly set value must equal to the inverted state of the peer-group. */ - uint32_t flags_invert; + uint64_t flags_invert; /* * Effective array for storing the peer/peer-group flags. In case of a * peer-group, the peer-specific overrides (see flags_override and * flags_invert) must be respected. + * When changing the structure of flags/af_flags, do not forget to + * change flags_invert/flags_override too. */ uint64_t flags; #define PEER_FLAG_PASSIVE (1ULL << 0) /* passive mode */ @@ -2129,7 +2147,7 @@ extern void bgp_recalculate_all_bestpaths(struct bgp *bgp); extern struct peer *peer_create(union sockunion *su, const char *conf_if, struct bgp *bgp, as_t local_as, as_t remote_as, int as_type, struct peer_group *group, - bool config_node); + bool config_node, const char *as_str); extern struct peer *peer_create_accept(struct bgp *); extern void peer_xfer_config(struct peer *dst, struct peer *src); extern char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, @@ -2156,7 +2174,9 @@ extern void bgp_option_norib_set_runtime(void); /* unset the bgp no-rib option during runtime and reset all peers */ extern void bgp_option_norib_unset_runtime(void); -extern int bgp_get(struct bgp **, as_t *, const char *, enum bgp_instance_type); +extern int bgp_get(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type kind, const char *as_pretty, + enum asnotation_mode asnotation); extern void bgp_instance_up(struct bgp *); extern void bgp_instance_down(struct bgp *); extern int bgp_delete(struct bgp *); @@ -2172,11 +2192,13 @@ extern void bgp_suppress_fib_pending_set(struct bgp *bgp, bool set); extern void bgp_cluster_id_set(struct bgp *bgp, struct in_addr *cluster_id); extern void bgp_cluster_id_unset(struct bgp *bgp); -extern void bgp_confederation_id_set(struct bgp *bgp, as_t as); +extern void bgp_confederation_id_set(struct bgp *bgp, as_t as, + const char *as_str); extern void bgp_confederation_id_unset(struct bgp *bgp); extern bool bgp_confederation_peers_check(struct bgp *, as_t); -extern void bgp_confederation_peers_add(struct bgp *bgp, as_t as); +extern void bgp_confederation_peers_add(struct bgp *bgp, as_t as, + const char *as_str); extern void bgp_confederation_peers_remove(struct bgp *bgp, as_t as); extern void bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime, @@ -2197,10 +2219,13 @@ extern void bgp_listen_limit_unset(struct bgp *bgp); extern bool bgp_update_delay_active(struct bgp *); extern bool bgp_update_delay_configured(struct bgp *); extern bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi); -extern void peer_as_change(struct peer *, as_t, int); -extern int peer_remote_as(struct bgp *, union sockunion *, const char *, as_t *, - int); -extern int peer_group_remote_as(struct bgp *, const char *, as_t *, int); +extern void peer_as_change(struct peer *peer, as_t as, int as_type, + const char *as_str); +extern int peer_remote_as(struct bgp *bgp, union sockunion *su, + const char *conf_if, as_t *as, int as_type, + const char *as_str); +extern int peer_group_remote_as(struct bgp *bgp, const char *peer_str, as_t *as, + int as_type, const char *as_str); extern int peer_delete(struct peer *peer); extern void peer_notify_unconfig(struct peer *peer); extern int peer_group_delete(struct peer_group *); @@ -2279,8 +2304,8 @@ extern int peer_distribute_unset(struct peer *, afi_t, safi_t, int); extern int peer_allowas_in_set(struct peer *, afi_t, safi_t, int, int); extern int peer_allowas_in_unset(struct peer *, afi_t, safi_t); -extern int peer_local_as_set(struct peer *, as_t, bool no_prepend, - bool replace_as); +extern int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, + bool replace_as, const char *as_str); extern int peer_local_as_unset(struct peer *); extern int peer_prefix_list_set(struct peer *, afi_t, safi_t, int, @@ -2339,6 +2364,7 @@ extern void peer_tx_shutdown_message_unset(struct peer *); extern void bgp_route_map_update_timer(struct thread *thread); extern const char *bgp_get_name_by_role(uint8_t role); +extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp); extern void bgp_route_map_terminate(void); diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 40cb8f8d5a..5b6961d18a 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -410,6 +410,7 @@ DEFUN (vnc_defaults_rd, } else { + /* TODO: save RD format */ ret = str2prefix_rd(argv[1]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed rd\n"); @@ -2874,6 +2875,7 @@ DEFUN (vnc_nve_group_rd, } else { + /* TODO: save RD format */ ret = str2prefix_rd(argv[1]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed rd\n"); @@ -3346,6 +3348,7 @@ DEFUN (vnc_vrf_policy_rd, } else { + /* TODO: save RD format */ ret = str2prefix_rd(argv[1]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed rd\n"); @@ -3924,7 +3927,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) value); } else - vty_out(vty, " rd %pRD\n", &rfg->rd); + vty_out(vty, " rd %pRDP\n", &rfg->rd); } if (rfg->rt_import_list && rfg->rt_export_list @@ -4144,7 +4147,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) value); } else - vty_out(vty, " rd %pRD\n", + vty_out(vty, " rd %pRDP\n", &hc->default_rd); } if (hc->default_response_lifetime @@ -4224,7 +4227,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) value); } else - vty_out(vty, " rd %pRD\n", + vty_out(vty, " rd %pRDP\n", &rfg->rd); } if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME) { diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 0c8fdc72e0..8d6db9d775 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -363,7 +363,7 @@ void del_vnc_route(struct rfapi_descriptor *rfd, bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); vnc_zlog_debug_verbose( - "%s: peer=%p, prefix=%pFX, prd=%pRD afi=%d, safi=%d bn=%p, bn->info=%p", + "%s: peer=%p, prefix=%pFX, prd=%pRDP afi=%d, safi=%d bn=%p, bn->info=%p", __func__, peer, p, prd, afi, safi, bn, (bn ? bgp_dest_get_bgp_path_info(bn) : NULL)); @@ -1053,7 +1053,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ bgp_process(bgp, bn, afi, safi); vnc_zlog_debug_any( - "%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%pRD)", + "%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%pRDP)", __func__, safi2str(safi), buf, bn, prd); done: @@ -3712,7 +3712,7 @@ int rfapi_set_autord_from_vn(struct prefix_rd *rd, struct rfapi_ip_addr *vn) memcpy(rd->val + 2, &vn->addr.v6.s6_addr32[3], 4); /* low order 4 bytes */ } - vnc_zlog_debug_verbose("%s: auto-RD is set to %pRD", __func__, rd); + vnc_zlog_debug_verbose("%s: auto-RD is set to %pRDP", __func__, rd); return 0; } diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 6d4fc53f83..5c68545398 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -2082,7 +2082,7 @@ static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */ assert(bpi); assert(bpi->extra); - vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRD", __func__, bpi, + vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, bpi->peer, &bpi->extra->vnc.import.rd); sl = RFAPI_RDINDEX_W_ALLOC(rn); @@ -2120,7 +2120,9 @@ static void rfapiItBiIndexDump(struct agg_node *rn) char buf[RD_ADDRSTRLEN]; char buf_aux_pfx[PREFIX_STRLEN]; - prefix_rd2str(&k->extra->vnc.import.rd, buf, sizeof(buf)); + prefix_rd2str( + &k->extra->vnc.import.rd, buf, sizeof(buf), + bgp_get_asnotation(k->peer ? k->peer->bgp : NULL)); if (k->extra->vnc.import.aux_prefix.family) { prefix2str(&k->extra->vnc.import.aux_prefix, buf_aux_pfx, sizeof(buf_aux_pfx)); @@ -2158,7 +2160,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( strlcpy(buf_aux_pfx, "(nil)", sizeof(buf_aux_pfx)); vnc_zlog_debug_verbose( - "%s want prd=%pRD, peer=%p, aux_prefix=%s", __func__, + "%s want prd=%pRDP, peer=%p, aux_prefix=%s", __func__, prd, peer, buf_aux_pfx); rfapiItBiIndexDump(rn); } @@ -2174,7 +2176,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( bpi_result = bpi_result->next) { #ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose( - "%s: bpi has prd=%pRD, peer=%p", __func__, + "%s: bpi has prd=%pRDP, peer=%p", __func__, &bpi_result->extra->vnc.import.rd, bpi_result->peer); #endif @@ -2238,7 +2240,7 @@ static void rfapiItBiIndexDel(struct agg_node *rn, /* Import table VPN node */ struct skiplist *sl; int rc; - vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRD", __func__, bpi, + vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, bpi->peer, &bpi->extra->vnc.import.rd); sl = RFAPI_RDINDEX(rn); @@ -2335,8 +2337,7 @@ static void rfapiMonitorEncapDelete(struct bgp_path_info *vpn_bpi) } /* - * quagga lib/thread.h says this must return int even though - * it doesn't do anything with the return value + * Timer callback for withdraw */ static void rfapiWithdrawTimerVPN(struct thread *t) { @@ -2346,19 +2347,27 @@ static void rfapiWithdrawTimerVPN(struct thread *t) const struct prefix *p; struct rfapi_monitor_vpn *moved; afi_t afi; + bool early_exit = false; if (bgp == NULL) { vnc_zlog_debug_verbose( "%s: NULL BGP pointer, assume shutdown race condition!!!", __func__); - return; + early_exit = true; } - if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + if (bgp && CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { vnc_zlog_debug_verbose( "%s: BGP delete in progress, assume shutdown race condition!!!", __func__); + early_exit = true; + } + + /* This callback is responsible for the withdraw object's memory */ + if (early_exit) { + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); return; } + assert(wcb->node); assert(bpi); assert(wcb->import_table); diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index b15620741b..f727f24f1d 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -1114,7 +1114,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, skiplist_insert(slRibPt, &ori->rk, ori); vnc_zlog_debug_verbose( - "%s: nomatch lPendCost item %p in slRibPt, added (rd=%pRD)", + "%s: nomatch lPendCost item %p in slRibPt, added (rd=%pRDP)", __func__, ri, &ori->rk.rd); } @@ -1356,7 +1356,7 @@ callback: ri->last_sent_time = monotime(NULL); #if DEBUG_RIB_SL_RD vnc_zlog_debug_verbose( - "%s: move route to recently deleted list, rd=%pRD", + "%s: move route to recently deleted list, rd=%pRDP", __func__, &ri->rk.rd); #endif @@ -2252,7 +2252,7 @@ static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty, } #endif - fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %pRD\n", + fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %pRDP\n", deleted ? 'r' : ' ', *printedprefix ? "" : str_pfx, str_vn, str_un, ri->cost, str_lifetime, str_age, &ri->rk.rd); diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 0144c28d58..2b768b8f8d 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -1589,7 +1589,7 @@ void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd) vty_out(vty, " "); rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr); vty_out(vty, " %p %p ", rfd->response_cb, rfd->cookie); - vty_out(vty, "%pRD", &rfd->rd); + vty_out(vty, "%pRDP", &rfd->rd); vty_out(vty, " %d", rfd->response_lifetime); vty_out(vty, " %s", (rfd->rfg ? rfd->rfg->name : "<orphaned>")); vty_out(vty, "%s", HVTYNL); @@ -4709,6 +4709,7 @@ static int vnc_add_vrf_prefix(struct vty *vty, const char *arg_vrf, if (arg_rd) { opt = &optary[cur_opt++]; opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; + /* TODO: save RD format */ if (!str2prefix_rd(arg_rd, &opt->v.internal_rd)) { vty_out(vty, "Malformed RD \"%s\"\n", arg_rd); return CMD_WARNING_CONFIG_FAILED; diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index e7cc278df0..5449220448 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -386,7 +386,6 @@ void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn, * withdraw the route */ bgp_withdraw(bpi->peer, p, 0, /* addpath_id */ - NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ NULL, 0, NULL); /* tag not used for unicast */ @@ -478,7 +477,6 @@ static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi) bgp_withdraw( ri->peer, bgp_dest_get_prefix(dest), 0, /* addpath_id */ - NULL, /* ignored */ AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, @@ -863,7 +861,6 @@ void vnc_direct_bgp_del_prefix(struct bgp *bgp, bgp_withdraw(irfd->peer, p, /* prefix */ 0, /* addpath_id */ - NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ @@ -893,7 +890,6 @@ void vnc_direct_bgp_del_prefix(struct bgp *bgp, bgp_withdraw(irfd->peer, p, /* prefix */ 0, /* addpath_id */ - NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ @@ -1130,7 +1126,6 @@ void vnc_direct_bgp_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) bgp_withdraw(irfd->peer, p, /* prefix */ 0, /* addpath_id */ - NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, @@ -1362,7 +1357,6 @@ static void vnc_direct_del_rn_group_rd(struct bgp *bgp, bgp_withdraw(irfd->peer, agg_node_get_prefix(rn), /* prefix */ 0, /* addpath_id */ - NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ NULL, 0, NULL); /* tag not used for unicast */ @@ -1481,7 +1475,6 @@ static void vnc_direct_bgp_unexport_table(afi_t afi, struct agg_table *rt, bgp_withdraw(irfd->peer, agg_node_get_prefix(rn), 0, /* addpath_id */ - NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, @@ -1720,7 +1713,6 @@ static void vncExportWithdrawTimer(struct thread *t) * withdraw the route */ bgp_withdraw(eti->peer, p, 0, /* addpath_id */ - NULL, /* attr, ignored */ family2afi(p->family), SAFI_UNICAST, eti->type, eti->subtype, NULL, /* RD not used for unicast */ NULL, 0, @@ -2005,7 +1997,6 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) bgp_withdraw(ri->peer, dest_p, /* prefix */ 0, /* addpath_id */ - NULL, /* ignored */ AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index 5d1dda06df..2a08531bd7 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -151,6 +151,7 @@ by the parser. : RANGE : MAC : MAC_PREFIX + : ASNUM selector: "<" `selector_seq_seq` ">" `varname_token` : "{" `selector_seq_seq` "}" `varname_token` : "[" `selector_seq_seq` "]" `varname_token` @@ -176,27 +177,29 @@ parser, but this is merely a dumb copy job. Here is a brief summary of the various token types along with examples. -+-----------------+-------------------+-------------------------------------------------------------+ -| Token type | Syntax | Description | -+=================+===================+=============================================================+ -| ``WORD`` | ``show ip bgp`` | Matches itself. In the given example every token is a WORD. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``IPV4`` | ``A.B.C.D`` | Matches an IPv4 address. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``IPV6`` | ``X:X::X:X`` | Matches an IPv6 address. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``IPV4_PREFIX`` | ``A.B.C.D/M`` | Matches an IPv4 prefix in CIDR notation. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``IPV6_PREFIX`` | ``X:X::X:X/M`` | Matches an IPv6 prefix in CIDR notation. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``MAC`` | ``X:X:X:X:X:X`` | Matches a 48-bit mac address. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``MAC_PREFIX`` | ``X:X:X:X:X:X/M`` | Matches a 48-bit mac address with a mask. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``VARIABLE`` | ``FOOBAR`` | Matches anything. | -+-----------------+-------------------+-------------------------------------------------------------+ -| ``RANGE`` | ``(X-Y)`` | Matches numbers in the range X..Y inclusive. | -+-----------------+-------------------+-------------------------------------------------------------+ ++-----------------+-------------------------+-------------------------------------------------------+ +| Token type | Syntax | Description | ++=================+=========================+=======================================================+ +| ``WORD`` | ``show ip bgp`` | Matches itself. In the example every token is a WORD. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``IPV4`` | ``A.B.C.D`` | Matches an IPv4 address. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``IPV6`` | ``X:X::X:X`` | Matches an IPv6 address. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``IPV4_PREFIX`` | ``A.B.C.D/M`` | Matches an IPv4 prefix in CIDR notation. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``IPV6_PREFIX`` | ``X:X::X:X/M`` | Matches an IPv6 prefix in CIDR notation. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``MAC`` | ``X:X:X:X:X:X`` | Matches a 48-bit mac address. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``MAC_PREFIX`` | ``X:X:X:X:X:X/M`` | Matches a 48-bit mac address with a mask. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``VARIABLE`` | ``FOOBAR`` | Matches anything. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``RANGE`` | ``(X-Y)`` | Matches numbers in the range X..Y inclusive. | ++-----------------+-------------------------+-------------------------------------------------------+ +| ``ASNUM`` | ``<A.B|(1-4294967295>`` | Matches an AS in plain or dot format. | ++-----------------+-------------------------+-------------------------------------------------------+ When presented with user input, the parser will search over all defined commands in the current context to find a match. It is aware of the various diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index c5319a8e88..1798f1ad2a 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -254,8 +254,9 @@ ASN and Router ID ----------------- First of all you must configure BGP router with the :clicmd:`router bgp ASN` -command. The AS number is an identifier for the autonomous system. The BGP -protocol uses the AS number for detecting whether the BGP connection is +command. The AS number is an identifier for the autonomous system. The AS +identifier can either be a number or two numbers separated by a period. The +BGP protocol uses the AS identifier for detecting whether the BGP connection is internal or external. .. clicmd:: router bgp ASN @@ -2113,9 +2114,6 @@ is 4 octet long. The following format is used to define the community value. ``7675:80`` can be used when AS 7675 wants to pass local policy value 80 to neighboring peer. -``internet`` - ``internet`` represents well-known communities value 0. - ``graceful-shutdown`` ``graceful-shutdown`` represents well-known communities value ``GRACEFUL_SHUTDOWN`` ``0xFFFF0000`` ``65535:0``. :rfc:`8326` implements @@ -2486,17 +2484,6 @@ community-list. match community FILTER -The communities value keyword ``internet`` has special meanings in standard -community lists. In the below example ``internet`` matches all BGP routes even -if the route does not have communities attribute at all. So community list -``INTERNET`` is the same as ``FILTER`` in the previous example. - -.. code-block:: frr - - bgp community-list standard INTERNET deny 1:1 - bgp community-list standard INTERNET permit internet - - The following configuration is an example of communities value deletion. With this configuration the community values ``100:1`` and ``100:2`` are removed from BGP updates. For communities value deletion, only ``permit`` @@ -2566,9 +2553,6 @@ Extended Community Lists there is no matched entry, deny will be returned. When `extcommunity` is empty it matches to any routes. - A special handling for ``internet`` community is applied. It matches - any community. - .. clicmd:: bgp extcommunity-list expanded NAME permit|deny LINE This command defines a new expanded extcommunity-list. `line` is a string @@ -3899,6 +3883,10 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. EVPN prefixes can also be filtered by EVPN route type. +.. clicmd:: show bgp l2vpn evpn route [detail] [type <ead|1|macip|2|multicast|3|es|4|prefix|5>] self-originate [json] + + Display self-originated EVPN prefixes which can also be filtered by EVPN route type. + .. clicmd:: show bgp vni <all|VNI> [vtep VTEP] [type <ead|1|macip|2|multicast|3>] [<detail|json>] Display per-VNI EVPN routing table in bgp. Filter route-type, vtep, or VNI. @@ -4055,6 +4043,15 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. If the ``json`` option is specified, output is displayed in JSON format. +.. clicmd:: show [ip] bgp [afi] [safi] [all] self-originate [wide|json] + + Display self-originated routes. + + If ``wide`` option is specified, then the prefix table's width is increased + to fully display the prefix and the nexthop. + + If the ``json`` option is specified, output is displayed in JSON format. + .. clicmd:: show [ip] bgp [afi] [safi] [all] neighbors A.B.C.D [advertised-routes|received-routes|filtered-routes] [<A.B.C.D/M|X:X::X:X/M> | detail] [json|wide] Display the routes advertised to a BGP neighbor or received routes @@ -4301,6 +4298,26 @@ Segment-Routing IPv6 vpn_policy[AFI_IP].tovpn_sid: none vpn_policy[AFI_IP6].tovpn_sid: 2001:db8:1:1::200 +AS-notation support +------------------- + +By default, the ASN value output follows how the BGP ASN instance is +expressed in the configuration. Three as-notation outputs are available: + +- plain output: both AS4B and AS2B use a single number. + ` router bgp 65536`. + +- dot output: AS4B values are using two numbers separated by a period. + `router bgp 1.1` means that the AS number is 65536. + +- dot+ output: AS2B and AS4B values are using two numbers separated by a + period. `router bgp 0.5` means that the AS number is 5. + +The below option permits forcing the as-notation output: + +.. clicmd:: router bgp ASN as-notation dot|dot+|plain + + The chosen as-notation format will override the BGP ASN output. .. _bgp-route-reflector: diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index b69230b99d..ce3648bf6d 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -794,13 +794,13 @@ Showing Information .. clicmd:: show ip ospf neighbor [json] -.. clicmd:: show ip ospf neighbor INTERFACE [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] neighbor INTERFACE [json] .. clicmd:: show ip ospf neighbor detail [json] -.. clicmd:: show ip ospf neighbor A.B.C.D [detail] [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] neighbor A.B.C.D [detail] [json] -.. clicmd:: show ip ospf neighbor INTERFACE detail [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] neighbor INTERFACE detail [json] Display lsa information of LSDB. Json o/p of this command covers base route information diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst index 5c60f4f4f1..67323e61f3 100644 --- a/doc/user/ripd.rst +++ b/doc/user/ripd.rst @@ -88,7 +88,7 @@ multipath routing. RIP Configuration ================= -.. clicmd:: router rip +.. clicmd:: router rip [vrf NAME] The `router rip` command is necessary to enable RIP. To disable RIP, use the `no router rip` command. RIP must be enabled before carrying out any of the @@ -149,13 +149,16 @@ RIP Configuration The default is to be passive on all interfaces. -.. clicmd:: ip split-horizon +.. clicmd:: ip split-horizon [poisoned-reverse] Control split-horizon on the interface. Default is `ip split-horizon`. If you don't perform split-horizon on the interface, please specify `no ip split-horizon`. + If `poisoned-reverse` is also set, the router sends the poisoned routes + with highest metric back to the sending router. + .. _rip-version-control: RIP Version Control @@ -174,8 +177,8 @@ discussion on the security implications of RIPv1 see :ref:`rip-authentication`. .. clicmd:: version VERSION - Set RIP version to accept for reads and send. ``VERSION`` can be either 1 or - 1. + Set RIP version to accept for reads and send. VERSION can be either + ``1`` or ``2``. Disabling RIPv1 by specifying version 2 is STRONGLY encouraged, :ref:`rip-authentication`. This may become the default in a future release. @@ -472,7 +475,7 @@ Show RIP Information To display RIP routes. -.. clicmd:: show ip rip +.. clicmd:: show ip rip [vrf NAME] Show RIP routes. @@ -481,7 +484,7 @@ through RIP, this command will display the time the packet was sent and the tag information. This command will also display this information for routes redistributed into RIP. -.. clicmd:: show ip rip status +.. clicmd:: show ip rip [vrf NAME] status The command displays current RIP status. It includes RIP timer, filtering, version, RIP enabled interface and RIP peer information. diff --git a/doc/user/ripngd.rst b/doc/user/ripngd.rst index c7ca22bf95..df7a0e249e 100644 --- a/doc/user/ripngd.rst +++ b/doc/user/ripngd.rst @@ -22,14 +22,10 @@ ripngd Configuration Currently ripngd supports the following commands: -.. clicmd:: router ripng +.. clicmd:: router ripng [vrf NAME] Enable RIPng. -.. clicmd:: flush_timer TIME - - Set flush timer. - .. clicmd:: network NETWORK Set RIPng enabled interface by NETWORK. @@ -48,7 +44,7 @@ Currently ripngd supports the following commands: ripngd Terminal Mode Commands ============================= -.. clicmd:: show ip ripng +.. clicmd:: show ipv6 ripng [vrf NAME] status .. clicmd:: show debugging ripng diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index e7e2e5d2ed..d7e768b710 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -974,7 +974,7 @@ unicast topology! Unreachable routes do not receive special treatment and do not cause fallback to a second lookup. -.. clicmd:: show ip rpf ADDR +.. clicmd:: show [ip|ipv6] rpf ADDR Performs a Multicast RPF lookup, as configured with ``ip multicast rpf-lookup-mode MODE``. ADDR specifies the multicast source address to look @@ -984,7 +984,6 @@ unicast topology! > show ip rpf 192.0.2.1 Routing entry for 192.0.2.0/24 using Unicast RIB - Known via "kernel", distance 0, metric 0, best * 198.51.100.1, via eth0 @@ -992,7 +991,7 @@ unicast topology! Indicates that a multicast source lookup for 192.0.2.1 would use an Unicast RIB entry for 192.0.2.0/24 with a gateway of 198.51.100.1. -.. clicmd:: show ip rpf +.. clicmd:: show [ip|ipv6] rpf Prints the entire Multicast RIB. Note that this is independent of the configured RPF lookup mode, the Multicast RIB may be printed yet not diff --git a/ldpd/address.c b/ldpd/address.c index 45d1bb14b8..107eb5d780 100644 --- a/ldpd/address.c +++ b/ldpd/address.c @@ -72,12 +72,13 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, if ((buf = ibuf_open(size)) == NULL) fatal(__func__); - err |= gen_ldp_hdr(buf, size); + SET_FLAG(err, gen_ldp_hdr(buf, size)); size -= LDP_HDR_SIZE; - err |= gen_msg_hdr(buf, msg_type, size); + SET_FLAG(err, gen_msg_hdr(buf, msg_type, size)); size -= LDP_MSG_SIZE; - err |= gen_address_list_tlv(buf, af, addr_list, tlv_addr_count); + SET_FLAG(err, gen_address_list_tlv(buf, af, addr_list, tlv_addr_count)); (void)size; + if (err) { address_list_clr(addr_list); ibuf_free(buf); @@ -158,10 +159,12 @@ send_mac_withdrawal(struct nbr *nbr, struct map *fec, uint8_t *mac) err = gen_ldp_hdr(buf, size); size -= LDP_HDR_SIZE; - err |= gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size); - err |= gen_address_list_tlv(buf, AF_INET, NULL, 0); - err |= gen_fec_tlv(buf, fec); - err |= gen_mac_list_tlv(buf, mac); + + SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size)); + SET_FLAG(err, gen_address_list_tlv(buf, AF_INET, NULL, 0)); + SET_FLAG(err, gen_fec_tlv(buf, fec)); + SET_FLAG(err, gen_mac_list_tlv(buf, mac)); + if (err) { ibuf_free(buf); return; @@ -340,12 +343,14 @@ gen_address_list_tlv(struct ibuf *buf, int af, struct if_addr_head *addr_list, } alt.length = htons(sizeof(alt.family) + addr_size * tlv_addr_count); - err |= ibuf_add(buf, &alt, sizeof(alt)); + SET_FLAG(err, ibuf_add(buf, &alt, sizeof(alt))); + if (addr_list == NULL) return (err); LIST_FOREACH(if_addr, addr_list, entry) { - err |= ibuf_add(buf, &if_addr->addr, addr_size); + SET_FLAG(err, ibuf_add(buf, &if_addr->addr, addr_size)); + if (--tlv_addr_count == 0) break; } @@ -365,7 +370,7 @@ gen_mac_list_tlv(struct ibuf *buf, uint8_t *mac) tlv.length = htons(ETH_ALEN); err = ibuf_add(buf, &tlv, sizeof(tlv)); if (mac) - err |= ibuf_add(buf, mac, ETH_ALEN); + SET_FLAG(err, ibuf_add(buf, mac, ETH_ALEN)); return (err); } diff --git a/ldpd/lde.c b/ldpd/lde.c index edd13fed35..325b33d057 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -261,8 +261,7 @@ static void lde_dispatch_imsg(struct thread *thread) case IMSG_LABEL_MAPPING_FULL: ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { - log_debug("%s: cannot find lde neighbor", - __func__); + log_debug("%s: cannot find lde neighbor", __func__); break; } @@ -273,15 +272,13 @@ static void lde_dispatch_imsg(struct thread *thread) case IMSG_LABEL_RELEASE: case IMSG_LABEL_WITHDRAW: case IMSG_LABEL_ABORT: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct map)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map)) fatalx("lde_dispatch_imsg: wrong imsg len"); map = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { - log_debug("%s: cannot find lde neighbor", - __func__); + log_debug("%s: cannot find lde neighbor", __func__); break; } @@ -304,49 +301,45 @@ static void lde_dispatch_imsg(struct thread *thread) } break; case IMSG_ADDRESS_ADD: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct lde_addr)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_addr)) fatalx("lde_dispatch_imsg: wrong imsg len"); lde_addr = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { - log_debug("%s: cannot find lde neighbor", - __func__); + log_debug("%s: cannot find lde neighbor", __func__); break; } + if (lde_address_add(ln, lde_addr) < 0) { log_debug("%s: cannot add address %s, it already exists", __func__, log_addr(lde_addr->af, &lde_addr->addr)); } break; case IMSG_ADDRESS_DEL: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct lde_addr)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_addr)) fatalx("lde_dispatch_imsg: wrong imsg len"); lde_addr = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { - log_debug("%s: cannot find lde neighbor", - __func__); + log_debug("%s: cannot find lde neighbor", __func__); break; } + if (lde_address_del(ln, lde_addr) < 0) { log_debug("%s: cannot delete address %s, it does not exist", __func__, log_addr(lde_addr->af, &lde_addr->addr)); } break; case IMSG_NOTIFICATION: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct notify_msg)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg)) fatalx("lde_dispatch_imsg: wrong imsg len"); nm = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { - log_debug("%s: cannot find lde neighbor", - __func__); + log_debug("%s: cannot find lde neighbor", __func__); break; } @@ -366,8 +359,7 @@ static void lde_dispatch_imsg(struct thread *thread) } break; case IMSG_NEIGHBOR_UP: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct lde_nbr)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct lde_nbr)) fatalx("lde_dispatch_imsg: wrong imsg len"); if (lde_nbr_find(imsg.hdr.peerid)) @@ -386,18 +378,15 @@ static void lde_dispatch_imsg(struct thread *thread) case IMSG_CTL_SHOW_L2VPN_PW: l2vpn_pw_ctl(imsg.hdr.pid); - lde_imsg_compose_ldpe(IMSG_CTL_END, 0, - imsg.hdr.pid, NULL, 0); + lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0); break; case IMSG_CTL_SHOW_L2VPN_BINDING: l2vpn_binding_ctl(imsg.hdr.pid); - lde_imsg_compose_ldpe(IMSG_CTL_END, 0, - imsg.hdr.pid, NULL, 0); + lde_imsg_compose_ldpe(IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0); break; default: - log_debug("%s: unexpected imsg %d", __func__, - imsg.hdr.type); + log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); @@ -452,8 +441,7 @@ static void lde_dispatch_parent(struct thread *thread) switch (imsg.hdr.type) { case IMSG_IFSTATUS: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kif)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif)) fatalx("IFSTATUS imsg with wrong len"); kif = imsg.data; @@ -481,18 +469,15 @@ static void lde_dispatch_parent(struct thread *thread) } break; case IMSG_PW_UPDATE: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct zapi_pw_status)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct zapi_pw_status)) fatalx("PW_UPDATE imsg with wrong len"); if (l2vpn_pw_status_update(imsg.data) != 0) - log_warnx("%s: error updating PW status", - __func__); + log_warnx("%s: error updating PW status", __func__); break; case IMSG_NETWORK_ADD: case IMSG_NETWORK_UPDATE: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kroute)) { + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kroute)) { log_warnx("%s: wrong imsg len", __func__); break; } @@ -516,9 +501,8 @@ static void lde_dispatch_parent(struct thread *thread) switch (imsg.hdr.type) { case IMSG_NETWORK_ADD: lde_kernel_insert(&fec, kr->af, &kr->nexthop, - kr->ifindex, kr->route_type, - kr->route_instance, - kr->flags & F_CONNECTED, NULL); + kr->ifindex, kr->route_type, kr->route_instance, + CHECK_FLAG(kr->flags, F_CONNECTED), NULL); break; case IMSG_NETWORK_UPDATE: lde_kernel_update(&fec); @@ -556,8 +540,7 @@ static void lde_dispatch_parent(struct thread *thread) ldp_agentx_enabled(); break; case IMSG_RECONF_CONF: - if ((nconf = malloc(sizeof(struct ldpd_conf))) == - NULL) + if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL) fatal(NULL); memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); @@ -681,8 +664,7 @@ static void lde_dispatch_parent(struct thread *thread) } break; default: - log_debug("%s: unexpected imsg %d", __func__, - imsg.hdr.type); + log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); @@ -708,7 +690,7 @@ static bool lde_fec_connected(const struct fec_node *fn) struct fec_nh *fnh; LIST_FOREACH(fnh, &fn->nexthops, entry) - if (fnh->flags & F_FEC_NH_CONNECTED) + if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED)) return true; return false; @@ -719,7 +701,7 @@ static bool lde_fec_outside_mpls_network(const struct fec_node *fn) struct fec_nh *fnh; LIST_FOREACH(fnh, &fn->nexthops, entry) - if (!(fnh->flags & F_FEC_NH_NO_LDP)) + if (!CHECK_FLAG(fnh->flags, F_FEC_NH_NO_LDP)) return false; return true; @@ -732,18 +714,20 @@ lde_update_label(struct fec_node *fn) /* should we allocate a label for this fec? */ switch (fn->fec.type) { case FEC_TYPE_IPV4: - if ((ldeconf->ipv4.flags & F_LDPD_AF_ALLOCHOSTONLY) + if (CHECK_FLAG(ldeconf->ipv4.flags, F_LDPD_AF_ALLOCHOSTONLY) && fn->fec.u.ipv4.prefixlen != IPV4_MAX_BITLEN) return (NO_LABEL); + if (lde_acl_check(ldeconf->ipv4.acl_label_allocate_for, AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix, fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT) return (NO_LABEL); break; case FEC_TYPE_IPV6: - if ((ldeconf->ipv6.flags & F_LDPD_AF_ALLOCHOSTONLY) + if (CHECK_FLAG(ldeconf->ipv6.flags, F_LDPD_AF_ALLOCHOSTONLY) && fn->fec.u.ipv6.prefixlen != IPV6_MAX_BITLEN) return (NO_LABEL); + if (lde_acl_check(ldeconf->ipv6.acl_label_allocate_for, AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix, fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT) @@ -764,16 +748,18 @@ lde_update_label(struct fec_node *fn) /* choose implicit or explicit-null depending on configuration */ switch (fn->fec.type) { case FEC_TYPE_IPV4: - if (!(ldeconf->ipv4.flags & F_LDPD_AF_EXPNULL)) + if (!CHECK_FLAG(ldeconf->ipv4.flags, F_LDPD_AF_EXPNULL)) return (MPLS_LABEL_IMPLICIT_NULL); + if (lde_acl_check(ldeconf->ipv4.acl_label_expnull_for, AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix, fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT) return (MPLS_LABEL_IMPLICIT_NULL); return MPLS_LABEL_IPV4_EXPLICIT_NULL; case FEC_TYPE_IPV6: - if (!(ldeconf->ipv6.flags & F_LDPD_AF_EXPNULL)) + if (!CHECK_FLAG(ldeconf->ipv6.flags, F_LDPD_AF_EXPNULL)) return (MPLS_LABEL_IMPLICIT_NULL); + if (lde_acl_check(ldeconf->ipv6.acl_label_expnull_for, AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix, fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT) @@ -803,7 +789,7 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) * Ordered Control: don't program label into HW until a * labelmap msg has been received from upstream router */ - if (fnh->flags & F_FEC_NH_DEFER) + if (CHECK_FLAG(fnh->flags, F_FEC_NH_DEFER)) return; switch (fn->fec.type) { @@ -818,8 +804,7 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.remote_label = fnh->remote_label; kr.route_type = fnh->route_type; kr.route_instance = fnh->route_instance; - lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, - sizeof(kr)); + lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr)); break; case FEC_TYPE_IPV6: memset(&kr, 0, sizeof(kr)); @@ -833,8 +818,7 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.route_type = fnh->route_type; kr.route_instance = fnh->route_instance; - lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, - sizeof(kr)); + lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr)); break; case FEC_TYPE_PWID: pw = (struct l2vpn_pw *) fn->data; @@ -871,8 +855,7 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.route_type = fnh->route_type; kr.route_instance = fnh->route_instance; - lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, - sizeof(kr)); + lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr)); break; case FEC_TYPE_IPV6: memset(&kr, 0, sizeof(kr)); @@ -886,8 +869,7 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.route_type = fnh->route_type; kr.route_instance = fnh->route_instance; - lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, - sizeof(kr)); + lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr)); break; case FEC_TYPE_PWID: pw = (struct l2vpn_pw *) fn->data; @@ -967,7 +949,7 @@ lde_fec2map(struct fec *fec, struct map *map) map->type = MAP_TYPE_PWID; map->fec.pwid.type = fec->u.pwid.type; map->fec.pwid.group_id = 0; - map->flags |= F_MAP_PW_ID; + SET_FLAG(map->flags, F_MAP_PW_ID); map->fec.pwid.pwid = fec->u.pwid.pwid; break; } @@ -1021,9 +1003,9 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) * a labelmap message is received from downstream router * and don't send labelmap back to downstream router */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) { LIST_FOREACH(fnh, &fn->nexthops, entry) { - if (fnh->flags & F_FEC_NH_DEFER) + if (CHECK_FLAG(fnh->flags, F_FEC_NH_DEFER)) continue; if (lde_address_find(ln, fnh->af, &fnh->nexthop)) @@ -1061,9 +1043,11 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) case FEC_TYPE_IPV4: if (!ln->v4_enabled) return; + if (lde_acl_check(ldeconf->ipv4.acl_label_advertise_to, AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT) return; + if (lde_acl_check(ldeconf->ipv4.acl_label_advertise_for, AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix, fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT) @@ -1072,9 +1056,11 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) case FEC_TYPE_IPV6: if (!ln->v6_enabled) return; + if (lde_acl_check(ldeconf->ipv6.acl_label_advertise_to, AF_INET, (union ldpd_addr *)&ln->id, 32) != FILTER_PERMIT) return; + if (lde_acl_check(ldeconf->ipv6.acl_label_advertise_for, AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix, fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT) @@ -1086,12 +1072,14 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) /* not the remote end of the pseudowire */ return; - map.flags |= F_MAP_PW_IFMTU; + SET_FLAG(map.flags, F_MAP_PW_IFMTU); map.fec.pwid.ifmtu = pw->l2vpn->mtu; - if (pw->flags & F_PW_CWORD) - map.flags |= F_MAP_PW_CWORD; - if (pw->flags & F_PW_STATUSTLV) { - map.flags |= F_MAP_PW_STATUS; + + if (CHECK_FLAG(pw->flags, F_PW_CWORD)) + SET_FLAG(map.flags, F_MAP_PW_CWORD); + + if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV)) { + SET_FLAG(map.flags, F_MAP_PW_STATUS); map.pw_status = pw->local_status; } break; @@ -1149,8 +1137,8 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, /* not the remote end of the pseudowire */ return; - if (pw->flags & F_PW_CWORD) - map.flags |= F_MAP_PW_CWORD; + if (CHECK_FLAG(pw->flags, F_PW_CWORD)) + SET_FLAG(map.flags, F_MAP_PW_CWORD); break; } map.label = fn->local_label; @@ -1161,7 +1149,7 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, map.st.status_code = st->status_code; map.st.msg_id = st->msg_id; map.st.msg_type = st->msg_type; - map.flags |= F_MAP_STATUS; + SET_FLAG(map.flags, F_MAP_STATUS); } /* SWd.1: send label withdraw. */ @@ -1184,10 +1172,10 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, if (lde_wildcard_apply(wcard, &fn->fec, me) == 0) continue; - lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, - &fn->fec); + lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); if (lw == NULL) lw = lde_wdraw_add(ln, fn); + lw->label = map.label; } } @@ -1271,8 +1259,8 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, /* not the remote end of the pseudowire */ return; - if (pw->flags & F_PW_CWORD) - map.flags |= F_MAP_PW_CWORD; + if (CHECK_FLAG(pw->flags, F_PW_CWORD)) + SET_FLAG(map.flags, F_MAP_PW_CWORD); break; } } else @@ -1333,8 +1321,7 @@ lde_send_labelrequest(struct lde_nbr *ln, struct fec_node *fn, lde_imsg_compose_ldpe(IMSG_REQUEST_ADD, ln->peerid, 0, &map, sizeof(map)); if (single) - lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, - ln->peerid, 0, NULL, 0); + lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, ln->peerid, 0, NULL, 0); /* SLRq.4: record sent request */ RB_FOREACH(f, fec_tree, &ft) { @@ -1386,7 +1373,7 @@ lde_send_notification_eol_prefix(struct lde_nbr *ln, int af) nm.fec.type = MAP_TYPE_TYPED_WCARD; nm.fec.fec.twcard.type = MAP_TYPE_PREFIX; nm.fec.fec.twcard.u.prefix_af = af; - nm.flags |= F_NOTIF_FEC; + SET_FLAG(nm.flags, F_NOTIF_FEC); lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, sizeof(nm)); @@ -1402,7 +1389,7 @@ lde_send_notification_eol_pwid(struct lde_nbr *ln, uint16_t pw_type) nm.fec.type = MAP_TYPE_TYPED_WCARD; nm.fec.fec.twcard.type = MAP_TYPE_PWID; nm.fec.fec.twcard.u.pw_type = pw_type; - nm.flags |= F_NOTIF_FEC; + SET_FLAG(nm.flags, F_NOTIF_FEC); lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, sizeof(nm)); @@ -1465,8 +1452,7 @@ lde_nbr_del(struct lde_nbr *ln) switch (f->type) { case FEC_TYPE_IPV4: case FEC_TYPE_IPV6: - if (!lde_address_find(ln, fnh->af, - &fnh->nexthop)) + if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) continue; /* @@ -1478,8 +1464,8 @@ lde_nbr_del(struct lde_nbr *ln) * to other neighbors for all fecs from neighbor * going down */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { - fnh->flags |= F_FEC_NH_DEFER; + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) { + SET_FLAG(fnh->flags, F_FEC_NH_DEFER); RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) { if (ln->peerid == lnbr->peerid) @@ -1594,8 +1580,7 @@ lde_nbr_addr_update(struct lde_nbr *ln, struct lde_addr *lde_addr, int removed) continue; LIST_FOREACH(fnh, &fn->nexthops, entry) { - if (ldp_addrcmp(fnh->af, &fnh->nexthop, - &lde_addr->addr)) + if (ldp_addrcmp(fnh->af, &fnh->nexthop, &lde_addr->addr)) continue; if (removed) { @@ -1658,15 +1643,13 @@ lde_map_add(struct lde_nbr *ln, struct fec_node *fn, int sent) RB_INSERT(lde_map_head, &fn->upstream, me); me->head = &fn->upstream; if (fec_insert(&ln->sent_map, &me->fec)) - log_warnx("failed to add %s to sent map", - log_fec(&me->fec)); + log_warnx("failed to add %s to sent map", log_fec(&me->fec)); /* XXX on failure more cleanup is needed */ } else { RB_INSERT(lde_map_head, &fn->downstream, me); me->head = &fn->downstream; if (fec_insert(&ln->recv_map, &me->fec)) - log_warnx("failed to add %s to recv map", - log_fec(&me->fec)); + log_warnx("failed to add %s to recv map", log_fec(&me->fec)); } return (me); @@ -1703,8 +1686,7 @@ lde_map_pending_add(struct lde_nbr *ln, struct fec_node *fn) *map = fn->fec; if (fec_insert(&ln->sent_map_pending, map)) - log_warnx("failed to add %s to sent map (pending)", - log_fec(map)); + log_warnx("failed to add %s to sent map (pending)", log_fec(map)); return (map); } @@ -1762,8 +1744,7 @@ lde_wdraw_add(struct lde_nbr *ln, struct fec_node *fn) lw->fec = fn->fec; if (fec_insert(&ln->sent_wdraw, &lw->fec)) - log_warnx("failed to add %s to sent wdraw", - log_fec(&lw->fec)); + log_warnx("failed to add %s to sent wdraw", log_fec(&lw->fec)); return (lw); } @@ -1786,13 +1767,9 @@ lde_change_egress_label(int af) RB_FOREACH(ln, nbr_tree, &lde_nbrs) { lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLICIT_NULL); if (ln->v4_enabled) - lde_send_labelwithdraw_wcard( - ln, - MPLS_LABEL_IPV4_EXPLICIT_NULL); + lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV4_EXPLICIT_NULL); if (ln->v6_enabled) - lde_send_labelwithdraw_wcard( - ln, - MPLS_LABEL_IPV6_EXPLICIT_NULL); + lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV6_EXPLICIT_NULL); } /* update label of connected routes */ @@ -1820,8 +1797,7 @@ lde_change_egress_label(int af) lde_send_labelmapping(ln, fn, 0); } RB_FOREACH(ln, nbr_tree, &lde_nbrs) - lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, - NULL, 0); + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0); } void @@ -1861,8 +1837,7 @@ lde_change_allocate_filter(int af) if (fn->local_label != new_label) { if (new_label == NO_LABEL) RB_FOREACH(ln, nbr_tree, &lde_nbrs) - lde_send_labelwithdraw(ln, fn, - NULL, NULL); + lde_send_labelwithdraw(ln, fn, NULL, NULL); fn->local_label = new_label; if (fn->local_label != NO_LABEL) @@ -1870,6 +1845,7 @@ lde_change_allocate_filter(int af) lde_send_labelmapping(ln, fn, 0); } } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0); @@ -1915,34 +1891,30 @@ lde_change_advertise_filter(int af) case AF_INET: if (fn->fec.type != FEC_TYPE_IPV4) continue; - prefix = (union ldpd_addr *) - &fn->fec.u.ipv4.prefix; + prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix; plen = fn->fec.u.ipv4.prefixlen; break; case FEC_TYPE_IPV6: if (fn->fec.type != FEC_TYPE_IPV6) continue; - prefix = (union ldpd_addr *) - &fn->fec.u.ipv6.prefix; + prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix; plen = fn->fec.u.ipv6.prefixlen; break; default: continue; } + if (lde_acl_check(acl_for_filter, af, prefix, plen) != FILTER_PERMIT) { - me = (struct lde_map *)fec_find( - &ln->sent_map, &fn->fec); + me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec); if (me) /* fec filtered withdraw */ - lde_send_labelwithdraw(ln, fn, - NULL, NULL); + lde_send_labelwithdraw(ln, fn, NULL, NULL); } else /* fec allowed send map */ lde_send_labelmapping(ln, fn, 0); } - lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, - ln->peerid, 0, NULL, 0); + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0); } } } @@ -1988,13 +1960,12 @@ lde_change_accept_filter(int af) RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; if (fn->fec.type == type) { - me = (struct lde_map *)fec_find( - &ln->recv_map, &fn->fec); + me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); if (me) lde_map_del(ln, me, 0); } } - } else if (ln->flags & F_NBR_CAP_TWCARD) { + } else if (CHECK_FLAG(ln->flags, F_NBR_CAP_TWCARD)) { /* This neighbor is allowed and supports type * wildcard so send a labelrequest * to get any new labels from neighbor @@ -2007,15 +1978,13 @@ lde_change_accept_filter(int af) case AF_INET: if (fn->fec.type != FEC_TYPE_IPV4) continue; - prefix = (union ldpd_addr *) - &fn->fec.u.ipv4.prefix; + prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix; plen = fn->fec.u.ipv4.prefixlen; break; case AF_INET6: if (fn->fec.type != FEC_TYPE_IPV6) continue; - prefix = (union ldpd_addr *) - &fn->fec.u.ipv6.prefix; + prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix; plen = fn->fec.u.ipv6.prefixlen; break; default: @@ -2023,8 +1992,7 @@ lde_change_accept_filter(int af) } if (lde_acl_check(acl_for_filter, af, prefix, plen) != FILTER_PERMIT) { - me = (struct lde_map *)fec_find( - &ln->recv_map, &fn->fec); + me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); if (me) lde_map_del(ln, me, 0); } @@ -2032,8 +2000,7 @@ lde_change_accept_filter(int af) lde_send_labelrequest_wcard(ln, af); } else /* Type Wildcard is not supported so restart session */ - lde_imsg_compose_ldpe(IMSG_NBR_SHUTDOWN, ln->peerid, 0, - NULL, 0); + lde_imsg_compose_ldpe(IMSG_NBR_SHUTDOWN, ln->peerid, 0, NULL, 0); } } @@ -2299,7 +2266,7 @@ lde_free_label(uint32_t label) for (ALL_LIST_ELEMENTS_RO(label_chunk_list, node, label_chunk)) { if (label <= label_chunk->end && label >= label_chunk->start) { pos = 1ULL << (label - label_chunk->start); - label_chunk->used_mask &= ~pos; + UNSET_FLAG(label_chunk->used_mask, pos); /* if nobody is using this chunk and it's not current_label_chunk, then free it */ if (!label_chunk->used_mask && (current_label_chunk != node)) { if (lde_release_label_chunk(label_chunk->start, label_chunk->end) != 0) @@ -2312,6 +2279,7 @@ lde_free_label(uint32_t label) break; } } + return; } @@ -2332,7 +2300,7 @@ lde_get_next_label(void) size = label_chunk->end - label_chunk->start + 1; for (i = 0, pos = 1; i < size; i++, pos <<= 1) { if (!(pos & label_chunk->used_mask)) { - label_chunk->used_mask |= pos; + SET_FLAG(label_chunk->used_mask, pos); label = label_chunk->start + i; goto end; } @@ -2360,12 +2328,15 @@ lde_check_filter_af(int af, struct ldpd_af_conf *af_conf, { if (strcmp(af_conf->acl_label_allocate_for, filter_name) == 0) lde_change_allocate_filter(af); + if ((strcmp(af_conf->acl_label_advertise_to, filter_name) == 0) || (strcmp(af_conf->acl_label_advertise_for, filter_name) == 0)) lde_change_advertise_filter(af); + if ((strcmp(af_conf->acl_label_accept_for, filter_name) == 0) || (strcmp(af_conf->acl_label_accept_from, filter_name) == 0)) lde_change_accept_filter(af); + if (strcmp(af_conf->acl_label_expnull_for, filter_name) == 0) lde_change_expnull_for_filter(af); } @@ -2379,6 +2350,7 @@ void lde_route_update(struct iface *iface, int af) /* update label of non-connected routes */ log_debug("update labels for interface %s", iface->name); + RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; if (IS_MPLS_UNRESERVED_LABEL(fn->local_label)) @@ -2405,13 +2377,13 @@ void lde_route_update(struct iface *iface, int af) * may need new label. If no LDP configured * treat fec as a connected route */ - if (fnh->flags & F_FEC_NH_CONNECTED) + if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED)) break; if (fnh->ifindex != iface->ifindex) continue; - fnh->flags &= ~F_FEC_NH_NO_LDP; + UNSET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); if (IS_MPLS_RESERVED_LABEL(fn->local_label)) { fn->local_label = NO_LABEL; fn->local_label = lde_update_label(fn); @@ -2423,6 +2395,7 @@ void lde_route_update(struct iface *iface, int af) break; } } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0); @@ -2438,6 +2411,7 @@ void lde_route_update_release(struct iface *iface, int af) /* update label of interfaces no longer running LDP */ log_debug("release all labels for interface %s af %s", iface->name, af == AF_INET ? "ipv4" : "ipv6"); + RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; @@ -2463,13 +2437,13 @@ void lde_route_update_release(struct iface *iface, int af) * removed from interface may need new label * and would be treated as a connected route */ - if (fnh->flags & F_FEC_NH_CONNECTED) + if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED)) break; if (fnh->ifindex != iface->ifindex) continue; - fnh->flags |= F_FEC_NH_NO_LDP; + SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); RB_FOREACH(ln, nbr_tree, &lde_nbrs) lde_send_labelwithdraw(ln, fn, NULL, NULL); lde_free_label(fn->local_label); @@ -2481,6 +2455,7 @@ void lde_route_update_release(struct iface *iface, int af) break; } } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, NULL, 0); @@ -2498,6 +2473,7 @@ void lde_route_update_release_all(int af) */ log_debug("release all labels for address family %s", af == AF_INET ? "ipv4" : "ipv6"); + RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; switch (af) { @@ -2517,7 +2493,7 @@ void lde_route_update_release_all(int af) lde_send_labelwithdraw(ln, fn, NULL, NULL); LIST_FOREACH(fnh, &fn->nexthops, entry) { - fnh->flags |= F_FEC_NH_NO_LDP; + SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); lde_send_delete_klabel(fn, fnh); } } diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index fe97c54831..2026a7857c 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -329,14 +329,18 @@ main(int argc, char *argv[]) if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2ldpe) == -1) fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2ldpe_sync) == -1) fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2lde) == -1) fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2lde_sync) == -1) fatal("socketpair"); + sock_set_nonblock(pipe_parent2ldpe[0]); sock_set_cloexec(pipe_parent2ldpe[0]); sock_set_nonblock(pipe_parent2ldpe[1]); @@ -387,6 +391,7 @@ main(int argc, char *argv[]) (iev_lde = calloc(1, sizeof(struct imsgev))) == NULL || (iev_lde_sync = calloc(1, sizeof(struct imsgev))) == NULL) fatal(NULL); + imsg_init(&iev_ldpe->ibuf, pipe_parent2ldpe[0]); iev_ldpe->handler_read = main_dispatch_ldpe; thread_add_read(master, iev_ldpe->handler_read, iev_ldpe, iev_ldpe->ibuf.fd, @@ -413,14 +418,15 @@ main(int argc, char *argv[]) if (main_imsg_send_ipc_sockets(&iev_ldpe->ibuf, &iev_lde->ibuf)) fatal("could not establish imsg links"); - main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, - sizeof(ldp_debug)); + + main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, sizeof(ldp_debug)); main_imsg_compose_both(IMSG_INIT, &init, sizeof(init)); main_imsg_send_config(ldpd_conf); - if (ldpd_conf->ipv4.flags & F_LDPD_AF_ENABLED) + if (CHECK_FLAG(ldpd_conf->ipv4.flags, F_LDPD_AF_ENABLED)) main_imsg_send_net_sockets(AF_INET); - if (ldpd_conf->ipv6.flags & F_LDPD_AF_ENABLED) + + if (CHECK_FLAG(ldpd_conf->ipv6.flags, F_LDPD_AF_ENABLED)) main_imsg_send_net_sockets(AF_INET6); frr_run(master); @@ -523,6 +529,7 @@ start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync) if (dup2(fd_async, LDPD_FD_ASYNC) == -1) fatal("cannot setup imsg async fd"); + if (dup2(fd_sync, LDPD_FD_SYNC) == -1) fatal("cannot setup imsg sync fd"); @@ -563,6 +570,7 @@ static void main_dispatch_ldpe(struct thread *thread) if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); + if (n == 0) /* connection closed */ shut = 1; @@ -608,6 +616,7 @@ static void main_dispatch_ldpe(struct thread *thread) THREAD_OFF(iev->ev_read); THREAD_OFF(iev->ev_write); ldpe_pid = 0; + if (lde_pid == 0) ldpd_shutdown(); else @@ -629,6 +638,7 @@ static void main_dispatch_lde(struct thread *thread) if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); + if (n == 0) /* connection closed */ shut = 1; @@ -704,8 +714,10 @@ static void main_dispatch_lde(struct thread *thread) imsg.hdr.type); break; } + imsg_free(&imsg); } + if (!shut) imsg_event_add(iev); else { @@ -746,6 +758,7 @@ main_imsg_compose_ldpe(int type, pid_t pid, void *data, uint16_t datalen) { if (iev_ldpe == NULL) return; + imsg_compose_event(iev_ldpe, type, 0, pid, -1, data, datalen); } @@ -760,10 +773,13 @@ main_imsg_compose_both(enum imsg_type type, void *buf, uint16_t len) { if (iev_ldpe == NULL || iev_lde == NULL) return (0); + if (imsg_compose_event(iev_ldpe, type, 0, 0, -1, buf, len) == -1) return (-1); + if (imsg_compose_event(iev_lde, type, 0, 0, -1, buf, len) == -1) return (-1); + return (0); } @@ -788,6 +804,7 @@ imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen)) != -1) imsg_event_add(iev); + return (ret); } @@ -836,6 +853,7 @@ main_imsg_send_ipc_sockets(struct imsgbuf *ldpe_buf, struct imsgbuf *lde_buf) if (imsg_compose(ldpe_buf, IMSG_SOCKET_IPC, 0, 0, pipe_ldpe2lde[0], NULL, 0) == -1) return (-1); + if (imsg_compose(lde_buf, IMSG_SOCKET_IPC, 0, 0, pipe_ldpe2lde[1], NULL, 0) == -1) return (-1); @@ -894,8 +912,10 @@ ldp_acl_request(struct imsgev *iev, char *acl_name, int af, /* receive (blocking) and parse result */ if (imsg_read(&iev->ibuf) == -1) fatal("imsg_read error"); + if (imsg_get(&iev->ibuf, &imsg) == -1) fatal("imsg_get"); + if (imsg.hdr.type != IMSG_ACL_CHECK || imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(int)) fatalx("ldp_acl_request: invalid response"); @@ -962,8 +982,8 @@ ldp_af_global_get(struct ldpd_global *xglobal, int af) int ldp_is_dual_stack(struct ldpd_conf *xconf) { - return ((xconf->ipv4.flags & F_LDPD_AF_ENABLED) && - (xconf->ipv6.flags & F_LDPD_AF_ENABLED)); + return (CHECK_FLAG(xconf->ipv4.flags, F_LDPD_AF_ENABLED) + && CHECK_FLAG(xconf->ipv6.flags, F_LDPD_AF_ENABLED)); } in_addr_t @@ -1017,11 +1037,13 @@ main_imsg_send_config(struct ldpd_conf *xconf) sizeof(*lif)) == -1) return (-1); } + RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) { if (main_imsg_compose_both(IMSG_RECONF_L2VPN_PW, pw, sizeof(*pw)) == -1) return (-1); } + RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree) { if (main_imsg_compose_both(IMSG_RECONF_L2VPN_IPW, pw, sizeof(*pw)) == -1) @@ -1065,12 +1087,12 @@ ldp_config_normalize(struct ldpd_conf *xconf) struct l2vpn *l2vpn; struct l2vpn_pw *pw, *ptmp; - if (!(xconf->flags & F_LDPD_ENABLED)) + if (!CHECK_FLAG(xconf->flags, F_LDPD_ENABLED)) ldp_config_reset_main(xconf); else { - if (!(xconf->ipv4.flags & F_LDPD_AF_ENABLED)) + if (!CHECK_FLAG(xconf->ipv4.flags, F_LDPD_AF_ENABLED)) ldp_config_reset_af(xconf, AF_INET); - if (!(xconf->ipv6.flags & F_LDPD_AF_ENABLED)) + if (!CHECK_FLAG(xconf->ipv6.flags, F_LDPD_AF_ENABLED)) ldp_config_reset_af(xconf, AF_INET6); RB_FOREACH_SAFE(iface, iface_head, &xconf->iface_tree, itmp) { @@ -1083,7 +1105,7 @@ ldp_config_normalize(struct ldpd_conf *xconf) } RB_FOREACH_SAFE(nbrp, nbrp_head, &xconf->nbrp_tree, ntmp) { - if (nbrp->flags & (F_NBRP_KEEPALIVE|F_NBRP_GTSM)) + if (CHECK_FLAG(nbrp->flags, (F_NBRP_KEEPALIVE|F_NBRP_GTSM))) continue; if (nbrp->auth.method != AUTH_NONE) continue; @@ -1096,7 +1118,7 @@ ldp_config_normalize(struct ldpd_conf *xconf) RB_FOREACH(l2vpn, l2vpn_head, &xconf->l2vpn_tree) { RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_tree, ptmp) { - if (!(pw->flags & F_PW_STATIC_NBR_ADDR)) { + if (!CHECK_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR)) { pw->af = AF_INET; pw->addr.v4 = pw->lsr_id; } @@ -1106,9 +1128,10 @@ ldp_config_normalize(struct ldpd_conf *xconf) RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw); RB_INSERT(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw); } + RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree, ptmp) { - if (!(pw->flags & F_PW_STATIC_NBR_ADDR)) { + if (!CHECK_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR)) { pw->af = AF_INET; pw->addr.v4 = pw->lsr_id; } @@ -1210,6 +1233,7 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf) RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif); free(lif); } + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) { pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree); @@ -1217,6 +1241,7 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf) RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw); free(pw); } + while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) { pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree); @@ -1224,6 +1249,7 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf) RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw); free(pw); } + QOBJ_UNREG(l2vpn); RB_REMOVE(l2vpn_head, &conf->l2vpn_tree, l2vpn); free(l2vpn); @@ -1244,18 +1270,21 @@ ldp_clear_config(struct ldpd_conf *xconf) RB_REMOVE(iface_head, &xconf->iface_tree, iface); free(iface); } + while (!RB_EMPTY(tnbr_head, &xconf->tnbr_tree)) { tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree); RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr); free(tnbr); } + while (!RB_EMPTY(nbrp_head, &xconf->nbrp_tree)) { nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree); RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp); free(nbrp); } + while (!RB_EMPTY(l2vpn_head, &xconf->l2vpn_tree)) { l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree); @@ -1289,8 +1318,8 @@ static void merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) { /* Removing global LDP config requires resetting LDP IGP Sync FSM */ - if ((conf->flags & F_LDPD_ENABLED) && - (!(xconf->flags & F_LDPD_ENABLED))) + if (CHECK_FLAG(conf->flags, F_LDPD_ENABLED) + && (!CHECK_FLAG(xconf->flags, F_LDPD_ENABLED))) { if (ldpd_process == PROC_LDP_ENGINE) ldp_sync_fsm_reset_all(); @@ -1313,8 +1342,8 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) * Configuration of ordered-control or independent-control * requires resetting all neighborships. */ - if ((conf->flags & F_LDPD_ORDERED_CONTROL) != - (xconf->flags & F_LDPD_ORDERED_CONTROL)) + if (CHECK_FLAG(conf->flags, F_LDPD_ORDERED_CONTROL) != + CHECK_FLAG(xconf->flags, F_LDPD_ORDERED_CONTROL)) ldpe_reset_nbrs(AF_UNSPEC); conf->lhello_holdtime = xconf->lhello_holdtime; @@ -1329,8 +1358,8 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) conf->trans_pref = xconf->trans_pref; } - if ((conf->flags & F_LDPD_DS_CISCO_INTEROP) != - (xconf->flags & F_LDPD_DS_CISCO_INTEROP)) { + if (CHECK_FLAG(conf->flags, F_LDPD_DS_CISCO_INTEROP) != + CHECK_FLAG(xconf->flags, F_LDPD_DS_CISCO_INTEROP)) { if (ldpd_process == PROC_LDP_ENGINE) ldpe_reset_ds_nbrs(); } @@ -1339,8 +1368,8 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) * Configuration of allow-broken-lsp requires reprograming all * labeled routes */ - if ((conf->flags & F_LDPD_ALLOW_BROKEN_LSP) != - (xconf->flags & F_LDPD_ALLOW_BROKEN_LSP)) { + if (CHECK_FLAG(conf->flags, F_LDPD_ALLOW_BROKEN_LSP) != + CHECK_FLAG(xconf->flags, F_LDPD_ALLOW_BROKEN_LSP)) { if (ldpd_process == PROC_LDE_ENGINE) lde_allow_broken_lsp_update(xconf->flags); } @@ -1368,17 +1397,19 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) af_conf->keepalive = xa->keepalive; stop_init_backoff = 1; } + af_conf->lhello_holdtime = xa->lhello_holdtime; af_conf->lhello_interval = xa->lhello_interval; af_conf->thello_holdtime = xa->thello_holdtime; af_conf->thello_interval = xa->thello_interval; /* update flags */ - if ((af_conf->flags & F_LDPD_AF_THELLO_ACCEPT) && - !(xa->flags & F_LDPD_AF_THELLO_ACCEPT)) + if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_THELLO_ACCEPT) && + !CHECK_FLAG(xa->flags, F_LDPD_AF_THELLO_ACCEPT)) remove_dynamic_tnbrs = 1; - if ((af_conf->flags & F_LDPD_AF_NO_GTSM) != - (xa->flags & F_LDPD_AF_NO_GTSM)) { + + if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_NO_GTSM) != + CHECK_FLAG(xa->flags, F_LDPD_AF_NO_GTSM)) { if (af == AF_INET6) /* need to set/unset IPV6_MINHOPCOUNT */ update_sockets = 1; @@ -1386,18 +1417,18 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) /* for LDPv4 just resetting the neighbors is enough */ reset_nbrs_ipv4 = 1; } - if ((af_conf->flags & F_LDPD_AF_EXPNULL) != - (xa->flags & F_LDPD_AF_EXPNULL)) + if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_EXPNULL) != + CHECK_FLAG(xa->flags, F_LDPD_AF_EXPNULL)) change_egress_label = 1; /* changing config of host only fec filtering */ - if ((af_conf->flags & F_LDPD_AF_ALLOCHOSTONLY) - != (xa->flags & F_LDPD_AF_ALLOCHOSTONLY)) + if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_ALLOCHOSTONLY) + != CHECK_FLAG(xa->flags, F_LDPD_AF_ALLOCHOSTONLY)) change_host_label = 1; /* disabling LDP for address family */ - if ((af_conf->flags & F_LDPD_AF_ENABLED) && - !(xa->flags & F_LDPD_AF_ENABLED)) + if (CHECK_FLAG(af_conf->flags, F_LDPD_AF_ENABLED) && + !CHECK_FLAG(xa->flags, F_LDPD_AF_ENABLED)) change_ldp_disabled = 1; af_conf->flags = xa->flags; @@ -1412,31 +1443,36 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) if (strcmp(af_conf->acl_label_allocate_for, xa->acl_label_allocate_for)) change_host_label = 1; - if (strcmp(af_conf->acl_label_advertise_to, - xa->acl_label_advertise_to) || - strcmp(af_conf->acl_label_advertise_for, - xa->acl_label_advertise_for) || - strcmp(af_conf->acl_label_accept_from, - xa->acl_label_accept_from) || - strcmp(af_conf->acl_label_accept_for, - xa->acl_label_accept_for)) + if (strcmp(af_conf->acl_label_advertise_to, xa->acl_label_advertise_to) || + strcmp(af_conf->acl_label_advertise_for, xa->acl_label_advertise_for) || + strcmp(af_conf->acl_label_accept_from, xa->acl_label_accept_from) || + strcmp(af_conf->acl_label_accept_for, xa->acl_label_accept_for)) reset_nbrs = 1; + if (strcmp(af_conf->acl_thello_accept_from, xa->acl_thello_accept_from)) remove_dynamic_tnbrs = 1; + if (strcmp(af_conf->acl_label_expnull_for, xa->acl_label_expnull_for)) change_egress_label = 1; + strlcpy(af_conf->acl_thello_accept_from, xa->acl_thello_accept_from, sizeof(af_conf->acl_thello_accept_from)); + strlcpy(af_conf->acl_label_allocate_for, xa->acl_label_allocate_for, sizeof(af_conf->acl_label_allocate_for)); + strlcpy(af_conf->acl_label_advertise_to, xa->acl_label_advertise_to, sizeof(af_conf->acl_label_advertise_to)); + strlcpy(af_conf->acl_label_advertise_for, xa->acl_label_advertise_for, sizeof(af_conf->acl_label_advertise_for)); + strlcpy(af_conf->acl_label_accept_from, xa->acl_label_accept_from, sizeof(af_conf->acl_label_accept_from)); + strlcpy(af_conf->acl_label_accept_for, xa->acl_label_accept_for, sizeof(af_conf->acl_label_accept_for)); + strlcpy(af_conf->acl_label_expnull_for, xa->acl_label_expnull_for, sizeof(af_conf->acl_label_expnull_for)); @@ -1445,8 +1481,10 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) case PROC_LDE_ENGINE: if (change_egress_label) lde_change_egress_label(af); + if (change_host_label) lde_change_allocate_filter(af); + if (change_ldp_disabled) lde_route_update_release_all(af); @@ -1529,6 +1567,7 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) */ if (iface->ipv4.enabled && !xi->ipv4.enabled) lde_route_update_release(iface, AF_INET); + if (iface->ipv6.enabled && !xi->ipv6.enabled) lde_route_update_release(iface, AF_INET6); @@ -1537,6 +1576,7 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) */ if (!iface->ipv4.enabled && xi->ipv4.enabled) lde_route_update(iface, AF_INET); + if (!iface->ipv6.enabled && xi->ipv6.enabled) lde_route_update(iface, AF_INET6); } @@ -1565,14 +1605,14 @@ merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf) struct tnbr *tnbr, *ttmp, *xt; RB_FOREACH_SAFE(tnbr, tnbr_head, &conf->tnbr_tree, ttmp) { - if (!(tnbr->flags & F_TNBR_CONFIGURED)) + if (!CHECK_FLAG(tnbr->flags, F_TNBR_CONFIGURED)) continue; /* find deleted tnbrs */ if (tnbr_find(xconf, tnbr->af, &tnbr->addr) == NULL) { switch (ldpd_process) { case PROC_LDP_ENGINE: - tnbr->flags &= ~F_TNBR_CONFIGURED; + UNSET_FLAG(tnbr->flags, F_TNBR_CONFIGURED); tnbr_check(conf, tnbr); break; case PROC_LDE_ENGINE: @@ -1601,8 +1641,8 @@ merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf) } /* update existing tnbrs */ - if (!(tnbr->flags & F_TNBR_CONFIGURED)) - tnbr->flags |= F_TNBR_CONFIGURED; + if (!CHECK_FLAG(tnbr->flags, F_TNBR_CONFIGURED)) + SET_FLAG(tnbr->flags, F_TNBR_CONFIGURED); } } @@ -1853,8 +1893,8 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) reinstall_tnbr = 0; /* changes that require a session restart */ - if ((pw->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF)) != - (xp->flags & (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF))) + if (CHECK_FLAG(pw->flags, (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF)) != + CHECK_FLAG(xp->flags, (F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF))) reset_nbr = 1; else reset_nbr = 0; @@ -1883,20 +1923,24 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) pw->pwid = xp->pwid; strlcpy(pw->ifname, xp->ifname, sizeof(pw->ifname)); pw->ifindex = xp->ifindex; - if (xp->flags & F_PW_CWORD_CONF) - pw->flags |= F_PW_CWORD_CONF; + if (CHECK_FLAG(xp->flags, F_PW_CWORD_CONF)) + SET_FLAG(pw->flags, F_PW_CWORD_CONF); else - pw->flags &= ~F_PW_CWORD_CONF; - if (xp->flags & F_PW_STATUSTLV_CONF) - pw->flags |= F_PW_STATUSTLV_CONF; + UNSET_FLAG(pw->flags, F_PW_CWORD_CONF); + + if (CHECK_FLAG(xp->flags, F_PW_STATUSTLV_CONF)) + SET_FLAG(pw->flags, F_PW_STATUSTLV_CONF); else - pw->flags &= ~F_PW_STATUSTLV_CONF; - if (xp->flags & F_PW_STATIC_NBR_ADDR) - pw->flags |= F_PW_STATIC_NBR_ADDR; + UNSET_FLAG(pw->flags, F_PW_STATUSTLV_CONF); + + if (CHECK_FLAG(xp->flags, F_PW_STATIC_NBR_ADDR)) + SET_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR); else - pw->flags &= ~F_PW_STATIC_NBR_ADDR; + UNSET_FLAG(pw->flags, F_PW_STATIC_NBR_ADDR); + if (ldpd_process == PROC_LDP_ENGINE && reinstall_tnbr) ldpe_l2vpn_pw_init(pw); + if (ldpd_process == PROC_LDE_ENGINE && reinstall_pwfec) { l2vpn->pw_type = xl->pw_type; l2vpn->mtu = xl->mtu; diff --git a/lib/asn.c b/lib/asn.c new file mode 100644 index 0000000000..c64666375d --- /dev/null +++ b/lib/asn.c @@ -0,0 +1,260 @@ +/* + * ASN functions + * + * Copyright 2022 6WIND + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include "log.h" +#include "asn.h" + +static bool relax_as_zero; + +static const struct message asnotation_mode_msg[] = { + {ASNOTATION_PLAIN, "plain"}, + {ASNOTATION_DOT, "dot"}, + {ASNOTATION_DOTPLUS, "dot+"}, + {ASNOTATION_UNDEFINED, "undefined"}, + {0} +}; + +/* converts a string into an Autonomous system number + * "1.1" => 65536 + * "65500" => 65500 + */ +static bool asn_str2asn_internal(const char *asstring, as_t *asn, + const char **next, bool *partial, + enum asnotation_mode *mode) +{ + uint32_t high = 0, low = 0; + uint64_t temp_val; + const char *p = asstring; + bool ret = false; + uint32_t digit; + enum asnotation_mode val = ASNOTATION_PLAIN; + + if (!asstring) + goto end; + + if (!isdigit((unsigned char)*p)) + goto end; + + temp_val = 0; + while (isdigit((unsigned char)*p)) { + digit = (*p) - '0'; + temp_val *= 10; + temp_val += digit; + if (temp_val > UINT32_MAX) + /* overflow */ + goto end; + p++; + } + high = (uint32_t)temp_val; + if (*p == '.') { /* dot format */ + p++; + temp_val = 0; + if (*p == '\0' && partial) { + *partial = true; + goto end; + } + while (isdigit((unsigned char)*p)) { + digit = (*p) - '0'; + temp_val *= 10; + temp_val += digit; + if (temp_val > UINT16_MAX) + /* overflow */ + goto end; + p++; + } + low = (uint32_t)temp_val; + + if (!next && *p != '\0' && !isdigit((unsigned char)*p)) + goto end; + /* AS <AS4B>.<AS4B> is forbidden */ + if (high > UINT16_MAX) + goto end; + /* AS 0.0 is authorised for some case only */ + if (!relax_as_zero && high == 0 && low == 0) { + if (partial) + *partial = true; + goto end; + } + if (asn) + *asn = (high << 16) + low; + ret = true; + if (high == 0) + val = ASNOTATION_DOTPLUS; + else + val = ASNOTATION_DOT; + goto end; + } + /* AS 0 is forbidden */ + if (!relax_as_zero && high == 0) + goto end; + if (!asn) { + ret = true; + goto end; + } + *asn = high; + ret = true; + end: + if (next) + *next = p; + if (mode) + *mode = val; + return ret; +} + +static void asn_asn2asdot(as_t asn, char *asstring, size_t len) +{ + uint16_t low, high; + + high = (asn >> 16) & 0xffff; + low = asn & 0xffff; + snprintf(asstring, len, "%hu.%hu", high, low); +} + +bool asn_str2asn(const char *asstring, as_t *asn) +{ + return asn_str2asn_internal(asstring, asn, NULL, NULL, NULL); +} + +const char *asn_asn2asplain(as_t asn) +{ + static char buf[ASN_STRING_MAX_SIZE]; + + snprintf(buf, sizeof(buf), "%u", asn); + return buf; +} + +const char *asn_str2asn_parse(const char *asstring, as_t *asn, bool *found_ptr) +{ + const char *p = NULL; + const char **next = &p; + bool found; + + found = asn_str2asn_internal(asstring, asn, next, NULL, NULL); + if (found_ptr) + *found_ptr = found; + return *next; +} + +void asn_relax_as_zero(bool relax) +{ + relax_as_zero = relax; +} + +enum match_type asn_str2asn_match(const char *str) +{ + bool found, partial = false; + + found = asn_str2asn_internal(str, NULL, NULL, &partial, NULL); + if (found && !partial) + return exact_match; + + if (partial) + return partly_match; + + return no_match; +} + +bool asn_str2asn_notation(const char *asstring, as_t *asn, + enum asnotation_mode *asnotation) +{ + return asn_str2asn_internal(asstring, asn, NULL, NULL, asnotation); +} + +const char *asn_mode2str(enum asnotation_mode asnotation) +{ + return lookup_msg(asnotation_mode_msg, asnotation, + "Unrecognized AS notation mode"); +} + +void asn_asn2json(json_object *json, const char *attr, + as_t asn, enum asnotation_mode asnotation) +{ + static char as_str[ASN_STRING_MAX_SIZE]; + + if ((asnotation == ASNOTATION_PLAIN) || + ((asnotation == ASNOTATION_DOT) && asn < UINT16_MAX)) + json_object_int_add(json, attr, asn); + else { + asn_asn2asdot(asn, as_str, sizeof(as_str)); + json_object_string_add(json, attr, as_str); + } +} + +void asn_asn2json_array(json_object *jseg_list, as_t asn, + enum asnotation_mode asnotation) +{ + static char as_str[ASN_STRING_MAX_SIZE]; + + if ((asnotation == ASNOTATION_PLAIN) || + ((asnotation == ASNOTATION_DOT) && asn < UINT16_MAX)) + json_object_array_add(jseg_list, + json_object_new_int64(asn)); + else { + asn_asn2asdot(asn, as_str, sizeof(as_str)); + json_array_string_add(jseg_list, as_str); + } +} + +char *asn_asn2string(const as_t *asn, char *buf, size_t len, + enum asnotation_mode asnotation) +{ + if ((asnotation == ASNOTATION_PLAIN) || + ((asnotation == ASNOTATION_DOT) && *asn < UINT16_MAX)) + snprintf(buf, len, "%u", *asn); + else + asn_asn2asdot(*asn, buf, len); + return buf; +} + +static ssize_t printfrr_asnotation(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr, + enum asnotation_mode asnotation) +{ + /* for alignemnt up to 33 chars - %33pASD for instance - */ + char as_str[ASN_STRING_MAX_SIZE*3]; + const as_t *asn; + + if (!ptr) + return bputs(buf, "(null)"); + asn = ptr; + asn_asn2string(asn, as_str, sizeof(as_str), asnotation); + return bputs(buf, as_str); +} + +printfrr_ext_autoreg_p("ASP", printfrr_asplain); +static ssize_t printfrr_asplain(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_asnotation(buf, ea, ptr, ASNOTATION_PLAIN); +} + +printfrr_ext_autoreg_p("ASD", printfrr_asdot); +static ssize_t printfrr_asdot(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_asnotation(buf, ea, ptr, ASNOTATION_DOT); +} + +printfrr_ext_autoreg_p("ASE", printfrr_asdotplus); +static ssize_t printfrr_asdotplus(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_asnotation(buf, ea, ptr, ASNOTATION_DOTPLUS); +} diff --git a/lib/asn.h b/lib/asn.h new file mode 100644 index 0000000000..81a42c658d --- /dev/null +++ b/lib/asn.h @@ -0,0 +1,81 @@ +/* + * AS number structure + * Copyright 2022 6WIND + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_ASN_H +#define _FRR_ASN_H + +#include "zebra.h" +#include "command_match.h" +#include "json.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ASN_STRING_MAX_SIZE 12 + +enum asnotation_mode { + ASNOTATION_PLAIN = 0, + ASNOTATION_DOT, + ASNOTATION_DOTPLUS, + ASNOTATION_UNDEFINED, +}; + +typedef uint32_t as_t; + +extern bool asn_str2asn(const char *asstring, as_t *asn); +extern const char *asn_asn2asplain(as_t asn); +extern const char *asn_str2asn_parse(const char *asstring, as_t *asn, + bool *found_ptr); +extern enum match_type asn_str2asn_match(const char *str); +extern bool asn_str2asn_notation(const char *asstring, as_t *asn, + enum asnotation_mode *asnotation); +extern const char *asn_mode2str(enum asnotation_mode asnotation); +void asn_asn2json_array(json_object *jseg_list, as_t asn, + enum asnotation_mode asnotation); +void asn_asn2json(json_object *jseg_list, const char *attr, + as_t asn, enum asnotation_mode asnotation); +extern char *asn_asn2string(const as_t *as, char *buf, size_t len, + enum asnotation_mode asnotation); +/* display AS in appropriate format */ +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pASP" (as_t *) +#pragma FRR printfrr_ext "%pASD" (as_t *) +#pragma FRR printfrr_ext "%pASE" (as_t *) +#endif + +#define ASN_FORMAT(mode) \ + ((mode == ASNOTATION_DOT) ? "%pASD" : \ + ((mode == ASNOTATION_DOTPLUS) ? "%pASE" : \ + "%pASP")) +#define ASN_FORMAT_SPACE(mode) \ + ((mode == ASNOTATION_DOT) ? "%10pASD" : \ + ((mode == ASNOTATION_DOTPLUS) ? "%10pASE" : \ + "%10pASP")) + +/* for test */ +extern void asn_relax_as_zero(bool relax); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_ASN_H */ diff --git a/lib/command.c b/lib/command.c index cf96df6f95..ee5a3889e8 100644 --- a/lib/command.c +++ b/lib/command.c @@ -56,6 +56,7 @@ const struct message tokennames[] = { item(IPV6_PREFIX_TKN), item(MAC_TKN), item(MAC_PREFIX_TKN), + item(ASNUM_TKN), item(FORK_TKN), item(JOIN_TKN), item(START_TKN), diff --git a/lib/command.h b/lib/command.h index 2121bfd623..6538e56588 100644 --- a/lib/command.h +++ b/lib/command.h @@ -391,7 +391,8 @@ struct cmd_node { #define DEBUG_STR "Debugging functions\n" #define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" #define ROUTER_STR "Enable a routing process\n" -#define AS_STR "AS number\n" +#define AS_STR \ + "AS number in plain <1-4294967295> or dotted <0-65535>.<0-65535> format\n" #define MAC_STR "MAC address\n" #define MBGP_STR "MBGP information\n" #define MATCH_STR "Match values from routing table\n" diff --git a/lib/command_graph.c b/lib/command_graph.c index 850fdeafcd..ff3c11db69 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -266,6 +266,7 @@ static bool cmd_nodes_equal(struct graph_node *ga, struct graph_node *gb) case END_TKN: case NEG_ONLY_TKN: case WORD_TKN: + case ASNUM_TKN: return true; } @@ -535,6 +536,7 @@ void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf) case MAC_PREFIX_TKN: case END_TKN: case VARIABLE_TKN: + case ASNUM_TKN: color = "#ffffff"; break; } diff --git a/lib/command_graph.h b/lib/command_graph.h index 8e84fa928d..25aa47db7b 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -45,6 +45,7 @@ enum cmd_token_type { IPV6_PREFIX_TKN, // IPV6 network prefixes MAC_TKN, // Ethernet address MAC_PREFIX_TKN, // Ethernet address w/ CIDR mask + ASNUM_TKN, // AS dot format /* plumbing types */ FORK_TKN, // marks subgraph beginning diff --git a/lib/command_lex.l b/lib/command_lex.l index 64f74498b8..dc89191c13 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -38,6 +38,7 @@ VARIABLE [A-Z][-_A-Z:0-9]+ WORD (\-|\+)?[a-zA-Z0-9\*][-+_a-zA-Z0-9\*]* NUMBER (\-|\+)?[0-9]{1,20} RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) +ASNUM ASNUM /* yytext shall be a pointer */ %pointer @@ -57,6 +58,7 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) %} [ \t]+ LOC_STEP /* ignore whitespace */; +{ASNUM} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return ASNUM;} {IPV4} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV4;} {IPV4_PREFIX} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV4_PREFIX;} {IPV6} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV6;} diff --git a/lib/command_match.c b/lib/command_match.c index 5ed643bc91..ff3c48fc31 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -10,6 +10,7 @@ #include "command_match.h" #include "memory.h" +#include "asn.h" DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack"); @@ -541,6 +542,7 @@ static enum match_type min_match_level(enum cmd_token_type type) case END_TKN: case NEG_ONLY_TKN: case VARIABLE_TKN: + case ASNUM_TKN: return exact_match; } @@ -564,6 +566,7 @@ static int score_precedence(enum cmd_token_type type) case IPV6_PREFIX_TKN: case MAC_TKN: case MAC_PREFIX_TKN: + case ASNUM_TKN: case RANGE_TKN: return 2; case WORD_TKN: @@ -698,6 +701,8 @@ static enum match_type match_token(struct cmd_token *token, char *input_token) return match_mac(input_token, false); case MAC_PREFIX_TKN: return match_mac(input_token, true); + case ASNUM_TKN: + return asn_str2asn_match(input_token); case END_TKN: case FORK_TKN: case JOIN_TKN: @@ -840,7 +845,6 @@ static enum match_type match_ipv4_prefix(const char *str) return exact_match; } - #define IPV6_ADDR_STR "0123456789abcdefABCDEF:." #define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" #define STATE_START 1 diff --git a/lib/command_parse.y b/lib/command_parse.y index a29e090ef0..8867e98ccc 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -88,6 +88,7 @@ %token <string> RANGE %token <string> MAC %token <string> MAC_PREFIX +%token <string> ASNUM /* special syntax, value is irrelevant */ %token <string> EXCL_BRACKET @@ -277,6 +278,11 @@ placeholder_token_real: $$ = new_token_node (ctx, MAC_PREFIX_TKN, $1, doc_next(ctx)); XFREE (MTYPE_LEX, $1); } +| ASNUM +{ + $$ = new_token_node (ctx, ASNUM_TKN, $1, doc_next(ctx)); + XFREE (MTYPE_LEX, $1); +} placeholder_token: placeholder_token_real varname_token diff --git a/lib/command_py.c b/lib/command_py.c index ceea5883d5..f8abcf8ef4 100644 --- a/lib/command_py.c +++ b/lib/command_py.c @@ -201,6 +201,7 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph, item(IPV6_PREFIX_TKN); // IPV6 network prefixes item(MAC_TKN); // MAC address item(MAC_PREFIX_TKN); // MAC address with mask + item(ASNUM_TKN); // ASNUM /* plumbing types */ item(FORK_TKN); diff --git a/lib/libfrr.c b/lib/libfrr.c index 0467dc1d7e..d1b7dd133e 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -992,7 +992,7 @@ static void frr_config_read_in(struct thread *t) int ret; context.client = NB_CLIENT_CLI; - ret = nb_candidate_commit(&context, vty_shared_candidate_config, + ret = nb_candidate_commit(context, vty_shared_candidate_config, true, "Read configuration file", NULL, errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) @@ -114,6 +114,21 @@ extern int proto_redistnum(int afi, const char *s); extern const char *zserv_command_string(unsigned int command); +#define OSPF_LOG(level, cond, fmt, ...) \ + do { \ + if (cond) \ + zlog_##level(fmt, ##__VA_ARGS__); \ + } while (0) + +#define OSPF_LOG_ERR(fmt, ...) OSPF_LOG(err, true, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_WARN(fmt, ...) OSPF_LOG(warn, true, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_INFO(fmt, ...) OSPF_LOG(info, true, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_DEBUG(cond, fmt, ...) OSPF_LOG(debug, cond, fmt, ##__VA_ARGS__) + +#define OSPF_LOG_NOTICE(fmt, ...) OSPF_LOG(notice, true, fmt, ##__VA_ARGS__) /* structure useful for avoiding repeated rendering of the same timestamp */ struct timestamp_control { diff --git a/lib/northbound.c b/lib/northbound.c index b755264be1..6f2c522a29 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -61,7 +61,7 @@ static int nb_callback_configuration(struct nb_context *context, struct nb_config_change *change, char *errmsg, size_t errmsg_len); static struct nb_transaction * -nb_transaction_new(struct nb_context *context, struct nb_config *config, +nb_transaction_new(struct nb_context context, struct nb_config *config, struct nb_config_cbs *changes, const char *comment, char *errmsg, size_t errmsg_len); static void nb_transaction_free(struct nb_transaction *transaction); @@ -835,7 +835,7 @@ int nb_candidate_validate(struct nb_context *context, return ret; } -int nb_candidate_commit_prepare(struct nb_context *context, +int nb_candidate_commit_prepare(struct nb_context context, struct nb_config *candidate, const char *comment, struct nb_transaction **transaction, @@ -860,9 +860,8 @@ int nb_candidate_commit_prepare(struct nb_context *context, return NB_ERR_NO_CHANGES; } - if (nb_candidate_validate_code(context, candidate, &changes, errmsg, - errmsg_len) - != NB_OK) { + if (nb_candidate_validate_code(&context, candidate, &changes, errmsg, + errmsg_len) != NB_OK) { flog_warn(EC_LIB_NB_CANDIDATE_INVALID, "%s: failed to validate candidate configuration", __func__); @@ -913,7 +912,7 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction, nb_transaction_free(transaction); } -int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate, +int nb_candidate_commit(struct nb_context context, struct nb_config *candidate, bool save_transaction, const char *comment, uint32_t *transaction_id, char *errmsg, size_t errmsg_len) @@ -1411,13 +1410,13 @@ static int nb_callback_configuration(struct nb_context *context, } static struct nb_transaction * -nb_transaction_new(struct nb_context *context, struct nb_config *config, +nb_transaction_new(struct nb_context context, struct nb_config *config, struct nb_config_cbs *changes, const char *comment, char *errmsg, size_t errmsg_len) { struct nb_transaction *transaction; - if (nb_running_lock_check(context->client, context->user)) { + if (nb_running_lock_check(context.client, context.user)) { strlcpy(errmsg, "running configuration is locked by another client", errmsg_len); @@ -1469,7 +1468,7 @@ static int nb_transaction_process(enum nb_event event, break; /* Call the appropriate callback. */ - ret = nb_callback_configuration(transaction->context, event, + ret = nb_callback_configuration(&transaction->context, event, change, errmsg, errmsg_len); switch (event) { case NB_EV_PREPARE: @@ -1584,7 +1583,7 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction, /* Call the 'apply_finish' callbacks, sorted by their priorities. */ RB_FOREACH (cb, nb_config_cbs, &cbs) - nb_callback_apply_finish(transaction->context, cb->nb_node, + nb_callback_apply_finish(&transaction->context, cb->nb_node, cb->dnode, errmsg, errmsg_len); /* Release memory. */ diff --git a/lib/northbound.h b/lib/northbound.h index c132daebdb..152810b3a9 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -622,22 +622,6 @@ struct nb_context { /* Northbound user (can be NULL). */ const void *user; - - /* Client-specific data. */ -#if 0 - union { - struct { - } cli; - struct { - } confd; - struct { - } sysrepo; - struct { - } grpc; - struct { - } pcep; - } client_data; -#endif }; /* Northbound configuration. */ @@ -666,7 +650,7 @@ struct nb_config_change { /* Northbound configuration transaction. */ struct nb_transaction { - struct nb_context *context; + struct nb_context context; char comment[80]; struct nb_config *config; struct nb_config_cbs changes; @@ -927,7 +911,7 @@ extern int nb_candidate_validate(struct nb_context *context, * the candidate configuration. * - NB_ERR for other errors. */ -extern int nb_candidate_commit_prepare(struct nb_context *context, +extern int nb_candidate_commit_prepare(struct nb_context context, struct nb_config *candidate, const char *comment, struct nb_transaction **transaction, @@ -1014,7 +998,7 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * the candidate configuration. * - NB_ERR for other errors. */ -extern int nb_candidate_commit(struct nb_context *context, +extern int nb_candidate_commit(struct nb_context context, struct nb_config *candidate, bool save_transaction, const char *comment, uint32_t *transaction_id, char *errmsg, diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 0dfa66b37e..fa5884fb78 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -46,7 +46,7 @@ static int nb_cli_classic_commit(struct vty *vty) context.client = NB_CLIENT_CLI; context.user = vty; - ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL, + ret = nb_candidate_commit(context, vty->candidate_config, true, NULL, NULL, errmsg, sizeof(errmsg)); switch (ret) { case NB_OK: @@ -313,7 +313,7 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty) context.client = NB_CLIENT_CLI; context.user = vty; ret = nb_candidate_commit( - &context, vty->confirmed_commit_rollback, true, + context, vty->confirmed_commit_rollback, true, "Rollback to previous configuration - confirmed commit has timed out", &transaction_id, errmsg, sizeof(errmsg)); if (ret == NB_OK) { @@ -394,9 +394,8 @@ static int nb_cli_commit(struct vty *vty, bool force, context.client = NB_CLIENT_CLI; context.user = vty; - ret = nb_candidate_commit(&context, vty->candidate_config, true, - comment, &transaction_id, errmsg, - sizeof(errmsg)); + ret = nb_candidate_commit(context, vty->candidate_config, true, comment, + &transaction_id, errmsg, sizeof(errmsg)); /* Map northbound return code to CLI return code. */ switch (ret) { @@ -1717,7 +1716,7 @@ static int nb_cli_rollback_configuration(struct vty *vty, context.client = NB_CLIENT_CLI; context.user = vty; - ret = nb_candidate_commit(&context, candidate, true, comment, NULL, + ret = nb_candidate_commit(context, candidate, true, comment, NULL, errmsg, sizeof(errmsg)); nb_config_free(candidate); switch (ret) { diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index 81ba313e81..2b57ff2707 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -311,7 +311,7 @@ static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) */ transaction = NULL; context.client = NB_CLIENT_CONFD; - ret = nb_candidate_commit_prepare(&context, candidate, NULL, + ret = nb_candidate_commit_prepare(context, candidate, NULL, &transaction, errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { enum confd_errcode errcode; diff --git a/lib/northbound_db.c b/lib/northbound_db.c index cefcfbcf1f..74abcde955 100644 --- a/lib/northbound_db.c +++ b/lib/northbound_db.c @@ -73,7 +73,7 @@ int nb_db_transaction_save(const struct nb_transaction *transaction, if (!ss) goto exit; - client_name = nb_client_name(transaction->context->client); + client_name = nb_client_name(transaction->context.client); /* * Always record configurations in the XML format, save the default * values too, as this covers the case where defaults may change. diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index f5d59d92d6..1459146eab 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -824,7 +824,7 @@ HandleUnaryCommit(UnaryRpcState<frr::CommitRequest, frr::CommitResponse> *tag) case frr::CommitRequest::PREPARE: grpc_debug("`-> Performing PREPARE"); ret = nb_candidate_commit_prepare( - &context, candidate->config, comment.c_str(), + context, candidate->config, comment.c_str(), &candidate->transaction, errmsg, sizeof(errmsg)); break; case frr::CommitRequest::ABORT: @@ -840,7 +840,7 @@ HandleUnaryCommit(UnaryRpcState<frr::CommitRequest, frr::CommitResponse> *tag) break; case frr::CommitRequest::ALL: grpc_debug("`-> Performing ALL"); - ret = nb_candidate_commit(&context, candidate->config, true, + ret = nb_candidate_commit(context, candidate->config, true, comment.c_str(), &transaction_id, errmsg, sizeof(errmsg)); break; diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 824d81a51e..096414ff24 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -268,7 +268,7 @@ static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, * Validate the configuration changes and allocate all resources * required to apply them. */ - ret = nb_candidate_commit_prepare(&context, candidate, NULL, + ret = nb_candidate_commit_prepare(context, candidate, NULL, &transaction, errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) flog_warn( diff --git a/lib/prefix.c b/lib/prefix.c index 655e28c9f0..a6aae08a6a 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -123,6 +123,23 @@ afi_t family2afi(int family) return 0; } +const char *afi2str_lower(afi_t afi) +{ + switch (afi) { + case AFI_IP: + return "ipv4"; + case AFI_IP6: + return "ipv6"; + case AFI_L2VPN: + return "l2vpn"; + case AFI_MAX: + case AFI_UNSPEC: + return "bad-value"; + } + + assert(!"Reached end of function we should never reach"); +} + const char *afi2str(afi_t afi) { switch (afi) { @@ -1431,9 +1448,11 @@ int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to) switch (addr->route_type) { case BGP_EVPN_MAC_IP_ROUTE: if (IS_IPADDR_V4(&addr->macip_addr.ip)) - ipaddr2prefix(&addr->macip_addr.ip, 32, to); + ipaddr2prefix(&addr->macip_addr.ip, IPV4_MAX_BITLEN, + to); else if (IS_IPADDR_V6(&addr->macip_addr.ip)) - ipaddr2prefix(&addr->macip_addr.ip, 128, to); + ipaddr2prefix(&addr->macip_addr.ip, IPV6_MAX_BITLEN, + to); else return -1; /* mac only? */ diff --git a/lib/prefix.h b/lib/prefix.h index 1fd652507f..9c57283706 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -383,6 +383,7 @@ extern afi_t family2afi(int); extern const char *family2str(int family); extern const char *safi2str(safi_t safi); extern const char *afi2str(afi_t afi); +extern const char *afi2str_lower(afi_t afi); static inline afi_t prefix_afi(union prefixconstptr pu) { @@ -644,7 +645,10 @@ static inline bool ipv4_mcast_ssm(const struct in_addr *addr) #pragma FRR printfrr_ext "%pFX" (struct prefix_eth *) #pragma FRR printfrr_ext "%pFX" (struct prefix_evpn *) #pragma FRR printfrr_ext "%pFX" (struct prefix_fs *) -#pragma FRR printfrr_ext "%pRD" (struct prefix_rd *) +#pragma FRR printfrr_ext "%pRDP" (struct prefix_rd *) +/* RD with AS4B with dot and dot+ format */ +#pragma FRR printfrr_ext "%pRDD" (struct prefix_rd *) +#pragma FRR printfrr_ext "%pRDE" (struct prefix_rd *) #pragma FRR printfrr_ext "%pPSG4" (struct prefix_sg *) #endif diff --git a/lib/routemap.c b/lib/routemap.c index 6b4627082e..9f5c9e693e 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1804,26 +1804,24 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix, struct route_map_index *index = NULL, *best_index = NULL; struct route_map_index *head_index = NULL; struct route_table *table = NULL; - struct prefix conv; - unsigned char family; - /* - * Handling for matching evpn_routes in the prefix table. - * - * We convert type2/5 prefix to ipv4/6 prefix to do longest - * prefix matching on. + /* Route-map optimization relies on LPM lookups of the prefix to reduce + * the amount of route-map clauses a given prefix needs to be processed + * against. These LPM trees are IPv4/IPv6-specific and prefix->family + * must be AF_INET or AF_INET6 in order for the lookup to succeed. So if + * the AF doesn't line up with the LPM trees, skip the optimization. */ - if (prefix->family == AF_EVPN) { - if (evpn_prefix2prefix(prefix, &conv) != 0) - return NULL; - - prefix = &conv; + if (map->optimization_disabled || + (prefix->family == AF_INET && !map->ipv4_prefix_table) || + (prefix->family == AF_INET6 && !map->ipv6_prefix_table)) { + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + zlog_debug( + "Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d", + map->name, prefix, prefix->family); + return map->head; } - - family = prefix->family; - - if (family == AF_INET) + if (prefix->family == AF_INET) table = map->ipv4_prefix_table; else table = map->ipv6_prefix_table; @@ -2545,6 +2543,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map, struct route_map_index *index = NULL; struct route_map_rule *set = NULL; bool skip_match_clause = false; + struct prefix conv; if (recursion > RMAP_RECURSION_LIMIT) { flog_warn( @@ -2562,37 +2561,53 @@ route_map_result_t route_map_apply_ext(struct route_map *map, map->applied++; - if ((!map->optimization_disabled) - && (map->ipv4_prefix_table || map->ipv6_prefix_table)) { - index = route_map_get_index(map, prefix, match_object, - &match_ret); - if (index) { - index->applied++; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + /* + * Handling for matching evpn_routes in the prefix table. + * + * We convert type2/5 prefix to ipv4/6 prefix to do longest + * prefix matching on. + */ + if (prefix->family == AF_EVPN) { + if (evpn_prefix2prefix(prefix, &conv) != 0) { + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) zlog_debug( - "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", - map->name, index->pref, prefix, - route_map_cmd_result_str(match_ret)); + "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup", + prefix); } else { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) zlog_debug( - "No best match sequence for pfx: %pFX in route-map: %s, result: %s", - prefix, map->name, - route_map_cmd_result_str(match_ret)); - /* - * No index matches this prefix. Return deny unless, - * match_ret = RMAP_NOOP. - */ - if (match_ret == RMAP_NOOP) - ret = RMAP_PERMITMATCH; - else - ret = RMAP_DENYMATCH; - goto route_map_apply_end; + "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup", + prefix, &conv); + + prefix = &conv; } - skip_match_clause = true; + } + + index = route_map_get_index(map, prefix, match_object, &match_ret); + if (index) { + index->applied++; + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + zlog_debug( + "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", + map->name, index->pref, prefix, + route_map_cmd_result_str(match_ret)); } else { - index = map->head; + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + zlog_debug( + "No best match sequence for pfx: %pFX in route-map: %s, result: %s", + prefix, map->name, + route_map_cmd_result_str(match_ret)); + /* + * No index matches this prefix. Return deny unless, + * match_ret = RMAP_NOOP. + */ + if (match_ret == RMAP_NOOP) + ret = RMAP_PERMITMATCH; + else + ret = RMAP_DENYMATCH; + goto route_map_apply_end; } + skip_match_clause = true; for (; index; index = index->next) { if (!skip_match_clause) { diff --git a/lib/subdir.am b/lib/subdir.am index 8d00668c8c..beef8675aa 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -13,6 +13,7 @@ lib_libfrr_la_SOURCES = \ lib/affinitymap_northbound.c \ lib/agg_table.c \ lib/atomlist.c \ + lib/asn.c \ lib/base64.c \ lib/bfd.c \ lib/buffer.c \ @@ -169,6 +170,7 @@ pkginclude_HEADERS += \ lib/admin_group.h \ lib/affinitymap.h \ lib/agg_table.h \ + lib/asn.h \ lib/atomlist.h \ lib/base64.h \ lib/bfd.h \ @@ -297,6 +297,13 @@ int vty_json_no_pretty(struct vty *vty, struct json_object *json) return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE); } +void vty_json_empty(struct vty *vty) +{ + json_object *json = json_object_new_object(); + + vty_json(vty, json); +} + /* Output current time to the vty. */ void vty_time_print(struct vty *vty, int cr) { @@ -2413,7 +2420,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp) context.client = NB_CLIENT_CLI; context.user = vty; - ret = nb_candidate_commit(&context, vty->candidate_config, true, + ret = nb_candidate_commit(context, vty->candidate_config, true, "Read configuration file", NULL, errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) @@ -344,7 +344,7 @@ extern bool vty_set_include(struct vty *vty, const char *regexp); */ extern int vty_json(struct vty *vty, struct json_object *json); extern int vty_json_no_pretty(struct vty *vty, struct json_object *json); - +extern void vty_json_empty(struct vty *vty); /* post fd to be passed to the vtysh client * fd is owned by the VTY code after this and will be closed when done */ diff --git a/ospfclient/ospfclient.py b/ospfclient/ospfclient.py index 7a7bfb13cf..7477ef8191 100755 --- a/ospfclient/ospfclient.py +++ b/ospfclient/ospfclient.py @@ -242,6 +242,16 @@ def nsm_name(state): return names.get(state, str(state)) +class WithNothing: + "An object that does nothing when used with `with` statement." + + async def __aenter__(self): + return + + async def __aexit__(self, *args, **kwargs): + return + + # -------------- # Client Classes # -------------- @@ -547,15 +557,17 @@ class OspfOpaqueClient(OspfApiClient): Args: server: hostname or IP address of server default is "localhost" + wait_ready: if True then wait for OSPF to signal ready, in newer versions + FRR ospfd is always ready so this overhead can be skipped. + default is False. Raises: Will raise exceptions for failures with various `socket` modules functions such as `socket.socket`, `socket.setsockopt`, `socket.bind`. """ - def __init__(self, server="localhost"): + def __init__(self, server="localhost", wait_ready=False): handlers = { - MSG_READY_NOTIFY: self._ready_msg, MSG_LSA_UPDATE_NOTIFY: self._lsa_change_msg, MSG_LSA_DELETE_NOTIFY: self._lsa_change_msg, MSG_NEW_IF: self._if_msg, @@ -565,9 +577,13 @@ class OspfOpaqueClient(OspfApiClient): MSG_REACHABLE_CHANGE: self._reachable_msg, MSG_ROUTER_ID_CHANGE: self._router_id_msg, } + if wait_ready: + handlers[MSG_READY_NOTIFY] = self._ready_msg + super().__init__(server, handlers) - self.ready_lock = Lock() + self.wait_ready = wait_ready + self.ready_lock = Lock() if wait_ready else WithNothing() self.ready_cond = { LSA_TYPE_OPAQUE_LINK: {}, LSA_TYPE_OPAQUE_AREA: {}, @@ -604,13 +620,9 @@ class OspfOpaqueClient(OspfApiClient): mp = struct.pack(msg_fmt[mt], lsa_type, otype) await self.msg_send_raises(mt, mp) - async def _assure_opaque_ready(self, lsa_type, otype): - async with self.ready_lock: - if self.ready_cond[lsa_type].get(otype) is True: - return - - await self._register_opaque_data(lsa_type, otype) - await self.wait_opaque_ready(lsa_type, otype) + # If we are not waiting, mark ready for register check + if not self.wait_ready: + self.ready_cond[lsa_type][otype] = True async def _handle_msg_loop(self): try: @@ -643,6 +655,8 @@ class OspfOpaqueClient(OspfApiClient): return lsa async def _ready_msg(self, mt, msg, extra, lsa_type, otype, addr): + assert self.wait_ready + if lsa_type == LSA_TYPE_OPAQUE_LINK: e = "ifaddr {}".format(ip(addr)) elif lsa_type == LSA_TYPE_OPAQUE_AREA: @@ -812,6 +826,7 @@ class OspfOpaqueClient(OspfApiClient): Raises: See `msg_send_raises` """ + assert self.ready_cond.get(lsa_type, {}).get(otype) is True, "Not Registered!" if lsa_type == LSA_TYPE_OPAQUE_LINK: ifaddr, aid = int(addr), 0 @@ -829,7 +844,6 @@ class OspfOpaqueClient(OspfApiClient): *OspfOpaqueClient._opaque_args(lsa_type, otype, oid, data), ) msg += data - await self._assure_opaque_ready(lsa_type, otype) await self.msg_send_raises(mt, msg) async def delete_opaque_data(self, addr, lsa_type, otype, oid, flags=0): @@ -841,21 +855,31 @@ class OspfOpaqueClient(OspfApiClient): Args: addr: depends on lsa_type, LINK => ifaddr, AREA => area ID, AS => ignored lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. oid: (3 octets) ID of this opaque data flags: (octet) optional flags (e.g., OSPF_API_DEL_ZERO_LEN_LSA, defaults to no flags) Raises: See `msg_send_raises` """ - if (lsa_type, otype) in self.opaque_change_cb: - del self.opaque_change_cb[(lsa_type, otype)] + assert self.ready_cond.get(lsa_type, {}).get(otype) is True, "Not Registered!" mt = MSG_DELETE_REQUEST - await self._assure_opaque_ready(lsa_type, otype) mp = struct.pack(msg_fmt[mt], int(addr), lsa_type, otype, flags, oid) await self.msg_send_raises(mt, mp) + async def is_registered(self, lsa_type, otype): + """Determine if an (lsa_type, otype) tuple has been registered with FRR + + This determines if the type has been registered, but not necessarily if it is + ready, if that is required use the `wait_opaque_ready` metheod. + + Args: + lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} + otype: (octet) opaque type. + """ + async with self.ready_lock: + return self.ready_cond.get(lsa_type, {}).get(otype) is not None + async def register_opaque_data(self, lsa_type, otype, callback=None): """Register intent to advertise opaque data. @@ -865,8 +889,7 @@ class OspfOpaqueClient(OspfApiClient): Args: lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. callback: if given, callback will be called when changes are received for LSA of the given (lsa_type, otype). The callbacks signature is: @@ -882,6 +905,10 @@ class OspfOpaqueClient(OspfApiClient): Raises: See `msg_send_raises` """ + assert not await self.is_registered( + lsa_type, otype + ), "Registering registered type" + if callback: self.opaque_change_cb[(lsa_type, otype)] = callback elif (lsa_type, otype) in self.opaque_change_cb: @@ -900,6 +927,8 @@ class OspfOpaqueClient(OspfApiClient): if cond is True: return + assert self.wait_ready + logging.debug( "waiting for ready %s opaque-type %s", lsa_typename(lsa_type), otype ) @@ -920,8 +949,7 @@ class OspfOpaqueClient(OspfApiClient): Args: lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. callback: if given, callback will be called when changes are received for LSA of the given (lsa_type, otype). The callbacks signature is: @@ -938,17 +966,8 @@ class OspfOpaqueClient(OspfApiClient): See `msg_send_raises` """ - if callback: - self.opaque_change_cb[(lsa_type, otype)] = callback - elif (lsa_type, otype) in self.opaque_change_cb: - logging.warning( - "OSPFCLIENT: register: removing callback for %s opaque-type %s", - lsa_typename(lsa_type), - otype, - ) - del self.opaque_change_cb[(lsa_type, otype)] - - return await self._assure_opaque_ready(lsa_type, otype) + await self.register_opaque_data(lsa_type, otype, callback) + await self.wait_opaque_ready(lsa_type, otype) async def unregister_opaque_data(self, lsa_type, otype): """Unregister intent to advertise opaque data. @@ -958,11 +977,13 @@ class OspfOpaqueClient(OspfApiClient): Args: lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS} - otype: (octet) opaque type. Note: the type will be registered if the user - has not explicity done that yet with `register_opaque_data`. + otype: (octet) opaque type. Raises: See `msg_send_raises` """ + assert await self.is_registered( + lsa_type, otype + ), "Unregistering unregistered type" if (lsa_type, otype) in self.opaque_change_cb: del self.opaque_change_cb[(lsa_type, otype)] @@ -1068,6 +1089,17 @@ class OspfOpaqueClient(OspfApiClient): # ================ # CLI/Script Usage # ================ +def next_action(action_list=None): + "Get next action from list or STDIN" + if action_list: + for action in action_list: + yield action + else: + while True: + action = input("") + if not action: + break + yield action.strip() async def async_main(args): @@ -1086,50 +1118,53 @@ async def async_main(args): await c.req_ism_states() await c.req_nsm_states() - if args.actions: - for action in args.actions: - _s = action.split(",") - what = _s.pop(False) - if what.casefold() == "wait": - stime = int(_s.pop(False)) - logging.info("waiting %s seconds", stime) - await asyncio.sleep(stime) - logging.info("wait complete: %s seconds", stime) - continue - ltype = int(_s.pop(False)) - if ltype == 11: - addr = ip(0) - else: - aval = _s.pop(False) - try: - addr = ip(int(aval)) - except ValueError: - addr = ip(aval) - oargs = [addr, ltype, int(_s.pop(False)), int(_s.pop(False))] - if what.casefold() == "add": + for action in next_action(args.actions): + _s = action.split(",") + what = _s.pop(False) + if what.casefold() == "wait": + stime = int(_s.pop(False)) + logging.info("waiting %s seconds", stime) + await asyncio.sleep(stime) + logging.info("wait complete: %s seconds", stime) + continue + ltype = int(_s.pop(False)) + if ltype == 11: + addr = ip(0) + else: + aval = _s.pop(False) + try: + addr = ip(int(aval)) + except ValueError: + addr = ip(aval) + oargs = [addr, ltype, int(_s.pop(False)), int(_s.pop(False))] + + if not await c.is_registered(oargs[1], oargs[2]): + await c.register_opaque_data_wait(oargs[1], oargs[2]) + + if what.casefold() == "add": + try: + b = bytes.fromhex(_s.pop(False)) + except IndexError: + b = b"" + logging.info("opaque data is %s octets", len(b)) + # Needs to be multiple of 4 in length + mod = len(b) % 4 + if mod: + b += b"\x00" * (4 - mod) + logging.info("opaque padding to %s octets", len(b)) + + await c.add_opaque_data(*oargs, b) + else: + assert what.casefold().startswith("del") + f = 0 + if len(_s) >= 1: try: - b = bytes.fromhex(_s.pop(False)) + f = int(_s.pop(False)) except IndexError: - b = b"" - logging.info("opaque data is %s octets", len(b)) - # Needs to be multiple of 4 in length - mod = len(b) % 4 - if mod: - b += b"\x00" * (4 - mod) - logging.info("opaque padding to %s octets", len(b)) - - await c.add_opaque_data(*oargs, b) - else: - assert what.casefold().startswith("del") - f = 0 - if len(_s) >= 1: - try: - f = int(_s.pop(False)) - except IndexError: - f = 0 - await c.delete_opaque_data(*oargs, f) - if args.exit: - return 0 + f = 0 + await c.delete_opaque_data(*oargs, f) + if not args.actions or args.exit: + return 0 except Exception as error: logging.error("async_main: unexpected error: %s", error, exc_info=True) return 2 @@ -1145,19 +1180,23 @@ async def async_main(args): def main(*args): ap = argparse.ArgumentParser(args) + ap.add_argument("--logtag", default="CLIENT", help="tag to identify log messages") ap.add_argument("--exit", action="store_true", help="Exit after commands") ap.add_argument("--server", default="localhost", help="OSPF API server") ap.add_argument("-v", "--verbose", action="store_true", help="be verbose") ap.add_argument( "actions", nargs="*", - help="(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA|DEL_FLAG]", + help="WAIT,SEC|(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA|DEL_FLAG]", ) args = ap.parse_args() level = logging.DEBUG if args.verbose else logging.INFO logging.basicConfig( - level=level, format="%(asctime)s %(levelname)s: CLIENT: %(name)s %(message)s" + level=level, + format="%(asctime)s %(levelname)s: {}: %(name)s %(message)s".format( + args.logtag + ), ) logging.info("ospfclient: starting") diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index 55cb8b183a..8f177cbce1 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -1559,6 +1559,254 @@ static void ospf_abr_announce_stub_defaults(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } +/** @brief Function to check and generate indication + * LSA for area on which we received + * indication LSA flush. + * @param Ospf instance. + * @param Area on which indication lsa flush is to be generated. + * @return Void. + */ +void ospf_generate_indication_lsa(struct ospf *ospf, struct ospf_area *area) +{ + bool area_fr_not_supp = false; + + /* Check if you have any area which doesn't support + * flood reduction. + */ + + area_fr_not_supp = ospf_check_fr_enabled_all(ospf) ? false : true; + + /* If any one of the area doestn't support FR, generate + * indication LSA on behalf of that area. + */ + + if (area_fr_not_supp && !area->fr_info.area_ind_lsa_recvd && + !area->fr_info.indication_lsa_self && + !area->fr_info.area_dc_clear) { + + struct prefix_ipv4 p; + struct ospf_lsa *new; + + p.family = AF_INET; + p.prefix = ospf->router_id; + p.prefixlen = IPV4_MAX_BITLEN; + + new = ospf_summary_asbr_lsa_originate(&p, OSPF_LS_INFINITY, + area); + if (!new) { + zlog_debug("%s: Indication lsa originate failed", + __func__); + return; + } + /* save the indication lsa for that area */ + area->fr_info.indication_lsa_self = new; + } +} + +/** @brief Function to receive and process indication LSA + * flush from area. + * @param lsa being flushed. + * @return Void. + */ +void ospf_recv_indication_lsa_flush(struct ospf_lsa *lsa) +{ + if (!IS_LSA_SELF(lsa) && IS_LSA_MAXAGE(lsa) && + ospf_check_indication_lsa(lsa)) { + lsa->area->fr_info.area_ind_lsa_recvd = false; + + OSPF_LOG_INFO("%s: Received an ind lsa: %pI4 area %pI4", + __func__, &lsa->data->id, &lsa->area->area_id); + + if (!IS_OSPF_ABR(lsa->area->ospf)) + return; + + /* If the LSA received is a indication LSA with maxage on + * the network, then check and regenerate indication + * LSA if any of our areas don't support flood reduction. + */ + ospf_generate_indication_lsa(lsa->area->ospf, lsa->area); + } +} + +/** @brief Function to generate indication LSAs. + * @param Ospf instance. + * @param Area on behalf of which indication + * LSA is generated LSA. + * @return Void. + */ +void ospf_abr_generate_indication_lsa(struct ospf *ospf, + const struct ospf_area *area) +{ + struct ospf_lsa *new; + struct listnode *node; + struct ospf_area *o_area; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, o_area)) { + if (o_area == area) + continue; + + if (o_area->fr_info.indication_lsa_self || + o_area->fr_info.area_ind_lsa_recvd || + o_area->fr_info.area_dc_clear) { + /* if the area has already received an + * indication LSA or if area already has + * LSAs with DC bit 0 other than + * indication LSA then don't generate + * indication LSA in those areas. + */ + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Area %pI4 has LSAs with dc bit clear", + &o_area->area_id); + continue; + + } else { + + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = ospf->router_id; + p.prefixlen = IPV4_MAX_BITLEN; + + new = ospf_summary_asbr_lsa_originate( + &p, OSPF_LS_INFINITY, o_area); + if (!new) { + zlog_debug( + "%s: Indication lsa originate Failed", + __func__); + return; + } + /* save the indication lsa for that area */ + o_area->fr_info.indication_lsa_self = new; + } + } +} + +/** @brief Flush the indication LSA from all the areas + * of ospf instance. + * @param Ospf instance. + * @return Void. + */ +void ospf_flush_indication_lsas(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area->fr_info.indication_lsa_self) { + OSPF_LOG_INFO( + "Flushing ind lsa: %pI4 area %pI4", + &area->fr_info.indication_lsa_self->data->id, + &area->area_id); + ospf_schedule_lsa_flush_area( + area, area->fr_info.indication_lsa_self); + area->fr_info.indication_lsa_self = NULL; + } + } +} + +/** @brief Check if flood reduction is enabled on + * all the areas. + * @param Ospf instance. + * @return Void. + */ +bool ospf_check_fr_enabled_all(struct ospf *ospf) +{ + const struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (!ospf_check_area_fr_enabled(area)) + return false; + + return true; +} + +/** @brief Abr function to check conditions for generation + * of indication. LSAs/announcing non-DNA routers + * in the area. + * @param thread + * @return 0. + */ +static void ospf_abr_announce_non_dna_routers(struct thread *thread) +{ + struct ospf_area *area; + struct listnode *node; + struct ospf *ospf = THREAD_ARG(thread); + + THREAD_OFF(ospf->t_abr_fr); + + if (!IS_OSPF_ABR(ospf)) + return; + + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "%s: Area %pI4 FR enabled: %d", __func__, + &area->area_id, area->fr_info.enabled); + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "LSA with DC bit clear: %d Recived indication LSA: %d", + area->fr_info.area_dc_clear, + area->fr_info.area_ind_lsa_recvd); + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "FR state change: %d", + area->fr_info.state_changed); + if (!OSPF_IS_AREA_BACKBONE(area) && + area->fr_info.area_dc_clear) { + /* rfc4136 rfc1793: Suppose if the abr is connected to + * a regular non-backbone OSPF area, Furthermore if + * the area has LSAs with the DC-bit clear, other + * than indication-LSAs. Then originate indication-LSAs + * into all other directly-connected "regular" areas, + * including the backbone area. + */ + ospf_abr_generate_indication_lsa(ospf, area); + } + + if (OSPF_IS_AREA_BACKBONE(area) && + (area->fr_info.area_dc_clear || + area->fr_info.area_ind_lsa_recvd)) { + /* rfc4136 rfc1793: Suppose if the abr is connected to + * backbone OSPF area. Furthermore, if backbone has + * LSAs with the DC-bit clear that are either + * a) not indication-LSAs or indication-LSAs or + * b) indication-LSAs that have been originated by + * other routers, + * then originate indication-LSAs into all other + * directly-connected "regular" non-backbone areas. + */ + ospf_abr_generate_indication_lsa(ospf, area); + } + + if (area->fr_info.enabled && area->fr_info.state_changed && + area->fr_info.indication_lsa_self) { + /* Ospf area flood reduction state changed + * area now supports flood reduction. + * check if all other areas support flood reduction + * if yes then flush indication LSAs generated in + * all the areas. + */ + if (ospf_check_fr_enabled_all(ospf)) + ospf_flush_indication_lsas(ospf); + + area->fr_info.state_changed = false; + } + + /* If previously we had generated indication lsa + * but now area has lsas with dc bit set to 0 + * apart from indication lsa, we'll clear indication lsa + */ + if (area->fr_info.area_dc_clear && + area->fr_info.indication_lsa_self) { + ospf_schedule_lsa_flush_area( + area, area->fr_info.indication_lsa_self); + area->fr_info.indication_lsa_self = NULL; + } + } + + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Stop", __func__); +} + static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf, struct ospf_lsa *lsa) { @@ -1613,9 +1861,13 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf) ospf_lsa_flush_area(lsa, area); LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) - if (ospf_lsa_is_self_originated(ospf, lsa)) - if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) - ospf_lsa_flush_area(lsa, area); + if (ospf_lsa_is_self_originated(ospf, lsa) && + !CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED) && + /* Do not remove indication LSAs while + * flushing unapproved summaries. + */ + !ospf_check_indication_lsa(lsa)) + ospf_lsa_flush_area(lsa, area); } if (IS_DEBUG_OSPF_EVENT) @@ -1778,6 +2030,20 @@ void ospf_abr_task(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: announce stub defaults", __func__); ospf_abr_announce_stub_defaults(ospf); + + if (ospf->fr_configured) { + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "%s(): announce non-DNArouters", + __func__); + /* + * Schedule indication lsa generation timer, + * giving time for route synchronization in + * all the routers. + */ + thread_add_timer( + master, ospf_abr_announce_non_dna_routers, ospf, + OSPF_ABR_DNA_TIMER, &ospf->t_abr_fr); + } } if (IS_DEBUG_OSPF_EVENT) diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h index 21e87669fa..19d444b125 100644 --- a/ospfd/ospf_abr.h +++ b/ospfd/ospf_abr.h @@ -8,6 +8,11 @@ #define _ZEBRA_OSPF_ABR_H #define OSPF_ABR_TASK_DELAY 5 +#define OSPF_ABR_DNA_TIMER 10 +/* Delay in announceing Non-DNA routers + * so that LSAs are completely synced + * before generating indication LSAs. + */ #define OSPF_AREA_RANGE_ADVERTISE (1 << 0) #define OSPF_AREA_RANGE_SUBSTITUTE (1 << 1) @@ -69,4 +74,20 @@ extern void ospf_schedule_abr_task(struct ospf *); extern void ospf_abr_announce_network_to_area(struct prefix_ipv4 *, uint32_t, struct ospf_area *); extern void ospf_abr_nssa_check_status(struct ospf *ospf); +extern void ospf_abr_generate_indication_lsa(struct ospf *ospf, + const struct ospf_area *area); +extern void ospf_flush_indication_lsas(struct ospf *ospf); +extern void ospf_generate_indication_lsa(struct ospf *ospf, + struct ospf_area *area); +extern bool ospf_check_fr_enabled_all(struct ospf *ospf); +extern void ospf_recv_indication_lsa_flush(struct ospf_lsa *lsa); + +/** @brief Static inline functions. + * @param Area pointer. + * @return area Flood Reduction status. + */ +static inline bool ospf_check_area_fr_enabled(const struct ospf_area *area) +{ + return area->fr_info.enabled ? true : false; +} #endif /* _ZEBRA_OSPF_ABR_H */ diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 728945032b..5e4fc30a28 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -2578,9 +2578,12 @@ static inline int cmp_route_nodes(struct route_node *orn, return 1; else if (!nrn) return -1; - else if (orn->p.u.prefix4.s_addr < nrn->p.u.prefix4.s_addr) + + uint32_t opn = ntohl(orn->p.u.prefix4.s_addr); + uint32_t npn = ntohl(nrn->p.u.prefix4.s_addr); + if (opn < npn) return -1; - else if (orn->p.u.prefix4.s_addr > nrn->p.u.prefix4.s_addr) + else if (opn > npn) return 1; else return 0; diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index de51500b0a..b74b84e37d 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -133,13 +133,6 @@ const char *ospf_if_name_string(struct ospf_interface *oi) return buf; } -/* Display only the nbr state.*/ -void ospf_nbr_state_message(struct ospf_neighbor *nbr, char *buf, size_t size) -{ - snprintf(buf, size, "%s", - lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); -} - int ospf_nbr_ism_state(struct ospf_neighbor *nbr) { int state; diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 596d49256f..0f217971ee 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -140,8 +140,6 @@ extern const char *ospf_area_name_string(struct ospf_area *); extern const char *ospf_area_desc_string(struct ospf_area *); extern const char *ospf_if_name_string(struct ospf_interface *); extern int ospf_nbr_ism_state(struct ospf_neighbor *nbr); -extern void ospf_nbr_state_message(struct ospf_neighbor *nbr, char *buf, - size_t size); extern void ospf_nbr_ism_state_message(struct ospf_neighbor *nbr, char *buf, size_t size); extern const char *ospf_timer_dump(struct thread *, char *, size_t); diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index debbcd964f..d0453bbc4a 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -35,6 +35,75 @@ extern struct zclient *zclient; +/** @brief Function to refresh type-5 and type-7 DNA + * LSAs when we receive an indication LSA. + * @param Ospf instance. + * @return Void. + */ +void ospf_refresh_dna_type5_and_type7_lsas(struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_lsa *lsa = NULL; + + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ospf_lsa_refresh(ospf, lsa); + + LSDB_LOOP (NSSA_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ospf_lsa_refresh(ospf, lsa); +} + +/** @brief Function to update area flood reduction states. + * @param area pointer. + * @return Void. + */ +void ospf_area_update_fr_state(struct ospf_area *area) +{ + unsigned int count_router_lsas = 0; + + if (area == NULL) + return; + + count_router_lsas = + (unsigned int)(ospf_lsdb_count(area->lsdb, OSPF_ROUTER_LSA) - + ospf_lsdb_count_self(area->lsdb, + OSPF_ROUTER_LSA)); + + if (count_router_lsas > + (unsigned int)area->fr_info.router_lsas_recv_dc_bit) { + area->fr_info.enabled = false; + area->fr_info.area_dc_clear = true; + return; + } else if (count_router_lsas < + (unsigned int)area->fr_info.router_lsas_recv_dc_bit) { + /* This can never happen, total number of router lsas received + * can never be less than router lsas received with dc bit set + */ + OSPF_LOG_ERR("%s: Counter mismatch for area %pI4", __func__, + &area->area_id); + OSPF_LOG_ERR( + "%s: router LSAs in lsdb %d router LSAs recvd with dc bit set %d", + __func__, count_router_lsas, + area->fr_info.router_lsas_recv_dc_bit); + return; + } + + area->fr_info.area_dc_clear = false; + + if (OSPF_FR_CONFIG(area->ospf, area)) { + if (!area->fr_info.enabled) { + area->fr_info.enabled = true; + area->fr_info.state_changed = true; + } + } else { + area->fr_info.enabled = false; + area->fr_info.area_dc_clear = true; + } +} + /* Do the LSA acking specified in table 19, Section 13.5, row 2 * This get called from ospf_flood_out_interface. Declared inline * for speed. */ @@ -413,6 +482,55 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, if (!(new = ospf_lsa_install(ospf, oi, new))) return -1; /* unknown LSA type or any other error condition */ + /* check if the installed LSA is an indication LSA */ + if (ospf_check_indication_lsa(new) && !IS_LSA_SELF(new) && + !IS_LSA_MAXAGE(new)) { + new->area->fr_info.area_ind_lsa_recvd = true; + /* check if there are already type 5 LSAs originated + * with DNA bit set, if yes reoriginate those LSAs. + */ + ospf_refresh_dna_type5_and_type7_lsas(ospf); + } + + /* Check if we recived an indication LSA flush on backbone + * network. + */ + ospf_recv_indication_lsa_flush(new); + + if (new->area && OSPF_FR_CONFIG(ospf, new->area)) { + struct lsa_header const *lsah = new->data; + + if (!CHECK_FLAG(lsah->options, OSPF_OPTION_DC) && + !ospf_check_indication_lsa(new)) { + + new->area->fr_info.area_dc_clear = true; + /* check of previously area supported flood reduction */ + if (new->area->fr_info.enabled) { + new->area->fr_info.enabled = false; + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "Flood Reduction STATE on -> off by %s LSA", + dump_lsa_key(new)); + /* if yes update all the lsa to the area the + * new LSAs will have DNA bit set to 0. + */ + ospf_refresh_area_self_lsas(new->area); + } + } else if (!new->area->fr_info.enabled) { + /* check again after installing new LSA that area + * supports flood reduction. + */ + ospf_area_update_fr_state(new->area); + if (new->area->fr_info.enabled) { + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "Flood Reduction STATE off -> on by %s LSA", + dump_lsa_key(new)); + ospf_refresh_area_self_lsas(new->area); + } + } + } + /* Acknowledge the receipt of the LSA by sending a Link State Acknowledgment packet back out the receiving interface. */ if (lsa_ack_flag) @@ -450,6 +568,25 @@ int ospf_flood_through_interface(struct ospf_interface *oi, if (!ospf_if_is_enable(oi)) return 0; + /* If flood reduction is configured, set the DC bit on the lsa. */ + if (IS_LSA_SELF(lsa)) { + if (OSPF_FR_CONFIG(oi->area->ospf, oi->area)) { + if (!ospf_check_indication_lsa(lsa)) { + SET_FLAG(lsa->data->options, OSPF_OPTION_DC); + ospf_lsa_checksum(lsa->data); + } + } else if (CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) { + UNSET_FLAG(lsa->data->options, OSPF_OPTION_DC); + ospf_lsa_checksum(lsa->data); + } + + /* If flood reduction is enabled then set DNA bit on the + * self lsas. + */ + if (oi->area->fr_info.enabled) + SET_FLAG(lsa->data->ls_age, DO_NOT_AGE); + } + /* Remember if new LSA is added to a retransmit list. */ retx_flag = 0; diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h index 6dce6937bf..3757400d0c 100644 --- a/ospfd/ospf_flood.h +++ b/ospfd/ospf_flood.h @@ -53,5 +53,7 @@ extern struct external_info *ospf_external_info_check(struct ospf *, struct ospf_lsa *); extern void ospf_lsdb_init(struct ospf_lsdb *); +extern void ospf_area_update_fr_state(struct ospf_area *area); +extern void ospf_refresh_dna_type5_and_type7_lsas(struct ospf *ospf); #endif /* _ZEBRA_OSPF_FLOOD_H */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index ffbfc2af27..49e342d72b 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -70,6 +70,16 @@ uint32_t get_metric(uint8_t *metric) return m; } +/** @brief The Function checks self generated DoNotAge. + * @param lsa pointer. + * @return true or false. + */ +bool ospf_check_dna_lsa(const struct ospf_lsa *lsa) +{ + return ((IS_LSA_SELF(lsa) && CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ? true + : false); +} struct timeval int2tv(int a) { @@ -121,6 +131,16 @@ int get_age(struct ospf_lsa *lsa) { struct timeval rel; + /* As per rfc4136, the self-originated LSAs in their + * own database keep aging, however rfc doesn't tell + * till how long the LSA should be aged, as of now + * we are capping it for OSPF_LSA_MAXAGE. + */ + + /* If LSA is marked as donotage */ + if (CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) && !IS_LSA_SELF(lsa)) + return ntohs(lsa->data->ls_age); + monotime_since(&lsa->tv_recv, &rel); return ntohs(lsa->data->ls_age) + rel.tv_sec; } @@ -1119,6 +1139,10 @@ static struct ospf_lsa *ospf_network_lsa_refresh(struct ospf_lsa *lsa) } return NULL; } + + if (oi->state != ISM_DR) + return NULL; + /* Delete LSA from neighbor retransmit-list. */ ospf_ls_retransmit_delete_nbr_area(area, lsa); @@ -1518,10 +1542,15 @@ static struct ospf_lsa *ospf_summary_asbr_lsa_refresh(struct ospf *ospf, struct ospf_lsa *new; struct summary_lsa *sl; struct prefix p; + bool ind_lsa = false; /* Sanity check. */ assert(lsa->data); + if (lsa->area->fr_info.indication_lsa_self && + (lsa->area->fr_info.indication_lsa_self == lsa)) + ind_lsa = true; + sl = (struct summary_lsa *)lsa->data; p.prefixlen = ip_masklen(sl->mask); new = ospf_summary_asbr_lsa_new(lsa->area, &p, GET_METRIC(sl->metric), @@ -1536,6 +1565,9 @@ static struct ospf_lsa *ospf_summary_asbr_lsa_refresh(struct ospf *ospf, /* Flood LSA through area. */ ospf_flood_through_area(new->area, NULL, new); + if (ind_lsa) + new->area->fr_info.indication_lsa_self = new; + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { zlog_debug("LSA[Type%d:%pI4]: summary-ASBR-LSA refresh", new->data->type, &new->data->id); @@ -3626,6 +3658,49 @@ void ospf_flush_self_originated_lsas_now(struct ospf *ospf) return; } +/** @brief Function to refresh all the self originated + * LSAs for area, when FR state change happens. + * @param area pointer. + * @return Void. + */ +void ospf_refresh_area_self_lsas(struct ospf_area *area) +{ + struct listnode *node2; + struct listnode *nnode2; + struct ospf_interface *oi; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (!area) + return; + + if (area->router_lsa_self) + ospf_lsa_refresh(area->ospf, area->router_lsa_self); + + for (ALL_LIST_ELEMENTS(area->oiflist, node2, nnode2, oi)) + if (oi->network_lsa_self) + ospf_lsa_refresh(oi->ospf, oi->network_lsa_self); + + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (EXTERNAL_LSDB(area->ospf), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_AS_LSDB(area->ospf), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); +} + /* If there is self-originated LSA, then return 1, otherwise return 0. */ /* An interface-independent version of ospf_lsa_is_self_originated */ int ospf_lsa_is_self_originated(struct ospf *ospf, struct ospf_lsa *lsa) @@ -3961,6 +4036,7 @@ void ospf_lsa_refresh_walker(struct thread *t) struct ospf_lsa *lsa; int i; struct list *lsa_to_refresh = list_new(); + bool dna_lsa; if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) zlog_debug("LSA[Refresh]: %s: start", __func__); @@ -4019,10 +4095,14 @@ void ospf_lsa_refresh_walker(struct thread *t) ospf->lsa_refresher_started = monotime(NULL); for (ALL_LIST_ELEMENTS(lsa_to_refresh, node, nnode, lsa)) { - ospf_lsa_refresh(ospf, lsa); - assert(lsa->lock > 0); - ospf_lsa_unlock( - &lsa); /* lsa_refresh_queue & temp for lsa_to_refresh*/ + dna_lsa = ospf_check_dna_lsa(lsa); + if (!dna_lsa) { /* refresh only non-DNA LSAs */ + ospf_lsa_refresh(ospf, lsa); + assert(lsa->lock > 0); + ospf_lsa_unlock(&lsa); /* lsa_refresh_queue & temp for + * lsa_to_refresh. + */ + } } list_delete(&lsa_to_refresh); diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 6fa6399a90..3c7ea3fda5 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -45,6 +45,7 @@ /* OSPF LSA header. */ struct lsa_header { uint16_t ls_age; +#define DO_NOT_AGE 0x8000 uint8_t options; uint8_t type; struct in_addr id; @@ -218,6 +219,9 @@ enum lsid_status { LSID_AVAILABLE = 0, LSID_CHANGE, LSID_NOT_AVAILABLE }; || (type == OSPF_SUMMARY_LSA) || (type == OSPF_ASBR_SUMMARY_LSA) \ || (type == OSPF_AS_EXTERNAL_LSA) || (type == OSPF_AS_NSSA_LSA)) +#define OSPF_FR_CONFIG(o, a) \ + (o->fr_configured || ((a != NULL) ? a->fr_info.configured : 0)) + /* Prototypes. */ /* XXX: Eek, time functions, similar are in lib/thread.c */ extern struct timeval int2tv(int); @@ -343,4 +347,24 @@ extern void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); extern void ospf_maxage_lsa_remover(struct thread *thread); +extern bool ospf_check_dna_lsa(const struct ospf_lsa *lsa); +extern void ospf_refresh_area_self_lsas(struct ospf_area *area); + +/** @brief Check if the LSA is an indication LSA. + * @param lsa pointer. + * @return true or false based on lsa info. + */ +static inline bool ospf_check_indication_lsa(struct ospf_lsa *lsa) +{ + struct summary_lsa *sl = NULL; + + if (lsa->data->type == OSPF_ASBR_SUMMARY_LSA) { + sl = (struct summary_lsa *)lsa->data; + if ((GET_METRIC(sl->metric) == OSPF_LS_INFINITY) && + !CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + return true; + } + + return false; +} #endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index 0d18e9f77e..0111c4924e 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -77,6 +77,21 @@ static void ospf_lsdb_delete_entry(struct ospf_lsdb *lsdb, lsdb->type[lsa->data->type].count--; lsdb->type[lsa->data->type].checksum -= ntohs(lsa->data->checksum); lsdb->total--; + + /* Decrement number of router LSAs received with DC bit set */ + if (lsa->area && (lsa->area->lsdb == lsdb) && !IS_LSA_SELF(lsa) && + (lsa->data->type == OSPF_ROUTER_LSA) && + CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + lsa->area->fr_info.router_lsas_recv_dc_bit--; + + /* + * If the LSA being deleted is indication LSA, then set the + * pointer to NULL. + */ + if (lsa->area && lsa->area->fr_info.indication_lsa_self && + (lsa->area->fr_info.indication_lsa_self == lsa)) + lsa->area->fr_info.indication_lsa_self = NULL; + rn->info = NULL; route_unlock_node(rn); #ifdef MONITOR_LSDB_CHANGE @@ -113,6 +128,12 @@ void ospf_lsdb_add(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) lsdb->type[lsa->data->type].count++; lsdb->total++; + /* Increment number of router LSAs received with DC bit set */ + if (lsa->area && (lsa->area->lsdb == lsdb) && !IS_LSA_SELF(lsa) && + (lsa->data->type == OSPF_ROUTER_LSA) && + CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + lsa->area->fr_info.router_lsas_recv_dc_bit++; + #ifdef MONITOR_LSDB_CHANGE if (lsdb->new_lsa_hook != NULL) (*lsdb->new_lsa_hook)(lsa); diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 2af5d09edc..2e8e48bb5a 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -19,6 +19,7 @@ #include "thread.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ +#include "printfrr.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -1147,11 +1148,13 @@ void ospf_opaque_config_write_debug(struct vty *vty) void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, json_object *json) { + char buf[128], *bp; struct lsa_header *lsah = lsa->data; uint32_t lsid = ntohl(lsah->id.s_addr); uint8_t opaque_type = GET_OPAQUE_TYPE(lsid); uint32_t opaque_id = GET_OPAQUE_ID(lsid); struct ospf_opaque_functab *functab; + int len, lenValid; /* Switch output functionality by vty address. */ if (vty != NULL) { @@ -1170,11 +1173,19 @@ void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, json, "opaqueType", ospf_opaque_type_name(opaque_type)); json_object_int_add(json, "opaqueId", opaque_id); - json_object_int_add(json, "opaqueDataLength", - ntohs(lsah->length) - - OSPF_LSA_HEADER_SIZE); + len = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE; + json_object_int_add(json, "opaqueDataLength", len); + lenValid = VALID_OPAQUE_INFO_LEN(lsah); json_object_boolean_add(json, "opaqueDataLengthValid", - VALID_OPAQUE_INFO_LEN(lsah)); + lenValid); + if (lenValid) { + bp = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), + "%*pHXn", (int)len, + (lsah + 1)); + json_object_string_add(json, "opaqueData", buf); + if (bp != buf) + XFREE(MTYPE_TMP, bp); + } } } else { zlog_debug(" Opaque-Type %u (%s)", opaque_type, diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 4c2f5d72b3..5268c9896b 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -28,6 +28,7 @@ #include "ospfd/ospf_network.h" #include "ospfd/ospf_interface.h" #include "ospfd/ospf_ism.h" +#include "ospfd/ospf_abr.h" #include "ospfd/ospf_asbr.h" #include "ospfd/ospf_lsa.h" #include "ospfd/ospf_lsdb.h" @@ -3317,6 +3318,14 @@ static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) else stream_putw(s, 0); /* hello-interval of 0 for fast-hellos */ + /* Check if flood-reduction is enabled, + * if yes set the DC bit in the options. + */ + if (OSPF_FR_CONFIG(oi->ospf, oi->area)) + SET_FLAG(OPTIONS(oi), OSPF_OPTION_DC); + else if (CHECK_FLAG(OPTIONS(oi), OSPF_OPTION_DC)) + UNSET_FLAG(OPTIONS(oi), OSPF_OPTION_DC); + if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: options: %x, int: %s", __func__, OPTIONS(oi), IF_NAME(oi)); @@ -3405,6 +3414,8 @@ static int ospf_make_db_desc(struct ospf_interface *oi, options = OPTIONS(oi); if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) SET_FLAG(options, OSPF_OPTION_O); + if (OSPF_FR_CONFIG(oi->ospf, oi->area)) + SET_FLAG(options, OSPF_OPTION_DC); stream_putc(s, options); /* DD flags */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index bcedd36ce2..5f18bff1cf 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -348,44 +348,49 @@ void ospf_route_install(struct ospf *ospf, struct route_table *rt) /* RFC2328 16.1. (4). For "router". */ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, - struct ospf_area *area, bool add_all) + struct ospf_area *area, bool add_only) { struct route_node *rn; struct ospf_route * or ; struct prefix_ipv4 p; struct router_lsa *lsa; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: Start", __func__); - + if (IS_DEBUG_OSPF_EVENT) { + if (!add_only) + zlog_debug("%s: Start", __func__); + else + zlog_debug("%s: REACHRUN: Start", __func__); + } lsa = (struct router_lsa *)v->lsa; if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: LS ID: %pI4", __func__, &lsa->header.id); - if (!OSPF_IS_AREA_BACKBONE(area)) - ospf_vl_up_check(area, lsa->header.id, v); + if (!add_only) { + if (!OSPF_IS_AREA_BACKBONE(area)) + ospf_vl_up_check(area, lsa->header.id, v); - if (!CHECK_FLAG(lsa->flags, ROUTER_LSA_SHORTCUT)) - area->shortcut_capability = 0; + if (!CHECK_FLAG(lsa->flags, ROUTER_LSA_SHORTCUT)) + area->shortcut_capability = 0; - /* If the newly added vertex is an area border router or AS boundary - router, a routing table entry is added whose destination type is - "router". */ - if (!add_all && !IS_ROUTER_LSA_BORDER(lsa) && - !IS_ROUTER_LSA_EXTERNAL(lsa)) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "%s: this router is neither ASBR nor ABR, skipping it", - __func__); - return; - } + /* If the newly added vertex is an area border router or AS + boundary router, a routing table entry is added whose + destination type is "router". */ + if (!IS_ROUTER_LSA_BORDER(lsa) && + !IS_ROUTER_LSA_EXTERNAL(lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this router is neither ASBR nor ABR, skipping it", + __func__); + return; + } - /* Update ABR and ASBR count in this area. */ - if (IS_ROUTER_LSA_BORDER(lsa)) - area->abr_count++; - if (IS_ROUTER_LSA_EXTERNAL(lsa)) - area->asbr_count++; + /* Update ABR and ASBR count in this area. */ + if (IS_ROUTER_LSA_BORDER(lsa)) + area->abr_count++; + if (IS_ROUTER_LSA_EXTERNAL(lsa)) + area->asbr_count++; + } /* The Options field found in the associated router-LSA is copied into the routing table entry's Optional capabilities field. Call @@ -433,8 +438,12 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, listnode_add(rn->info, or); - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: Stop", __func__); + if (IS_DEBUG_OSPF_EVENT) { + if (!add_only) + zlog_debug("%s: Stop", __func__); + else + zlog_debug("%s: REACHRUN: Stop", __func__); + } } /* RFC2328 16.1. (4). For transit network. */ @@ -971,6 +980,16 @@ void ospf_prune_unreachable_routers(struct route_table *rtrs) &or->u.std.area_id); } + /* Unset the DNA flag on lsa, if the router + * which generated this lsa is no longer + * reachabele. + */ + (CHECK_FLAG(or->u.std.origin->ls_age, + DO_NOT_AGE)) + ? UNSET_FLAG(or->u.std.origin->ls_age, + DO_NOT_AGE) + : 0; + listnode_delete(paths, or); ospf_route_free(or); } diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index b3e543d225..eb03a9c3a7 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3038,6 +3038,43 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area, ospf_lsdb_checksum(area->lsdb, OSPF_OPAQUE_AREA_LSA)); } + if (area->fr_info.configured) { + if (use_json) + json_object_string_add(json_area, "areaFloodReduction", + "configured"); + else + vty_out(vty, " Flood Reduction is configured.\n"); + } + + if (area->fr_info.enabled) { + if (use_json) { + json_object_boolean_true_add( + json_area, "areaFloodReductionEnabled"); + if (area->fr_info.router_lsas_recv_dc_bit) + json_object_boolean_true_add( + json_area, "lsasRecvDCbitSet"); + if (area->fr_info.area_ind_lsa_recvd) + json_object_string_add(json_area, + "areaIndicationLsaRecv", + "received"); + if (area->fr_info.indication_lsa_self) + json_object_string_addf( + json_area, "areaIndicationLsa", "%pI4", + &area->fr_info.indication_lsa_self->data + ->id); + } else { + vty_out(vty, " Flood Reduction is enabled.\n"); + vty_out(vty, " No of LSAs rcv'd with DC bit set %d\n", + area->fr_info.router_lsas_recv_dc_bit); + if (area->fr_info.area_ind_lsa_recvd) + vty_out(vty, " Ind LSA by other abr.\n"); + if (area->fr_info.indication_lsa_self) + vty_out(vty, " Ind LSA generated %pI4\n", + &area->fr_info.indication_lsa_self->data + ->id); + } + } + if (use_json) json_object_object_add(json_areas, inet_ntop(AF_INET, &area->area_id, @@ -3273,6 +3310,14 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, : ZEBRA_OSPF_DISTANCE_DEFAULT); } + if (ospf->fr_configured) { + if (json) + json_object_string_add(json_vrf, "floodReduction", + "configured"); + else + vty_out(vty, " Flood Reduction is configured.\n"); + } + /* Show ABR/ASBR flags. */ if (CHECK_FLAG(ospf->flags, OSPF_FLAG_ABR)) { if (json) @@ -4855,9 +4900,8 @@ DEFUN (show_ip_ospf_instance_neighbor_all, } static int show_ip_ospf_neighbor_int_common(struct vty *vty, struct ospf *ospf, - int arg_base, - struct cmd_token **argv, - bool use_json, uint8_t use_vrf) + const char *ifname, bool use_json, + uint8_t use_vrf) { struct interface *ifp; struct route_node *rn; @@ -4876,7 +4920,7 @@ static int show_ip_ospf_neighbor_int_common(struct vty *vty, struct ospf *ospf, ospf_show_vrf_name(ospf, vty, json, use_vrf); - ifp = if_lookup_by_name(argv[arg_base]->arg, ospf->vrf_id); + ifp = if_lookup_by_name(ifname, ospf->vrf_id); if (!ifp) { if (use_json) json_object_boolean_true_add(json, "noSuchIface"); @@ -4902,76 +4946,22 @@ static int show_ip_ospf_neighbor_int_common(struct vty *vty, struct ospf *ospf, return CMD_SUCCESS; } -DEFUN (show_ip_ospf_neighbor_int, - show_ip_ospf_neighbor_int_cmd, - "show ip ospf [vrf <NAME>] neighbor IFNAME [json]", - SHOW_STR - IP_STR - "OSPF information\n" - VRF_CMD_HELP_STR - "Neighbor list\n" - "Interface name\n" - JSON_STR) -{ - struct ospf *ospf; - int idx_ifname = 0; - int idx_vrf = 0; - bool uj = use_json(argc, argv); - int ret = CMD_SUCCESS; - struct interface *ifp = NULL; - char *vrf_name = NULL; - vrf_id_t vrf_id = VRF_DEFAULT; - struct vrf *vrf = NULL; - - if (argv_find(argv, argc, "vrf", &idx_vrf)) - vrf_name = argv[idx_vrf + 1]->arg; - if (vrf_name && strmatch(vrf_name, VRF_DEFAULT_NAME)) - vrf_name = NULL; - if (vrf_name) { - vrf = vrf_lookup_by_name(vrf_name); - if (vrf) - vrf_id = vrf->vrf_id; - } - ospf = ospf_lookup_by_vrf_id(vrf_id); - - if (!ospf || !ospf->oi_running) - return ret; - - if (!uj) - show_ip_ospf_neighbour_header(vty); - - argv_find(argv, argc, "IFNAME", &idx_ifname); - - ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf_id); - if (!ifp) - return ret; - - ret = show_ip_ospf_neighbor_int_common(vty, ospf, idx_ifname, - argv, uj, 0); - return ret; -} - -DEFUN (show_ip_ospf_instance_neighbor_int, - show_ip_ospf_instance_neighbor_int_cmd, - "show ip ospf (1-65535) neighbor IFNAME [json]", - SHOW_STR - IP_STR - "OSPF information\n" - "Instance ID\n" - "Neighbor list\n" - "Interface name\n" - JSON_STR) +DEFPY(show_ip_ospf_instance_neighbor_int, + show_ip_ospf_instance_neighbor_int_cmd, + "show ip ospf (1-65535)$instance neighbor IFNAME$ifname [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "Interface name\n" + JSON_STR) { - int idx_number = 3; - int idx_ifname = 5; struct ospf *ospf; - unsigned short instance = 0; - bool uj = use_json(argc, argv); - if (!uj) + if (!json) show_ip_ospf_neighbour_header(vty); - instance = strtoul(argv[idx_number]->arg, NULL, 10); if (instance != ospf_instance) return CMD_NOT_MY_INSTANCE; @@ -4979,11 +4969,10 @@ DEFUN (show_ip_ospf_instance_neighbor_int, if (!ospf || !ospf->oi_running) return CMD_SUCCESS; - if (!uj) + if (!json) show_ip_ospf_neighbour_header(vty); - return show_ip_ospf_neighbor_int_common(vty, ospf, idx_ifname, argv, uj, - 0); + return show_ip_ospf_neighbor_int_common(vty, ospf, ifname, !!json, 0); } static void show_ip_ospf_nbr_nbma_detail_sub(struct vty *vty, @@ -5123,19 +5112,36 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, json_object_string_add(json_neigh, "areaId", ospf_area_desc_string(oi->area)); json_object_string_add(json_neigh, "ifaceName", oi->ifp->name); - } else - vty_out(vty, " In the area %s via interface %s\n", + if (oi->address) + json_object_string_addf(json_neigh, "localIfaceAddress", + "%pI4", + &oi->address->u.prefix4); + } else { + vty_out(vty, " In the area %s via interface %s", ospf_area_desc_string(oi->area), oi->ifp->name); + if (oi->address) + vty_out(vty, " local interface IP %pI4\n", + &oi->address->u.prefix4); + else + vty_out(vty, "\n"); + } /* Show neighbor priority and state. */ ospf_nbr_ism_state_message(nbr, neigh_state, sizeof(neigh_state)); if (use_json) { json_object_int_add(json_neigh, "nbrPriority", nbr->priority); json_object_string_add(json_neigh, "nbrState", neigh_state); - } else - vty_out(vty, " Neighbor priority is %d, State is %s,", - nbr->priority, neigh_state); - + json_object_string_add(json_neigh, "role", + lookup_msg(ospf_ism_state_msg, + ospf_nbr_ism_state(nbr), + NULL)); + } else { + vty_out(vty, + " Neighbor priority is %d, State is %s, Role is %s,", + nbr->priority, neigh_state, + lookup_msg(ospf_ism_state_msg, ospf_nbr_ism_state(nbr), + NULL)); + } /* Show state changes. */ if (use_json) json_object_int_add(json_neigh, "stateChangeCounter", @@ -5369,7 +5375,8 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, static int show_ip_ospf_neighbor_id_common(struct vty *vty, struct ospf *ospf, struct in_addr *router_id, bool use_json, uint8_t use_vrf, - bool is_detail) + bool is_detail, + json_object *json_vrf) { struct listnode *node; struct ospf_neighbor *nbr; @@ -5403,6 +5410,14 @@ static int show_ip_ospf_neighbor_id_common(struct vty *vty, struct ospf *ospf, use_json); } + if (json_vrf && use_json) { + json_object_object_add( + json_vrf, + (ospf->vrf_id == VRF_DEFAULT) ? "default" : ospf->name, + json); + return CMD_SUCCESS; + } + if (use_json) vty_json(vty, json); else @@ -5411,23 +5426,50 @@ static int show_ip_ospf_neighbor_id_common(struct vty *vty, struct ospf *ospf, return CMD_SUCCESS; } -DEFPY(show_ip_ospf_neighbor_id, show_ip_ospf_neighbor_id_cmd, - "show ip ospf neighbor A.B.C.D$router_id [detail$detail] [json$json]", - SHOW_STR IP_STR +DEFPY(show_ip_ospf_neighbor_id, + show_ip_ospf_neighbor_id_cmd, + "show ip ospf [vrf NAME$vrf_name] neighbor A.B.C.D$router_id [detail$detail] [json$json]", + SHOW_STR + IP_STR "OSPF information\n" + VRF_CMD_HELP_STR "Neighbor list\n" "Neighbor ID\n" - "Detailed output\n" JSON_STR) + "Detailed output\n" + JSON_STR) { struct ospf *ospf; struct listnode *node; int ret = CMD_SUCCESS; + int inst = 0; - for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { - if (!ospf->oi_running) - continue; - ret = show_ip_ospf_neighbor_id_common(vty, ospf, &router_id, - !!json, 0, !!detail); + if (vrf_name && !strmatch(vrf_name, "all")) { + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (!json) + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + else + vty_json_empty(vty); + return CMD_SUCCESS; + } + ret = show_ip_ospf_neighbor_id_common( + vty, ospf, &router_id, !!json, 0, !!detail, NULL); + } else { + json_object *json_vrf = NULL; + + if (json) + json_vrf = json_object_new_object(); + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = show_ip_ospf_neighbor_id_common( + vty, ospf, &router_id, !!json, 0, !!detail, + json_vrf); + } + if (json) + vty_json(vty, json_vrf); } return ret; @@ -5452,7 +5494,7 @@ DEFPY(show_ip_ospf_instance_neighbor_id, show_ip_ospf_instance_neighbor_id_cmd, return CMD_SUCCESS; return show_ip_ospf_neighbor_id_common(vty, ospf, &router_id, !!json, 0, - !!detail); + !!detail, NULL); } static int show_ip_ospf_neighbor_detail_common(struct vty *vty, @@ -5517,77 +5559,71 @@ static int show_ip_ospf_neighbor_detail_common(struct vty *vty, return CMD_SUCCESS; } -DEFUN (show_ip_ospf_neighbor_detail, - show_ip_ospf_neighbor_detail_cmd, - "show ip ospf [vrf <NAME|all>] neighbor detail [json]", - SHOW_STR - IP_STR - "OSPF information\n" - VRF_CMD_HELP_STR - "All VRFs\n" - "Neighbor list\n" - "detail of all neighbors\n" - JSON_STR) +DEFPY(show_ip_ospf_neighbor_detail, + show_ip_ospf_neighbor_detail_cmd, + "show ip ospf [vrf <NAME|all>$vrf_name] neighbor detail [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Neighbor list\n" + "detail of all neighbors\n" + JSON_STR) { struct ospf *ospf; - bool uj = use_json(argc, argv); struct listnode *node = NULL; - char *vrf_name = NULL; - bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; - int idx_vrf = 0; uint8_t use_vrf = 0; - json_object *json = NULL; - - OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + json_object *json_vrf = NULL; - if (uj) - json = json_object_new_object(); + if (json) + json_vrf = json_object_new_object(); /* vrf input is provided could be all or specific vrf*/ if (vrf_name) { use_vrf = 1; - if (all_vrf) { + if (strmatch(vrf_name, "all")) { for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { if (!ospf->oi_running) continue; ret = show_ip_ospf_neighbor_detail_common( - vty, ospf, json, uj, use_vrf); + vty, ospf, json_vrf, !!json, use_vrf); } - if (uj) - vty_json(vty, json); + if (json) + vty_json(vty, json_vrf); return ret; } ospf = ospf_lookup_by_inst_name(inst, vrf_name); if (ospf == NULL || !ospf->oi_running) { - if (uj) - json_object_free(json); + if (json) + vty_json(vty, json_vrf); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); return CMD_SUCCESS; } } else { /* Display default ospf (instance 0) info */ ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf == NULL || !ospf->oi_running) { - if (uj) - json_object_free(json); + if (json) + vty_json(vty, json_vrf); + else + vty_out(vty, "%% OSPF is not enabled\n"); return CMD_SUCCESS; } } - if (ospf) { - ret = show_ip_ospf_neighbor_detail_common(vty, ospf, json, uj, - use_vrf); - if (uj) { - vty_out(vty, "%s\n", - json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - } - } + if (ospf) + ret = show_ip_ospf_neighbor_detail_common(vty, ospf, json_vrf, + !!json, use_vrf); - if (uj) - json_object_free(json); + if (json) + vty_json(vty, json_vrf); return ret; } @@ -5816,9 +5852,9 @@ DEFUN (show_ip_ospf_instance_neighbor_detail_all, static int show_ip_ospf_neighbor_int_detail_common(struct vty *vty, struct ospf *ospf, - int arg_base, - struct cmd_token **argv, - bool use_json) + const char *ifname, + bool use_json, + json_object *json_vrf) { struct ospf_interface *oi; struct interface *ifp; @@ -5826,8 +5862,15 @@ static int show_ip_ospf_neighbor_int_detail_common(struct vty *vty, struct ospf_neighbor *nbr; json_object *json = NULL; - if (use_json) + if (use_json) { json = json_object_new_object(); + if (json_vrf) + json_object_object_add(json_vrf, + (ospf->vrf_id == VRF_DEFAULT) + ? "default" + : ospf->name, + json); + } if (ospf->instance) { if (use_json) @@ -5837,13 +5880,13 @@ static int show_ip_ospf_neighbor_int_detail_common(struct vty *vty, vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); } - ifp = if_lookup_by_name(argv[arg_base]->arg, ospf->vrf_id); + ifp = if_lookup_by_name(ifname, ospf->vrf_id); if (!ifp) { - if (!use_json) + if (!use_json) { vty_out(vty, "No such interface.\n"); - else { - vty_out(vty, "{}\n"); - json_object_free(json); + } else { + if (!json_vrf) + vty_json(vty, json); } return CMD_WARNING; } @@ -5871,37 +5914,114 @@ static int show_ip_ospf_neighbor_int_detail_common(struct vty *vty, } } - if (use_json) - vty_json(vty, json); - else + if (use_json) { + if (!json_vrf) + vty_json(vty, json); + } else { vty_out(vty, "\n"); + } return CMD_SUCCESS; } -DEFUN (show_ip_ospf_neighbor_int_detail, - show_ip_ospf_neighbor_int_detail_cmd, - "show ip ospf neighbor IFNAME detail [json]", - SHOW_STR - IP_STR - "OSPF information\n" - "Neighbor list\n" - "Interface name\n" - "detail of all neighbors\n" - JSON_STR) +DEFPY(show_ip_ospf_neighbor_int, + show_ip_ospf_neighbor_int_cmd, + "show ip ospf [vrf NAME$vrf_name] neighbor IFNAME$ifname [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "Neighbor list\n" + "Interface name\n" + JSON_STR) +{ + struct ospf *ospf; + int ret = CMD_SUCCESS; + struct interface *ifp = NULL; + vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf = NULL; + + if (vrf_name && strmatch(vrf_name, VRF_DEFAULT_NAME)) + vrf_name = NULL; + if (vrf_name) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) + vrf_id = vrf->vrf_id; + } + ospf = ospf_lookup_by_vrf_id(vrf_id); + + if (!ospf || !ospf->oi_running) { + if (json) + vty_json_empty(vty); + return ret; + } + + if (!json) + show_ip_ospf_neighbour_header(vty); + + ifp = if_lookup_by_name(ifname, vrf_id); + if (!ifp) { + if (json) + vty_json_empty(vty); + else + vty_out(vty, "No such interface.\n"); + return ret; + } + + ret = show_ip_ospf_neighbor_int_common(vty, ospf, ifname, !!json, 0); + return ret; +} + +DEFPY(show_ip_ospf_neighbor_int_detail, + show_ip_ospf_neighbor_int_detail_cmd, + "show ip ospf [vrf NAME$vrf_name] neighbor IFNAME$ifname detail [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "Neighbor list\n" + "Interface name\n" + "detail of all neighbors\n" + JSON_STR) { struct ospf *ospf; - bool uj = use_json(argc, argv); struct listnode *node = NULL; int ret = CMD_SUCCESS; bool ospf_output = false; + if (vrf_name && !strmatch(vrf_name, "all")) { + int inst = 0; + + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (!json) + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + else + vty_json_empty(vty); + return CMD_SUCCESS; + } + return show_ip_ospf_neighbor_int_detail_common( + vty, ospf, ifname, !!json, NULL); + } + + json_object *json_vrf = NULL; + + if (json) + json_vrf = json_object_new_object(); + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { if (!ospf->oi_running) continue; ospf_output = true; - ret = show_ip_ospf_neighbor_int_detail_common(vty, ospf, 4, - argv, uj); + ret = show_ip_ospf_neighbor_int_detail_common(vty, ospf, ifname, + !!json, json_vrf); + } + + if (json) { + vty_json(vty, json_vrf); + return ret; } if (!ospf_output) @@ -5910,25 +6030,20 @@ DEFUN (show_ip_ospf_neighbor_int_detail, return ret; } -DEFUN (show_ip_ospf_instance_neighbor_int_detail, - show_ip_ospf_instance_neighbor_int_detail_cmd, - "show ip ospf (1-65535) neighbor IFNAME detail [json]", - SHOW_STR - IP_STR - "OSPF information\n" - "Instance ID\n" - "Neighbor list\n" - "Interface name\n" - "detail of all neighbors\n" - JSON_STR) +DEFPY(show_ip_ospf_instance_neighbor_int_detail, + show_ip_ospf_instance_neighbor_int_detail_cmd, + "show ip ospf (1-65535)$instance neighbor IFNAME$ifname detail [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "Interface name\n" + "detail of all neighbors\n" + JSON_STR) { - int idx_number = 3; - int idx_ifname = 5; struct ospf *ospf; - unsigned short instance = 0; - bool uj = use_json(argc, argv); - instance = strtoul(argv[idx_number]->arg, NULL, 10); if (instance != ospf_instance) return CMD_NOT_MY_INSTANCE; @@ -5936,8 +6051,8 @@ DEFUN (show_ip_ospf_instance_neighbor_int_detail, if (!ospf || !ospf->oi_running) return CMD_SUCCESS; - return show_ip_ospf_neighbor_int_detail_common(vty, ospf, idx_ifname, - argv, uj); + return show_ip_ospf_neighbor_int_detail_common(vty, ospf, ifname, + !!json, NULL); } /* Show functions */ @@ -5949,118 +6064,109 @@ static int show_lsa_summary(struct vty *vty, struct ospf_lsa *lsa, int self, struct as_external_lsa *asel; struct prefix_ipv4 p; - if (lsa != NULL) { - /* If self option is set, check LSA self flag. */ - if (self == 0 || IS_LSA_SELF(lsa)) { + if (lsa == NULL) + return 0; - if (!json_lsa) { - /* LSA common part show. */ - vty_out(vty, "%-15pI4", - &lsa->data->id); - vty_out(vty, "%-15pI4 %4d 0x%08lx 0x%04x", - &lsa->data->adv_router, LS_AGE(lsa), - (unsigned long)ntohl( - lsa->data->ls_seqnum), - ntohs(lsa->data->checksum)); - } else { - char seqnum[10]; - char checksum[10]; - - snprintf(seqnum, sizeof(seqnum), "%x", - ntohl(lsa->data->ls_seqnum)); - snprintf(checksum, sizeof(checksum), "%x", - ntohs(lsa->data->checksum)); - json_object_string_addf(json_lsa, "lsId", - "%pI4", &lsa->data->id); - json_object_string_addf( - json_lsa, "advertisedRouter", "%pI4", - &lsa->data->adv_router); - json_object_int_add(json_lsa, "lsaAge", - LS_AGE(lsa)); - json_object_string_add( - json_lsa, "sequenceNumber", seqnum); - json_object_string_add(json_lsa, "checksum", - checksum); - } + /* If self option is set, check LSA self flag. */ + if (self == 0 || IS_LSA_SELF(lsa)) { - /* LSA specific part show. */ - switch (lsa->data->type) { - case OSPF_ROUTER_LSA: - rl = (struct router_lsa *)lsa->data; + if (!json_lsa) { + /* LSA common part show. */ + vty_out(vty, "%-15pI4", &lsa->data->id); + vty_out(vty, "%-15pI4 %4d 0x%08lx 0x%04x", + &lsa->data->adv_router, LS_AGE(lsa), + (unsigned long)ntohl(lsa->data->ls_seqnum), + ntohs(lsa->data->checksum)); + } else { + char seqnum[10]; + char checksum[10]; + + snprintf(seqnum, sizeof(seqnum), "%x", + ntohl(lsa->data->ls_seqnum)); + snprintf(checksum, sizeof(checksum), "%x", + ntohs(lsa->data->checksum)); + json_object_string_addf(json_lsa, "lsId", "%pI4", + &lsa->data->id); + json_object_string_addf(json_lsa, "advertisedRouter", + "%pI4", &lsa->data->adv_router); + json_object_int_add(json_lsa, "lsaAge", LS_AGE(lsa)); + json_object_string_add(json_lsa, "sequenceNumber", + seqnum); + json_object_string_add(json_lsa, "checksum", checksum); + } + + /* LSA specific part show. */ + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + rl = (struct router_lsa *)lsa->data; - if (!json_lsa) - vty_out(vty, " %-d", ntohs(rl->links)); - else - json_object_int_add(json_lsa, - "numOfRouterLinks", - ntohs(rl->links)); - break; - case OSPF_SUMMARY_LSA: - sl = (struct summary_lsa *)lsa->data; + if (!json_lsa) + vty_out(vty, " %-d", ntohs(rl->links)); + else + json_object_int_add(json_lsa, + "numOfRouterLinks", + ntohs(rl->links)); + break; + case OSPF_SUMMARY_LSA: + sl = (struct summary_lsa *)lsa->data; - p.family = AF_INET; - p.prefix = sl->header.id; - p.prefixlen = ip_masklen(sl->mask); - apply_mask_ipv4(&p); + p.family = AF_INET; + p.prefix = sl->header.id; + p.prefixlen = ip_masklen(sl->mask); + apply_mask_ipv4(&p); - if (!json_lsa) - vty_out(vty, " %pFX", &p); - else { - json_object_string_addf( - json_lsa, "summaryAddress", - "%pFX", &p); - } - break; - case OSPF_AS_EXTERNAL_LSA: - case OSPF_AS_NSSA_LSA: - asel = (struct as_external_lsa *)lsa->data; - - p.family = AF_INET; - p.prefix = asel->header.id; - p.prefixlen = ip_masklen(asel->mask); - apply_mask_ipv4(&p); - - if (!json_lsa) - vty_out(vty, " %s %pFX [0x%lx]", - IS_EXTERNAL_METRIC( - asel->e[0].tos) - ? "E2" - : "E1", - &p, - (unsigned long)ntohl( - asel->e[0].route_tag)); - else { - json_object_string_add( - json_lsa, "metricType", - IS_EXTERNAL_METRIC( - asel->e[0].tos) - ? "E2" - : "E1"); - json_object_string_addf( - json_lsa, "route", "%pFX", &p); - json_object_int_add( - json_lsa, "tag", - (unsigned long)ntohl( - asel->e[0].route_tag)); - } - break; - case OSPF_NETWORK_LSA: - case OSPF_ASBR_SUMMARY_LSA: - case OSPF_OPAQUE_LINK_LSA: - case OSPF_OPAQUE_AREA_LSA: - case OSPF_OPAQUE_AS_LSA: - default: - break; + if (!json_lsa) + vty_out(vty, " %pFX", &p); + else { + json_object_string_addf( + json_lsa, "summaryAddress", "%pFX", &p); } + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + asel = (struct as_external_lsa *)lsa->data; + + p.family = AF_INET; + p.prefix = asel->header.id; + p.prefixlen = ip_masklen(asel->mask); + apply_mask_ipv4(&p); if (!json_lsa) - vty_out(vty, "\n"); + vty_out(vty, " %s %pFX [0x%lx]", + IS_EXTERNAL_METRIC(asel->e[0].tos) + ? "E2" + : "E1", + &p, + (unsigned long)ntohl( + asel->e[0].route_tag)); + else { + json_object_string_add( + json_lsa, "metricType", + IS_EXTERNAL_METRIC(asel->e[0].tos) + ? "E2" + : "E1"); + json_object_string_addf(json_lsa, "route", + "%pFX", &p); + json_object_int_add( + json_lsa, "tag", + (unsigned long)ntohl( + asel->e[0].route_tag)); + } + break; + case OSPF_NETWORK_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + default: + break; } - return 1; + if (!json_lsa) + vty_out(vty, "\n"); } - return 0; + return 1; } static const char *const show_database_desc[] = { @@ -6129,7 +6235,16 @@ static void show_ip_ospf_database_header(struct vty *vty, struct ospf_lsa *lsa, struct router_lsa *rlsa = (struct router_lsa *)lsa->data; if (!json) { - vty_out(vty, " LS age: %d\n", LS_AGE(lsa)); + if (IS_LSA_SELF(lsa)) + vty_out(vty, " LS age: %d%s\n", LS_AGE(lsa), + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) + ? "(S-DNA)" + : ""); + else + vty_out(vty, " LS age: %d%s\n", LS_AGE(lsa), + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) + ? "(DNA)" + : ""); vty_out(vty, " Options: 0x%-2x : %s\n", lsa->data->options, ospf_options_dump(lsa->data->options)); vty_out(vty, " LS Flags: 0x%-2x %s\n", lsa->flags, @@ -12188,6 +12303,9 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) if (PREFIX_NAME_OUT(area)) vty_out(vty, " area %s filter-list prefix %s out\n", buf, PREFIX_NAME_OUT(area)); + + if (area->fr_info.configured) + vty_out(vty, " area %s flood-reduction\n", buf); } return 0; @@ -12565,6 +12683,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (ospf->lsa_refresh_interval != OSPF_LSA_REFRESH_INTERVAL_DEFAULT) vty_out(vty, " refresh timer %d\n", ospf->lsa_refresh_interval); + if (ospf->fr_configured) + vty_out(vty, " flood-reduction\n"); + /* Redistribute information print. */ config_write_ospf_redistribute(vty, ospf); @@ -12947,6 +13068,143 @@ DEFPY_HIDDEN(ospf_maxage_delay_timer, ospf_maxage_delay_timer_cmd, return CMD_SUCCESS; } +/* + * ------------------------------------------------------------------------* + * Following is (vty) configuration functions for flood-reduction handling. + * ------------------------------------------------------------------------ + */ + +DEFPY(flood_reduction, flood_reduction_cmd, "flood-reduction", + "flood reduction feature\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *area; + struct listnode *node; + + /* Turn on the Flood Reduction feature for the router. */ + if (!ospf->fr_configured) { + ospf->fr_configured = true; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction: OFF -> ON"); + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area) { + ospf_area_update_fr_state(area); + ospf_refresh_area_self_lsas(area); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY(flood_reduction_area, flood_reduction_area_cmd, + "area <A.B.C.D|(0-4294967295)> flood-reduction", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable flood reduction for area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *oa; + int idx = 1; + int format; + int ret; + const char *areaid; + struct in_addr area_id; + + areaid = argv[idx]->arg; + + ret = str2area_id(areaid, &area_id, &format); + if (ret < 0) { + vty_out(vty, "Please specify area by A.B.C.D|<0-4294967295>\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + oa = ospf_area_lookup_by_area_id(ospf, area_id); + if (!oa) { + vty_out(vty, "OSPF area ID not present\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Turn on the Flood Reduction feature for the area. */ + if (!oa->fr_info.configured) { + oa->fr_info.configured = true; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction area %pI4 : OFF -> ON", + &oa->area_id); + ospf_area_update_fr_state(oa); + ospf_refresh_area_self_lsas(oa); + } + + return CMD_SUCCESS; +} + +DEFPY(no_flood_reduction, no_flood_reduction_cmd, "no flood-reduction", + NO_STR "flood reduction feature\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct listnode *node; + struct ospf_area *area; + + /* Turn off the Flood Reduction feature for the router. */ + if (ospf->fr_configured) { + ospf->fr_configured = false; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction: ON -> OFF"); + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area) { + ospf_area_update_fr_state(area); + ospf_refresh_area_self_lsas(area); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY(no_flood_reduction_area, no_flood_reduction_area_cmd, + "no area <A.B.C.D|(0-4294967295)> flood-reduction", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Disable flood reduction for area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *oa; + int idx = 2; + int format; + int ret; + const char *areaid; + struct in_addr area_id; + + areaid = argv[idx]->arg; + + ret = str2area_id(areaid, &area_id, &format); + if (ret < 0) { + vty_out(vty, "Please specify area by A.B.C.D|<0-4294967295>\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + oa = ospf_area_lookup_by_area_id(ospf, area_id); + if (!oa) { + vty_out(vty, "OSPF area ID not present\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Turn off the Flood Reduction feature for the area. */ + if (oa->fr_info.configured) { + oa->fr_info.configured = false; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction area %pI4 : ON -> OFF", + &oa->area_id); + ospf_area_update_fr_state(oa); + ospf_refresh_area_self_lsas(oa); + } + + return CMD_SUCCESS; +} + void ospf_vty_clear_init(void) { install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd); @@ -13107,6 +13365,12 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_lsa_refresh_timer_cmd); install_element(OSPF_NODE, &ospf_maxage_delay_timer_cmd); + /* Flood Reduction commands */ + install_element(OSPF_NODE, &flood_reduction_cmd); + install_element(OSPF_NODE, &no_flood_reduction_cmd); + install_element(OSPF_NODE, &flood_reduction_area_cmd); + install_element(OSPF_NODE, &no_flood_reduction_area_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 1c8ebccea3..0296d9d9f5 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -452,6 +452,8 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) */ ospf_gr_nvm_read(new); + new->fr_configured = false; + return new; } @@ -802,6 +804,7 @@ static void ospf_finish_final(struct ospf *ospf) THREAD_OFF(ospf->t_maxage); THREAD_OFF(ospf->t_maxage_walker); THREAD_OFF(ospf->t_abr_task); + THREAD_OFF(ospf->t_abr_fr); THREAD_OFF(ospf->t_asbr_check); THREAD_OFF(ospf->t_asbr_nssa_redist_update); THREAD_OFF(ospf->t_distribute_update); @@ -947,6 +950,15 @@ struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id) /* Self-originated LSAs initialize. */ new->router_lsa_self = NULL; + /* Initialize FR field */ + new->fr_info.enabled = false; + new->fr_info.configured = false; + new->fr_info.state_changed = false; + new->fr_info.router_lsas_recv_dc_bit = 0; + new->fr_info.indication_lsa_self = NULL; + new->fr_info.area_ind_lsa_recvd = false; + new->fr_info.area_dc_clear = false; + ospf_opaque_type10_lsa_init(new); new->oiflist = list_new(); diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index cb26ef5fbf..4df65ea759 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -101,7 +101,25 @@ struct ospf_redist { struct route_map *map; } route_map; /* +1 is for default-information */ #define ROUTEMAP_NAME(R) (R->route_map.name) -#define ROUTEMAP(R) (R->route_map.map) +#define ROUTEMAP(R) (R->route_map.map) +}; + +/* OSPF area flood reduction info */ +struct ospf_area_fr_info { + bool enabled; /* Area support for Flood Reduction */ + bool configured; /* Flood Reduction configured per area knob */ + bool state_changed; /* flood reduction state change info */ + int router_lsas_recv_dc_bit; /* Number of unique router lsas + * received with DC bit set. + * (excluding self) + */ + bool area_ind_lsa_recvd; /* Indication lsa received in this area */ + bool area_dc_clear; /* Area has atleast one lsa with dc bit 0( + * excluding indication lsa) + */ + struct ospf_lsa *indication_lsa_self; /* Indication LSA generated + * in the area. + */ }; /* ospf->config */ @@ -240,6 +258,7 @@ struct ospf { /* Threads. */ struct thread *t_abr_task; /* ABR task timer. */ + struct thread *t_abr_fr; /* ABR FR timer. */ struct thread *t_asbr_check; /* ASBR check timer. */ struct thread *t_asbr_nssa_redist_update; /* ASBR NSSA redistribution update timer. */ @@ -391,6 +410,9 @@ struct ospf { bool ti_lfa_enabled; enum protection_type ti_lfa_protection_type; + /* Flood Reduction configuration state */ + bool fr_configured; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf); @@ -576,6 +598,8 @@ struct ospf_area { uint32_t act_ints; /* Active interfaces. */ uint32_t full_nbrs; /* Fully adjacent neighbors. */ uint32_t full_vls; /* Fully adjacent virtual neighbors. */ + + struct ospf_area_fr_info fr_info; /* Flood reduction info. */ }; /* OSPF config network structure. */ diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index af510ce29e..02b50c9af2 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -34,6 +34,8 @@ #include "pim_msg.h" static void mroute_read_on(struct pim_instance *pim); +static int pim_upstream_mroute_update(struct channel_oil *c_oil, + const char *name); int pim_mroute_set(struct pim_instance *pim, int enable) { @@ -145,45 +147,66 @@ int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg) { struct pim_interface *pim_ifp = ifp->info; struct pim_upstream *up; - struct pim_rpf *rpg; pim_sgaddr sg; + bool desync = false; - rpg = pim_ifp ? RP(pim_ifp->pim, msg->msg_im_dst) : NULL; - /* - * If the incoming interface is unknown OR - * the Interface type is SSM we don't need to - * do anything here - */ - if (!rpg || pim_rpf_addr_is_inaddr_any(rpg)) { - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug( - "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP", - __func__); + memset(&sg, 0, sizeof(sg)); + sg.src = msg->msg_im_src; + sg.grp = msg->msg_im_dst; + if (!pim_ifp) { + if (PIM_DEBUG_MROUTE) + zlog_debug( + "%s: PIM not enabled on interface, dropping packet to %pSG", + ifp->name, &sg); return 0; } + if (!pim_is_grp_ssm(pim_ifp->pim, sg.grp)) { + /* for ASM, check that we have enough information (i.e. path + * to RP) to make a decision on what to do with this packet. + * + * for SSM, this is meaningless, everything is join-driven, + * and for NOCACHE we need to install an empty OIL MFC entry + * so the kernel doesn't keep nagging us. + */ + struct pim_rpf *rpg; + + rpg = RP(pim_ifp->pim, msg->msg_im_dst); + if (!rpg) { + if (PIM_DEBUG_MROUTE) + zlog_debug("%s: no RPF for packet to %pSG", + ifp->name, &sg); + return 0; + } + if (pim_rpf_addr_is_inaddr_any(rpg)) { + if (PIM_DEBUG_MROUTE) + zlog_debug("%s: null RPF for packet to %pSG", + ifp->name, &sg); + return 0; + } + } + /* * If we've received a multicast packet that isn't connected to * us */ if (!pim_if_connected_to_source(ifp, msg->msg_im_src)) { - if (PIM_DEBUG_MROUTE_DETAIL) + if (PIM_DEBUG_MROUTE) zlog_debug( - "%s: Received incoming packet that doesn't originate on our seg", - __func__); + "%s: incoming packet to %pSG from non-connected source", + ifp->name, &sg); return 0; } - memset(&sg, 0, sizeof(sg)); - sg.src = msg->msg_im_src; - sg.grp = msg->msg_im_dst; - if (!(PIM_I_am_DR(pim_ifp))) { + /* unlike the other debug messages, this one is further in the + * "normal operation" category and thus under _DETAIL + */ if (PIM_DEBUG_MROUTE_DETAIL) zlog_debug( - "%s: Interface is not the DR blackholing incoming traffic for %pSG", - __func__, &sg); + "%s: not DR on interface, not forwarding traffic for %pSG", + ifp->name, &sg); /* * We are not the DR, but we are still receiving packets @@ -204,6 +227,12 @@ int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg) up = pim_upstream_find_or_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __func__); + if (up->channel_oil->installed) { + zlog_warn( + "%s: NOCACHE for %pSG, MFC entry disappeared - reinstalling", + ifp->name, &sg); + desync = true; + } /* * I moved this debug till after the actual add because @@ -227,6 +256,11 @@ int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg) /* if we have receiver, inherit from parent */ pim_upstream_inherited_olist_decide(pim_ifp->pim, up); + /* we just got NOCACHE from the kernel, so... MFC is not in the + * kernel for some reason or another. Try installing again. + */ + if (desync) + pim_upstream_mroute_update(up->channel_oil, __func__); return 0; } diff --git a/python/clidef.py b/python/clidef.py index d71b482a99..244a8205bf 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -51,6 +51,12 @@ _fail = (_end == argv[_i]->arg) || (*_end != '\\0');""" ) +class AsDotHandler(RenderHandler): + argtype = "as_t" + decl = Template("as_t $varname = 0;") + code = Template("_fail = !asn_str2asn(argv[_i]->arg, &$varname);") + + # A.B.C.D/M (prefix_ipv4) and # X:X::X:X/M (prefix_ipv6) are "compatible" and can merge into a # struct prefix: @@ -152,6 +158,7 @@ handlers = { "IPV6_PREFIX_TKN": Prefix6Handler, "MAC_TKN": PrefixEthHandler, "MAC_PREFIX_TKN": PrefixEthHandler, + "ASNUM_TKN": AsDotHandler, } # core template invoked for each occurence of DEFPY. diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 8806bcd8fc..926097f571 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -55,6 +55,7 @@ static struct test_segment { const uint8_t asdata[1024]; int len; struct test_spec sp; + enum asnotation_mode asnotation; } test_segments[] = { { /* 0 */ @@ -64,6 +65,7 @@ static struct test_segment { 10, {"8466 3 52737 4096", "8466 3 52737 4096", 4, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 1 */ @@ -72,8 +74,16 @@ static struct test_segment { {0x2, 0x1, 0x22, 0x12, 0x2, 0x1, 0x00, 0x04}, 8, { - "8722 4", "8722 4", 2, 0, NOT_ALL_PRIVATE, 4, 5, 8722, + "8722 4", + "8722 4", + 2, + 0, + NOT_ALL_PRIVATE, + 4, + 5, + 8722, }, + 0, }, { /* 2 */ @@ -84,6 +94,7 @@ static struct test_segment { 14, {"8466 3 52737 4096 8722 4", "8466 3 52737 4096 8722 4", 6, 0, NOT_ALL_PRIVATE, 3, 5, 8466}, + 0, }, { /* 3 */ @@ -93,6 +104,7 @@ static struct test_segment { 10, {"8482 51457 {5204}", "8482 51457 {5204}", 3, 0, NOT_ALL_PRIVATE, 5204, 51456, 8482}, + 0, }, { /* 4 */ @@ -104,6 +116,7 @@ static struct test_segment { {"8467 59649 {4196,48658} {17322,30745}", "8467 59649 {4196,48658} {17322,30745}", 4, 0, NOT_ALL_PRIVATE, 48658, 1, 8467}, + 0, }, { /* 5 */ @@ -116,6 +129,7 @@ static struct test_segment { {"6435 59408 21665 {2457,4369,61697} 1842 41590 51793", "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", 7, 0, NOT_ALL_PRIVATE, 51793, 1, 6435}, + 0, }, { /* 6 */ @@ -124,6 +138,7 @@ static struct test_segment { {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15}, 8, {"(123 456 789)", "", 0, 3, NOT_ALL_PRIVATE, 789, 1, NULL_ASN}, + 0, }, { /* 7 */ @@ -134,6 +149,7 @@ static struct test_segment { 14, {"(123 456 789) (111 222)", "", 0, 5, NOT_ALL_PRIVATE, 111, 1, NULL_ASN}, + 0, }, { /* 8 */ @@ -142,6 +158,7 @@ static struct test_segment { {0x4, 0x3, 0x01, 0xc8, 0x00, 0x7b, 0x03, 0x15}, 8, {"[123,456,789]", "", 0, 1, NOT_ALL_PRIVATE, 123, 1, NULL_ASN}, + 0, }, { /* 9 */ @@ -153,6 +170,7 @@ static struct test_segment { 24, {"(123 456 789) [111,222] 8722 {4196,48658}", "8722 {4196,48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1, NULL_ASN}, + 0, }, { /* 10 */ @@ -163,6 +181,7 @@ static struct test_segment { 14, {"8466 2 52737 4096 8722 4", "8466 2 52737 4096 8722 4", 6, 0, NOT_ALL_PRIVATE, 4096, 1, 8466}, + 0, }, { /* 11 */ @@ -174,6 +193,7 @@ static struct test_segment { {"8466 2 52737 4096 8722 4 8722", "8466 2 52737 4096 8722 4 8722", 7, 0, NOT_ALL_PRIVATE, 4096, 1, 8466}, + 0, }, { /* 12 */ @@ -183,6 +203,7 @@ static struct test_segment { 10, {"8466 64512 52737 65535", "8466 64512 52737 65535", 4, 0, NOT_ALL_PRIVATE, 65535, 4, 8466}, + 0, }, { /* 13 */ @@ -192,6 +213,7 @@ static struct test_segment { 10, {"65534 64512 64513 65535", "65534 64512 64513 65535", 4, 0, ALL_PRIVATE, 65534, 4, 65534}, + 0, }, { /* 14 */ @@ -260,6 +282,7 @@ static struct test_segment { "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285", 250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 15 */ @@ -270,6 +293,7 @@ static struct test_segment { 12, {"8466 3 52737 4096 3456", "8466 3 52737 4096 3456", 5, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 16 */ @@ -278,6 +302,7 @@ static struct test_segment { {}, 0, {"", "", 0, 0, 0, 0, 0, 0}, + 0, }, { /* 17 */ @@ -293,6 +318,7 @@ static struct test_segment { "8466 3 52737 4096 3456 {7099,8153}", "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 18 */ @@ -305,6 +331,7 @@ static struct test_segment { {"6435 59408 21665 {23456} 23456 23456 23456", "6435 59408 21665 {23456} 23456 23456 23456", 7, 0, NOT_ALL_PRIVATE, 23456, 1, 6435}, + 0, }, { /* 19 */ @@ -316,6 +343,7 @@ static struct test_segment { {"{2457,4369,61697} 1842 41591 51793", "{2457,4369,61697} 1842 41591 51793", 4, 0, NOT_ALL_PRIVATE, 51793, 1, 2457}, + 0, }, { /* 20 */ @@ -329,44 +357,88 @@ static struct test_segment { {"(123 456 789) [124,456,788] 6435 59408 21665 {23456} 23456 23456 23456", "6435 59408 21665 {23456} 23456 23456 23456", 7, 4, NOT_ALL_PRIVATE, 23456, 1, 6435}, + 0, }, { /* 21 */ "reconcile_start_trans", "seq(23456,23456,23456) seq(6435,59408,21665)", { - 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0, 0x2, 0x3, - 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1, + 0x2, + 0x3, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x2, + 0x3, + 0x19, + 0x23, + 0xe8, + 0x10, + 0x54, + 0xa1, }, 16, {"23456 23456 23456 6435 59408 21665", "23456 23456 23456 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE, 21665, 1, 23456}, + 0, }, { /* 22 */ "reconcile_start_trans4", "seq(1842,41591,51793) seq(6435,59408,21665)", { - 0x2, 0x3, 0x07, 0x32, 0xa2, 0x77, 0xca, 0x51, 0x2, 0x3, - 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1, + 0x2, + 0x3, + 0x07, + 0x32, + 0xa2, + 0x77, + 0xca, + 0x51, + 0x2, + 0x3, + 0x19, + 0x23, + 0xe8, + 0x10, + 0x54, + 0xa1, }, 16, {"1842 41591 51793 6435 59408 21665", "1842 41591 51793 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE, 41591, 1, 1842}, + 0, }, { /* 23 */ "reconcile_start_trans_error", "seq(23456,23456,23456) seq(6435,59408)", { - 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0, 0x2, 0x2, - 0x19, 0x23, 0xe8, 0x10, + 0x2, + 0x3, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x2, + 0x2, + 0x19, + 0x23, + 0xe8, + 0x10, }, 14, {"23456 23456 23456 6435 59408", "23456 23456 23456 6435 59408", 5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456}, + 0, }, { /* 24 */ @@ -382,6 +454,7 @@ static struct test_segment { "8466 3 52737 4096 3456 {7099,8153}", "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 25 */ @@ -391,6 +464,7 @@ static struct test_segment { 0x80}, 12, {NULL, NULL, 0, 0, 0, 0, 0, 0}, + 0, }, { /* 26 */ @@ -400,6 +474,7 @@ static struct test_segment { 0x00, 0x0d, 0x80}, 14, {NULL, NULL, 0, 0, 0, 0, 0, 0}, + 0, }, { /* 27 */ @@ -408,20 +483,66 @@ static struct test_segment { {0x8, 0x2, 0x10, 0x00, 0x0d, 0x80}, 14, {NULL, NULL, 0, 0, 0, 0, 0, 0}, + 0, }, { /* 28 */ "BGP_AS_ZERO", "seq(8466,3,52737,0,4096)", - {0x2, 0x5, - 0x21, 0x12, - 0x00, 0x03, - 0xce, 0x01, - 0x00, 0x00, - 0x10, 0x00}, + {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10, + 0x00}, 12, {"8466 3 52737 0 4096", "8466 3 52737 0 4096", 5, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, + }, + { + /* 29 */ + "seq3_asdot+", + "seq(0.8466,0.3,0.52737,0.4096,0.8722,0.4)", + {0x2, 0x6, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x22, + 0x12, 0x00, 0x04}, + 14, + {"0.8466 0.3 0.52737 0.4096 0.8722 0.4", + "0.8466 0.3 0.52737 0.4096 0.8722 0.4", 6, 0, NOT_ALL_PRIVATE, + 3, 5, 8466}, + ASNOTATION_DOTPLUS, + }, + { + /* 30 */ + "confmulti_asdot+", + "confseq(0.123,0.456,0.789) confset(0.222,0.111) seq(0.8722) set(0.4196,0.48658)", + {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, + 0x4, 0x2, 0x00, 0xde, 0x00, 0x6f, 0x2, 0x1, + 0x22, 0x12, 0x1, 0x2, 0x10, 0x64, 0xbe, 0x12}, + 24, + {"(0.123 0.456 0.789) [0.111,0.222] 0.8722 {0.4196,0.48658}", + "0.8722 {0.4196,0.48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1, + NULL_ASN}, + ASNOTATION_DOTPLUS, + }, + { + /* 31 */ + "someprivate asdot+", + "seq(0.8466,0.64512,0.52737,0.65535)", + {0x2, 0x4, 0x21, 0x12, 0xfc, 0x00, 0xce, 0x01, 0xff, 0xff}, + 10, + {"0.8466 0.64512 0.52737 0.65535", + "0.8466 0.64512 0.52737 0.65535", 4, 0, NOT_ALL_PRIVATE, 65535, + 4, 8466}, + ASNOTATION_DOTPLUS, + }, + { + /* 32 */ + "BGP_AS_ZERO asdot+", + "seq(0.8466,0.3,0.52737,0.0,0.4096)", + {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10, + 0x00}, + 12, + {"0.8466 0.3 0.52737 0.0 0.4096", + "0.8466 0.3 0.52737 0.0 0.4096", 5, 0, NOT_ALL_PRIVATE, 4096, + 4, 8466}, + ASNOTATION_DOTPLUS, }, {NULL, NULL, {0}, 0, {NULL, 0, 0}}}; @@ -856,16 +977,16 @@ struct compare_tests { }; /* make an aspath from a data stream */ -static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit) +static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit, + enum asnotation_mode asnotation) { struct stream *s = NULL; struct aspath *as; - if (len) { s = stream_new(len); stream_put(s, data, len); } - as = aspath_parse(s, len, use32bit); + as = aspath_parse(s, len, use32bit, asnotation); if (s) stream_free(s); @@ -901,15 +1022,16 @@ static int validate(struct aspath *as, const struct test_spec *sp) } out = aspath_snmp_pathseg(as, &bytes); - asinout = make_aspath(out, bytes, 0); - + asinout = make_aspath(out, bytes, 0, as->asnotation); /* Excercise AS4 parsing a bit, with a dogfood test */ if (!s) s = stream_new(BGP_MAX_PACKET_SIZE); bytes4 = aspath_put(s, as, 1); - as4 = make_aspath(STREAM_DATA(s), bytes4, 1); + as4 = make_aspath(STREAM_DATA(s), bytes4, 1, as->asnotation); - asstr = aspath_str2aspath(sp->shouldbe); + asn_relax_as_zero(true); + asstr = aspath_str2aspath(sp->shouldbe, as->asnotation); + asn_relax_as_zero(false); asconfeddel = aspath_delete_confed_seq(aspath_dup(asinout)); @@ -1036,7 +1158,7 @@ static void parse_test(struct test_segment *t) printf("%s: %s\n", t->name, t->desc); - asp = make_aspath(t->asdata, t->len, 0); + asp = make_aspath(t->asdata, t->len, 0, t->asnotation); printf("aspath: %s\nvalidating...:\n", aspath_print(asp)); @@ -1058,8 +1180,10 @@ static void prepend_test(struct tests *t) printf("prepend %s: %s\n", t->test1->name, t->test1->desc); printf("to %s: %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath(t->test1->asdata, t->test1->len, 0); - asp2 = make_aspath(t->test2->asdata, t->test2->len, 0); + asp1 = make_aspath(t->test1->asdata, t->test1->len, 0, + ASNOTATION_PLAIN); + asp2 = make_aspath(t->test2->asdata, t->test2->len, 0, + ASNOTATION_PLAIN); ascratch = aspath_dup(asp2); aspath_unintern(&asp2); @@ -1085,8 +1209,8 @@ static void empty_prepend_test(struct test_segment *t) printf("empty prepend %s: %s\n", t->name, t->desc); - asp1 = make_aspath(t->asdata, t->len, 0); - asp2 = aspath_empty(); + asp1 = make_aspath(t->asdata, t->len, 0, t->asnotation); + asp2 = aspath_empty(t->asnotation); ascratch = aspath_dup(asp2); aspath_unintern(&asp2); @@ -1113,8 +1237,10 @@ static void as4_reconcile_test(struct tests *t) printf("reconciling %s:\n %s\n", t->test1->name, t->test1->desc); printf("with %s:\n %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath(t->test1->asdata, t->test1->len, 0); - asp2 = make_aspath(t->test2->asdata, t->test2->len, 0); + asp1 = make_aspath(t->test1->asdata, t->test1->len, 0, + ASNOTATION_PLAIN); + asp2 = make_aspath(t->test2->asdata, t->test2->len, 0, + ASNOTATION_PLAIN); ascratch = aspath_reconcile_as4(asp1, asp2); @@ -1138,8 +1264,10 @@ static void aggregate_test(struct tests *t) printf("aggregate %s: %s\n", t->test1->name, t->test1->desc); printf("with %s: %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath(t->test1->asdata, t->test1->len, 0); - asp2 = make_aspath(t->test2->asdata, t->test2->len, 0); + asp1 = make_aspath(t->test1->asdata, t->test1->len, 0, + ASNOTATION_PLAIN); + asp2 = make_aspath(t->test2->asdata, t->test2->len, 0, + ASNOTATION_PLAIN); ascratch = aspath_aggregate(asp1, asp2); @@ -1171,8 +1299,8 @@ static void cmp_test(void) printf("left cmp %s: %s\n", t1->name, t1->desc); printf("and %s: %s\n", t2->name, t2->desc); - asp1 = make_aspath(t1->asdata, t1->len, 0); - asp2 = make_aspath(t2->asdata, t2->len, 0); + asp1 = make_aspath(t1->asdata, t1->len, 0, ASNOTATION_PLAIN); + asp2 = make_aspath(t2->asdata, t2->len, 0, ASNOTATION_PLAIN); if (aspath_cmp_left(asp1, asp2) != left_compare[i].shouldbe_cmp || aspath_cmp_left(asp2, asp1) @@ -1210,7 +1338,9 @@ static int handle_attr_test(struct aspath_tests *t) struct aspath *asp; size_t datalen; - asp = make_aspath(t->segment->asdata, t->segment->len, 0); + asp = make_aspath(t->segment->asdata, t->segment->len, 0, + t->segment->asnotation); + bgp.asnotation = t->segment->asnotation; peer.curr = stream_new(BGP_MAX_PACKET_SIZE); peer.obuf = stream_fifo_new(); @@ -1286,8 +1416,8 @@ int main(void) parse_test(&test_segments[i]); empty_prepend_test(&test_segments[i++]); } - i = 0; + while (prepend_tests[i].test1) { printf("prepend test %u\n", i); prepend_test(&prepend_tests[i++]); diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 01b0740b87..8ef5748671 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -937,7 +937,8 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_PLAIN) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 3abf7bd141..b1f3314f1f 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1079,7 +1079,8 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_PLAIN) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index 1a2d21ad22..aeebbd9a35 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -50,7 +50,8 @@ int main(int argc, char *argv[]) vrf_init(NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_PLAIN) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index 090fb9a9f9..652aaa25d4 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -650,21 +650,14 @@ static const char *str_from_afi(afi_t afi) return "ipv4"; case AFI_IP6: return "ipv6"; - default: - return "<unknown AFI>"; + case AFI_L2VPN: + return "l2vpn"; + case AFI_MAX: + case AFI_UNSPEC: + return "bad-value"; } -} -static const char *str_from_safi(safi_t safi) -{ - switch (safi) { - case SAFI_UNICAST: - return "unicast"; - case SAFI_MULTICAST: - return "multicast"; - default: - return "<unknown SAFI>"; - } + assert(!"Reached end of function we should never reach"); } static const char *str_from_attr_type(enum test_peer_attr_type at) @@ -1150,7 +1143,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa) test_log(test, "prepare: switch address-family to [%s]", get_afi_safi_str(pa->afi, pa->safi, false)); test_execute(test, "address-family %s %s", - str_from_afi(pa->afi), str_from_safi(pa->safi)); + str_from_afi(pa->afi), safi2str(pa->safi)); test_execute(test, "neighbor %s activate", g->name); test_execute(test, "neighbor %s activate", p->host); } @@ -1217,7 +1210,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa) test_log(test, "prepare: switch address-family to [%s]", get_afi_safi_str(pa->afi, pa->safi, false)); test_execute(test, "address-family %s %s", - str_from_afi(pa->afi), str_from_safi(pa->safi)); + str_from_afi(pa->afi), safi2str(pa->safi)); test_execute(test, "neighbor %s activate", g->name); test_execute(test, "neighbor %s activate", p->host); } @@ -1265,7 +1258,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa) test_log(test, "prepare: switch address-family to [%s]", get_afi_safi_str(pa->afi, pa->safi, false)); test_execute(test, "address-family %s %s", - str_from_afi(pa->afi), str_from_safi(pa->safi)); + str_from_afi(pa->afi), safi2str(pa->safi)); test_execute(test, "neighbor %s activate", g->name); test_execute(test, "neighbor %s activate", p->host); } @@ -1464,7 +1457,7 @@ int main(void) if (pa->afi && pa->safi) desc = asprintfrr(MTYPE_TMP, "peer\\%s-%s\\%s", str_from_afi(pa->afi), - str_from_safi(pa->safi), pa->cmd); + safi2str(pa->safi), pa->cmd); else desc = asprintfrr(MTYPE_TMP, "peer\\%s", pa->cmd); diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 9cabae8f20..0ab40b2ecd 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -12,6 +12,7 @@ #include "lib/memory.h" #include "lib/prefix.h" #include "lib/nexthop.h" +#include "lib/asn.h" static int errors; @@ -145,6 +146,7 @@ int main(int argc, char **argv) struct in_addr ip; char *p; char buf[256]; + as_t asn; printcmp("%d %u %d %u", 123, 123, -456, -456); printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL); @@ -392,6 +394,13 @@ int main(int argc, char **argv) printchk("-00:09", "%pTSIm", &ts); printchk("--:--", "%pTVImx", &tv); printchk("--:--", "%pTTImx", &tt); + /* ASN checks */ + asn = 65536; + printchk("1.0", "%pASD", &asn); + asn = 65400; + printchk("65400", "%pASP", &asn); + printchk("0.65400", "%pASE", &asn); + printchk("65400", "%pASD", &asn); return !!errors; } diff --git a/tests/topotests/bgp_asdot_regex/__init__.py b/tests/topotests/bgp_asdot_regex/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/__init__.py diff --git a/tests/topotests/bgp_asdot_regex/r1/bgpd.conf b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf new file mode 100644 index 0000000000..4dd95dd8b7 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf @@ -0,0 +1,27 @@ +router bgp 1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.2 remote-as 1.2 + address-family ipv4 unicast + network 172.31.1.0/24 route-map rmapout + network 172.31.2.0/24 route-map rmapout + neighbor 192.168.255.2 route-map rmapin in + neighbor 192.168.255.2 activate + exit-address-family +exit +bgp as-path access-list only1_4 permit _1.4_ +bgp as-path access-list only65540 permit _65540_ +access-list 172313 permit 172.31.3.0/24 +access-list 172314 permit 172.31.4.0/24 +route-map rmapout permit 1 + set as-path prepend 1.4 +exit +route-map rmapin permit 1 + match ip address 172313 + match as-path only1_4 +exit +route-map rmapin permit 2 + match ip address 172314 + match as-path only65540 +exit + diff --git a/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json new file mode 100644 index 0000000000..e3703bf953 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json @@ -0,0 +1,80 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 3, + "routerId": "192.168.255.1", + "defaultLocPrf": 100, + "localAS": "1.1", + "routes": { "172.31.1.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.1.0", + "prefixLen":24, + "network":"172.31.1.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.2.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.2.0", + "prefixLen":24, + "network":"172.31.2.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.3.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.3.0", + "prefixLen":24, + "network":"172.31.3.0/24", + "metric":0, + "weight":0, + "peerId":"192.168.255.2", + "path":"1.2 1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.255.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] + } } diff --git a/tests/topotests/bgp_asdot_regex/r1/zebra.conf b/tests/topotests/bgp_asdot_regex/r1/zebra.conf new file mode 100644 index 0000000000..6e9b0b4a7e --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_asdot_regex/r2/bgpd.conf b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf new file mode 100644 index 0000000000..216dbd19ef --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf @@ -0,0 +1,26 @@ +router bgp 65538 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.1 remote-as 65537 + address-family ipv4 unicast + network 172.31.3.0/24 route-map rmapout + network 172.31.4.0/24 route-map rmapout + neighbor 192.168.255.1 route-map rmapin in + neighbor 192.168.255.1 activate + exit-address-family +exit +bgp as-path access-list only65540 permit _65540_ +bgp as-path access-list only1_4 permit _1.4_ +access-list 172311 permit 172.31.1.0/24 +access-list 172312 permit 172.31.2.0/24 +route-map rmapout permit 1 + set as-path prepend 65540 +exit +route-map rmapin permit 1 + match ip address 172311 + match as-path only65540 +exit +route-map rmapin permit 2 + match ip address 172312 + match as-path only1_4 +exit diff --git a/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json new file mode 100644 index 0000000000..1af4ff7e3d --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json @@ -0,0 +1,80 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 3, + "routerId": "192.168.255.2", + "defaultLocPrf": 100, + "localAS": 65538, + "routes": { "172.31.1.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.1.0", + "prefixLen":24, + "network":"172.31.1.0/24", + "metric":0, + "weight":0, + "peerId":"192.168.255.1", + "path":"65537 65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.255.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.3.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.3.0", + "prefixLen":24, + "network":"172.31.3.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.4.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.4.0", + "prefixLen":24, + "network":"172.31.4.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] + } } diff --git a/tests/topotests/bgp_asdot_regex/r2/zebra.conf b/tests/topotests/bgp_asdot_regex/r2/zebra.conf new file mode 100644 index 0000000000..6c14de583b --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py new file mode 100644 index 0000000000..5d5f1659e9 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +# +# test_bgp_asdot_regex.py +# Part of Topotests +# +# Copyright 2022 6WIND S.A. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_asdot_regex.py: + +Test how regex applies when asnotation to forge bgp config is based on dot or not. +""" + +import os +import sys +import json +import pytest +from functools import partial + +# add after imports, before defining classes or functions: +pytestmark = [pytest.mark.bgpd] + +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 +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_asdot_regex(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + logger.info("Check if neighbor sessions are up in {}".format(router1.name)) + test_func = partial(_bgp_converge, router1) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router1.name) + + logger.info("BGP neighbor session is up in {}".format(router1.name)) + + logger.info("waiting for bgp peers exchanging UPDATES") + + for router in tgen.routers().values(): + ref_file = "{}/{}/show_bgp_ipv4.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show bgp ipv4 unicast json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=2.5) + assertmsg = "{}: BGP UPDATE exchange failure".format(router.name) + assert res is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py index 7db6a51c92..324f53f3a6 100644 --- a/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py +++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py @@ -11,7 +11,7 @@ Following tests are covered to test bgp community functionality: 1. Verify that BGP well known communities work fine for eBGP and iBGP peers. - Well known communities tested: no-export, local-AS, internet + Well known communities tested: no-export, local-AS """ @@ -140,11 +140,11 @@ def teardown_module(mod): ##################################################### -def test_bgp_no_export_local_as_and_internet_communities_p0(request): +def test_bgp_no_export_local_as_communities_p0(request): """ Verify that BGP well known communities work fine for eBGP and iBGP peers. - Well known communities tested: no-export, local-AS, internet + Well known communities tested: no-export, local-AS """ tc_name = request.node.name @@ -170,7 +170,7 @@ def test_bgp_no_export_local_as_and_internet_communities_p0(request): tc_name, result ) - for comm_type in ["no-export", "local-AS", "internet"]: + for comm_type in ["no-export", "local-AS"]: step("Create a route-map on R1 to set community as {}".format(comm_type)) @@ -258,45 +258,23 @@ def test_bgp_no_export_local_as_and_internet_communities_p0(request): tc_name, result ) - if comm_type == "internet": - step( - "Verify that these prefixes, originated on R1, are" - "received on both R2 and R3" - ) - - result = verify_rib( - tgen, - addr_type, - "r3", - input_dict_4, - next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[ - 0 - ], - ) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result - ) - else: - step( - "Verify that these prefixes, originated on R1, are not" - "received on R3 but received on R2" - ) - - result = verify_rib( - tgen, - addr_type, - "r3", - input_dict_4, - next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[ - 0 - ], - expected=False, - ) - assert result is not True, ( - "Testcase {} : Failed \n " - "Expected: Routes are still present in rib of r3 \n " - "Found: {}".format(tc_name, result) - ) + step( + "Verify that these prefixes, originated on R1, are not" + "received on R3 but received on R2" + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_4, + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes are still present in rib of r3 \n " + "Found: {}".format(tc_name, result) + ) step("Remove route-map from redistribute static on R1") input_dict_2 = { diff --git a/tests/topotests/bgp_confederation_astype/__init__.py b/tests/topotests/bgp_confederation_astype/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/__init__.py diff --git a/tests/topotests/bgp_confederation_astype/r1/bgpd.conf b/tests/topotests/bgp_confederation_astype/r1/bgpd.conf new file mode 100644 index 0000000000..1859a1b942 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp confederation identifier 65300 + bgp confederation peers 65002 65003 + neighbor fabric peer-group + neighbor fabric remote-as external + neighbor 192.168.1.2 peer-group fabric + neighbor 192.168.2.2 remote-as external + address-family ipv4 unicast + neighbor fabric activate + exit-address-family +! diff --git a/tests/topotests/bgp_confederation_astype/r1/zebra.conf b/tests/topotests/bgp_confederation_astype/r1/zebra.conf new file mode 100644 index 0000000000..608a2411a3 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r1/zebra.conf @@ -0,0 +1,7 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +int r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_confederation_astype/r2/bgpd.conf b/tests/topotests/bgp_confederation_astype/r2/bgpd.conf new file mode 100644 index 0000000000..697af9743c --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r2/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + bgp confederation identifier 65300 + bgp confederation peers 65001 + neighbor fabric peer-group + neighbor fabric remote-as external + neighbor 192.168.1.1 peer-group fabric + address-family ipv4 unicast + network 172.16.255.254/32 + neighbor fabric activate + exit-address-family +! diff --git a/tests/topotests/bgp_confederation_astype/r2/zebra.conf b/tests/topotests/bgp_confederation_astype/r2/zebra.conf new file mode 100644 index 0000000000..cffe827363 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_confederation_astype/r3/bgpd.conf b/tests/topotests/bgp_confederation_astype/r3/bgpd.conf new file mode 100644 index 0000000000..c1f93f66e9 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r3/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + bgp confederation identifier 65300 + bgp confederation peers 65001 + neighbor 192.168.2.1 remote-as external + address-family ipv4 unicast + network 172.16.255.254/32 + exit-address-family +! diff --git a/tests/topotests/bgp_confederation_astype/r3/zebra.conf b/tests/topotests/bgp_confederation_astype/r3/zebra.conf new file mode 100644 index 0000000000..e5a37c98ca --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/r3/zebra.conf @@ -0,0 +1,4 @@ +! +int r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py b/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py new file mode 100644 index 0000000000..5310d3b595 --- /dev/null +++ b/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2022 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if BGP confederation works properly when using +remote-as internal/external. + +Also, check if the same works with peer-groups as well. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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 setup_module(mod): + topodef = {"s1": ("r1", "r2"), "s2": ("r1", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_confederation_astype(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peerCount": 2, + "peers": { + "192.168.1.2": { + "hostname": "r2", + "remoteAs": 65002, + "localAs": 65001, + "pfxRcd": 1, + "state": "Established", + }, + "192.168.2.2": { + "hostname": "r3", + "remoteAs": 65003, + "localAs": 65001, + "pfxRcd": 1, + "state": "Established", + }, + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge" + + def _bgp_check_neighbors(): + output = json.loads(r1.vtysh_cmd("show bgp neighbors json")) + expected = { + "192.168.1.2": { + "nbrCommonAdmin": True, + "nbrConfedExternalLink": True, + "hostname": "r2", + }, + "192.168.2.2": { + "nbrCommonAdmin": True, + "nbrConfedExternalLink": True, + "hostname": "r3", + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_neighbors) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't see neighbors to be in BGP confederation" + + def _bgp_check_routes(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.254/32": [ + { + "valid": True, + "pathFrom": "external", + "path": "(65003)", + }, + { + "valid": True, + "pathFrom": "external", + "path": "(65002)", + }, + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't see routes to be in BGP confederation" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py b/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf new file mode 100644 index 0000000000..1846df24f3 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 0.65000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 0.1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 0.500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf new file mode 100644 index 0000000000..c9adfa4671 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 0.1000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 0.500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf new file mode 100644 index 0000000000..9a831270b4 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 3000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf new file mode 100644 index 0000000000..39499a198d --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf new file mode 100644 index 0000000000..c9adfa4671 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 0.1000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 0.500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf new file mode 100644 index 0000000000..b85911504e --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py new file mode 100644 index 0000000000..efecad3eb2 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# +# bgp_local_as_private_remove.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_local_as_private_remove.py: +Test if primary AS number is not removed in cases when `local-as` +used together with `remove-private-AS`. +""" + +import os +import sys +import json +import time +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + 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["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_remove_private_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + while True: + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + if output["192.168.255.1"]["bgpState"] == "Established": + time.sleep(1) + return True + + def _bgp_as_path(router): + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp 172.16.255.254/32 json") + ) + if output["prefix"] == "172.16.255.254/32": + return output["paths"][0]["aspath"]["segments"][0]["list"] + + if _bgp_converge("r2"): + assert len(_bgp_as_path("r2")) == 1 + assert '0.65000' not in _bgp_as_path("r2") + + if _bgp_converge("r4"): + assert len(_bgp_as_path("r4")) == 2 + assert '0.3000' in _bgp_as_path("r4") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json new file mode 100644 index 0000000000..b481932449 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json @@ -0,0 +1,147 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.2.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::2:0/128", + "next_hop":"Null0" + }] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json new file mode 100644 index 0000000000..afacab4946 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json @@ -0,0 +1,317 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + + ], + "static_routes":[ + { + "network":"10.0.0.1/32", + "next_hop":"Null0" + }, + { + "network":"10::1/128", + "next_hop":"Null0" + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json new file mode 100644 index 0000000000..02aacf791a --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json @@ -0,0 +1,132 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py new file mode 100644 index 0000000000..cb8fa1e9f9 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. +""" + +import os +import sys +import time +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, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK_1_1 = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NETWORK_1_2 = {"ipv4": "10.1.2.0/32", "ipv6": "10:1::2:0/128"} +AGGREGATE_NW = {"ipv4": "10.1.0.0/16", "ipv6": "10:1::/96"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_agg.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +#################################################################################################################### +# +# Testcases +# +#################################################################################################################### + + +def test_verify_bgp_local_as_agg_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Done in base config: Advertise prefix 10.1.1.0/24 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/120 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK_1_1[addr_type]}]} + } + + input_static_verify_r2 = { + "r2": {"static_routes": [{"network": NETWORK_1_2[addr_type]}]} + } + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_static_verify_r2) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure aggregate-address to summarise all the advertised routes.") + for addr_type in ADDR_TYPES: + route_aggregate = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + "as_set": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see a summarised route on advertising router R3 " + "and receiving router R4 for both AFIs" + ) + + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + input_static_r1 = { + "r1": {"static_routes": [{"network": [NETWORK_1_1[addr_type]]}]} + } + + input_static_r2 = { + "r2": {"static_routes": [{"network": [NETWORK_1_2[addr_type]]}]} + } + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_agg_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1", "r2"], [input_static_r1, input_static_r2]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 1.110 {1.100,1.110,1.200} by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "{1.100,1.110,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "{1.100,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 {1.100,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py new file mode 100644 index 0000000000..6937a61c33 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py @@ -0,0 +1,524 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# +# +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### +########################################################################################################################################### +# +# 1.10.1.7. Verify the BGP Local AS functionality with ECMP on 8 links by adding no-prepend and replace-as command in between eBGP Peers. +# +################################################################################################################################################# + +import os +import sys +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_ecmp.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### + + +def test_verify_bgp_local_as_in_ecmp_EBGP_p0(request): + """ + Verify the BGP Local AS functionality with ECMP on 8 links by + adding no-prepend and replace-as command in between eBGP Peers. + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": {"local_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + dest_link = {} + for link_no in range(1, 9): + link = "r4-link" + str(link_no) + dest_link[link] = {"local_asn": {"remote_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r3": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = { + "local_asn": {"local_as": "1.110", "no_prepend": True, "replace_as": True} + } + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py new file mode 100644 index 0000000000..e9234f5172 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py @@ -0,0 +1,3655 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +########################################################################################################## +# +# Functionality Testcases +# +########################################################################################################## +""" +1. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between eBGP Peers. +2. Verify the BGP Local AS functionality by configuring 4 Byte AS at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers. +3. Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. +5. Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. +6. Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. +7. Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. +8. Verify the BGP Local AS functionality with different AS configurations. +9. Verify the BGP Local AS functionality with R3& R4 with different AS configurations. +""" + +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + get_frr_ipv6_linklocal, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, + create_route_maps, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + clear_bgp_and_verify, + verify_bgp_rib, + modify_as_number, + create_router_bgp, + verify_bgp_advertised_routes_from_neighbor, + verify_graceful_restart, + verify_r_bit, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +NEXT_HOP_IP_GR = {"ipv4": "10.0.0.5", "ipv6": "fd00:0:0:1::2/64"} +NEXT_HOP_IP_1 = {"ipv4": "10.0.0.101", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "10.0.0.102", "ipv6": "fd00::2"} + +BGP_CONVERGENCE = False +PREFERRED_NEXT_HOP = "link_local" +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################## +# +# Local APIs +# +########################################################################################################## + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +########################################################################################################## +# +# Testcases +# +########################################################################################################## + + +def test_verify_bgp_local_as_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and + replace-as command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_4B_AS_mid_4B_AS_p0(request): + """ + Verify the BGP Local AS functionality by configuring 4 Byte AS + at R3 and 4 Byte AS at R2 & R4 in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: { + "local_asn": { + "remote_as": "183.2926" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-183.2926 is got added in the AS list 183.2926 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "183.2926 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "183.2926 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_GR_EBGP_p0(request): + """ + Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + """ + GR Steps : Helper BGP router R2, mark and unmark IPV4 routes + as stale as the restarting router R3 come up within the restart time + """ + # Create route-map to prefer global next-hop + input_dict = { + "r2": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + "r3": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_neigh_rm = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_neigh_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "1.110"}, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "1.110"}, + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r2" + peer = "r3" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + input_topo = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, "bgp") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 2] : R3 goes for reload ") + + kill_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info( + "[Phase 3] : R3 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB" + " and ZEBRA" + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 5] : R3 is about to come up now ") + start_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info("[Phase 5] : R3 is UP Now ! ") + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result) + + # Verifying GR stats + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_aspath_p0(request): + """ + Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R3 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "1.1000 1.1000", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R4") + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.300 is got replaced with 1.200 in the AS list 1.110 1.1000 1.1000 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r4" + aspath = "1.110 1.1000 1.1000 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modify AS Number for R3") + input_dict_modify_as_number = {"r3": {"bgp": {"local_as": "1.200"}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "next_hop_self": True, + "local_asn": { + "remote_as": "1.200", + }, + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_allow_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modidy AS Number for R4") + input_dict_modify_as_number = {"r4": {"bgp": {"local_as": "1.100"}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure allow-as at R4") + for addr_type in ADDR_TYPES: + allow_as_config_r4 = { + "r4": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "allowas-in": { + "number_occurences": 1 + } + } + } + } + } + } + } + } + } + ] + } + } + + step( + "Configuring allow-as for {} address-family on router R4 ".format(addr_type) + ) + result = create_router_bgp(tgen, topo, allow_as_config_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + # now modify the as in r4 and reconfig bgp in r3 with new remote as. + topo1 = deepcopy(topo) + topo1["routers"]["r4"]["bgp"]["local_as"] = "1.100" + + delete_bgp = {"r3": {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo1, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + build_config_from_json(tgen, topo1, save_bkup=False) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_port_reset_p0(request): + """ + Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Api call to modfiy BGP timers at R3") + for addr_type in ADDR_TYPES: + input_dict_r3_timers = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_timers) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for count in range(1, 1): + step("Iteration {}".format(count)) + step("Shut down connecting interface between R3<<>>R4 on R3.") + + intf1 = topo["routers"]["r3"]["links"]["r4"]["interface"] + + interfaces = [intf1] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r3", intf, False) + + step( + "On R3, all BGP peering in respective vrf instances go down" + " when the interface is shut" + ) + + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: BGP will not be converged \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified after restart of r3") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative2_p0(request): + """ + Verify the BGP Local AS functionality with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + step("Verify that AS-1.300 is replaced with AS-1.110 at R3 router.") + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # configure negative scenarios + step("Configure local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.110", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative3_p0(request): + """ + Verify the BGP Local AS functionality with R3& R4 with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Perform Negative scenarios + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_restart_daemons_p0(request): + """ + Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands" + ) + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands" + ) + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index f1937f45be..7609c7f899 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -8,6 +8,7 @@ import ipaddress import sys from copy import deepcopy +from time import sleep # Import common_config to use commomnly used APIs from lib.common_config import ( @@ -188,6 +189,24 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf): cmd = "no maximum-paths" config_data.append(cmd) + # Flood reduction. + flood_data = ospf_data.setdefault("flood-reduction", {}) + if flood_data: + cmd = "flood-reduction" + del_action = ospf_data.setdefault("del_flood_reduction", False) + if del_action: + cmd = "no flood-reduction" + config_data.append(cmd) + + # LSA refresh timer - A hidden command. + refresh_data = ospf_data.setdefault("lsa-refresh", {}) + if refresh_data: + cmd = "ospf lsa-refresh {}".format(refresh_data) + del_action = ospf_data.setdefault("del_lsa_refresh", False) + if del_action: + cmd = "no ospf lsa-refresh" + config_data.append(cmd) + # redistribute command redistribute_data = ospf_data.setdefault("redistribute", {}) if redistribute_data: @@ -220,6 +239,9 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf): if "type" in area: cmd = cmd + " {}".format(area["type"]) + if "flood-reduction" in area: + cmd = cmd + " flood-reduction" + del_action = area.setdefault("delete", False) if del_action: cmd = "no {}".format(cmd) @@ -724,9 +746,6 @@ def verify_ospf_neighbor( return result -################################ -# Verification procs -################################ @retry(retry_timeout=50) def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False): """ @@ -1339,8 +1358,10 @@ def verify_ospf_interface( return result -@retry(retry_timeout=20) -def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): +@retry(retry_timeout=40) +def verify_ospf_database( + tgen, topo, dut, input_dict, vrf=None, lsatype=None, rid=None, expected=True +): """ This API is to verify ospf lsa's by running show ip ospf database command. @@ -1398,7 +1419,23 @@ def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): rnode = tgen.routers()[dut] logger.info("Verifying OSPF interface on router %s:", dut) - show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True) + + if not rid: + rid = "self-originate" + if lsatype: + if vrf is None: + command = "show ip ospf database {} {} json".format(lsatype, rid) + else: + command = "show ip ospf database {} {} vrf {} json".format( + lsatype, rid, vrf + ) + else: + if vrf is None: + command = "show ip ospf database json" + else: + command = "show ip ospf database vrf {} json".format(vrf) + + show_ospf_json = run_frr_cmd(rnode, command, isjson=True) # Verifying output dictionary show_ospf_json is empty or not if not bool(show_ospf_json): errormsg = "OSPF is not running" @@ -1407,26 +1444,40 @@ def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): # for inter and inter lsa's ospf_db_data = input_dict.setdefault("areas", None) ospf_external_lsa = input_dict.setdefault("AS External Link States", None) + # import pdb; pdb.set_trace() if ospf_db_data: for ospf_area, area_lsa in ospf_db_data.items(): - if ospf_area in show_ospf_json["areas"]: - if "Router Link States" in area_lsa: - for lsa in area_lsa["Router Link States"]: + if ospf_area in show_ospf_json["routerLinkStates"]["areas"]: + if "routerLinkStates" in area_lsa: + for lsa in area_lsa["routerLinkStates"]: + _advrtr = lsa.setdefault("advertisedRouter", None) + _options = lsa.setdefault("options", None) + if ( - lsa - in show_ospf_json["areas"][ospf_area]["Router Link States"] + _options + and lsa["lsaId"] + == show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["linkStateId"] + and lsa["options"] + == show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["options"] ): - logger.info( - "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s", - router, - ospf_area, - lsa, - ) result = True + break else: - errormsg = ( - "[DUT: {}] OSPF LSDB area {}: expected" - " Router LSA is {}".format(router, ospf_area, lsa) + errormsg = '[DUT: {}] OSPF LSA options: expected {}, Received Options are {} lsa["options"] {} OSPF LSAID: expected lsaid {}, Received lsaid {}'.format( + dut, + show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["options"], + _options, + lsa["options"], + show_ospf_json["routerLinkStates"]["areas"][ospf_area][ + 0 + ]["linkStateId"], + lsa["lsaId"], ) return errormsg if "Net Link States" in area_lsa: @@ -2476,3 +2527,495 @@ def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return result + + +def get_ospf_database(tgen, topo, dut, input_dict, vrf=None, lsatype=None, rid=None): + """ + This API is to return ospf lsa's by running + show ip ospf database command. + + Parameters + ---------- + * `tgen` : Topogen object + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `topo` : next to be verified + * `vrf` : vrf to be checked + * `lsatype` : type of lsa to be checked + * `rid` : router id for lsa to be checked + Usage + ----- + input_dict = { + "areas": { + "0.0.0.0": { + "routerLinkStates": { + "100.1.1.0-100.1.1.0": { + "LSID": "100.1.1.0", + "Advertised router": "100.1.1.0", + "LSA Age": 130, + "Sequence Number": "80000006", + "Checksum": "a703", + "Router links": 3 + } + }, + "networkLinkStates": { + "10.0.0.2-100.1.1.1": { + "LSID": "10.0.0.2", + "Advertised router": "100.1.1.1", + "LSA Age": 137, + "Sequence Number": "80000001", + "Checksum": "9583" + } + }, + }, + } + } + result = get_ospf_database(tgen, topo, dut, input_dict) + + Returns + ------- + True or False (Error Message) + """ + + result = False + router = dut + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + sleep(10) + if "ospf" not in topo["routers"][dut]: + errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut) + return errormsg + + rnode = tgen.routers()[dut] + + logger.info("Verifying OSPF interface on router %s:", dut) + if not rid: + rid = "self-originate" + if lsatype: + if vrf is None: + command = "show ip ospf database {} {} json".format(lsatype, rid) + else: + command = "show ip ospf database {} {} vrf {} json".format( + lsatype, rid, vrf + ) + else: + if vrf is None: + command = "show ip ospf database json" + else: + command = "show ip ospf database vrf {} json".format(vrf) + + show_ospf_json = run_frr_cmd(rnode, command, isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # for inter and inter lsa's + ospf_db_data = input_dict.setdefault("areas", None) + ospf_external_lsa = input_dict.setdefault("asExternalLinkStates", None) + + if ospf_db_data: + for ospf_area, area_lsa in ospf_db_data.items(): + if "areas" in show_ospf_json and ospf_area in show_ospf_json["areas"]: + if "routerLinkStates" in area_lsa: + for lsa in area_lsa["routerLinkStates"]: + for rtrlsa in show_ospf_json["areas"][ospf_area][ + "routerLinkStates" + ]: + _advrtr = lsa.setdefault("advertisedRouter", None) + _options = lsa.setdefault("options", None) + if ( + _advrtr + and lsa["lsaId"] == rtrlsa["lsaId"] + and lsa["advertisedRouter"] + == rtrlsa["advertisedRouter"] + ): + result = True + break + if ( + _options + and lsa["lsaId"] == rtrlsa["lsaId"] + and lsa["options"] == rtrlsa["options"] + ): + result = True + break + + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Router LSA is {}\n found Router LSA: {}".format( + router, ospf_area, lsa, rtrlsa + ) + ) + return errormsg + + if "networkLinkStates" in area_lsa: + for lsa in area_lsa["networkLinkStates"]: + for netlsa in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ]: + if ( + lsa + in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ] + ): + if ( + lsa["lsaId"] == netlsa["lsaId"] + and lsa["advertisedRouter"] + == netlsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Network LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "summaryLinkStates" in area_lsa: + for lsa in area_lsa["summaryLinkStates"]: + for t3lsa in show_ospf_json["areas"][ospf_area][ + "summaryLinkStates" + ]: + if ( + lsa["lsaId"] == t3lsa["lsaId"] + and lsa["advertisedRouter"] == t3lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "nssaExternalLinkStates" in area_lsa: + for lsa in area_lsa["nssaExternalLinkStates"]: + for t7lsa in show_ospf_json["areas"][ospf_area][ + "nssaExternalLinkStates" + ]: + if ( + lsa["lsaId"] == t7lsa["lsaId"] + and lsa["advertisedRouter"] == t7lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Type7 LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "asbrSummaryLinkStates" in area_lsa: + for lsa in area_lsa["asbrSummaryLinkStates"]: + for t4lsa in show_ospf_json["areas"][ospf_area][ + "asbrSummaryLinkStates" + ]: + if ( + lsa["lsaId"] == t4lsa["lsaId"] + and lsa["advertisedRouter"] == t4lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + result = True + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " ASBR Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "linkLocalOpaqueLsa" in area_lsa: + for lsa in area_lsa["linkLocalOpaqueLsa"]: + try: + for lnklsa in show_ospf_json["areas"][ospf_area][ + "linkLocalOpaqueLsa" + ]: + if ( + lsa["lsaId"] in lnklsa["lsaId"] + and "linkLocalOpaqueLsa" + in show_ospf_json["areas"][ospf_area] + ): + logger.info( + ( + "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA" + "%s", + ospf_area, + lsa, + ) + ) + result = True + else: + errormsg = ( + "[DUT: FRR] OSPF LSDB area: {} " + "expected Opaque-LSA is {}, Found is {}".format( + ospf_area, lsa, show_ospf_json + ) + ) + raise ValueError(errormsg) + return errormsg + except KeyError: + errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present" + return errormsg + else: + if "routerLinkStates" in area_lsa: + for lsa in area_lsa["routerLinkStates"]: + for rtrlsa in show_ospf_json["routerLinkStates"]: + _advrtr = lsa.setdefault("advertisedRouter", None) + _options = lsa.setdefault("options", None) + _age = lsa.setdefault("lsaAge", None) + if ( + _options + and lsa["options"] + == show_ospf_json["routerLinkStates"][rtrlsa][ + ospf_area + ][0]["options"] + ): + result = True + break + if ( + _age is not "get" + and lsa["lsaAge"] + == show_ospf_json["routerLinkStates"][rtrlsa][ + ospf_area + ][0]["lsaAge"] + ): + result = True + break + + if _age == "get": + return "{}".format( + show_ospf_json["routerLinkStates"][rtrlsa][ + ospf_area + ][0]["lsaAge"] + ) + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Router LSA is {}\n found Router LSA: {}".format( + router, + ospf_area, + lsa, + show_ospf_json["routerLinkStates"], + ) + ) + return errormsg + + if "networkLinkStates" in area_lsa: + for lsa in area_lsa["networkLinkStates"]: + for netlsa in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ]: + if ( + lsa + in show_ospf_json["areas"][ospf_area][ + "networkLinkStates" + ] + ): + if ( + lsa["lsaId"] == netlsa["lsaId"] + and lsa["advertisedRouter"] + == netlsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Network LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "summaryLinkStates" in area_lsa: + for lsa in area_lsa["summaryLinkStates"]: + for t3lsa in show_ospf_json["areas"][ospf_area][ + "summaryLinkStates" + ]: + if ( + lsa["lsaId"] == t3lsa["lsaId"] + and lsa["advertisedRouter"] == t3lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "nssaExternalLinkStates" in area_lsa: + for lsa in area_lsa["nssaExternalLinkStates"]: + for t7lsa in show_ospf_json["areas"][ospf_area][ + "nssaExternalLinkStates" + ]: + if ( + lsa["lsaId"] == t7lsa["lsaId"] + and lsa["advertisedRouter"] == t7lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s", + router, + ospf_area, + lsa, + ) + break + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " Type7 LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "asbrSummaryLinkStates" in area_lsa: + for lsa in area_lsa["asbrSummaryLinkStates"]: + for t4lsa in show_ospf_json["areas"][ospf_area][ + "asbrSummaryLinkStates" + ]: + if ( + lsa["lsaId"] == t4lsa["lsaId"] + and lsa["advertisedRouter"] == t4lsa["advertisedRouter"] + ): + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s", + router, + ospf_area, + lsa, + ) + result = True + else: + errormsg = ( + "[DUT: {}] OSPF LSDB area {}: expected" + " ASBR Summary LSA is {}".format(router, ospf_area, lsa) + ) + return errormsg + + if "linkLocalOpaqueLsa" in area_lsa: + for lsa in area_lsa["linkLocalOpaqueLsa"]: + try: + for lnklsa in show_ospf_json["areas"][ospf_area][ + "linkLocalOpaqueLsa" + ]: + if ( + lsa["lsaId"] in lnklsa["lsaId"] + and "linkLocalOpaqueLsa" + in show_ospf_json["areas"][ospf_area] + ): + logger.info( + ( + "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA" + "%s", + ospf_area, + lsa, + ) + ) + result = True + else: + errormsg = ( + "[DUT: FRR] OSPF LSDB area: {} " + "expected Opaque-LSA is {}, Found is {}".format( + ospf_area, lsa, show_ospf_json + ) + ) + raise ValueError(errormsg) + return errormsg + except KeyError: + errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present" + return errormsg + + if ospf_external_lsa: + for lsa in ospf_external_lsa: + try: + for t5lsa in show_ospf_json["asExternalLinkStates"]: + if ( + lsa["lsaId"] == t5lsa["lsaId"] + and lsa["advertisedRouter"] == t5lsa["advertisedRouter"] + ): + result = True + break + except KeyError: + result = False + if result: + logger.info("[DUT: %s] OSPF LSDB:External LSA %s", router, lsa) + result = True + else: + errormsg = ( + "[DUT: {}] OSPF LSDB : expected" + " External LSA is {}".format(router, lsa) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result diff --git a/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json b/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json new file mode 100644 index 0000000000..74443cdc7d --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_flood_reduction.json @@ -0,0 +1,214 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.1", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.1", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4, + "network": "broadcast" + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4, + "network": "broadcast" + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.0" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [ + { + "id": "0.0.0.2", + "type": "nssa" + } + ], + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + }, + "graceful-restart": { + "helper-only": [] + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py b/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py new file mode 100644 index 0000000000..bd7db644d1 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_flood_reduction.py @@ -0,0 +1,1066 @@ +#!/usr/bin/python + +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +pytestmark = [pytest.mark.ospfd, pytest.mark.staticd] +# Global variables +topo = None + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topolog import logger +from lib.topojson import build_config_from_json + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_static_routes, + step, + topo_daemons, + shutdown_bringup_interface, + check_router_status, + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_static_routes, + step, + shutdown_bringup_interface, + check_router_status, + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + stop_router, + start_router, + step, + create_static_routes, + kill_router_daemons, + check_router_status, + start_router_daemons, +) +from lib.topolog import logger +from lib.topogen import Topogen, get_topogen + +from lib.topojson import build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + clear_ospf, + create_router_ospf, + verify_ospf_database, + get_ospf_database, +) + +# Global variables +topo = None +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ] +} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. Verify OSPF Flood reduction functionality with ospf enabled on process level. +2. Verify OSPF Flood reduction functionality with ospf enabled on area level. +3. Verify OSPF Flood reduction functionality between different area's. +4. Verify OSPF Flood reduction functionality with ospf enabled on process level with default lsa refresh timer. +5. Chaos - Verify OSPF TTL GTSM and flood reduction functionality in chaos scenarios. +""" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/ospf_flood_reduction.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen) + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## +def test_ospf_flood_red_tc1_p0(request): + """Verify OSPF Flood reduction functionality with ospf enabled on process level.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + + global topo + + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + red_static("r0") + step("Base config should be up, verify using OSPF convergence on all the routers") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable flood reduction in process level on R0") + ospf_flood = {"r0": {"ospf": {"flood-reduction": True}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Verify that ospf lsa's are set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure the custom refresh timer") + ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Enable flood. reduction in all the routers of the topology.") + for rtr in topo["routers"].keys(): + ospf_flood = {rtr: {"ospf": {"lsa-refresh": 120, "flood-reduction": True}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lsa's are set with dc bit 1.") + for rtr in topo["routers"]: + dut = rtr + lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"]) + input_dict_db = { + "routerId": lsid, + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": lsid, + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + # this wait is put so that we wait for 5secs to check if lsa is refreshed. + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Disable flood reduction in R0.") + ospf_flood = { + "r0": {"ospf": {"flood-reduction": True, "del_flood_reduction": True}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + clear_ospf(tgen, "r0") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are not set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: OSPF LSA should not be set with DC bit in {} \n " + "Found: {}".format(tc_name, dut, result) + ) + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + if result2 is not result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Enable flood reduction on each router with 10 secs delay of between each router." + ) + for rtr in topo["routers"].keys(): + ospf_flood = {rtr: {"ospf": {"lsa-refresh": 120, "flood-reduction": True}}} + sleep(10) + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify OSPF neighborship when OSPF flood reduction is configured and ospf process is restarted" + ) + + step("Kill OSPFd daemon on R0.") + kill_router_daemons(tgen, "r0", ["ospfd"]) + start_router_daemons(tgen, "r0", ["ospfd"]) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_flood_red_tc2_p0(request): + """Verify OSPF Flood reduction functionality with ospf enabled on area level.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + + global topo + + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + red_static("r0") + step("Base config should be up, verify using OSPF convergence on all the routers") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable flood reduction in area level on R0 in area 0") + ospf_flood = { + "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Verify that ospf lsa's are set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure the custom refresh timer") + ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Enable flood. reduction in all the routers of the topology.") + for rtr in topo["routers"].keys(): + ospf_flood = { + rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lsa's are set with dc bit 1.") + for rtr in topo["routers"]: + dut = rtr + lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"]) + input_dict_db = { + "routerId": lsid, + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": lsid, + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Disable flood reduction in R0.") + ospf_flood = { + "r0": { + "ospf": { + "area": [{"id": "0.0.0.0", "flood-reduction": True, "delete": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + clear_ospf(tgen, "r0") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are not set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: OSPF LSA should not be set with DC bit in {} \n " + "Found: {}".format(tc_name, dut, result) + ) + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + if result2 is not result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Enable flood reduction on each router with 10 secs delay of between each router." + ) + for rtr in topo["routers"].keys(): + ospf_flood = { + rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + sleep(10) + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that LSA's are not refreshed. Do not age bit should be set to 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_flood_red_tc3_p0(request): + """Verify OSPF Flood reduction functionality between different area's""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + check_router_status(tgen) + + global topo + + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + red_static("r0") + step("Base config should be up, verify using OSPF convergence on all the routers") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable flood reduction in area level on R0 in area 0") + ospf_flood = { + "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step("Verify that ospf lsa's are set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure the custom refresh timer") + ospf_flood = {"r0": {"ospf": {"lsa-refresh": 120}}} + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + step( + "Enable flood. reduction in all the routers of the topology in area 0. Redistribute static route in area 0 R1 and area1 in R2." + ) + for rtr in topo["routers"].keys(): + ospf_flood = { + rtr: {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + sleep(10) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lsa's are set with dc bit 1.") + for rtr in topo["routers"]: + dut = rtr + lsid = "{}".format(topo["routers"][rtr]["ospf"]["router_id"]) + input_dict_db = { + "routerId": lsid, + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": lsid, + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid=lsid + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + assert (result1 == result2) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Enable flood reduction in area 1.") + + ospf_flood = { + "r0": {"ospf": {"area": [{"id": "0.0.0.0", "flood-reduction": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r1": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True}, + {"id": "0.0.0.1", "flood-reduction": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r2": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True}, + {"id": "0.0.0.1", "flood-reduction": True}, + {"id": "0.0.0.2", "flood-reduction": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r3": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True}, + {"id": "0.0.0.2", "flood-reduction": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are set with dc bit 1.") + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.1", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.1", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1" + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.1", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.1", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.1" + ) + + if result2 is result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Disable flood reduction in R0.") + + ospf_flood = { + "r0": { + "ospf": { + "area": [{"id": "0.0.0.0", "flood-reduction": True, "delete": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r1": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.1", "flood-reduction": True, "delete": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r2": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.1", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.2", "flood-reduction": True, "delete": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + ospf_flood = { + "r3": { + "ospf": { + "area": [ + {"id": "0.0.0.0", "flood-reduction": True, "delete": True}, + {"id": "0.0.0.2", "flood-reduction": True, "delete": True}, + ] + } + } + } + result = create_router_ospf(tgen, topo, ospf_flood) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + clear_ospf(tgen, "r0") + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "Testcase :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Verify that ospf lea's are not set with dc bit 1.") + dut = "r0" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + { + "lsaId": "100.1.1.0", + "options": "*|-|DC|-|-|-|E|-", + }, + ] + } + }, + } + result = verify_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0", expected=False + ) + + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: OSPF LSA should not be set with DC bit in {} \n " + "Found: {}".format(tc_name, dut, result) + ) + + step("Wait for 120 secs and verify that LSA's are not refreshed. ") + # get LSA age + dut = "r1" + input_dict_db = { + "routerId": "100.1.1.0", + "areas": { + "0.0.0.0": { + "routerLinkStates": [ + {"lsaId": "100.1.1.0", "lsaAge": "get"}, + ] + } + }, + } + sleep(10) + + result1 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + sleep(5) + result2 = get_ospf_database( + tgen, topo, dut, input_dict_db, lsatype="router", rid="100.1.1.0" + ) + + if result2 is not result1: + result = True + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospfapi/r1/ospfd.conf b/tests/topotests/ospfapi/r1/ospfd.conf index 8d13556847..338691bbf3 100644 --- a/tests/topotests/ospfapi/r1/ospfd.conf +++ b/tests/topotests/ospfapi/r1/ospfd.conf @@ -4,7 +4,12 @@ interface r1-eth0 ip ospf dead-interval 10 ip ospf area 1.2.3.4 ! +interface r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 1.2.3.4 +! router ospf - ospf router-id 192.168.0.1 + ospf router-id 1.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r1/zebra.conf b/tests/topotests/ospfapi/r1/zebra.conf index aae87408c3..745159141d 100644 --- a/tests/topotests/ospfapi/r1/zebra.conf +++ b/tests/topotests/ospfapi/r1/zebra.conf @@ -2,3 +2,5 @@ interface r1-eth0 ip address 10.0.1.1/24 ! +interface r1-eth1 + ip address 10.0.4.1/24 diff --git a/tests/topotests/ospfapi/r2/ospfd.conf b/tests/topotests/ospfapi/r2/ospfd.conf index be0712742b..1bf2a0c7f2 100644 --- a/tests/topotests/ospfapi/r2/ospfd.conf +++ b/tests/topotests/ospfapi/r2/ospfd.conf @@ -10,6 +10,6 @@ interface r2-eth1 ip ospf area 1.2.3.4 ! router ospf - ospf router-id 192.168.0.2 + ospf router-id 2.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r3/ospfd.conf b/tests/topotests/ospfapi/r3/ospfd.conf index 77cd86a975..ecf2042b42 100644 --- a/tests/topotests/ospfapi/r3/ospfd.conf +++ b/tests/topotests/ospfapi/r3/ospfd.conf @@ -10,6 +10,6 @@ interface r3-eth1 ip ospf area 1.2.3.4 ! router ospf - ospf router-id 192.168.0.3 + ospf router-id 3.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r4/ospfd.conf b/tests/topotests/ospfapi/r4/ospfd.conf index 32e55d546b..4de8caeb17 100644 --- a/tests/topotests/ospfapi/r4/ospfd.conf +++ b/tests/topotests/ospfapi/r4/ospfd.conf @@ -4,7 +4,12 @@ interface r4-eth0 ip ospf dead-interval 10 ip ospf area 1.2.3.4 ! +interface r4-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 1.2.3.4 +! router ospf - ospf router-id 192.168.0.4 + ospf router-id 4.0.0.0 capability opaque ! diff --git a/tests/topotests/ospfapi/r4/zebra.conf b/tests/topotests/ospfapi/r4/zebra.conf index 702219720d..233ffa5c7b 100644 --- a/tests/topotests/ospfapi/r4/zebra.conf +++ b/tests/topotests/ospfapi/r4/zebra.conf @@ -2,3 +2,5 @@ interface r4-eth0 ip address 10.0.3.4/24 ! +interface r4-eth1 + ip address 10.0.4.4/24
\ No newline at end of file diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py index 7622a4ca54..b01c96226e 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -21,7 +21,7 @@ from datetime import datetime, timedelta import pytest from lib.common_config import retry, run_frr_cmd, step -from lib.micronet import comm_error +from lib.micronet import Timeout, comm_error from lib.topogen import Topogen, TopoRouter from lib.topotest import interface_set_status, json_cmp @@ -43,15 +43,20 @@ assert os.path.exists( # Test Setup # ---------- +# +# r1 - r2 +# | | +# r4 - r3 +# + @pytest.fixture(scope="function", name="tgen") def _tgen(request): "Setup/Teardown the environment and provide tgen argument to tests" nrouters = request.param - if nrouters == 1: - topodef = {"sw1:": ("r1",)} - else: - topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)} + topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)} + if nrouters == 4: + topodef["sw4"] = ("r4", "r1") tgen = Topogen(topodef, request.module.__name__) tgen.start_topology() @@ -94,23 +99,23 @@ def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json" def myreadline(f): buf = b"" while True: - # logging.info("READING 1 CHAR") + # logging.debug("READING 1 CHAR") c = f.read(1) if not c: return buf if buf else None buf += c - # logging.info("READ CHAR: '%s'", c) + # logging.debug("READ CHAR: '%s'", c) if c == b"\n": return buf -def _wait_output(p, regex, timeout=120): - retry_until = datetime.now() + timedelta(seconds=timeout) - while datetime.now() < retry_until: +def _wait_output(p, regex, maxwait=120): + timeout = Timeout(maxwait) + while not timeout.is_expired(): # line = p.stdout.readline() line = myreadline(p.stdout) if not line: - assert None, "Timeout waiting for '{}'".format(regex) + assert None, "EOF waiting for '{}'".format(regex) line = line.decode("utf-8") line = line.rstrip() if line: @@ -118,7 +123,9 @@ def _wait_output(p, regex, timeout=120): m = re.search(regex, line) if m: return m - assert None, "Failed to get output withint {}s".format(timeout) + assert None, "Failed to get output matching '{}' withint {} actual {}s".format( + regex, maxwait, timeout.elapsed() + ) # ----- @@ -128,12 +135,13 @@ def _wait_output(p, regex, timeout=120): def _test_reachability(tgen, testbin): waitlist = [ - "192.168.0.1,192.168.0.2,192.168.0.4", - "192.168.0.2,192.168.0.4", - "192.168.0.1,192.168.0.2,192.168.0.4", + "1.0.0.0,2.0.0.0,4.0.0.0", + "2.0.0.0,4.0.0.0", + "1.0.0.0,2.0.0.0,4.0.0.0", ] r2 = tgen.gears["r2"] r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] wait_args = [f"--wait={x}" for x in waitlist] @@ -151,10 +159,12 @@ def _test_reachability(tgen, testbin): step("reachable: check for modified reachability") interface_set_status(r2, "r2-eth0", False) + interface_set_status(r4, "r4-eth1", False) _wait_output(p, "SUCCESS: {}".format(waitlist[1])) step("reachable: check for restored reachability") interface_set_status(r2, "r2-eth0", True) + interface_set_status(r4, "r4-eth1", True) _wait_output(p, "SUCCESS: {}".format(waitlist[2])) except Exception as error: logging.error("ERROR: %s", error) @@ -169,16 +179,16 @@ def _test_reachability(tgen, testbin): def test_ospf_reachability(tgen): testbin = os.path.join(TESTDIR, "ctester.py") rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) _test_reachability(tgen, testbin) def _test_router_id(tgen, testbin): r1 = tgen.gears["r1"] waitlist = [ - "192.168.0.1", + "1.0.0.0", "1.1.1.1", - "192.168.0.1", + "1.0.0.0", ] mon_args = [f"--monitor={x}" for x in waitlist] @@ -200,7 +210,7 @@ def _test_router_id(tgen, testbin): _wait_output(p, "SUCCESS: {}".format(waitlist[1])) step("router id: check for restored router id") - r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 192.168.0.1") + r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.0.0.0") _wait_output(p, "SUCCESS: {}".format(waitlist[2])) except Exception as error: logging.error("ERROR: %s", error) @@ -215,7 +225,7 @@ def _test_router_id(tgen, testbin): def test_ospf_router_id(tgen): testbin = os.path.join(TESTDIR, "ctester.py") rc, o, e = tgen.gears["r1"].net.cmd_status([testbin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e) _test_router_id(tgen, testbin) @@ -230,13 +240,13 @@ def _test_add_data(tgen, apibin): try: p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "areas": { "1.2.3.4": { "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", } ], @@ -252,7 +262,7 @@ def _test_add_data(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", "opaqueData": "00000202", }, @@ -272,13 +282,13 @@ def _test_add_data(tgen, apibin): p = None p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "areas": { "1.2.3.4": { "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", "lsaAge": 3600, } @@ -286,7 +296,7 @@ def _test_add_data(tgen, apibin): "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", }, ], @@ -302,7 +312,7 @@ def _test_add_data(tgen, apibin): "1.2.3.4": [ { "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", "opaqueData": "00010101", }, @@ -323,13 +333,13 @@ def _test_add_data(tgen, apibin): p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "areas": { "1.2.3.4": { "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", "lsaAge": 3600, }, @@ -339,7 +349,7 @@ def _test_add_data(tgen, apibin): "asExternalOpaqueLsa": [ { "lsId": "232.0.0.3", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", }, ], @@ -351,7 +361,7 @@ def _test_add_data(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.3", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", "opaqueData": "deadbeaf01234567", }, @@ -369,11 +379,11 @@ def _test_add_data(tgen, apibin): p = None input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "asExternalOpaqueLsa": [ { "lsId": "232.0.0.3", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", "lsaAge": 3600, }, @@ -387,11 +397,11 @@ def _test_add_data(tgen, apibin): # Originate it again p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"]) input_dict = { - "routerId": "192.168.0.1", + "routerId": "1.0.0.0", "asExternalOpaqueLsa": [ { "lsId": "232.0.0.3", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000002", }, ], @@ -402,7 +412,7 @@ def _test_add_data(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.3", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", "opaqueData": "ebadf00d", }, @@ -412,6 +422,7 @@ def _test_add_data(tgen, apibin): json_cmd = "show ip ospf da opaque-as json" assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None + logging.debug("sending interrupt to writer api client") p.send_signal(signal.SIGINT) time.sleep(2) p.wait() @@ -426,6 +437,7 @@ def _test_add_data(tgen, apibin): raise finally: if p: + logging.debug("cleanup: sending interrupt to writer api client") p.terminate() p.wait() @@ -434,7 +446,7 @@ def _test_add_data(tgen, apibin): def test_ospf_opaque_add_data3(tgen): apibin = os.path.join(CLIENTDIR, "ospfclient.py") rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) _test_add_data(tgen, apibin) @@ -446,10 +458,12 @@ def _test_opaque_add_del(tgen, apibin): p = None pread = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+") try: step("reachable: check for add notification") pread = r2.popen( - ["/usr/bin/timeout", "120", apibin, "-v"], + ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"], encoding=None, # don't buffer stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, @@ -479,30 +493,30 @@ def _test_opaque_add_del(tgen, apibin): "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", }, { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", }, ], "linkLocalOpaqueLsaCount": 2, "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", }, { "lsId": "231.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "6d30", + "checksum": "7690", }, ], "areaLocalOpaqueLsaCount": 2, @@ -511,15 +525,15 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "lsId": "232.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", }, { "lsId": "232.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", }, ], "asExternalOpaqueLsaCount": 2, @@ -543,17 +557,17 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", "length": 24, "opaqueId": 2, "opaqueDataLength": 4, @@ -568,17 +582,17 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "231.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "6d30", + "checksum": "7690", "length": 28, "opaqueDataLength": 8, }, @@ -590,17 +604,17 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "232.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", "length": 24, "opaqueDataLength": 4, }, @@ -642,32 +656,32 @@ def _test_opaque_add_del(tgen, apibin): "linkLocalOpaqueLsa": [ { "lsId": "230.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", }, { "lsId": "230.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "lsaAge": 3600, "sequenceNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", }, ], "linkLocalOpaqueLsaCount": 2, "areaLocalOpaqueLsa": [ { "lsId": "231.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", }, { "lsId": "231.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "lsaAge": 3600, "sequenceNumber": "80000002", - "checksum": "4682", + "checksum": "4fe2", }, ], "areaLocalOpaqueLsaCount": 2, @@ -676,16 +690,16 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "lsId": "232.0.0.1", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "lsaAge": 3600, "sequenceNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", }, { "lsId": "232.0.0.2", - "advertisedRouter": "192.168.0.1", + "advertisedRouter": "1.0.0.0", "sequenceNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", }, ], "asExternalOpaqueLsaCount": 2, @@ -703,18 +717,18 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", "length": 24, "opaqueId": 2, "opaqueDataLength": 4, @@ -729,18 +743,18 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", "length": 20, "opaqueDataLength": 0, }, { "lsaAge": 3600, "linkStateId": "231.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", - "checksum": "4682", + "checksum": "4fe2", # data removed "length": 20, "opaqueDataLength": 0, @@ -753,18 +767,18 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "232.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", "length": 24, "opaqueDataLength": 4, }, @@ -795,19 +809,19 @@ def _test_opaque_add_del(tgen, apibin): "1.2.3.4": [ { "linkStateId": "230.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "6d5f", + "checksum": "76bf", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "230.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "8142", + "checksum": "8aa2", "length": 24, "opaqueId": 2, "opaqueDataLength": 4, @@ -823,18 +837,18 @@ def _test_opaque_add_del(tgen, apibin): { "lsaAge": 3600, "linkStateId": "231.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000001", - "checksum": "5278", + "checksum": "5bd8", "length": 20, "opaqueDataLength": 0, }, { "lsaAge": 3600, "linkStateId": "231.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaSeqNumber": "80000002", - "checksum": "4682", + "checksum": "4fe2", # data removed "length": 20, "opaqueDataLength": 0, @@ -847,19 +861,19 @@ def _test_opaque_add_del(tgen, apibin): "asExternalOpaqueLsa": [ { "linkStateId": "232.0.0.1", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "5575", + "checksum": "5ed5", "length": 20, "opaqueDataLength": 0, }, { "linkStateId": "232.0.0.2", - "advertisingRouter": "192.168.0.1", + "advertisingRouter": "1.0.0.0", "lsaAge": 3600, "lsaSeqNumber": "80000001", - "checksum": "d05d", + "checksum": "d9bd", "length": 24, "opaqueDataLength": 4, }, @@ -918,7 +932,7 @@ def _test_opaque_add_del(tgen, apibin): def test_ospf_opaque_delete_data3(tgen): apibin = os.path.join(CLIENTDIR, "ospfclient.py") rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) - logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) _test_opaque_add_del(tgen, apibin) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index a17ee812e4..200427fb6e 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1673,10 +1673,14 @@ DEFUNSH(VTYSH_ZEBRA, srv6_locator, srv6_locator_cmd, #ifdef HAVE_BGPD DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd, - "router bgp [(1-4294967295) [<view|vrf> VIEWVRFNAME]]", + "router bgp [ASNUM [<view|vrf> VIEWVRFNAME] [as-notation <dot|dot+|plain>]]", ROUTER_STR BGP_STR AS_STR "BGP view\nBGP VRF\n" - "View/VRF name\n") + "View/VRF name\n" + "Force the AS notation output\n" + "use 'AA.BB' format for AS 4 byte values\n" + "use 'AA.BB' format for all AS values\n" + "use plain format for all AS values\n") { vty->node = BGP_NODE; return CMD_SUCCESS; diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index ad2a142fef..8e288194ec 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -356,6 +356,102 @@ module frr-bgp-route-map { } } + typedef route-distinguisher { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})|' + + '((6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0)|' + + '((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '((429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '((6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0).' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):' + + '(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))'; + } + + description + "A Route Distinguisher is an 8-octet value used to + distinguish routes from different BGP VPNs (RFC 4364). + A Route Distinguisher will have the same format as a + Route Target as per RFC 4360 and will consist of + two or three fields: a 2-octet Type field, an administrator + field, and, optionally, an assigned number field. + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + Additionally, a generic pattern is defined for future + route discriminator types: + 2-octet-other-hex-number:6-octet-hex-number + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00. + The following route distinguisher with two fields are also + accepted : 10000:44 1.2.3.4:44."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + typedef extcommunity-lb-type { type enumeration { enum "explicit-bandwidth" { @@ -598,7 +694,7 @@ module frr-bgp-route-map { description "Match eVPN route-distinguisher"; leaf route-distinguisher { - type rt-types:route-distinguisher; + type route-distinguisher; } } diff --git a/zebra/rib.h b/zebra/rib.h index 53ae63354e..70968d3900 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -392,12 +392,9 @@ extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, const union g_addr *addr, struct route_node **rn_out); -extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, - struct in_addr addr, - struct route_node **rn_out); -extern struct route_entry *rib_match_ipv6_multicast(vrf_id_t vrf_id, - struct in6_addr addr, - struct route_node **rn_out); +extern struct route_entry *rib_match_multicast(afi_t afi, vrf_id_t vrf_id, + union g_addr *gaddr, + struct route_node **rn_out); extern struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index b7344d7cc1..59152df2f1 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2236,17 +2236,18 @@ static void zread_nexthop_lookup_mrib(ZAPI_HANDLER_ARGS) { struct ipaddr addr; struct route_entry *re = NULL; + union g_addr gaddr; STREAM_GET_IPADDR(msg, &addr); switch (addr.ipa_type) { case IPADDR_V4: - re = rib_match_ipv4_multicast(zvrf_id(zvrf), addr.ipaddr_v4, - NULL); + gaddr.ipv4 = addr.ipaddr_v4; + re = rib_match_multicast(AFI_IP, zvrf_id(zvrf), &gaddr, NULL); break; case IPADDR_V6: - re = rib_match_ipv6_multicast(zvrf_id(zvrf), addr.ipaddr_v6, - NULL); + gaddr.ipv6 = addr.ipaddr_v6; + re = rib_match_multicast(AFI_IP6, zvrf_id(zvrf), &gaddr, NULL); break; case IPADDR_NONE: /* ??? */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index fc3f57eeb8..6c499b77d7 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -509,31 +509,28 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, return NULL; } -struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, - struct in_addr addr, - struct route_node **rn_out) +struct route_entry *rib_match_multicast(afi_t afi, vrf_id_t vrf_id, + union g_addr *gaddr, + struct route_node **rn_out) { struct route_entry *re = NULL, *mre = NULL, *ure = NULL; struct route_node *m_rn = NULL, *u_rn = NULL; - union g_addr gaddr = {.ipv4 = addr}; switch (zrouter.ipv4_multicast_mode) { case MCAST_MRIB_ONLY: - return rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, - rn_out); + return rib_match(afi, SAFI_MULTICAST, vrf_id, gaddr, rn_out); case MCAST_URIB_ONLY: - return rib_match(AFI_IP, SAFI_UNICAST, vrf_id, &gaddr, rn_out); + return rib_match(afi, SAFI_UNICAST, vrf_id, gaddr, rn_out); case MCAST_NO_CONFIG: case MCAST_MIX_MRIB_FIRST: - re = mre = rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, - &m_rn); + re = mre = rib_match(afi, SAFI_MULTICAST, vrf_id, gaddr, &m_rn); if (!mre) - re = ure = rib_match(AFI_IP, SAFI_UNICAST, vrf_id, - &gaddr, &u_rn); + re = ure = rib_match(afi, SAFI_UNICAST, vrf_id, gaddr, + &u_rn); break; case MCAST_MIX_DISTANCE: - mre = rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, &m_rn); - ure = rib_match(AFI_IP, SAFI_UNICAST, vrf_id, &gaddr, &u_rn); + mre = rib_match(afi, SAFI_MULTICAST, vrf_id, gaddr, &m_rn); + ure = rib_match(afi, SAFI_UNICAST, vrf_id, gaddr, &u_rn); if (mre && ure) re = ure->distance < mre->distance ? ure : mre; else if (mre) @@ -542,8 +539,8 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, re = ure; break; case MCAST_MIX_PFXLEN: - mre = rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, &m_rn); - ure = rib_match(AFI_IP, SAFI_UNICAST, vrf_id, &gaddr, &u_rn); + mre = rib_match(afi, SAFI_MULTICAST, vrf_id, gaddr, &m_rn); + ure = rib_match(afi, SAFI_UNICAST, vrf_id, gaddr, &u_rn); if (mre && ure) re = u_rn->p.prefixlen > m_rn->p.prefixlen ? ure : mre; else if (mre) @@ -558,10 +555,12 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, if (IS_ZEBRA_DEBUG_RIB) { char buf[BUFSIZ]; - inet_ntop(AF_INET, &addr, buf, BUFSIZ); + inet_ntop(afi == AFI_IP ? AF_INET : AF_INET6, gaddr, buf, + BUFSIZ); - zlog_debug("%s: %s: vrf: %s(%u) found %s, using %s", __func__, - buf, vrf_id_to_name(vrf_id), vrf_id, + zlog_debug("%s: %s: %pRN vrf: %s(%u) found %s, using %s", + __func__, buf, (re == mre) ? m_rn : u_rn, + vrf_id_to_name(vrf_id), vrf_id, mre ? (ure ? "MRIB+URIB" : "MRIB") : ure ? "URIB" : "nothing", re == ure ? "URIB" : re == mre ? "MRIB" : "none"); @@ -569,62 +568,6 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, return re; } -struct route_entry *rib_match_ipv6_multicast(vrf_id_t vrf_id, - struct in6_addr addr, - struct route_node **rn_out) -{ - struct route_entry *re = NULL, *mre = NULL, *ure = NULL; - struct route_node *m_rn = NULL, *u_rn = NULL; - union g_addr gaddr = {.ipv6 = addr}; - - switch (zrouter.ipv4_multicast_mode) { - case MCAST_MRIB_ONLY: - return rib_match(AFI_IP6, SAFI_MULTICAST, vrf_id, &gaddr, - rn_out); - case MCAST_URIB_ONLY: - return rib_match(AFI_IP6, SAFI_UNICAST, vrf_id, &gaddr, rn_out); - case MCAST_NO_CONFIG: - case MCAST_MIX_MRIB_FIRST: - re = mre = rib_match(AFI_IP6, SAFI_MULTICAST, vrf_id, &gaddr, - &m_rn); - if (!mre) - re = ure = rib_match(AFI_IP6, SAFI_UNICAST, vrf_id, - &gaddr, &u_rn); - break; - case MCAST_MIX_DISTANCE: - mre = rib_match(AFI_IP6, SAFI_MULTICAST, vrf_id, &gaddr, &m_rn); - ure = rib_match(AFI_IP6, SAFI_UNICAST, vrf_id, &gaddr, &u_rn); - if (mre && ure) - re = ure->distance < mre->distance ? ure : mre; - else if (mre) - re = mre; - else if (ure) - re = ure; - break; - case MCAST_MIX_PFXLEN: - mre = rib_match(AFI_IP6, SAFI_MULTICAST, vrf_id, &gaddr, &m_rn); - ure = rib_match(AFI_IP6, SAFI_UNICAST, vrf_id, &gaddr, &u_rn); - if (mre && ure) - re = u_rn->p.prefixlen > m_rn->p.prefixlen ? ure : mre; - else if (mre) - re = mre; - else if (ure) - re = ure; - break; - } - - if (rn_out) - *rn_out = (re == mre) ? m_rn : u_rn; - - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug("%s: %pI6: vrf: %s(%u) found %s, using %s", __func__, - &addr, vrf_id_to_name(vrf_id), vrf_id, - mre ? (ure ? "MRIB+URIB" : "MRIB") - : ure ? "URIB" : "nothing", - re == ure ? "URIB" : re == mre ? "MRIB" : "none"); - return re; -} - struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; @@ -3924,7 +3867,9 @@ static void rib_link(struct route_node *rn, struct route_entry *re, int process) rmap_name = zebra_get_import_table_route_map(afi, re->table); zebra_add_import_table_entry(zvrf, rn, re, rmap_name); - } else if (process) + } + + if (process) rib_queue_add(rn); } @@ -3999,11 +3944,9 @@ void rib_delnode(struct route_node *rn, struct route_entry *re) zlog_debug("%s(%u):%pRN: Freeing route rn %p, re %p (%s)", vrf_id_to_name(re->vrf_id), re->vrf_id, rn, rn, re, zebra_route_string(re->type)); - - rib_unlink(rn, re); - } else { - rib_queue_add(rn); } + + rib_queue_add(rn); } /* diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index a4aef36dbd..6d61430029 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -131,11 +131,12 @@ DEFUN (no_ip_multicast_mode, } -DEFUN (show_ip_rpf, +DEFPY (show_ip_rpf, show_ip_rpf_cmd, - "show ip rpf [json]", + "show [ip$ip|ipv6$ipv6] rpf [json]", SHOW_STR IP_STR + IPV6_STR "Display RPF information for multicast source\n" JSON_STR) { @@ -144,32 +145,46 @@ DEFUN (show_ip_rpf, .multi = false, }; - return do_show_ip_route(vty, VRF_DEFAULT_NAME, AFI_IP, SAFI_MULTICAST, - false, uj, 0, NULL, false, 0, 0, 0, false, - &ctx); + return do_show_ip_route(vty, VRF_DEFAULT_NAME, ip ? AFI_IP : AFI_IP6, + SAFI_MULTICAST, false, uj, 0, NULL, false, 0, 0, + 0, false, &ctx); } -DEFUN (show_ip_rpf_addr, +DEFPY (show_ip_rpf_addr, show_ip_rpf_addr_cmd, - "show ip rpf A.B.C.D", + "show ip rpf A.B.C.D$address", SHOW_STR IP_STR "Display RPF information for multicast source\n" "IP multicast source address (e.g. 10.0.0.0)\n") { - int idx_ipv4 = 3; - struct in_addr addr; struct route_node *rn; struct route_entry *re; - int ret; - ret = inet_aton(argv[idx_ipv4]->arg, &addr); - if (ret == 0) { - vty_out(vty, "%% Malformed address\n"); - return CMD_WARNING; - } + re = rib_match_multicast(AFI_IP, VRF_DEFAULT, (union g_addr *)&address, + &rn); + + if (re) + vty_show_ip_route_detail(vty, rn, 1, false, false); + else + vty_out(vty, "%% No match for RPF lookup\n"); + + return CMD_SUCCESS; +} + +DEFPY (show_ipv6_rpf_addr, + show_ipv6_rpf_addr_cmd, + "show ipv6 rpf X:X::X:X$address", + SHOW_STR + IPV6_STR + "Display RPF information for multicast source\n" + "IPv6 multicast source address\n") +{ + struct route_node *rn; + struct route_entry *re; - re = rib_match_ipv4_multicast(VRF_DEFAULT, addr, &rn); + re = rib_match_multicast(AFI_IP6, VRF_DEFAULT, (union g_addr *)&address, + &rn); if (re) vty_show_ip_route_detail(vty, rn, 1, false, false); @@ -4576,6 +4591,7 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_ip_rpf_cmd); install_element(VIEW_NODE, &show_ip_rpf_addr_cmd); + install_element(VIEW_NODE, &show_ipv6_rpf_addr_cmd); install_element(CONFIG_NODE, &ip_nht_default_route_cmd); install_element(CONFIG_NODE, &no_ip_nht_default_route_cmd); |
