diff options
213 files changed, 7840 insertions, 1845 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 4c1615a5c6..a86b42e250 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -297,6 +297,8 @@ static struct aspath *aspath_new(enum asnotation_mode asnotation) as = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); as->asnotation = asnotation; + as->count = 0; + return as; } @@ -400,6 +402,11 @@ unsigned int aspath_count_confeds(struct aspath *aspath) unsigned int aspath_count_hops(const struct aspath *aspath) { + return aspath->count; +} + +static unsigned int aspath_count_hops_internal(const struct aspath *aspath) +{ int count = 0; struct assegment *seg = aspath->segments; @@ -708,6 +715,7 @@ struct aspath *aspath_dup(struct aspath *aspath) else new->str[0] = '\0'; + new->count = aspath->count; return new; } @@ -729,6 +737,7 @@ static void *aspath_hash_alloc(void *arg) new->str_len = aspath->str_len; new->json = aspath->json; new->asnotation = aspath->asnotation; + new->count = aspath->count; return new; } @@ -856,6 +865,8 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit, if (assegments_parse(s, length, &as.segments, use32bit) < 0) return NULL; + as.count = aspath_count_hops_internal(&as); + /* If already same aspath exist then return it. */ find = hash_get(ashash, &as, aspath_hash_alloc); @@ -1032,7 +1043,7 @@ static struct assegment *aspath_aggregate_as_set_add(struct aspath *aspath, asset->as[asset->length - 1] = as; } - + aspath->count = aspath_count_hops_internal(aspath); return asset; } @@ -1113,6 +1124,8 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) assegment_normalise(aspath->segments); aspath_str_update(aspath, false); + aspath->count = aspath_count_hops_internal(aspath); + return aspath; } @@ -1268,6 +1281,7 @@ struct aspath *aspath_replace_regex_asn(struct aspath *aspath, } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); return new; } @@ -1293,6 +1307,8 @@ struct aspath *aspath_replace_specific_asn(struct aspath *aspath, } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); + return new; } @@ -1315,6 +1331,8 @@ struct aspath *aspath_replace_all_asn(struct aspath *aspath, as_t our_asn) } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); + return new; } @@ -1341,6 +1359,8 @@ struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn, } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); + return new; } @@ -1413,6 +1433,7 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn) if (!aspath->refcnt) aspath_free(aspath); aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); return new; } @@ -1469,6 +1490,7 @@ static struct aspath *aspath_merge(struct aspath *as1, struct aspath *as2) last->next = as2->segments; as2->segments = new; aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } @@ -1486,6 +1508,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) if (as2->segments == NULL) { as2->segments = assegment_dup_all(as1->segments); aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } @@ -1506,6 +1529,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) if (!as2->segments) { as2->segments = assegment_dup_all(as1->segments); aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } @@ -1551,6 +1575,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) * the inbetween AS_SEQUENCE of seg2 in the process */ aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } else { /* AS_SET merge code is needed at here. */ @@ -1662,6 +1687,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source, lastseg = newseg; } aspath_str_update(newpath, false); + newpath->count = aspath_count_hops_internal(newpath); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1680,6 +1706,7 @@ struct aspath *aspath_filter_exclude_all(struct aspath *source) newpath = aspath_new(source->asnotation); aspath_str_update(newpath, false); + newpath->count = aspath_count_hops_internal(newpath); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1767,6 +1794,7 @@ struct aspath *aspath_filter_exclude_acl(struct aspath *source, aspath_str_update(source, false); + source->count = aspath_count_hops_internal(source); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1805,6 +1833,7 @@ static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno, } aspath_str_update(aspath, false); + aspath->count = aspath_count_hops_internal(aspath); return aspath; } @@ -1896,6 +1925,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, if (!hops) { newpath = aspath_dup(as4path); aspath_str_update(newpath, false); + /* dup sets the count properly */ return newpath; } @@ -1957,6 +1987,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, aspath_free(newpath); mergedpath->segments = assegment_normalise(mergedpath->segments); aspath_str_update(mergedpath, false); + mergedpath->count = aspath_count_hops_internal(mergedpath); if (BGP_DEBUG(as4, AS4)) zlog_debug("[AS4] result of synthesizing is %s", @@ -2027,8 +2058,10 @@ struct aspath *aspath_delete_confed_seq(struct aspath *aspath) seg = next; } - if (removed_confed_segment) + if (removed_confed_segment) { aspath_str_update(aspath, false); + aspath->count = aspath_count_hops_internal(aspath); + } return aspath; } diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index f7e57fd66d..46202fd34a 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -59,6 +59,7 @@ struct aspath { and AS path regular expression match. */ char *str; unsigned short str_len; + uint32_t count; /* AS notation used by string expression of AS path */ enum asnotation_mode asnotation; diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b62319b211..2280aa9097 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -697,14 +697,9 @@ static uint32_t srv6_l3vpn_hash_key_make(const void *p) uint32_t key = 0; key = jhash(&l3vpn->sid, 16, key); - key = jhash_1word(l3vpn->sid_flags, key); - key = jhash_1word(l3vpn->endpoint_behavior, key); - key = jhash_1word(l3vpn->loc_block_len, key); - key = jhash_1word(l3vpn->loc_node_len, key); - key = jhash_1word(l3vpn->func_len, key); - key = jhash_1word(l3vpn->arg_len, key); - key = jhash_1word(l3vpn->transposition_len, key); - key = jhash_1word(l3vpn->transposition_offset, key); + key = jhash_3words(l3vpn->sid_flags, l3vpn->endpoint_behavior, l3vpn->loc_block_len, key); + key = jhash_3words(l3vpn->loc_node_len, l3vpn->func_len, l3vpn->arg_len, key); + key = jhash_2words(l3vpn->transposition_len, l3vpn->transposition_offset, key); return key; } @@ -863,15 +858,11 @@ unsigned int attrhash_key_make(const void *p) if (vnc_subtlvs) MIX(encap_hash_key_make(vnc_subtlvs)); #endif - MIX(attr->mp_nexthop_len); + MIX3(attr->mp_nexthop_len, attr->rmap_table_id, attr->nh_type); key = jhash(attr->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key); key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key); MIX3(attr->nh_ifindex, attr->nh_lla_ifindex, attr->distance); - MIX(attr->rmap_table_id); - MIX(attr->nh_type); - MIX(attr->bh_type); - MIX(attr->otc); - MIX(bgp_attr_get_aigp_metric(attr)); + MIX3(attr->bh_type, attr->otc, bgp_attr_get_aigp_metric(attr)); return key; } @@ -976,8 +967,11 @@ static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty) "\n", attr->flag, attr->distance, attr->med, attr->local_pref, attr->origin, attr->weight, attr->label, sid, attr->aigp_metric); - vty_out(vty, "\taspath: %s Community: %s Large Community: %s\n", - aspath_print(attr->aspath), + vty_out(vty, + "\tnh_ifindex: %u nh_flags: %u distance: %u nexthop_global: %pI6 nexthop_local: %pI6 nexthop_local_ifindex: %u\n", + attr->nh_ifindex, attr->nh_flags, attr->distance, &attr->mp_nexthop_global, + &attr->mp_nexthop_local, attr->nh_lla_ifindex); + vty_out(vty, "\taspath: %s Community: %s Large Community: %s\n", aspath_print(attr->aspath), community_str(attr->community, false, false), lcommunity_str(attr->lcommunity, false, false)); vty_out(vty, "\tExtended Community: %s Extended IPv6 Community: %s\n", diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 14ff5f2e11..a331585d32 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -26,6 +26,7 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_packet.h" +#include "bgpd/bgp_network.h" DEFINE_MTYPE_STATIC(BGPD, BFD_CONFIG, "BFD configuration data"); @@ -53,14 +54,23 @@ static void bfd_session_status_update(struct bfd_session_params *bsp, peer->host); return; } - peer->last_reset = PEER_DOWN_BFD_DOWN; - /* rfc9384 */ - if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) - bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_BFD_DOWN); - - BGP_EVENT_ADD(peer->connection, BGP_Stop); + /* Once the BFD session is UP, and later BGP session is UP, + * BFD notices that peer->su_local changed, and BFD session goes down. + * We should trigger BGP session reset if BFD session is UP + * only when BGP session is UP already. + * Otherwise, we end up resetting BGP session when BFD session is UP, + * when the source address is changed, e.g. 0.0.0.0 -> 10.0.0.1. + */ + if (bss->last_event > peer->uptime) { + peer->last_reset = PEER_DOWN_BFD_DOWN; + /* rfc9384 */ + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) + bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_BFD_DOWN); + + BGP_EVENT_ADD(peer->connection, BGP_Stop); + } } if (bss->state == BSS_UP && bss->previous_state != BSS_UP && @@ -142,23 +152,40 @@ void bgp_peer_config_apply(struct peer *p, struct peer_group *pg) void bgp_peer_bfd_update_source(struct peer *p) { struct bfd_session_params *session = p->bfd_config->session; - const union sockunion *source; + const union sockunion *source = NULL; bool changed = false; int family; union { struct in_addr v4; struct in6_addr v6; } src, dst; + struct interface *ifp; + union sockunion addr; /* Nothing to do for groups. */ if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) return; /* Figure out the correct source to use. */ - if (CHECK_FLAG(p->flags, PEER_FLAG_UPDATE_SOURCE) && p->update_source) - source = p->update_source; - else + if (CHECK_FLAG(p->flags, PEER_FLAG_UPDATE_SOURCE)) { + if (p->update_source) { + source = p->update_source; + } else if (p->update_if) { + ifp = if_lookup_by_name(p->update_if, p->bgp->vrf_id); + if (ifp) { + sockunion_init(&addr); + if (bgp_update_address(ifp, &p->connection->su, &addr)) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug("%s: can't find the source address for interface %s", + __func__, p->update_if); + } + + source = &addr; + } + } + } else { source = p->su_local; + } /* Update peer's source/destination addresses. */ bfd_sess_addresses(session, &family, &src.v6, &dst.v6); diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 4d9e580a23..2e3a0388d0 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -2988,8 +2988,7 @@ static int bmp_config_write(struct bgp *bgp, struct vty *vty) afi2str_lower(afi), safi2str(safi)); } frr_each (bmp_listeners, &bt->listeners, bl) - vty_out(vty, " \n bmp listener %pSU port %d\n", - &bl->addr, bl->port); + vty_out(vty, " bmp listener %pSU port %d\n", &bl->addr, bl->port); frr_each (bmp_actives, &bt->actives, ba) { vty_out(vty, " bmp connect %s port %u min-retry %u max-retry %u", diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index ad154e638b..61ba527498 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -182,7 +182,7 @@ community_list_insert(struct community_list_handler *ch, const char *name, } /* In case of name is all digit character */ - if (i == strlen(name)) { + if (i == strlen(name) && number <= COMMUNITY_LIST_NUMBER_MAX) { new->sort = COMMUNITY_LIST_NUMBER; /* Set access_list to number list. */ diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 29bd880c93..7f35a6d53e 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -20,6 +20,10 @@ /* Number and string based community-list name. */ #define COMMUNITY_LIST_STRING 0 #define COMMUNITY_LIST_NUMBER 1 +/* The numbered community-list (including large/ext communities) + * have a range between 1-500. + */ +#define COMMUNITY_LIST_NUMBER_MAX 500 #define COMMUNITY_SEQ_NUMBER_AUTO -1 diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index df32d75103..065f4fef37 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -361,16 +361,22 @@ static void ecommunity_color_str(char *buf, size_t bufsz, uint8_t *ptr) { /* * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | 0x03 | Sub-Type(0x0b) | Flags | + * | 0x03 | Sub-Type(0x0b) | CO| Flags | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Color Value | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * https://datatracker.ietf.org/doc/rfc9256/, Section 8.8.1 + * The CO bits can have 4 different values: 00 01 10 11 */ uint32_t colorid; + uint8_t color_type; + /* get the color type */ + ptr++; + color_type = (*ptr) >> 6; - memcpy(&colorid, ptr + 3, 4); + memcpy(&colorid, ptr + 2, 4); colorid = ntohl(colorid); - snprintf(buf, bufsz, "Color:%d", colorid); + snprintf(buf, bufsz, "Color:%d%d:%d", (color_type & 0x2) >> 1, color_type & 0x1, colorid); } /* Initialize Extended Comminities related hash. */ @@ -531,7 +537,7 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, eval6->val[19] = val & 0xff; } else if (type == ECOMMUNITY_ENCODE_OPAQUE && sub_type == ECOMMUNITY_COLOR) { - encode_color(val, eval); + encode_color(val, as, eval); } else { encode_route_target_as4(as, val, eval, trans); } @@ -739,6 +745,13 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, */ if (!asn_str2asn(buf, &as)) goto error; + } else if (type == ECOMMUNITY_COLOR) { + /* If extcommunity is color, only support 00/01/10/11, max value is 3 */ + /* color value */ + as = strtoul(buf, &endptr, 2); + if (*endptr != '\0' || as > 3) + goto error; + val_color = 0; } else { /* Parsing A AS number in A:MN */ errno = 0; @@ -753,6 +766,8 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, if (*endptr != '\0' || tmp_as > BGP_AS4_MAX || errno) goto error; + if (*token == ecommunity_token_color && as > 3) + goto error; as = (as_t)tmp_as; } } else if (*p == '.') { @@ -791,13 +806,15 @@ static const char *ecommunity_gettoken(const char *str, void *eval_ptr, /* Encode result into extended community for AS format or color. */ if (as > BGP_AS_MAX) ecomm_type = ECOMMUNITY_ENCODE_AS4; - else if (as > 0) - ecomm_type = ECOMMUNITY_ENCODE_AS; - else if (val_color) { + else if (type == ECOMMUNITY_COLOR) { ecomm_type = ECOMMUNITY_ENCODE_OPAQUE; sub_type = ECOMMUNITY_COLOR; - val = val_color; - } + if (val_color) { + val = val_color; + as = 1; + } + } else if (as > 0) + ecomm_type = ECOMMUNITY_ENCODE_AS; } if (ecommunity_encode(ecomm_type, sub_type, 1, as, ip, val, eval)) goto error; @@ -1419,7 +1436,15 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) else if (sub_type == ECOMMUNITY_EXTENDED_LINK_BANDWIDTH) ipv6_ecommunity_lb_str(encbuf, sizeof(encbuf), pnt, len); - else + else if (sub_type == ECOMMUNITY_OPAQUE_SUBTYPE_COLOR) { + uint32_t color; + /* get the color type */ + uint8_t color_type = (*pnt) >> 6; + memcpy(&color, pnt + 2, 4); + color = ntohl(color); + snprintf(encbuf, sizeof(encbuf), "Color:%d%d:%u", + (color_type & 0x2) >> 1, color_type & 0x1, color); + } else unk_ecom = true; } else if (CHECK_FLAG(type, ECOMMUNITY_ENCODE_IP_NON_TRANS)) { sub_type = *pnt++; @@ -1439,6 +1464,7 @@ unknown: sub_type); int r = strlcat(str_buf, encbuf, str_size); + assert(r < str_size); } diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 0544cbd316..af9d481c19 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -85,6 +85,7 @@ /* Low-order octet of the Extended Communities type field for OPAQUE types */ #define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP 0x0c +#define ECOMMUNITY_OPAQUE_SUBTYPE_COLOR 0x0b /* Extended communities attribute string format. */ #define ECOMMUNITY_FORMAT_ROUTE_MAP 0 @@ -328,26 +329,25 @@ static inline void encode_node_target(struct in_addr *node_id, /* * Encode BGP Color extended community - * is's a transitive opaque Extended community (RFC 9012 4.3) + * is's a transitive opaque Extended community (RFC 9256 8.8.1) * flag is set to 0 - * RFC 9012 14.10: No values have currently been registered. - * 4.3: this field MUST be set to zero by the originator - * and ignored by the receiver; * */ -static inline void encode_color(uint32_t color_id, struct ecommunity_val *eval) +static inline void encode_color(uint32_t color_id, uint32_t flags, struct ecommunity_val *eval) { /* * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | 0x03 | Sub-Type(0x0b) | Flags | + * | 0x03 | Sub-Type(0x0b) |CO | Flags | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Color Value | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * https://datatracker.ietf.org/doc/rfc9256/, Section 8.8.1 + * The CO bits can have 4 different values: 00 01 10 11 */ memset(eval, 0, sizeof(*eval)); eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE; eval->val[1] = ECOMMUNITY_COLOR; - eval->val[2] = 0x00; + eval->val[2] = (flags << 6) & 0xff; eval->val[3] = 0x00; eval->val[4] = (color_id >> 24) & 0xff; eval->val[5] = (color_id >> 16) & 0xff; diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 567af5bb75..8c9050185b 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -178,6 +178,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) EVENT_OFF(going_away->t_delayopen); EVENT_OFF(going_away->t_connect_check_r); EVENT_OFF(going_away->t_connect_check_w); + EVENT_OFF(going_away->t_stop_with_notify); EVENT_OFF(keeper->t_routeadv); EVENT_OFF(keeper->t_connect); EVENT_OFF(keeper->t_delayopen); @@ -688,6 +689,11 @@ static void bgp_set_llgr_stale(struct peer *peer, afi_t afi, safi_t safi) COMMUNITY_NO_LLGR)) continue; + if (bgp_attr_get_community(pi->attr) && + community_include(bgp_attr_get_community(pi->attr), + COMMUNITY_LLGR_STALE)) + continue; + if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP Long-lived set stale community (LLGR_STALE) for: %pFX", @@ -698,7 +704,6 @@ static void bgp_set_llgr_stale(struct peer *peer, afi_t afi, safi_t safi) pi->attr = bgp_attr_intern(&attr); bgp_process(peer->bgp, rm, pi, afi, safi); - break; } } } else { @@ -715,6 +720,11 @@ static void bgp_set_llgr_stale(struct peer *peer, afi_t afi, safi_t safi) COMMUNITY_NO_LLGR)) continue; + if (bgp_attr_get_community(pi->attr) && + community_include(bgp_attr_get_community(pi->attr), + COMMUNITY_LLGR_STALE)) + continue; + if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP Long-lived set stale community (LLGR_STALE) for: %pFX", @@ -724,7 +734,6 @@ static void bgp_set_llgr_stale(struct peer *peer, afi_t afi, safi_t safi) bgp_attr_add_llgr_community(&attr); pi->attr = bgp_attr_intern(&attr); bgp_process(peer->bgp, dest, pi, afi, safi); - break; } } } @@ -1335,11 +1344,6 @@ enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection) peer->nsf_af_count = 0; - /* deregister peer */ - if (peer->bfd_config - && peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE) - bfd_sess_uninstall(peer->bfd_config->session); - if (peer_dynamic_neighbor_no_nsf(peer) && !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) { if (bgp_debug_neighbor_events(peer)) @@ -1359,6 +1363,10 @@ enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection) if (peer_established(connection)) { peer->dropped++; + if (peer->bfd_config && (peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE || + peer->last_reset == PEER_DOWN_MULTIHOP_CHANGE)) + bfd_sess_uninstall(peer->bfd_config->session); + /* Notify BGP conditional advertisement process */ peer->advmap_table_change = true; @@ -1475,6 +1483,8 @@ enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection) EVENT_OFF(connection->t_connect_check_r); EVENT_OFF(connection->t_connect_check_w); + EVENT_OFF(connection->t_stop_with_notify); + /* Stop all timers. */ EVENT_OFF(connection->t_start); EVENT_OFF(connection->t_connect); @@ -3032,3 +3042,10 @@ void bgp_peer_gr_flags_update(struct peer *peer) } } } + +void bgp_event_stop_with_notify(struct event *event) +{ + struct peer_connection *connection = EVENT_ARG(event); + + bgp_stop_with_notify(connection, BGP_NOTIFY_SEND_HOLD_ERR, 0); +} diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index 85c488962f..013c60ce23 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -109,6 +109,7 @@ enum bgp_fsm_state_progress { extern void bgp_fsm_nht_update(struct peer_connection *connection, struct peer *peer, bool has_valid_nexthops); extern void bgp_event(struct event *event); +extern void bgp_event_stop_with_notify(struct event *event); extern int bgp_event_update(struct peer_connection *connection, enum bgp_fsm_events event); extern enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection); diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index af10111600..5db3621738 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -38,7 +38,7 @@ static void *bgp_labels_hash_alloc(void *p) struct bgp_labels *new; uint8_t i; - new = XMALLOC(MTYPE_BGP_LABELS, sizeof(struct bgp_labels)); + new = XCALLOC(MTYPE_BGP_LABELS, sizeof(struct bgp_labels)); new->num_labels = labels->num_labels; for (i = 0; i < labels->num_labels; i++) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 13da55ffb7..ca7f73dde9 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1598,6 +1598,15 @@ vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi, return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]); } + if (is_bgp_static_route && pi->nexthop->nexthop->type == NEXTHOP_TYPE_IFINDEX) { + /* "network" imported prefixes from vrf + * fallback to per-vrf label. + */ + + bgp_mplsvpn_path_nh_label_unlink(pi); + return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]); + } + return _vpn_leak_from_vrf_get_per_nexthop_label(pi, to_bgp, from_bgp, afi); } @@ -1613,8 +1622,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ struct attr static_attr = {0}; struct attr *new_attr = NULL; safi_t safi = SAFI_MPLS_VPN; - mpls_label_t label_val; - mpls_label_t label; + mpls_label_t label_val = { 0 }; + mpls_label_t label = { 0 }; struct bgp_dest *bn; const char *debugmsg; int nexthop_self_flag = 0; diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 401549c4e8..357d5292da 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -1001,7 +1001,7 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, if (bnc->is_evpn_gwip_nexthop) json_object_boolean_true_add(json_nexthop, "isEvpnGatewayIp"); - json_object_string_addf(json, "resolvedPrefix", "%pFX", + json_object_string_addf(json_nexthop, "resolvedPrefix", "%pFX", &bnc->resolved_prefix); } else { vty_out(vty, " %s valid [IGP metric %d], #paths %d", diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 59566ee6d6..9b633b7139 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -652,11 +652,12 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, * we receive from bgp. This is to allow us * to work with v4 routing over v6 nexthops */ - if (peer && !peer->ifp - && CHECK_FLAG(peer->flags, - PEER_FLAG_CAPABILITY_ENHE) - && nhr->prefix.family == AF_INET6 - && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { + if (peer && !peer->ifp && + CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) && + !CHECK_FLAG(bnc->bgp->flags, + BGP_FLAG_IPV6_NO_AUTO_RA) && + nhr->prefix.family == AF_INET6 && + nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { struct interface *ifp; ifp = if_lookup_by_index(nexthop->ifindex, @@ -1529,6 +1530,10 @@ void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) return; bgp = peer->bgp; + + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA)) + return; + if (!sockunion2hostprefix(&peer->connection->su, &p)) { zlog_warn("%s: Unable to convert sockunion to prefix for %s", __func__, peer->host); diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 6b116db107..a76a300c11 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -122,42 +122,38 @@ static void bgp_packet_add(struct peer_connection *connection, peer->last_sendq_ok = monotime(NULL); stream_fifo_push(connection->obuf, s); + } - delta = monotime(NULL) - peer->last_sendq_ok; + delta = monotime(NULL) - peer->last_sendq_ok; - if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) - holdtime = atomic_load_explicit(&peer->holdtime, - memory_order_relaxed); - else - holdtime = peer->bgp->default_holdtime; + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) + holdtime = atomic_load_explicit(&peer->holdtime, memory_order_relaxed); + else + holdtime = peer->bgp->default_holdtime; - sendholdtime = holdtime * 2; + sendholdtime = holdtime * 2; - /* Note that when we're here, we're adding some packet to the - * OutQ. That includes keepalives when there is nothing to - * do, so there's a guarantee we pass by here once in a while. - * - * That implies there is no need to go set up another separate - * timer that ticks down SendHoldTime, as we'll be here sooner - * or later anyway and will see the checks below failing. - */ - if (!holdtime) { - /* no holdtime, do nothing. */ - } else if (delta > sendholdtime) { - flog_err( - EC_BGP_SENDQ_STUCK_PROPER, - "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session", - peer, sendholdtime); - bgp_stop_with_notify(connection, - BGP_NOTIFY_SEND_HOLD_ERR, 0); - } else if (delta > (intmax_t)holdtime && - monotime(NULL) - peer->last_sendq_warn > 5) { - flog_warn( - EC_BGP_SENDQ_STUCK_WARN, - "%pBP has not made any SendQ progress for 1 holdtime (%us), peer overloaded?", - peer, holdtime); - peer->last_sendq_warn = monotime(NULL); - } + /* Note that when we're here, we're adding some packet to the + * OutQ. That includes keepalives when there is nothing to + * do, so there's a guarantee we pass by here once in a while. + * + * That implies there is no need to go set up another separate + * timer that ticks down SendHoldTime, as we'll be here sooner + * or later anyway and will see the checks below failing. + */ + if (!holdtime) { + /* no holdtime, do nothing. */ + } else if (delta > sendholdtime) { + flog_err(EC_BGP_SENDQ_STUCK_PROPER, + "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session", + peer, sendholdtime); + event_add_event(bm->master, bgp_event_stop_with_notify, connection, 0, + &connection->t_stop_with_notify); + } else if (delta > (intmax_t)holdtime && monotime(NULL) - peer->last_sendq_warn > 5) { + flog_warn(EC_BGP_SENDQ_STUCK_WARN, + "%pBP has not made any SendQ progress for 1 holdtime (%us), peer overloaded?", + peer, holdtime); + peer->last_sendq_warn = monotime(NULL); } } @@ -2318,7 +2314,7 @@ static int bgp_update_receive(struct peer_connection *connection, attr.label = MPLS_INVALID_LABEL; memset(&nlris, 0, sizeof(nlris)); memset(peer->rcvd_attr_str, 0, BUFSIZ); - peer->rcvd_attr_printed = 0; + peer->rcvd_attr_printed = false; s = peer->curr; end = stream_pnt(s) + size; @@ -2426,7 +2422,7 @@ static int bgp_update_receive(struct peer_connection *connection, BGP_DEBUG(update, UPDATE_DETAIL)) { zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer, peer->rcvd_attr_str); - peer->rcvd_attr_printed = 1; + peer->rcvd_attr_printed = true; } } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 70da39ee8b..988be7f4de 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -745,6 +745,8 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *bpi_ultimate; struct peer *peer_new, *peer_exist; + bgp->bestpath_runs++; + *paths_eq = 0; /* 0. Null check. */ @@ -1064,7 +1066,32 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } } - /* Tie-breaker - AIGP (Metric TLV) attribute */ + /* 3. Local route check. We prefer: + * - BGP_ROUTE_STATIC + * - BGP_ROUTE_AGGREGATE + * - BGP_ROUTE_REDISTRIBUTE + */ + new_origin = !(new->sub_type == BGP_ROUTE_NORMAL || new->sub_type == BGP_ROUTE_IMPORTED); + exist_origin = !(exist->sub_type == BGP_ROUTE_NORMAL || + exist->sub_type == BGP_ROUTE_IMPORTED); + + if (new_origin && !exist_origin) { + *reason = bgp_path_selection_local_route; + if (debug) + zlog_debug("%s: %s wins over %s due to preferred BGP_ROUTE type", pfx_buf, + new_buf, exist_buf); + return 1; + } + + if (!new_origin && exist_origin) { + *reason = bgp_path_selection_local_route; + if (debug) + zlog_debug("%s: %s loses to %s due to preferred BGP_ROUTE type", pfx_buf, + new_buf, exist_buf); + return 0; + } + + /* 3.5. Tie-breaker - AIGP (Metric TLV) attribute */ if (CHECK_FLAG(newattr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) && CHECK_FLAG(existattr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) && CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) { @@ -1094,34 +1121,6 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } } - /* 3. Local route check. We prefer: - * - BGP_ROUTE_STATIC - * - BGP_ROUTE_AGGREGATE - * - BGP_ROUTE_REDISTRIBUTE - */ - new_origin = !(new->sub_type == BGP_ROUTE_NORMAL || - new->sub_type == BGP_ROUTE_IMPORTED); - exist_origin = !(exist->sub_type == BGP_ROUTE_NORMAL || - exist->sub_type == BGP_ROUTE_IMPORTED); - - if (new_origin && !exist_origin) { - *reason = bgp_path_selection_local_route; - if (debug) - zlog_debug( - "%s: %s wins over %s due to preferred BGP_ROUTE type", - pfx_buf, new_buf, exist_buf); - return 1; - } - - if (!new_origin && exist_origin) { - *reason = bgp_path_selection_local_route; - if (debug) - zlog_debug( - "%s: %s loses to %s due to preferred BGP_ROUTE type", - pfx_buf, new_buf, exist_buf); - return 0; - } - /* Here if these are imported routes then get ultimate pi for * path compare. */ @@ -1133,9 +1132,9 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, /* 4. AS path length check. */ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE)) { int exist_hops = aspath_count_hops(existattr->aspath); - int exist_confeds = aspath_count_confeds(existattr->aspath); if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED)) { + int exist_confeds = aspath_count_confeds(existattr->aspath); int aspath_hops; aspath_hops = aspath_count_hops(newattr->aspath); @@ -3201,15 +3200,23 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, struct bgp_path_info *end = bgp_dest_get_bgp_path_info(dest); - for (; end && end->next != NULL; end = end->next) - ; + if (end && any_comparisons) { + for (; end && end->next != NULL; end = end->next) + ; - if (end) - end->next = first; - else + if (end) + end->next = first; + else + bgp_dest_set_bgp_path_info(dest, first); + first->prev = end; + first->next = NULL; + } else { bgp_dest_set_bgp_path_info(dest, first); - first->prev = end; - first->next = NULL; + if (end) + end->prev = first; + first->next = end; + first->prev = NULL; + } dest->reason = first->reason; } else { @@ -4114,8 +4121,10 @@ static void bgp_process_internal(struct bgp *bgp, struct bgp_dest *dest, } /* already scheduled for processing? */ - if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED)) + if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED)) { + bgp->node_already_on_queue++; return; + } /* If the flag BGP_NODE_SELECT_DEFER is set, do not add route to * the workqueue @@ -4124,6 +4133,7 @@ static void bgp_process_internal(struct bgp *bgp, struct bgp_dest *dest, if (BGP_DEBUG(update, UPDATE_OUT)) zlog_debug("BGP_NODE_SELECT_DEFER set for route %p", dest); + bgp->node_deferred_on_queue++; return; } @@ -4378,12 +4388,9 @@ void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi, bgp_process(peer->bgp, dest, pi, afi, safi); } -static void bgp_rib_withdraw(struct bgp_dest *dest, struct bgp_path_info *pi, - struct peer *peer, afi_t afi, safi_t safi, - struct prefix_rd *prd) +static void bgp_rib_withdraw(const struct prefix *p, struct bgp_dest *dest, struct bgp_path_info *pi, + struct peer *peer, afi_t afi, safi_t safi, struct prefix_rd *prd) { - const struct prefix *p = bgp_dest_get_prefix(dest); - /* apply dampening, if result is suppressed, we'll be retaining * the bgp_path_info in the RIB for historical reference. */ @@ -4613,6 +4620,60 @@ static bool bgp_accept_own(struct peer *peer, afi_t afi, safi_t safi, return false; } +static inline void +bgp_update_nexthop_reachability_check(struct bgp *bgp, struct peer *peer, struct bgp_dest *dest, + const struct prefix *p, afi_t afi, safi_t safi, + struct bgp_path_info *pi, struct attr *attr_new, + const struct prefix *bgp_nht_param_prefix, bool accept_own) +{ + bool connected; + afi_t nh_afi; + + if (((afi == AFI_IP || afi == AFI_IP6) && + (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST || + (safi == SAFI_MPLS_VPN && pi->sub_type != BGP_ROUTE_IMPORTED))) || + (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p))) { + if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP && + peer->ttl == BGP_DEFAULT_TTL && + !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) && + !CHECK_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + connected = true; + else + connected = false; + + struct bgp *bgp_nexthop = bgp; + + if (pi->extra && pi->extra->vrfleak && pi->extra->vrfleak->bgp_orig) + bgp_nexthop = pi->extra->vrfleak->bgp_orig; + + nh_afi = BGP_ATTR_NH_AFI(afi, pi->attr); + + if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, nh_afi, safi, pi, NULL, connected, + bgp_nht_param_prefix) || + CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) { + if (accept_own) + bgp_path_info_set_flag(dest, pi, BGP_PATH_ACCEPT_OWN); + + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + } else { + if (BGP_DEBUG(nht, NHT)) { + zlog_debug("%s(%pI4): NH unresolved for existing %pFX pi %p flags 0x%x", + __func__, (in_addr_t *)&attr_new->nexthop, p, pi, + pi->flags); + } + bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); + } + } else { + /* case mpls-vpn routes with accept-own community + * (which have the BGP_ROUTE_IMPORTED subtype) + * case other afi/safi not supporting nexthop tracking + */ + if (accept_own) + bgp_path_info_set_flag(dest, pi, BGP_PATH_ACCEPT_OWN); + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + } +} + void bgp_update(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, @@ -4620,7 +4681,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, struct bgp_route_evpn *evpn) { int ret; - int aspath_loop_count = 0; struct bgp_dest *dest; struct bgp *bgp; struct attr new_attr = {}; @@ -4629,12 +4689,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, struct bgp_path_info *new = NULL; const char *reason; char pfx_buf[BGP_PRD_PATH_STRLEN]; - int connected = 0; - int do_loop_check = 1; - afi_t nh_afi; bool force_evpn_import = false; safi_t orig_safi = safi; - int allowas_in = 0; struct bgp_labels bgp_labels = {}; uint8_t i; @@ -4659,11 +4715,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp = peer->bgp; dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); - if ((afi == AFI_L2VPN && safi == SAFI_EVPN) || - bgp_is_valid_label(&label[0])) + if (num_labels && + ((afi == AFI_L2VPN && safi == SAFI_EVPN) || bgp_is_valid_label(&label[0]))) { bgp_labels.num_labels = num_labels; - for (i = 0; i < bgp_labels.num_labels; i++) - bgp_labels.label[i] = label[i]; + for (i = 0; i < bgp_labels.num_labels; i++) + bgp_labels.label[i] = label[i]; + } /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ @@ -4676,17 +4733,15 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * will not be interned. In which case, it is ok to update the * attr->evpn_overlay, so that, this can be stored in adj_in. */ - if ((afi == AFI_L2VPN) && evpn) - bgp_attr_set_evpn_overlay(attr, evpn); - else - evpn_overlay_free(evpn); + if (evpn) { + if (afi == AFI_L2VPN) + bgp_attr_set_evpn_overlay(attr, evpn); + else + evpn_overlay_free(evpn); + } bgp_adj_in_set(dest, peer, attr, addpath_id, &bgp_labels); } - /* Update permitted loop count */ - if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) - allowas_in = peer->allowas_in[afi][safi]; - /* Check previously received route. */ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == peer && pi->type == type @@ -4696,8 +4751,11 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* AS path local-as loop check. */ if (peer->change_local_as) { - if (allowas_in) - aspath_loop_count = allowas_in; + int32_t aspath_loop_count = 0; + + /* Update permitted loop count */ + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) + aspath_loop_count = peer->allowas_in[afi][safi]; else if (!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) aspath_loop_count = 1; @@ -4710,14 +4768,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } - /* If the peer is configured for "allowas-in origin" and the last ASN in - * the - * as-path is our ASN then we do not need to call aspath_loop_check - */ - if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN_ORIGIN)) - if (aspath_get_last_as(attr->aspath) == bgp->as) - do_loop_check = 0; - /* When using bgp ipv4 labeled session, the local prefix is * received by a peer, and finds out that the proposed prefix * and its next-hop are the same. To avoid a route loop locally, @@ -4738,24 +4788,30 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, else bgp_nht_param_prefix = p; - /* AS path loop check. */ - if (do_loop_check) { + /* + * If the peer is configured for "allowas-in origin" and the last ASN in + * the as-path is our ASN then we do not need to call aspath_loop_check + */ + if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN_ORIGIN) || + (aspath_get_last_as(attr->aspath) != bgp->as)) { + /* AS path loop check. */ if (aspath_loop_check(attr->aspath, bgp->as) > peer->allowas_in[afi][safi]) { peer->stat_pfx_aspath_loop++; reason = "as-path contains our own AS;"; goto filtered; } - } - /* If we're a CONFED we need to loop check the CONFED ID too */ - if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION) && do_loop_check) - if (aspath_loop_check_confed(attr->aspath, bgp->confed_id) > - peer->allowas_in[afi][safi]) { - peer->stat_pfx_aspath_loop++; - reason = "as-path contains our own confed AS;"; - goto filtered; + /* If we're a CONFED we need to loop check the CONFED ID too */ + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { + if (aspath_loop_check_confed(attr->aspath, bgp->confed_id) > + peer->allowas_in[afi][safi]) { + peer->stat_pfx_aspath_loop++; + reason = "as-path contains our own confed AS;"; + goto filtered; + } } + } /* Route reflector originator ID check. If ACCEPT_OWN mechanism is * enabled, then take care of that too. @@ -4780,6 +4836,28 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, goto filtered; } + /* RFC 8212 to prevent route leaks. + * This specification intends to improve this situation by requiring the + * explicit configuration of both BGP Import and Export Policies for any + * External BGP (EBGP) session such as customers, peers, or + * confederation boundaries for all enabled address families. Through + * codification of the aforementioned requirement, operators will + * benefit from consistent behavior across different BGP + * implementations. + */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)) + if (!bgp_inbound_policy_exists(peer, &peer->filter[afi][safi])) { + reason = "inbound policy missing"; + if (monotime_since(&bgp->ebgprequirespolicywarning, NULL) > + FIFTEENMINUTE2USEC || + bgp->ebgprequirespolicywarning.tv_sec == 0) { + zlog_warn( + "EBGP inbound/outbound policy not properly setup, please configure in order for your peering to work correctly"); + monotime(&bgp->ebgprequirespolicywarning); + } + goto filtered; + } + /* Apply incoming filter. */ if (bgp_input_filter(peer, p, attr, afi, orig_safi) == FILTER_DENY) { peer->stat_pfx_filter++; @@ -4812,29 +4890,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } - /* RFC 8212 to prevent route leaks. - * This specification intends to improve this situation by requiring the - * explicit configuration of both BGP Import and Export Policies for any - * External BGP (EBGP) session such as customers, peers, or - * confederation boundaries for all enabled address families. Through - * codification of the aforementioned requirement, operators will - * benefit from consistent behavior across different BGP - * implementations. - */ - if (CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)) - if (!bgp_inbound_policy_exists(peer, - &peer->filter[afi][safi])) { - reason = "inbound policy missing"; - if (monotime_since(&bgp->ebgprequirespolicywarning, - NULL) > FIFTEENMINUTE2USEC || - bgp->ebgprequirespolicywarning.tv_sec == 0) { - zlog_warn( - "EBGP inbound/outbound policy not properly setup, please configure in order for your peering to work correctly"); - monotime(&bgp->ebgprequirespolicywarning); - } - goto filtered; - } - /* draft-ietf-idr-deprecate-as-set-confed-set * Filter routes having AS_SET or AS_CONFED_SET in the path. * Eventually, This document (if approved) updates RFC 4271 @@ -4855,10 +4910,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * attr->evpn_overlay with evpn directly. Instead memcpy * evpn to new_atr.evpn_overlay before it is interned. */ - if (soft_reconfig && (afi == AFI_L2VPN) && evpn) - bgp_attr_set_evpn_overlay(&new_attr, evpn); - else - evpn_overlay_free(evpn); + if (soft_reconfig && evpn) { + if (afi == AFI_L2VPN) + bgp_attr_set_evpn_overlay(&new_attr, evpn); + else + evpn_overlay_free(evpn); + } /* Apply incoming route-map. * NB: new_attr may now contain newly allocated values from route-map @@ -4923,7 +4980,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, goto filtered; } - if (bgp_mac_entry_exists(p) || bgp_mac_exist(&attr->rmac)) { + if (safi == SAFI_EVPN && (bgp_mac_entry_exists(p) || bgp_mac_exist(&attr->rmac))) { peer->stat_pfx_nh_invalid++; reason = "self mac;"; bgp_attr_flush(&new_attr); @@ -4978,7 +5035,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (get_active_bdc_from_pi(pi, afi, safi) && peer->sort == BGP_PEER_EBGP && CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { - if (bgp_debug_update(peer, p, NULL, 1)) { + if (unlikely(bgp_debug_update(peer, p, NULL, 1))) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, @@ -4996,13 +5053,13 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } else /* Duplicate - odd */ { - if (bgp_debug_update(peer, p, NULL, 1)) { + if (unlikely(bgp_debug_update(peer, p, NULL, 1))) { if (!peer->rcvd_attr_printed) { zlog_debug( "%pBP rcvd UPDATE w/ attr: %s", peer, peer->rcvd_attr_str); - peer->rcvd_attr_printed = 1; + peer->rcvd_attr_printed = true; } bgp_debug_rdpfxpath2str( @@ -5032,7 +5089,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Withdraw/Announce before we fully processed the withdraw */ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - if (bgp_debug_update(peer, p, NULL, 1)) { + if (unlikely(bgp_debug_update(peer, p, NULL, 1))) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, addpath_id, evpn, @@ -5060,7 +5117,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } /* Received Logging. */ - if (bgp_debug_update(peer, p, NULL, 1)) { + if (unlikely(bgp_debug_update(peer, p, NULL, 1))) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, addpath_id, evpn, pfx_buf, @@ -5131,7 +5188,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_attr_get_ecommunity(pi->attr), bgp_attr_get_ecommunity(attr_new)); if (!cmp) { - if (bgp_debug_update(peer, p, NULL, 1)) + if (unlikely(bgp_debug_update(peer, p, NULL, 1))) zlog_debug( "Change in EXT-COMM, existing %s new %s", ecommunity_str( @@ -5195,62 +5252,11 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } + bgp_update_nexthop_reachability_check(bgp, peer, dest, p, afi, safi, pi, attr_new, + bgp_nht_param_prefix, accept_own); /* Nexthop reachability check - for unicast and * labeled-unicast.. */ - if (((afi == AFI_IP || afi == AFI_IP6) && - (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST || - (safi == SAFI_MPLS_VPN && - pi->sub_type != BGP_ROUTE_IMPORTED))) || - (safi == SAFI_EVPN && - bgp_evpn_is_prefix_nht_supported(p))) { - if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP - && peer->ttl == BGP_DEFAULT_TTL - && !CHECK_FLAG(peer->flags, - PEER_FLAG_DISABLE_CONNECTED_CHECK) - && !CHECK_FLAG(bgp->flags, - BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - connected = 1; - else - connected = 0; - - struct bgp *bgp_nexthop = bgp; - - if (pi->extra && pi->extra->vrfleak && - pi->extra->vrfleak->bgp_orig) - bgp_nexthop = pi->extra->vrfleak->bgp_orig; - - nh_afi = BGP_ATTR_NH_AFI(afi, pi->attr); - if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, nh_afi, - safi, pi, NULL, connected, - bgp_nht_param_prefix) || - CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) { - if (accept_own) - bgp_path_info_set_flag( - dest, pi, BGP_PATH_ACCEPT_OWN); - - bgp_path_info_set_flag(dest, pi, - BGP_PATH_VALID); - } else { - if (BGP_DEBUG(nht, NHT)) { - zlog_debug("%s(%pI4): NH unresolved for existing %pFX pi %p flags 0x%x", - __func__, - (in_addr_t *)&attr_new->nexthop, - p, pi, pi->flags); - } - bgp_path_info_unset_flag(dest, pi, - BGP_PATH_VALID); - } - } else { - /* case mpls-vpn routes with accept-own community - * (which have the BGP_ROUTE_IMPORTED subtype) - * case other afi/safi not supporting nexthop tracking - */ - if (accept_own) - bgp_path_info_set_flag(dest, pi, - BGP_PATH_ACCEPT_OWN); - bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); - } #ifdef ENABLE_BGP_VNC if (safi == SAFI_MPLS_VPN) { @@ -5292,7 +5298,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * EVPN nexthop is decremented appropriately. */ else if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) { - if (BGP_DEBUG(nht, NHT)) + if (unlikely(BGP_DEBUG(nht, NHT))) zlog_debug("%s unimport EVPN %pFX as pi %p is not VALID", __func__, p, pi); bgp_evpn_unimport_route(bgp, afi, safi, p, pi); @@ -5332,11 +5338,11 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } // End of implicit withdraw /* Received Logging. */ - if (bgp_debug_update(peer, p, NULL, 1)) { + if (unlikely(bgp_debug_update(peer, p, NULL, 1))) { if (!peer->rcvd_attr_printed) { zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer, peer->rcvd_attr_str); - peer->rcvd_attr_printed = 1; + peer->rcvd_attr_printed = true; } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, @@ -5352,48 +5358,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_path_info_extra_get(new); new->extra->labels = bgp_labels_intern(&bgp_labels); - /* Nexthop reachability check. */ - if (((afi == AFI_IP || afi == AFI_IP6) && - (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST || - (safi == SAFI_MPLS_VPN && - new->sub_type != BGP_ROUTE_IMPORTED))) || - (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p))) { - if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP - && peer->ttl == BGP_DEFAULT_TTL - && !CHECK_FLAG(peer->flags, - PEER_FLAG_DISABLE_CONNECTED_CHECK) - && !CHECK_FLAG(bgp->flags, - BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - connected = 1; - else - connected = 0; - - nh_afi = BGP_ATTR_NH_AFI(afi, new->attr); - - if (bgp_find_or_add_nexthop(bgp, bgp, nh_afi, safi, new, NULL, - connected, bgp_nht_param_prefix) || - CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) { - if (accept_own) - bgp_path_info_set_flag(dest, new, - BGP_PATH_ACCEPT_OWN); - - bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); - } else { - if (BGP_DEBUG(nht, NHT)) - zlog_debug("%s(%pI4): NH unresolved", __func__, - &attr_new->nexthop); - bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID); - } - } else { - /* case mpls-vpn routes with accept-own community - * (which have the BGP_ROUTE_IMPORTED subtype) - * case other afi/safi not supporting nexthop tracking - */ - if (accept_own) - bgp_path_info_set_flag(dest, new, BGP_PATH_ACCEPT_OWN); - bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); - } - + bgp_update_nexthop_reachability_check(bgp, peer, dest, p, afi, safi, new, attr_new, + bgp_nht_param_prefix, accept_own); /* If maximum prefix count is configured and current prefix * count exeed it. */ @@ -5480,7 +5446,7 @@ filtered: if (!peer->rcvd_attr_printed) { zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer, peer->rcvd_attr_str); - peer->rcvd_attr_printed = 1; + peer->rcvd_attr_printed = true; } bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, @@ -5589,7 +5555,7 @@ void bgp_withdraw(struct peer *peer, const struct prefix *p, break; /* Logging. */ - if (bgp_debug_update(peer, p, NULL, 1)) { + if (unlikely(bgp_debug_update(peer, p, NULL, 1))) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, addpath_id, NULL, pfx_buf, sizeof(pfx_buf)); @@ -5599,18 +5565,14 @@ void bgp_withdraw(struct peer *peer, const struct prefix *p, /* Withdraw specified route from routing table. */ if (pi && !CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { - bgp_rib_withdraw(dest, pi, peer, afi, safi, prd); + bgp_rib_withdraw(p, dest, pi, peer, afi, safi, prd); if (SAFI_UNICAST == safi && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, pi); - } - if ((SAFI_MPLS_VPN == safi) - && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { - + } else if ((SAFI_MPLS_VPN == safi) && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) vpn_leak_to_vrf_withdraw(pi); - } - } else if (bgp_debug_update(peer, p, NULL, 1)) { + } else if (unlikely(bgp_debug_update(peer, p, NULL, 1))) { bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels, addpath_id ? 1 : 0, addpath_id, NULL, pfx_buf, sizeof(pfx_buf)); @@ -6323,7 +6285,6 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) vpn_leak_to_vrf_withdraw(pi); bgp_rib_remove(rm, pi, peer, afi, safi); - break; } } } else { @@ -6352,7 +6313,6 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) pi); bgp_rib_remove(dest, pi, peer, afi, safi); - break; } } } @@ -9087,6 +9047,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, memset(&rmap_path, 0, sizeof(rmap_path)); rmap_path.peer = bgp->peer_self; rmap_path.attr = &attr_new; + rmap_path.type = type; SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE); @@ -12328,7 +12289,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, mpls_lse_decode(dest->local_label, &label, &ttl, &exp, &bos); - has_valid_label = bgp_is_valid_label(&label); + has_valid_label = bgp_is_valid_label(&dest->local_label); if (safi == SAFI_EVPN) { if (!json) { @@ -13086,6 +13047,9 @@ DEFUN(show_ip_bgp_afi_safi_statistics, show_ip_bgp_afi_safi_statistics_cmd, json = json_object_new_object(); json_object_object_add(json, get_afi_safi_str(afi, safi, true), json_afi_safi); + json_object_int_add(json, "bgpBestPathCalls", bgp->bestpath_runs); + json_object_int_add(json, "bgpNodeOnQueue", bgp->node_already_on_queue); + json_object_int_add(json, "bgpNodeDeferredOnQueue", bgp->node_deferred_on_queue); vty_json(vty, json); } return ret; diff --git a/bgpd/bgp_snmp_bgp4v2.c b/bgpd/bgp_snmp_bgp4v2.c index e8c3e6513d..5f36e29876 100644 --- a/bgpd/bgp_snmp_bgp4v2.c +++ b/bgpd/bgp_snmp_bgp4v2.c @@ -933,7 +933,9 @@ static uint8_t *bgp4v2PathAttrTable(struct variable *v, oid name[], else return SNMP_IPADDRESS(bgp_empty_addr); case BGP4V2_NLRI_AS_PATH_CALC_LENGTH: - return SNMP_INTEGER(path->attr->aspath->segments->length); + return SNMP_INTEGER((path->attr->aspath && path->attr->aspath->segments) + ? path->attr->aspath->segments->length + : 0); case BGP4V2_NLRI_AS_PATH: return aspath_snmp_pathseg(path->attr->aspath, var_len); case BGP4V2_NLRI_PATH_ATTR_UNKNOWN: diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 62b79541bf..e86d1dc656 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -5034,6 +5034,27 @@ DEFUN(no_bgp_fast_convergence, no_bgp_fast_convergence_cmd, return CMD_SUCCESS; } +DEFPY (bgp_ipv6_auto_ra, + bgp_ipv6_auto_ra_cmd, + "[no] bgp ipv6-auto-ra", + NO_STR + BGP_STR + "Allow enabling IPv6 ND RA sending\n") +{ + if (vty->node == CONFIG_NODE) { + struct listnode *node, *nnode; + struct bgp *bgp; + + COND_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA, no); + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + COND_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA, no); + } else { + VTY_DECLVAR_CONTEXT(bgp, bgp); + COND_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA, no); + } + return CMD_SUCCESS; +} + static int peer_conf_interface_get(struct vty *vty, const char *conf_if, int v6only, const char *peer_group_name, @@ -18711,7 +18732,7 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, } /* TCP max segment size */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_TCP_MSS)) + if (peergroup_flag_check(peer, PEER_FLAG_TCP_MSS)) vty_out(vty, " neighbor %s tcp-mss %d\n", addr, peer->tcp_mss); /* passive */ @@ -19418,6 +19439,9 @@ int bgp_config_write(struct vty *vty) if (CHECK_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA)) vty_out(vty, "bgp send-extra-data zebra\n"); + if (CHECK_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA)) + vty_out(vty, "no bgp ipv6-auto-ra\n"); + /* DSCP value for outgoing packets in BGP connections */ if (bm->ip_tos != IPTOS_PREC_INTERNETCONTROL) vty_out(vty, "bgp session-dscp %u\n", bm->ip_tos >> 2); @@ -19839,6 +19863,11 @@ int bgp_config_write(struct vty *vty) if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) vty_out(vty, " bgp shutdown\n"); + /* Automatic RA enabling by BGP */ + if (!CHECK_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA)) + vty_out(vty, " no bgp ipv6-auto-ra\n"); + if (bgp->allow_martian) vty_out(vty, " bgp allow-martian-nexthop\n"); @@ -20379,6 +20408,12 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_fast_convergence_cmd); install_element(BGP_NODE, &no_bgp_fast_convergence_cmd); + /* global bgp ipv6-auto-ra command */ + install_element(CONFIG_NODE, &bgp_ipv6_auto_ra_cmd); + + /* bgp ipv6-auto-ra command */ + install_element(BGP_NODE, &bgp_ipv6_auto_ra_cmd); + /* global bgp update-delay command */ install_element(CONFIG_NODE, &bgp_global_update_delay_cmd); install_element(CONFIG_NODE, &no_bgp_global_update_delay_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index bffa5a0e6b..16f4a0d2df 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2327,6 +2327,9 @@ void bgp_zebra_initiate_radv(struct bgp *bgp, struct peer *peer) { uint32_t ra_interval = BGP_UNNUM_DEFAULT_RA_INTERVAL; + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA)) + return; + /* Don't try to initiate if we're not connected to Zebra */ if (zclient->sock < 0) return; @@ -3696,7 +3699,7 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) // refresh functions for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) { tmp_prefi.family = AF_INET6; - tmp_prefi.prefixlen = 128; + tmp_prefi.prefixlen = IPV6_MAX_BITLEN; tmp_prefi.prefix = func->sid; if (prefix_match((struct prefix *)&loc.prefix, (struct prefix *)&tmp_prefi)) { @@ -3714,7 +3717,7 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; if (tovpn_sid) { tmp_prefi.family = AF_INET6; - tmp_prefi.prefixlen = 128; + tmp_prefi.prefixlen = IPV6_MAX_BITLEN; tmp_prefi.prefix = *tovpn_sid; if (prefix_match((struct prefix *)&loc.prefix, (struct prefix *)&tmp_prefi)) @@ -3726,7 +3729,7 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; if (tovpn_sid) { tmp_prefi.family = AF_INET6; - tmp_prefi.prefixlen = 128; + tmp_prefi.prefixlen = IPV6_MAX_BITLEN; tmp_prefi.prefix = *tovpn_sid; if (prefix_match((struct prefix *)&loc.prefix, (struct prefix *)&tmp_prefi)) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a186243ffc..aa2bd5c371 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1412,6 +1412,8 @@ int bgp_global_gr_init(struct bgp *bgp) bgp->rib_stale_time = bm->rib_stale_time; if (CHECK_FLAG(bm->flags, BM_FLAG_GR_PRESERVE_FWD)) SET_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD); + if (CHECK_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA)) + SET_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA); bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; @@ -3029,6 +3031,9 @@ static void peer_group2peer_config_copy(struct peer_group *group, bgp_peer_configure_bfd(peer, false); bgp_peer_config_apply(peer, group); } + /* peer tcp-mss */ + if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TCP_MSS)) + PEER_ATTR_INHERIT(peer, group, tcp_mss); } /* Peer group's remote AS configuration. */ @@ -4221,8 +4226,6 @@ int bgp_delete(struct bgp *bgp) if (bgp->process_queue) work_queue_free_and_null(&bgp->process_queue); - event_master_free_unused(bm->master); - if (!IS_BGP_INSTANCE_HIDDEN(bgp)) bgp_unlock(bgp); /* initial reference */ else { @@ -4796,6 +4799,7 @@ static const struct peer_flag_action peer_flag_action_list[] = { {PEER_FLAG_AS_LOOP_DETECTION, 0, peer_change_none}, {PEER_FLAG_EXTENDED_LINK_BANDWIDTH, 0, peer_change_none}, {PEER_FLAG_LONESOUL, 0, peer_change_reset_out}, + {PEER_FLAG_TCP_MSS, 0, peer_change_none}, {0, 0, 0}}; static const struct peer_flag_action peer_af_flag_action_list[] = { @@ -5390,7 +5394,7 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) { struct peer_group *group; struct listnode *node, *nnode; - struct peer *peer1; + struct peer *member; if (peer->sort == BGP_PEER_IBGP || peer->conf_if) return 0; @@ -5406,12 +5410,11 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) if (group->conf->gtsm_hops != BGP_GTSM_HOPS_DISABLED) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, - peer1)) { - if (peer1->sort == BGP_PEER_IBGP) + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, member)) { + if (member->sort == BGP_PEER_IBGP) continue; - if (peer1->gtsm_hops != BGP_GTSM_HOPS_DISABLED) + if (member->gtsm_hops != BGP_GTSM_HOPS_DISABLED) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; } } else { @@ -5438,23 +5441,21 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) } } else { group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (peer->sort == BGP_PEER_IBGP) + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, member)) { + if (member->sort == BGP_PEER_IBGP) continue; - peer->ttl = group->conf->ttl; + member->ttl = group->conf->ttl; - if (BGP_IS_VALID_STATE_FOR_NOTIF( - peer->connection->status)) - bgp_notify_send(peer->connection, - BGP_NOTIFY_CEASE, + if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status)) + bgp_notify_send(member->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else - bgp_session_reset(peer); + bgp_session_reset(member); /* Reconfigure BFD peer with new TTL. */ - if (peer->bfd_config) - bgp_peer_bfd_update_source(peer); + if (member->bfd_config) + bgp_peer_bfd_update_source(member); } } return 0; @@ -5462,6 +5463,7 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) int peer_ebgp_multihop_unset(struct peer *peer) { + struct peer *member; struct peer_group *group; struct listnode *node, *nnode; int ttl; @@ -5494,25 +5496,23 @@ int peer_ebgp_multihop_unset(struct peer *peer) bgp_peer_bfd_update_source(peer); } else { group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (peer->sort == BGP_PEER_IBGP) + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, member)) { + if (member->sort == BGP_PEER_IBGP) continue; - peer->ttl = BGP_DEFAULT_TTL; + member->ttl = BGP_DEFAULT_TTL; - if (peer->connection->fd >= 0) { - if (BGP_IS_VALID_STATE_FOR_NOTIF( - peer->connection->status)) - bgp_notify_send(peer->connection, - BGP_NOTIFY_CEASE, + if (member->connection->fd >= 0) { + if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status)) + bgp_notify_send(member->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else - bgp_session_reset(peer); + bgp_session_reset(member); } /* Reconfigure BFD peer with new TTL. */ - if (peer->bfd_config) - bgp_peer_bfd_update_source(peer); + if (member->bfd_config) + bgp_peer_bfd_update_source(member); } } return 0; @@ -6097,9 +6097,27 @@ void peer_port_unset(struct peer *peer) */ void peer_tcp_mss_set(struct peer *peer, uint32_t tcp_mss) { + struct peer *member; + struct listnode *node, *nnode; + + peer_flag_set(peer, PEER_FLAG_TCP_MSS); peer->tcp_mss = tcp_mss; - SET_FLAG(peer->flags, PEER_FLAG_TCP_MSS); - bgp_tcp_mss_set(peer); + + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + bgp_tcp_mss_set(peer); + return; + } + + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->flags_override, PEER_FLAG_TCP_MSS)) + continue; + + /* Set flag and configuration on peer-group member. */ + SET_FLAG(member->flags, PEER_FLAG_TCP_MSS); + PEER_ATTR_INHERIT(member, peer->group, tcp_mss); + bgp_tcp_mss_set(member); + } } /* Reset the TCP-MSS value in the peer structure, @@ -6108,9 +6126,39 @@ void peer_tcp_mss_set(struct peer *peer, uint32_t tcp_mss) */ void peer_tcp_mss_unset(struct peer *peer) { - UNSET_FLAG(peer->flags, PEER_FLAG_TCP_MSS); - peer->tcp_mss = 0; - bgp_tcp_mss_set(peer); + struct peer *member; + struct listnode *node, *nnode; + + /* Inherit configuration from peer-group if peer is member. */ + if (peer_group_active(peer)) { + peer_flag_inherit(peer, PEER_FLAG_TCP_MSS); + PEER_ATTR_INHERIT(peer, peer->group, tcp_mss); + } else { + /* Otherwise remove flag and configuration from peer. */ + peer_flag_unset(peer, PEER_FLAG_TCP_MSS); + peer->tcp_mss = 0; + } + + /* Skip peer-group mechanics for regular peers. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + bgp_tcp_mss_set(peer); + return; + } + + /* + * Remove flag and configuration from all peer-group members, unless + * they are explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->flags_override, PEER_FLAG_TCP_MSS)) + continue; + + /* Remove flag and configuration on peer-group member. */ + UNSET_FLAG(member->flags, PEER_FLAG_TCP_MSS); + member->tcp_mss = 0; + bgp_tcp_mss_set(member); + } } /* diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 852efdf19d..f123188ae8 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -172,6 +172,7 @@ struct bgp_master { #define BM_FLAG_GR_PRESERVE_FWD (1 << 5) #define BM_FLAG_GRACEFUL_RESTART (1 << 6) #define BM_FLAG_GR_COMPLETE (1 << 7) +#define BM_FLAG_IPV6_NO_AUTO_RA (1 << 8) #define BM_FLAG_GR_CONFIGURED (BM_FLAG_GR_RESTARTER | BM_FLAG_GR_DISABLED) @@ -551,6 +552,8 @@ struct bgp { #define BGP_FLAG_DYNAMIC_CAPABILITY (1ULL << 37) #define BGP_FLAG_VNI_DOWN (1ULL << 38) #define BGP_FLAG_INSTANCE_HIDDEN (1ULL << 39) +/* Prohibit BGP from enabling IPv6 RA on interfaces */ +#define BGP_FLAG_IPV6_NO_AUTO_RA (1ULL << 40) /* BGP default address-families. * New peers inherit enabled afi/safis from bgp instance. @@ -861,6 +864,10 @@ struct bgp { /* BGP route flap dampening configuration */ struct bgp_damp_config damp[AFI_MAX][SAFI_MAX]; + uint64_t bestpath_runs; + uint64_t node_already_on_queue; + uint64_t node_deferred_on_queue; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(bgp); @@ -1128,7 +1135,8 @@ enum bgp_fsm_rfc_codes { BGP_FSM_NotifMsg = 25, BGP_FSM_KeepAliveMsg = 26, BGP_FSM_UpdateMsg = 27, - BGP_FSM_UpdateMsgErr = 28 + BGP_FSM_UpdateMsgErr = 28, + BGP_FSM_SendHoldTimer_Expires = 29, }; /* @@ -1223,6 +1231,8 @@ struct peer_connection { struct event *t_process_packet; struct event *t_process_packet_error; + struct event *t_stop_with_notify; + union sockunion su; #define BGP_CONNECTION_SU_UNSPEC(connection) \ (connection->su.sa.sa_family == AF_UNSPEC) @@ -1760,8 +1770,15 @@ struct peer { /* Text description of last attribute rcvd */ char rcvd_attr_str[BUFSIZ]; - /* Track if we printed the attribute in debugs */ - int rcvd_attr_printed; + /* + * Track if we printed the attribute in debugs + * + * These two rcvd_attr_str and rcvd_attr_printed are going to + * be fun in the long term when we want to break up parsing + * of data from the nlri in multiple pthreads or really + * if we ever change order of things this will just break + */ + bool rcvd_attr_printed; /* Accepted prefix count */ uint32_t pcount[AFI_MAX][SAFI_MAX]; diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index a452ebe48e..1f43ef42a1 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -4403,6 +4403,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) { const char *s = ""; + (void)s; /* clang-SA */ switch (hc->redist_mode) { case VNC_REDIST_MODE_PLAIN: s = "plain"; diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 8f1f509bbb..90e61f1ace 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -1257,6 +1257,8 @@ static int rfapiShowRemoteRegistrationsIt(struct bgp *bgp, void *stream, const char *agetype = ""; char *s; const char *type = ""; + + (void)type; /* clang-SA */ if (show_imported) { type = "Imported"; } else { diff --git a/doc/developer/conf.py b/doc/developer/conf.py index 634f4aa804..a5b5148140 100644 --- a/doc/developer/conf.py +++ b/doc/developer/conf.py @@ -71,6 +71,7 @@ release = "?.?-?" html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") # Tell Jinja2 templates the build is running on Read the Docs +html_context = {} if os.environ.get("READTHEDOCS", "") == "True": html_context["READTHEDOCS"] = True diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 1ec516a1e1..5077745a15 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -1323,6 +1323,8 @@ You can run scripts inside the node, or use vtysh's <tab> or <?> feature. loc1 1 2001:db8:1:1::/64 Up loc2 2 2001:db8:2:2::/64 Up +.. _writing-tests: + Writing Tests """"""""""""" @@ -1347,9 +1349,9 @@ or using unified config (specifying which daemons to run is optional): for _, (rname, router) in enumerate(router_list.items(), 1): router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)), [ - TopoRouter.RD_ZEBRA - TopoRouter.RD_MGMTD, - TopoRouter.RD_BGP]) + (TopoRouter.RD_ZEBRA, "-s 90000000"), + (TopoRouter.RD_MGMTD, None), + (TopoRouter.RD_BGP, None)] - The topology definition or build function @@ -1408,7 +1410,7 @@ Requirements: - Always use IPv4 :rfc:`5737` (``192.0.2.0/24``, ``198.51.100.0/24``, ``203.0.113.0/24``) and IPv6 :rfc:`3849` (``2001:db8::/32``) ranges reserved for documentation; -- Use unified config (``frr.conf``) for all new [tests](#writing-tests). +- Use unified config (``frr.conf``) for all new tests. See :ref:`writing-tests`. Tips: diff --git a/doc/user/about.rst b/doc/user/about.rst index ba80a324d3..e16ed7e3cc 100644 --- a/doc/user/about.rst +++ b/doc/user/about.rst @@ -343,6 +343,8 @@ BGP :t:`Making Route Flap Damping Usable. C. Pelsser, R. Bush, K. Patel, P. Mohapatra, O. Maennel. May 2014.` - :rfc:`7300` :t:`Reservation of Last Autonomous System (AS) Numbers. J. Haas, J. Mitchell. July 2014.` +- :rfc:`7311` + :t:`The Accumulated IGP Metric Attribute for BGP. P. Mohapatra, R. Fernando, E. Rosen, J. Uttaro. August 2014.` - :rfc:`7313` :t:`Enhanced Route Refresh Capability for BGP-4. K. Patel, E. Chen, B. Venkatachalapathy. July 2014.` - :rfc:`7606` @@ -389,6 +391,8 @@ BGP :t:`A BGP Cease NOTIFICATION Subcode for Bidirectional Forwarding Detection (BFD). J. Haas. March 2023.` - :rfc:`9494` :t:`Long-Lived Graceful Restart for BGP. J. Uttaro, E. Chen, B. Decraene, J. Scudder. November 2023.` +- :rfc:`9687` + :t:`Border Gateway Protocol 4 (BGP-4) Send Hold Timer. J. Snijders, B. Cartwright-Cox, Y. Qu. November 2024.` OSPF ---- diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 6a46128d72..0c7fcecb9b 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -160,16 +160,16 @@ bottom until one of the factors can be used. Prefer higher local preference routes to lower. +3. **Local route check** + + Prefer local routes (statics, aggregates, redistributed) to received routes. + If ``bgp bestpath aigp`` is enabled, and both paths that are compared have AIGP attribute, BGP uses AIGP tie-breaking unless both of the paths have the AIGP metric attribute. This means that the AIGP attribute is not evaluated during the best path selection process between two paths when one path does not have the AIGP attribute. -3. **Local route check** - - Prefer local routes (statics, aggregates, redistributed) to received routes. - 4. **AS path length check** Prefer shortest hop-count AS_PATHs. @@ -1290,6 +1290,13 @@ IPv6 Support address family is enabled by default for all new neighbors. +.. clicmd:: bgp ipv6-auto-ra + + By default, bgpd can ask Zebra to enable sending IPv6 router advertisement + messages on interfaces. For example, this happens for unnumbered peers + support or when extended-nexthop capability is used. The ``no`` form of this + command disables such behaviour. + .. _bgp-route-aggregation: Route Aggregation @@ -2921,6 +2928,23 @@ BGP Extended Communities in Route Map This command sets colors values. +:rfc:`9256`. + +``CO:COLOR`` + This is a format to define colors value. ``CO`` part is always 00 (default), + it can be used to support the requirements of Color-Only steering when using + a Null Endpoint in the SR-TE Policy as specified in Section 8.8 of [RFC9256]. + The below shows in detail what the different combinations of ``CO`` bits can + match on to for the purpose of determining what type of SR-TE Policy Tunnel + a BGP route can resolve over, and it also shows the order for resolving the + BGP route if there are different tunnels. + - ``00`` Can match on a specific endpoint only which should be the nexthop + of the route(Default Setting). + - ``01`` Can match on a specific endpoint or a null endpoint. + - ``10`` Can match on a specific endpoint, null endpoint or any endpoint. + - ``11`` Reserved for future use and shuould not be used. + + .. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive] This command sets the BGP link-bandwidth extended community for the prefix diff --git a/doc/user/conf.py b/doc/user/conf.py index 236a90e902..f9f178f5ff 100644 --- a/doc/user/conf.py +++ b/doc/user/conf.py @@ -73,11 +73,11 @@ release = "?.?-?" html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") # Tell Jinja2 templates the build is running on Read the Docs +html_context = {} if os.environ.get("READTHEDOCS", "") == "True": html_context["READTHEDOCS"] = True - # ----------------------------------------------------------------------------- # Extract values from codebase for substitution into docs. # ----------------------------------------------------------------------------- diff --git a/doc/user/evpn.rst b/doc/user/evpn.rst index 7c4d9fe7d9..a72ea56fea 100644 --- a/doc/user/evpn.rst +++ b/doc/user/evpn.rst @@ -56,13 +56,13 @@ FRR learns about the system's Linux network interface configuration from the kernel via Netlink, however it does not manage network interfaces directly. The following sections will include examples of Linux interface configurations that are compatible with FRR's EVPN implementation. While there are multiple -interface managers that can setup a proper kernel config (e.g. ifupdown2), +interface managers that can set up a proper kernel config (e.g. ifupdown2), these examples will use iproute2 to add/configure the interfaces. All of the examples will follow the same basic setup but use different, yet compatible, interface configurations. -In this example we will setup the following: +In this example we will set up the following: * An IP-VRF named vrf1, associated with L3VNI 100 * An IP-VRF named vrf2, associated with L3VNI 200 @@ -78,7 +78,7 @@ In this example we will setup the following: Sample Configuration -------------------- This is a sample FRR configuration that implements the above EVPN environment. -The first snippet will be the config in its entiretly, then each config element +The first snippet will be the config in its entirety, then each config element will be explained individually later in the document. The following snippet will result in a functional EVPN control plane if the @@ -484,7 +484,7 @@ VNI, The interface settings are the same for an L2VNI as they are for an L3VNI. Finally, to limit a traditional bridge's broadcast domain to traffic matching specific VLAN-IDs, ``vlan`` subinterfaces of a host/network port need to be -setup. This example shows the creation of a VLAN subinterface of "eth0" +set up. This example shows the creation of a VLAN subinterface of "eth0" matching VID 10 with the name "eth0.10". By enslaving "eth0.10" to "br10" (instead of "eth0") we ensure that only Ethernet frames ingressing "eth0" tagged with VID 10 will be associated with the "br10" broadcast domain. diff --git a/doc/user/ipv6.rst b/doc/user/ipv6.rst index 4f01061e7b..18aae00bdb 100644 --- a/doc/user/ipv6.rst +++ b/doc/user/ipv6.rst @@ -25,7 +25,8 @@ Router Advertisement .. clicmd:: ipv6 nd suppress-ra Don't send router advertisement messages. The ``no`` form of this command - enables sending RA messages. + enables sending RA messages. Note that while being suppressed, RA messages + might still be enabled by other daemons, such as bgpd or vrrpd. .. clicmd:: ipv6 nd prefix ipv6prefix [valid-lifetime] [preferred-lifetime] [off-link] [no-autoconfig] [router-address] diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index b80adba7f0..b2571dfd70 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -200,6 +200,44 @@ To start OSPF process you have to specify the OSPF router. This command supersedes the *timers spf* command in previous FRR releases. +.. clicmd:: timers throttle lsa all (0-5000) + + This command sets the minumum interval between originations of the + same LSA or the `minimum LSA refresh interval`. The time is specified + in milliseconds and the default is 5 seconds (5000 milliseconds) consistent + with the architectual constant MinLSInterval specified in Appendix D of + RFC 2328. When a self-originated LSA needs to be reoriginated, it may be + delayed for up to this interval. + + .. code-block:: frr + + router ospf + timers throttle lsa all 1000 + + + In this example, the `mininum LSA refresh interval` is set to 1000ms. This + command reduces the delay between successive originations of a self-originated + LSA from 5000 milliseconds to 1000 milliseconds. + +.. clicmd:: timers lsa min-arrival (0-5000) + + This command sets the minumum interval between receptions of instances of + the same LSA or the `minimum LSA arrival interval`. The time is specified in + milliseconds and the default is 1 second (1000 milliseconds) consistent with + the architectual constant MinLSArrival specified in Appendix D of RFC 2328. If a + newer instance of the same LSA is received in less than this interval, it is + ignored. + + .. code-block:: frr + + router ospf + timers lsa min-arrival 50 + + + In this example, the `minimum LSA arrival interval` is set to 50ms. This + command reduces the minimum interval required between instances of the same + LSA from 1000 milliseconds to 50 milliseconds. + .. clicmd:: max-metric router-lsa [on-startup (5-86400)|on-shutdown (5-100)] .. clicmd:: max-metric router-lsa administrative diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 02d674dff0..1481a1bbe8 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -101,9 +101,10 @@ cont .. clicmd:: clear route-map counter [WORD] - Clear counters that are being stored about the route-map utilization - so that subsuquent show commands will indicate since the last clear. - If WORD is specified clear just that particular route-map's counters. + Clear counters as well as cpu time spent that are being stored about + the route-map utilization so that subsequent show commands will indicate + since the last clear. If WORD is specified clear just that particular + route-map's counters. .. _route-map-command: diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 06a19a6139..b862ba9f50 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -985,7 +985,7 @@ and this section also helps that case. :: router# configure terminal - router(config)# segment-routinig + router(config)# segment-routing router(config-sr)# srv6 router(config-srv6)# locators router(config-srv6-locs)# locator loc1 @@ -1013,7 +1013,7 @@ and this section also helps that case. :: router# configure terminal - router(config)# segment-routinig + router(config)# segment-routing router(config-sr)# srv6 router(config-srv6)# locators router(config-srv6-locators)# locator loc1 @@ -1042,7 +1042,7 @@ and this section also helps that case. :: router# configure terminal - router(config)# segment-routinig + router(config)# segment-routing router(config-sr)# srv6 router(config-srv6)# locators router(config-srv6-locators)# locator loc1 @@ -1099,7 +1099,7 @@ and this section also helps that case. :: router# configure terminal - router(config)# segment-routinig + router(config)# segment-routing router(config-sr)# srv6 router(config-srv6)# formats router(config-srv6-formats)# format usid-f3216 @@ -1287,6 +1287,25 @@ IPv6 example for OSPFv3. Set the delay before any route-maps are processed in zebra. The default time for this is 5 seconds. + +.. _zebra-table-import: + +zebra Table Import +================== + +Zebra supports importing an alternate routing table into the main unicast RIB (URIB). +An imported table will continously sync all changes to the main URIB as routes are +added or deleted from the alternate table. +Zebra also supports importing into the main multicast RIB (MRIB) which can be used +to affect how multicast RPF lookups are performed as described in :ref: `_pim-multicast-rib`. + +.. clicmd:: ip import-table (1-252) [mrib] [distance (1-255)] [route-map RMAP_NAME] + + Import table, by given table id, into the main URIB (or MRIB). Optional distance can override + the default distance when importing routes from the alternate table. An optional route map + can be provided to filter routes that are imported into the main table. + + .. _zebra-fib-push-interface: zebra FIB push interface @@ -1864,6 +1883,10 @@ Debugging Nexthop and nexthop-group events. +.. clicmd:: debug zebra srv6 + + Segment Routing for IPv6 dataplane debugging. + Scripting ========= diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index fa1ce3007f..9a967bc1e3 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -851,11 +851,13 @@ void isis_circuit_down(struct isis_circuit *circuit) isis_dr_resign(circuit, 1); circuit->u.bc.is_dr[0] = 0; } + circuit->u.bc.run_dr_elect[0] = 0; memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); if (circuit->u.bc.is_dr[1]) { isis_dr_resign(circuit, 2); circuit->u.bc.is_dr[1] = 0; } + circuit->u.bc.run_dr_elect[1] = 0; memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); memset(circuit->u.bc.snpa, 0, ETH_ALEN); diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index e6cc794bae..93f7bbf753 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -2015,12 +2015,12 @@ void cli_show_isis_prefix_sid_algorithm(struct vty *vty, const char *sid_value_type; const char *sid_value; bool n_flag_clear; - uint32_t algorithm; + uint8_t algorithm; prefix = yang_dnode_get_string(dnode, "prefix"); sid_value_type = yang_dnode_get_string(dnode, "sid-value-type"); sid_value = yang_dnode_get_string(dnode, "sid-value"); - algorithm = yang_dnode_get_uint32(dnode, "algo"); + algorithm = yang_dnode_get_uint8(dnode, "algo"); lh_behavior = yang_dnode_get_string(dnode, "last-hop-behavior"); n_flag_clear = yang_dnode_get_bool(dnode, "n-flag-clear"); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 0f0c900ec2..fb391534e2 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -2633,14 +2633,14 @@ int isis_instance_segment_routing_algorithm_prefix_sid_create( struct isis_area *area; struct prefix prefix; struct sr_prefix_cfg *pcfg; - uint32_t algorithm; + uint8_t algorithm; if (args->event != NB_EV_APPLY) return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); - algorithm = yang_dnode_get_uint32(args->dnode, "algo"); + algorithm = yang_dnode_get_uint8(args->dnode, "algo"); pcfg = isis_sr_cfg_prefix_add(area, &prefix, algorithm); pcfg->algorithm = algorithm; diff --git a/isisd/isis_route.h b/isisd/isis_route.h index 4d49a5ae9c..d9572336af 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -12,6 +12,7 @@ #ifndef _ZEBRA_ISIS_ROUTE_H #define _ZEBRA_ISIS_ROUTE_H +#include "lib/table.h" #include "lib/nexthop.h" struct isis_nexthop { diff --git a/isisd/isisd.c b/isisd/isisd.c index 2863fd913f..fed6d3c6dc 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -2366,35 +2366,34 @@ static void common_isis_summary_json(struct json_object *json, struct isis *isis) { int level; - json_object *areas_json, *area_json, *tx_pdu_json, *rx_pdu_json, - *levels_json, *level_json; + json_object *vrf_json, *areas_json, *area_json, *tx_pdu_json, *rx_pdu_json, *levels_json, + *level_json; struct listnode *node, *node2; struct isis_area *area; time_t cur; char uptime[MONOTIME_STRLEN]; char stier[5]; - json_object_string_add(json, "vrf", isis->name); - json_object_int_add(json, "process-id", isis->process_id); + vrf_json = json_object_new_object(); + json_object_string_add(vrf_json, "vrf", isis->name); + json_object_int_add(vrf_json, "process-id", isis->process_id); if (isis->sysid_set) - json_object_string_addf(json, "system-id", "%pSY", isis->sysid); + json_object_string_addf(vrf_json, "system-id", "%pSY", isis->sysid); cur = time(NULL); cur -= isis->uptime; frrtime_to_interval(cur, uptime, sizeof(uptime)); - json_object_string_add(json, "up-time", uptime); + json_object_string_add(vrf_json, "up-time", uptime); if (isis->area_list) - json_object_int_add(json, "number-areas", - isis->area_list->count); + json_object_int_add(vrf_json, "number-areas", isis->area_list->count); areas_json = json_object_new_array(); - json_object_object_add(json, "areas", areas_json); + json_object_object_add(vrf_json, "areas", areas_json); for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { area_json = json_object_new_object(); json_object_string_add(area_json, "area", area->area_tag ? area->area_tag : "null"); - if (fabricd) { uint8_t tier = fabricd_tier(area); snprintfrr(stier, sizeof(stier), "%s", &tier); @@ -2471,6 +2470,7 @@ static void common_isis_summary_json(struct json_object *json, } json_object_array_add(areas_json, area_json); } + json_object_array_add(json, vrf_json); } static void common_isis_summary_vty(struct vty *vty, struct isis *isis) @@ -2573,13 +2573,27 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis) } } -static void common_isis_summary(struct vty *vty, struct json_object *json, - struct isis *isis) +static void common_isis_summary(struct vty *vty, struct json_object *json, const char *vrf_name, + bool all_vrf) { - if (json) { - common_isis_summary_json(json, isis); + struct listnode *node; + struct isis *isis; + + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + if (json) + common_isis_summary_json(json, isis); + else + common_isis_summary_vty(vty, isis); + } } else { - common_isis_summary_vty(vty, isis); + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + if (json) + common_isis_summary_json(json, isis); + else + common_isis_summary_vty(vty, isis); + } } } @@ -2590,31 +2604,24 @@ DEFUN(show_isis_summary, show_isis_summary_cmd, "json output\n" "summary\n") { - struct listnode *node; int idx_vrf = 0; - struct isis *isis; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; bool uj = use_json(argc, argv); - json_object *json = NULL; + json_object *json = NULL, *vrfs_json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) if (!im) { vty_out(vty, PROTO_NAME " is not running\n"); return CMD_SUCCESS; } - if (uj) + if (uj) { json = json_object_new_object(); - - if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - common_isis_summary(vty, json, isis); - - return CMD_SUCCESS; + vrfs_json = json_object_new_array(); + json_object_object_add(json, "vrfs", vrfs_json); } - isis = isis_lookup_by_vrfname(vrf_name); - if (isis != NULL) - common_isis_summary(vty, json, isis); + + common_isis_summary(vty, vrfs_json, vrf_name, all_vrf); if (uj) vty_json(vty, json); diff --git a/lib/event.c b/lib/event.c index c573923f51..cfe8c3adc0 100644 --- a/lib/event.c +++ b/lib/event.c @@ -669,24 +669,6 @@ static void thread_array_free(struct event_loop *m, struct event **thread_array) XFREE(MTYPE_EVENT_POLL, thread_array); } -/* - * event_master_free_unused - * - * As threads are finished with they are put on the - * unuse list for later reuse. - * If we are shutting down, Free up unused threads - * So we can see if we forget to shut anything off - */ -void event_master_free_unused(struct event_loop *m) -{ - frr_with_mutex (&m->mtx) { - struct event *t; - - while ((t = event_list_pop(&m->unuse))) - thread_free(m, t); - } -} - /* Stop thread scheduler. */ void event_master_free(struct event_loop *m) { @@ -793,7 +775,6 @@ static struct event *thread_get(struct event_loop *m, uint8_t type, thread = XCALLOC(MTYPE_THREAD, sizeof(struct event)); /* mutex only needs to be initialized at struct creation. */ pthread_mutex_init(&thread->mtx, NULL); - m->alloc++; } thread->type = type; @@ -832,10 +813,6 @@ static struct event *thread_get(struct event_loop *m, uint8_t type, static void thread_free(struct event_loop *master, struct event *thread) { - /* Update statistics. */ - assert(master->alloc > 0); - master->alloc--; - /* Free allocated resources. */ pthread_mutex_destroy(&thread->mtx); XFREE(MTYPE_THREAD, thread); diff --git a/lib/frrevent.h b/lib/frrevent.h index 94640a76b7..44776b29a7 100644 --- a/lib/frrevent.h +++ b/lib/frrevent.h @@ -85,7 +85,6 @@ struct event_loop { int io_pipe[2]; int fd_limit; struct fd_handler handler; - unsigned long alloc; long selectpoll_timeout; bool spin; bool handle_signals; @@ -227,7 +226,6 @@ static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b) extern struct event_loop *event_master_create(const char *name); void event_master_set_name(struct event_loop *master, const char *name); extern void event_master_free(struct event_loop *m); -extern void event_master_free_unused(struct event_loop *m); extern void _event_add_read_write(const struct xref_eventsched *xref, struct event_loop *master, diff --git a/lib/frrscript.c b/lib/frrscript.c index dbae31b666..06460b014d 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -27,6 +27,16 @@ struct frrscript_names_head frrscript_names_hash; void _lua_decode_noop(lua_State *L, ...) {} +void frrscript_names_config_write(struct vty *vty) +{ + struct frrscript_names_entry *lua_script_entry; + + frr_each (frrscript_names, &frrscript_names_hash, lua_script_entry) + if (lua_script_entry->script_name[0] != '\0') + vty_out(vty, "zebra on-rib-process script %s\n", + lua_script_entry->script_name); +} + /* * Wrapper for frrscript_names_add * Use this to register hook calls when a daemon starts up diff --git a/lib/frrscript.h b/lib/frrscript.h index ce313a1b63..75ac53c609 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -44,6 +44,8 @@ struct frrscript_names_entry { extern struct frrscript_names_head frrscript_names_hash; +extern void frrscript_names_config_write(struct vty *vty); + int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1, const struct frrscript_names_entry *snhe2); uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe); @@ -1002,12 +1002,6 @@ void if_terminate(struct vrf *vrf) while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) { ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name); - - if (ifp->node) { - ifp->node->info = NULL; - route_unlock_node(ifp->node); - ifp->node = NULL; - } if_delete(&ifp); } } @@ -295,8 +295,6 @@ struct interface { struct if_data stats; #endif /* HAVE_NET_RT_IFLIST */ - struct route_node *node; - struct vrf *vrf; /* diff --git a/lib/libfrr.c b/lib/libfrr.c index 313fe99fd3..d1a9f0b1cb 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -1239,10 +1239,6 @@ void frr_early_fini(void) void frr_fini(void) { - FILE *fp; - char filename[128]; - int have_leftovers = 0; - hook_call(frr_fini); vty_terminate(); @@ -1263,32 +1259,27 @@ void frr_fini(void) event_master_free(master); master = NULL; zlog_tls_buffer_fini(); - zlog_fini(); + + if (0) { + /* this is intentionally disabled. zlog remains running until + * exit(), so even the very last item done during shutdown can + * have its zlog() messages written out. + * + * Yes this causes memory leaks. They are explicitly marked + * with DEFINE_MGROUP_ACTIVEATEXIT, which is only used for + * log target memory allocations, and excluded from leak + * reporting at shutdown. This is strongly preferable over + * just discarding error messages at shutdown. + */ + zlog_fini(); + } + /* frrmod_init -> nothing needed / hooks */ rcu_shutdown(); frrmod_terminate(); - /* also log memstats to stderr when stderr goes to a file*/ - if (debug_memstats_at_exit || !isatty(STDERR_FILENO)) - have_leftovers = log_memstats(stderr, di->name); - - /* in case we decide at runtime that we want exit-memstats for - * a daemon - * (only do this if we actually have something to print though) - */ - if (!debug_memstats_at_exit || !have_leftovers) - return; - - snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", - di->name, (unsigned long long)getpid(), - (unsigned long long)time(NULL)); - - fp = fopen(filename, "w"); - if (fp) { - log_memstats(fp, di->name); - fclose(fp); - } + log_memstats(di->name, debug_memstats_at_exit); } struct json_object *frr_daemon_state_load(void) diff --git a/lib/libospf.h b/lib/libospf.h index 8a208beb3c..5becc1594e 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -27,8 +27,9 @@ extern "C" { #else #define OSPF_LS_REFRESH_TIME 1800 #endif -#define OSPF_MIN_LS_INTERVAL 5000 /* msec */ -#define OSPF_MIN_LS_ARRIVAL 1000 /* in milliseconds */ +#define OSPF_MIN_LS_INTERVAL 5000 /* milliseconds */ +#define OSPF_MIN_LS_ARRIVAL 1000 /* milliseconds */ +#define OSPF_MIN_LS_ARRIVAL_MAX 5000 /* milliseconds */ #define OSPF_LSA_INITIAL_AGE 0 /* useful for debug */ #define OSPF_LSA_MAXAGE 3600 #define OSPF_CHECK_AGE 300 @@ -306,7 +306,7 @@ void memory_oom(size_t size, const char *name) "out of memory: failed to allocate %zu bytes for %s object", size, name); zlog_backtrace(LOG_CRIT); - log_memstats(stderr, "log"); + log_memstats(zlog_progname, true); abort(); } diff --git a/lib/memory.c b/lib/memory.c index ac39516edd..9da46bdb89 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -148,35 +148,108 @@ int qmem_walk(qmem_walk_fn *func, void *arg) } struct exit_dump_args { - FILE *fp; - const char *prefix; + const char *daemon_name; + bool do_log; + bool do_file; + bool do_stderr; int error; + FILE *fp; + struct memgroup *last_mg; }; +static void qmem_exit_fopen(struct exit_dump_args *eda) +{ + char filename[128]; + + if (eda->fp || !eda->do_file || !eda->daemon_name) + return; + + snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", eda->daemon_name, + (unsigned long long)getpid(), (unsigned long long)time(NULL)); + eda->fp = fopen(filename, "w"); + + if (!eda->fp) { + zlog_err("failed to open memstats dump file %pSQq: %m", filename); + /* don't try opening file over and over again */ + eda->do_file = false; + } +} + static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt) { struct exit_dump_args *eda = arg; + const char *prefix = eda->daemon_name ?: "NONE"; + char size[32]; + + if (!mt) + /* iterator calls mg=X, mt=NULL first */ + return 0; - if (!mt) { - fprintf(eda->fp, - "%s: showing active allocations in memory group %s\n", - eda->prefix, mg->name); + if (!mt->n_alloc) + return 0; - } else if (mt->n_alloc) { - char size[32]; - if (!mg->active_at_exit) - eda->error++; + if (mt->size != SIZE_VAR) snprintf(size, sizeof(size), "%10zu", mt->size); - fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", - eda->prefix, mt->name, mt->n_alloc, - mt->size == SIZE_VAR ? "(variably sized)" : size); + else + snprintf(size, sizeof(size), "(variably sized)"); + + if (mg->active_at_exit) { + /* not an error - this memgroup has allocations remain active + * at exit. Only printed to zlog_debug. + */ + if (!eda->do_log) + return 0; + + if (eda->last_mg != mg) { + zlog_debug("showing active allocations in memory group %s (not an error)", + mg->name); + eda->last_mg = mg; + } + zlog_debug("memstats: %-30s: %6zu * %s", mt->name, mt->n_alloc, size); + return 0; } + + eda->error++; + if (eda->do_file) + qmem_exit_fopen(eda); + + if (eda->last_mg != mg) { + if (eda->do_log) + zlog_warn("showing active allocations in memory group %s", mg->name); + if (eda->do_stderr) + fprintf(stderr, "%s: showing active allocations in memory group %s\n", + prefix, mg->name); + if (eda->fp) + fprintf(eda->fp, "%s: showing active allocations in memory group %s\n", + prefix, mg->name); + eda->last_mg = mg; + } + + if (eda->do_log) + zlog_warn("memstats: %-30s: %6zu * %s", mt->name, mt->n_alloc, size); + if (eda->do_stderr) + fprintf(stderr, "%s: memstats: %-30s: %6zu * %s\n", prefix, mt->name, mt->n_alloc, + size); + if (eda->fp) + fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", prefix, mt->name, mt->n_alloc, + size); return 0; } -int log_memstats(FILE *fp, const char *prefix) +int log_memstats(const char *daemon_name, bool enabled) { - struct exit_dump_args eda = {.fp = fp, .prefix = prefix, .error = 0}; + struct exit_dump_args eda = { + .daemon_name = daemon_name, + .do_log = enabled, + .do_file = enabled, + .do_stderr = enabled || !isatty(STDERR_FILENO), + .error = 0, + }; + qmem_walk(qmem_exit_walker, &eda); + if (eda.fp) + fclose(eda.fp); + if (eda.error && eda.do_log) + zlog_warn("exiting with %d leaked MTYPEs", eda.error); return eda.error; } diff --git a/lib/memory.h b/lib/memory.h index 8e8c61da04..8658018832 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -67,6 +67,8 @@ struct memgroup { * but MGROUP_* aren't. */ +/* clang-format off */ + #define DECLARE_MGROUP(name) extern struct memgroup _mg_##name #define _DEFINE_MGROUP(mname, desc, ...) \ struct memgroup _mg_##mname _DATA_SECTION("mgroups") = { \ @@ -75,6 +77,7 @@ struct memgroup { .next = NULL, \ .insert = NULL, \ .ref = NULL, \ + __VA_ARGS__ \ }; \ static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \ static void _mginit_##mname(void) \ @@ -136,6 +139,8 @@ struct memgroup { DEFINE_MTYPE_ATTR(group, name, static, desc) \ /* end */ +/* clang-format on */ + DECLARE_MGROUP(LIB); DECLARE_MTYPE(TMP); DECLARE_MTYPE(TMP_TTABLE); @@ -176,8 +181,7 @@ static inline size_t mtype_stats_alloc(struct memtype *mt) * last value from qmem_walk_fn. */ typedef int qmem_walk_fn(void *arg, struct memgroup *mg, struct memtype *mt); extern int qmem_walk(qmem_walk_fn *func, void *arg); -extern int log_memstats(FILE *fp, const char *); -#define log_memstats_stderr(prefix) log_memstats(stderr, prefix) +extern int log_memstats(const char *daemon_name, bool enabled); extern __attribute__((__noreturn__)) void memory_oom(size_t size, const char *name); diff --git a/lib/routemap.c b/lib/routemap.c index ea917ebd8c..120731fa61 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -941,11 +941,12 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, json_object_boolean_add(json_rmap, "processedChange", map->to_be_processed); json_object_object_add(json_rmap, "rules", json_rules); + json_object_int_add(json_rmap, "cpuTimeMS", map->cputime / 1000); } else { vty_out(vty, "route-map: %s Invoked: %" PRIu64 - " Optimization: %s Processed Change: %s\n", - map->name, map->applied - map->applied_clear, + " (%zu milliseconds total) Optimization: %s Processed Change: %s\n", + map->name, map->applied - map->applied_clear, map->cputime / 1000, map->optimization_disabled ? "disabled" : "enabled", map->to_be_processed ? "true" : "false"); } @@ -967,6 +968,7 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, json_object_int_add(json_rule, "invoked", index->applied - index->applied_clear); + json_object_int_add(json_rule, "cpuTimeMS", index->cputime / 1000); /* Description */ if (index->description) @@ -1018,9 +1020,10 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, json_object_string_add(json_rule, "action", action); } else { - vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n", + vty_out(vty, + " %s, sequence %d Invoked %" PRIu64 " (%zu milliseconds total)\n", route_map_type_str(index->type), index->pref, - index->applied - index->applied_clear); + index->applied - index->applied_clear, index->cputime / 1000); /* Description */ if (index->description) @@ -2548,6 +2551,9 @@ 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; + RUSAGE_T mbefore, mafter; + RUSAGE_T ibefore, iafter; + unsigned long cputime; if (recursion > RMAP_RECURSION_LIMIT) { if (map) @@ -2570,6 +2576,9 @@ route_map_result_t route_map_apply_ext(struct route_map *map, map->applied++; + GETRUSAGE(&mbefore); + ibefore = mbefore; + if (prefix->family == AF_EVPN) { index = map->head; } else { @@ -2580,6 +2589,12 @@ route_map_result_t route_map_apply_ext(struct route_map *map, if (index) { index->applied++; + + GETRUSAGE(&iafter); + event_consumed_time(&iafter, &ibefore, &cputime); + index->cputime += cputime; + ibefore = iafter; + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug( "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", @@ -2718,6 +2733,10 @@ route_map_result_t route_map_apply_ext(struct route_map *map, goto route_map_apply_end; } } + GETRUSAGE(&iafter); + event_consumed_time(&iafter, &ibefore, &cputime); + index->cputime += cputime; + ibefore = iafter; } route_map_apply_end: @@ -2733,6 +2752,13 @@ route_map_apply_end: *pref = 65536; } + if (map) { + GETRUSAGE(&mbefore); + GETRUSAGE(&mafter); + event_consumed_time(&mafter, &mbefore, &cputime); + map->cputime += cputime; + } + return (ret); } @@ -3090,8 +3116,11 @@ static void clear_route_map_helper(struct route_map *map) struct route_map_index *index; map->applied_clear = map->applied; - for (index = map->head; index; index = index->next) + map->cputime = 0; + for (index = map->head; index; index = index->next) { index->applied_clear = index->applied; + index->cputime = 0; + } } DEFPY (rmap_clear_counters, diff --git a/lib/routemap.h b/lib/routemap.h index ef9b3cb160..e0f738502b 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -176,6 +176,7 @@ struct route_map_index { /* Keep track how many times we've try to apply */ uint64_t applied; uint64_t applied_clear; + size_t cputime; /* List of match/sets contexts. */ TAILQ_HEAD(, routemap_hook_context) rhclist; @@ -210,6 +211,7 @@ struct route_map { /* How many times have we applied this route-map */ uint64_t applied; uint64_t applied_clear; + size_t cputime; /* Counter to track active usage of this route-map */ uint16_t use_count; diff --git a/lib/sigevent.c b/lib/sigevent.c index 3e69f280da..7c465bfcec 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -237,8 +237,18 @@ core_handler(int signo, siginfo_t *siginfo, void *context) zlog_signal(signo, "aborting...", siginfo, pc); - /* dump memory stats on core */ - log_memstats(stderr, "core_handler"); + /* there used to be a log_memstats() call here, to dump MTYPE counters + * on a coredump. This is not possible since log_memstats is not + * AS-Safe, as it calls fopen(), fprintf(), and cousins. This can + * lead to a deadlock depending on where we crashed - very much not a + * good thing if the process just hangs there after a crash. + * + * The alarm(1) above tries to alleviate this, but that's really a + * last resort recovery. Stick with AS-safe calls here. + * + * If the fprintf() calls are removed from log_memstats(), this can be + * added back in, since writing to log with zlog_sigsafe() is AS-safe. + */ /* * This is a buffer flush because FRR is going down diff --git a/lib/sockunion.c b/lib/sockunion.c index c37ab1d6dd..7acb5004db 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -403,8 +403,7 @@ int sockunion_same(const union sockunion *su1, const union sockunion *su2) sizeof(struct in_addr)); break; case AF_INET6: - ret = memcmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, - sizeof(struct in6_addr)); + ret = IPV6_ADDR_CMP(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); if ((ret == 0) && IN6_IS_ADDR_LINKLOCAL(&su1->sin6.sin6_addr)) { /* compare interface indices */ if (su1->sin6.sin6_scope_id && su2->sin6.sin6_scope_id) @@ -588,23 +587,6 @@ static void __attribute__((unused)) sockunion_print(const union sockunion *su) } } -int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2) -{ - unsigned int i; - const uint8_t *p1, *p2; - - p1 = (const uint8_t *)addr1; - p2 = (const uint8_t *)addr2; - - for (i = 0; i < sizeof(struct in6_addr); i++) { - if (p1[i] > p2[i]) - return 1; - else if (p1[i] < p2[i]) - return -1; - } - return 0; -} - int sockunion_cmp(const union sockunion *su1, const union sockunion *su2) { if (su1->sa.sa_family > su2->sa.sa_family) @@ -621,7 +603,8 @@ int sockunion_cmp(const union sockunion *su1, const union sockunion *su2) return -1; } if (su1->sa.sa_family == AF_INET6) - return in6addr_cmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); + return IPV6_ADDR_CMP(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); + return 0; } @@ -727,8 +710,7 @@ int sockunion_is_null(const union sockunion *su) case AF_INET: return (su->sin.sin_addr.s_addr == 0); case AF_INET6: - return !memcmp(su->sin6.sin6_addr.s6_addr, null_s6_addr, - sizeof(null_s6_addr)); + return !IPV6_ADDR_CMP(su->sin6.sin6_addr.s6_addr, null_s6_addr); default: return 0; } diff --git a/lib/sockunion.h b/lib/sockunion.h index 146651225c..5152e70a23 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -93,7 +93,6 @@ enum connect_result { connect_error, connect_success, connect_in_progress }; /* Prototypes. */ extern int str2sockunion(const char *, union sockunion *); extern const char *sockunion2str(const union sockunion *, char *, size_t); -int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2); extern int sockunion_cmp(const union sockunion *, const union sockunion *); extern int sockunion_same(const union sockunion *, const union sockunion *); extern unsigned int sockunion_hash(const union sockunion *); diff --git a/lib/wheel.c b/lib/wheel.c index 2520e81d49..3e90693b45 100644 --- a/lib/wheel.c +++ b/lib/wheel.c @@ -18,7 +18,7 @@ static int debug_timer_wheel = 0; static void wheel_timer_thread(struct event *t); -static void wheel_timer_thread_helper(struct event *t) +static void wheel_timer_thread(struct event *t) { struct listnode *node, *nextnode; unsigned long long curr_slot; @@ -51,15 +51,6 @@ static void wheel_timer_thread_helper(struct event *t) wheel->nexttime * slots_to_skip, &wheel->timer); } -static void wheel_timer_thread(struct event *t) -{ - struct timer_wheel *wheel; - - wheel = EVENT_ARG(t); - - event_execute(wheel->master, wheel_timer_thread_helper, wheel, 0, NULL); -} - struct timer_wheel *wheel_init(struct event_loop *master, int period, size_t slots, unsigned int (*slot_key)(const void *), @@ -70,7 +61,6 @@ struct timer_wheel *wheel_init(struct event_loop *master, int period, wheel = XCALLOC(MTYPE_TIMER_WHEEL, sizeof(struct timer_wheel)); - wheel->name = XSTRDUP(MTYPE_TIMER_WHEEL, run_name); wheel->slot_key = slot_key; wheel->slot_run = slot_run; @@ -101,7 +91,6 @@ void wheel_delete(struct timer_wheel *wheel) EVENT_OFF(wheel->timer); XFREE(MTYPE_TIMER_WHEEL_LIST, wheel->wheel_slot_lists); - XFREE(MTYPE_TIMER_WHEEL, wheel->name); XFREE(MTYPE_TIMER_WHEEL, wheel); } diff --git a/lib/wheel.h b/lib/wheel.h index 0d9ac10020..a8fa2f9564 100644 --- a/lib/wheel.h +++ b/lib/wheel.h @@ -12,7 +12,6 @@ extern "C" { #endif struct timer_wheel { - char *name; struct event_loop *master; int slots; long long curr_slot; diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index d2c1a8c401..fa11980c18 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -1169,22 +1169,55 @@ static bool nhrp_connection_authorized(struct nhrp_packet_parser *pp) struct nhrp_extension_header *ext; struct zbuf *extensions, pl; int cmp = 1; + int pl_pass_length, auth_pass_length; + size_t auth_size, pl_size; extensions = zbuf_alloc(zbuf_used(&pp->extensions)); zbuf_copy_peek(extensions, &pp->extensions, zbuf_used(&pp->extensions)); while ((ext = nhrp_ext_pull(extensions, &pl)) != NULL) { switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { case NHRP_EXTENSION_AUTHENTICATION: - cmp = memcmp(auth->buf, pl.buf, zbuf_size(auth)); + /* Size of authentication extensions + * (varies based on password length) + */ + auth_size = zbuf_size(auth); + pl_size = zbuf_size(&pl); auth_ext = (struct nhrp_cisco_authentication_extension *) auth->buf; - debugf(NHRP_DEBUG_COMMON, - "Processing Authentication Extension for (%s:%s|%d)", - auth_ext->secret, - ((struct nhrp_cisco_authentication_extension *) - pl.buf) - ->secret, - cmp); + + if (auth_size == pl_size) + cmp = memcmp(auth_ext, pl.buf, auth_size); + else + cmp = 1; + + if (unlikely(debug_flags & NHRP_DEBUG_COMMON)) { + /* 4 bytes in nhrp_cisco_authentication_extension are allocated + * toward the authentication type. The remaining bytes are used for the + * password - so the password length is just the length of the extension - 4 + */ + auth_pass_length = (auth_size - 4); + pl_pass_length = (pl_size - 4); + /* Because characters are to be printed in HEX, (2* the max pass length) + 1 + * is needed for the string representation + */ + char auth_pass[(2 * NHRP_CISCO_PASS_LEN) + 1] = { 0 }, + pl_pass[(2 * NHRP_CISCO_PASS_LEN) + 1] = { 0 }; + /* Converting bytes in buffer to HEX and saving output as a string - + * Passphrase is converted to HEX in order to avoid printing + * non ACII-compliant characters + */ + for (int i = 0; i < (auth_pass_length); i++) + snprintf(auth_pass + (i * 2), 3, "%02X", + auth_ext->secret[i]); + for (int i = 0; i < (pl_pass_length); i++) + snprintf(pl_pass + (i * 2), 3, "%02X", + ((struct nhrp_cisco_authentication_extension *)pl.buf) + ->secret[i]); + + debugf(NHRP_DEBUG_COMMON, + "Processing Authentication Extension for (%s:%s|%d)", + auth_pass, pl_pass, cmp); + } break; default: /* Ignoring all received extensions except Authentication*/ diff --git a/nhrpd/nhrp_protocol.h b/nhrpd/nhrp_protocol.h index 8cf1ebbcd6..a4fb315b0e 100644 --- a/nhrpd/nhrp_protocol.h +++ b/nhrpd/nhrp_protocol.h @@ -73,6 +73,7 @@ /* NHRP Authentication extension types (ala Cisco) */ #define NHRP_AUTHENTICATION_PLAINTEXT 0x00000001 +#define NHRP_CISCO_PASS_LEN 8 /* NHRP Packet Structures */ struct nhrp_packet_header { diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index f202576960..199f4d75d4 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -467,7 +467,6 @@ DEFUN(if_no_nhrp_holdtime, if_no_nhrp_holdtime_cmd, return CMD_SUCCESS; } -#define NHRP_CISCO_PASS_LEN 8 DEFPY(if_nhrp_authentication, if_nhrp_authentication_cmd, AFI_CMD "nhrp authentication PASSWORD$password", AFI_STR diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 324cd7abe8..4765c29e1e 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -578,7 +578,7 @@ static char *ospf6_link_lsa_get_prefix_str(struct ospf6_lsa *lsa, char *buf, struct ospf6_prefix *prefix = nth_prefix(lsa->header, pos); struct in6_addr in6 = { 0 }; - if (!lsa || !prefix || !buf || buflen < (1 + INET6_ADDRSTRLEN)) + if (!prefix || !buf || buflen < (1 + INET6_ADDRSTRLEN)) return NULL; /* position zero is used for the lladdr in the body of the LSA */ @@ -772,7 +772,7 @@ static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, char tbuf[16]; /* ensure buflen >= INET6_ADDRSTRLEN + '/128\0' */ - if (!lsa || !prefix || !buf || buflen < (5 + INET6_ADDRSTRLEN)) + if (!prefix || !buf || buflen < (5 + INET6_ADDRSTRLEN)) return NULL; memcpy(&in6, OSPF6_PREFIX_BODY(prefix), diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index a8b8de6d5b..1350487898 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -101,26 +101,37 @@ struct timeval msec2tv(int a) return ret; } -int ospf_lsa_refresh_delay(struct ospf_lsa *lsa) +int tv2msec(struct timeval tv) +{ + int msecs; + + msecs = tv.tv_sec * 1000; + msecs += (tv.tv_usec + 1000) / 1000; + + return msecs; +} + +int ospf_lsa_refresh_delay(struct ospf *ospf, struct ospf_lsa *lsa) { struct timeval delta; int delay = 0; - if (monotime_since(&lsa->tv_orig, &delta) - < OSPF_MIN_LS_INTERVAL * 1000LL) { - struct timeval minv = msec2tv(OSPF_MIN_LS_INTERVAL); - timersub(&minv, &delta, &minv); + if (monotime_since(&lsa->tv_orig, &delta) < ospf->min_ls_interval * 1000LL) { + struct timeval minv = msec2tv(ospf->min_ls_interval); - /* TBD: remove padding to full sec, return timeval instead */ - delay = minv.tv_sec + !!minv.tv_usec; + timersub(&minv, &delta, &minv); + delay = tv2msec(minv); if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "LSA[Type%d:%pI4]: Refresh timer delay %d seconds", - lsa->data->type, &lsa->data->id, - delay); - - assert(delay > 0); + zlog_debug("LSA[Type%d:%pI4]: Refresh timer delay %d milliseconds", + lsa->data->type, &lsa->data->id, delay); + + if (delay <= 0) { + zlog_warn("LSA[Type%d:%pI4]: Invalid refresh timer delay %d milliseconds Seq: 0x%x Age:%u", + lsa->data->type, &lsa->data->id, delay, + ntohl(lsa->data->ls_seqnum), ntohs(lsa->data->ls_age)); + delay = 0; + } } return delay; diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index c751f081fb..418675cb10 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -225,12 +225,14 @@ enum lsid_status { LSID_AVAILABLE = 0, LSID_CHANGE, LSID_NOT_AVAILABLE }; /* Prototypes. */ /* XXX: Eek, time functions, similar are in lib/thread.c */ extern struct timeval int2tv(int); + extern struct timeval msec2tv(int a); +extern int tv2msec(struct timeval tv); extern int get_age(struct ospf_lsa *lsa); extern uint16_t ospf_lsa_checksum(struct lsa_header *lsah); extern int ospf_lsa_checksum_valid(struct lsa_header *lsah); -extern int ospf_lsa_refresh_delay(struct ospf_lsa *lsa); +extern int ospf_lsa_refresh_delay(struct ospf *ospf, struct ospf_lsa *lsa); extern const char *dump_lsa_key(struct ospf_lsa *lsa); extern uint32_t lsa_seqnum_increment(struct ospf_lsa *lsa); diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 5d2d65658f..d8b272d60b 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -2051,7 +2051,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) struct opaque_info_per_type *oipt; struct opaque_info_per_id *oipi; struct ospf_lsa *lsa; - struct ospf *top; + struct ospf *ospf; int delay; if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL @@ -2076,6 +2076,11 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) goto out; } + if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) + ospf = lsa0->area->ospf; + else + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + /* Delete this lsa from neighbor retransmit-list. */ switch (lsa->data->type) { case OSPF_OPAQUE_LINK_LSA: @@ -2083,10 +2088,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) ospf_ls_retransmit_delete_nbr_area(lsa->area, lsa); break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(VRF_DEFAULT); - if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) - top = lsa0->area->ospf; - ospf_ls_retransmit_delete_nbr_as(top, lsa); + ospf_ls_retransmit_delete_nbr_as(ospf, lsa); break; default: flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", @@ -2094,17 +2096,14 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) goto out; } - delay = ospf_lsa_refresh_delay(lsa); + delay = ospf_lsa_refresh_delay(ospf, lsa); if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "Schedule Type-%u Opaque-LSA to REFRESH in %d sec later: [opaque-type=%u, opaque-id=%x]", - lsa->data->type, delay, - GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), - GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr))); + zlog_debug("Schedule Type-%u Opaque-LSA to REFRESH in %d msec later: [opaque-type=%u, opaque-id=%x]", + lsa->data->type, delay, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), + GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr))); - OSPF_OPAQUE_TIMER_ON(oipi->t_opaque_lsa_self, - ospf_opaque_lsa_refresh_timer, oipi, delay * 1000); + OSPF_OPAQUE_TIMER_ON(oipi->t_opaque_lsa_self, ospf_opaque_lsa_refresh_timer, oipi, delay); out: return; } diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 01cbfedc1c..0457b13337 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -339,6 +339,12 @@ DEFPY (no_ospf_router_id, return CMD_SUCCESS; } +ALIAS_HIDDEN (no_ospf_router_id, + no_router_id_cmd, + "no router-id [A.B.C.D]", + NO_STR + "router-id for the OSPF process\n" + "OSPF router-id in IP address format\n") static void ospf_passive_interface_default_update(struct ospf *ospf, uint8_t newval) @@ -748,6 +754,8 @@ DEFUN (no_ospf_area_range, ospf_area_range_unset(ospf, area, area->ranges, &p); + ospf_area_check_free(ospf, area_id); + return CMD_SUCCESS; } @@ -1264,7 +1272,7 @@ DEFUN (ospf_area_vlink_intervals, DEFUN (no_ospf_area_vlink_intervals, no_ospf_area_vlink_intervals_cmd, - "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D {hello-interval (1-65535)|retransmit-interval (1-65535)|retransmit-window (20-1000)|transmit-delay (1-65535)|dead-interval (1-65535)}", + "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D {hello-interval [(1-65535)]|retransmit-interval [(1-65535)]|retransmit-window [(20-1000)]|transmit-delay [(1-65535)]|dead-interval [(1-65535)]}", NO_STR VLINK_HELPSTR_IPADDR VLINK_HELPSTR_TIME_PARAM) @@ -1581,7 +1589,7 @@ DEFPY (no_ospf_area_nssa, "no area <A.B.C.D|(0-4294967295)>$area_str nssa\ [{\ <translate-candidate|translate-never|translate-always>\ - |default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\ + |default-information-originate [{metric [(0-16777214)]|metric-type [(1-2)]}]\ |no-summary\ |suppress-fa\ }]", @@ -1662,7 +1670,7 @@ DEFPY (ospf_area_nssa_range, DEFPY (no_ospf_area_nssa_range, no_ospf_area_nssa_range_cmd, - "no area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise|cost (0-16777215)>]", + "no area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise|cost [(0-16777215)]>]", NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" @@ -1741,7 +1749,7 @@ DEFUN (ospf_area_default_cost, DEFUN (no_ospf_area_default_cost, no_ospf_area_default_cost_cmd, - "no area <A.B.C.D|(0-4294967295)> default-cost (0-16777215)", + "no area <A.B.C.D|(0-4294967295)> default-cost [(0-16777215)]", NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" @@ -2305,34 +2313,9 @@ static int ospf_timers_spf_set(struct vty *vty, unsigned int delay, return CMD_SUCCESS; } -DEFUN (ospf_timers_min_ls_interval, +DEFPY (ospf_timers_min_ls_interval, ospf_timers_min_ls_interval_cmd, - "timers throttle lsa all (0-5000)", - "Adjust routing timers\n" - "Throttling adaptive timer\n" - "LSA delay between transmissions\n" - "All LSA types\n" - "Delay (msec) between sending LSAs\n") -{ - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - int idx_number = 4; - unsigned int interval; - - if (argc < 5) { - vty_out(vty, "Insufficient arguments\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - interval = strtoul(argv[idx_number]->arg, NULL, 10); - - ospf->min_ls_interval = interval; - - return CMD_SUCCESS; -} - -DEFUN (no_ospf_timers_min_ls_interval, - no_ospf_timers_min_ls_interval_cmd, - "no timers throttle lsa all [(0-5000)]", + "[no] timers throttle lsa all ![(0-5000)]$lsa_refresh_interval", NO_STR "Adjust routing timers\n" "Throttling adaptive timer\n" @@ -2341,7 +2324,11 @@ DEFUN (no_ospf_timers_min_ls_interval, "Delay (msec) between sending LSAs\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - ospf->min_ls_interval = OSPF_MIN_LS_INTERVAL; + + if (no) + ospf->min_ls_interval = OSPF_MIN_LS_INTERVAL; + else + ospf->min_ls_interval = strtoul(lsa_refresh_interval_str, NULL, 10); return CMD_SUCCESS; } @@ -2390,40 +2377,35 @@ DEFUN (no_ospf_timers_throttle_spf, } -DEFUN (ospf_timers_lsa_min_arrival, +DEFPY (ospf_timers_lsa_min_arrival, ospf_timers_lsa_min_arrival_cmd, - "timers lsa min-arrival (0-600000)", + "[no] timers lsa min-arrival ![(0-5000)]$min_arrival", + NO_STR "Adjust routing timers\n" "OSPF LSA timers\n" - "Minimum delay in receiving new version of a LSA\n" + "Minimum delay in receiving new version of an LSA\n" "Delay in milliseconds\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - ospf->min_ls_arrival = strtoul(argv[argc - 1]->arg, NULL, 10); + if (no) + ospf->min_ls_arrival = OSPF_MIN_LS_ARRIVAL; + else + ospf->min_ls_arrival = strtoul(min_arrival_str, NULL, 10); return CMD_SUCCESS; } -DEFUN (no_ospf_timers_lsa_min_arrival, - no_ospf_timers_lsa_min_arrival_cmd, - "no timers lsa min-arrival [(0-600000)]", - NO_STR - "Adjust routing timers\n" - "OSPF LSA timers\n" - "Minimum delay in receiving new version of a LSA\n" - "Delay in milliseconds\n") +DEFPY_HIDDEN (ospf_timers_lsa_min_arrival_deprecated, + ospf_timers_lsa_min_arrival_deprecated_cmd, + "timers lsa min-arrival [(5001-60000)]$min_arrival", + "Adjust routing timers\n" + "OSPF LSA timers\n" + "Minimum delay in receiving new version of an LSA\n" + "Deprecated delay in milliseconds - delays in this range default to 5000 msec\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - unsigned int minarrival; - - if (argc > 4) { - minarrival = strtoul(argv[argc - 1]->arg, NULL, 10); - - if (ospf->min_ls_arrival != minarrival - || minarrival == OSPF_MIN_LS_ARRIVAL) - return CMD_SUCCESS; - } - - ospf->min_ls_arrival = OSPF_MIN_LS_ARRIVAL; + vty_out(vty, "%% OSPF `timers lsa min-arrival` set to the maximum of %u milliseconds\n", + OSPF_MIN_LS_ARRIVAL_MAX); + ospf->min_ls_arrival = OSPF_MIN_LS_ARRIVAL_MAX; return CMD_SUCCESS; } @@ -2589,7 +2571,7 @@ ALIAS(ospf_write_multiplier, write_multiplier_cmd, "write-multiplier (1-100)", DEFUN (no_ospf_write_multiplier, no_ospf_write_multiplier_cmd, - "no ospf write-multiplier (1-100)", + "no ospf write-multiplier [(1-100)]", NO_STR "OSPF specific commands\n" "Write multiplier\n" @@ -9558,7 +9540,7 @@ DEFUN (ospf_default_information_originate, DEFUN (no_ospf_default_information_originate, no_ospf_default_information_originate_cmd, - "no default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + "no default-information originate [{always|metric [(0-16777214)]|metric-type [(1-2)]|route-map [RMAP_NAME]}]", NO_STR "Control distribution of default information\n" "Distribute a default route\n" @@ -9642,7 +9624,7 @@ DEFUN (ospf_distance, DEFUN (no_ospf_distance, no_ospf_distance_cmd, - "no distance (1-255)", + "no distance [(1-255)]", NO_STR "Administrative distance\n" "OSPF Administrative distance\n") @@ -13627,6 +13609,7 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_router_id_cmd); install_element(OSPF_NODE, &ospf_router_id_old_cmd); install_element(OSPF_NODE, &no_ospf_router_id_cmd); + install_element(OSPF_NODE, &no_router_id_cmd); /* "passive-interface" commands. */ install_element(OSPF_NODE, &ospf_passive_interface_default_cmd); @@ -13710,9 +13693,8 @@ void ospf_vty_init(void) /* LSA timers commands */ install_element(OSPF_NODE, &ospf_timers_min_ls_interval_cmd); - install_element(OSPF_NODE, &no_ospf_timers_min_ls_interval_cmd); install_element(OSPF_NODE, &ospf_timers_lsa_min_arrival_cmd); - install_element(OSPF_NODE, &no_ospf_timers_lsa_min_arrival_cmd); + install_element(OSPF_NODE, &ospf_timers_lsa_min_arrival_deprecated_cmd); /* refresh timer commands */ install_element(OSPF_NODE, &ospf_refresh_timer_cmd); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 9d7870d2f7..d72afec1e4 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -147,15 +147,10 @@ void ospf_process_refresh_data(struct ospf *ospf, bool reset) /* Select the router ID based on these priorities: 1. Statically assigned router ID is always the first choice. - 2. If there is no statically assigned router ID, then try to stick - with the most recent value, since changing router ID's is very - disruptive. - 3. Last choice: just go with whatever the zebra daemon recommends. + 2. Just go with whatever the zebra daemon recommends. */ if (ospf->router_id_static.s_addr != INADDR_ANY) router_id = ospf->router_id_static; - else if (ospf->router_id.s_addr != INADDR_ANY) - router_id = ospf->router_id; else router_id = ospf->router_id_zebra; @@ -1730,8 +1725,6 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id) area->no_summary = 0; area->suppress_fa = 0; area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; - if (area->NSSATranslatorState == OSPF_NSSA_TRANSLATE_ENABLED) - ospf_asbr_status_update(ospf, --ospf->redistribute); area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT; ospf_area_type_set(area, OSPF_AREA_DEFAULT); diff --git a/pimd/pim_autorp.c b/pimd/pim_autorp.c index 3fc30347a6..d36b792e39 100644 --- a/pimd/pim_autorp.c +++ b/pimd/pim_autorp.c @@ -241,8 +241,11 @@ static bool pim_autorp_add_rp(struct pim_autorp *autorp, pim_addr rpaddr, { struct pim_autorp_rp *rp; struct pim_autorp_rp *trp = NULL; + int ret; - if (pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP)) { + ret = pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP); + /* There may not be a path to the RP right now, but that doesn't mean it failed to add the RP */ + if (ret != PIM_SUCCESS && ret != PIM_RP_NO_PATH) { zlog_err("%s: Failed to add new RP addr=%pI4, grp=%pFX, grplist=%s", __func__, &rpaddr, &grp, (listname ? listname : "NONE")); diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index 115aec8933..a44e4e08f3 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -1650,8 +1650,18 @@ static void pim_cand_bsr_pending_expire(struct event *t) struct bsm_scope *scope = EVENT_ARG(t); assertf(scope->state == BSR_PENDING, "state=%d", scope->state); - assertf(pim_addr_is_any(scope->current_bsr), "current_bsr=%pPA", - &scope->current_bsr); + + if (!pim_addr_is_any(scope->current_bsr)) { + assertf(scope->cand_bsr_prio >= scope->current_bsr_prio, + "cand_bsr %pPA prio %u is less than current_bsr %pPA prio %u", + &scope->bsr_addrsel.run_addr, scope->current_bsr_prio, &scope->current_bsr, + scope->cand_bsr_prio); + + if (scope->cand_bsr_prio == scope->current_bsr_prio) + assertf(pim_addr_cmp(scope->bsr_addrsel.run_addr, scope->current_bsr) > 0, + "cand_bsr %pPA < current_bsr %pPA", &scope->bsr_addrsel.run_addr, + &scope->current_bsr); + } if (PIM_DEBUG_BSM) zlog_debug("Elected BSR, wait expired without preferable BSMs"); diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 5a161c4f16..f2dbfa9765 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -342,9 +342,19 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, nbr = pim_neighbor_find(ifp, znh->nexthop_addr, true); if (!nbr) continue; - - return znh->ifindex == src_ifp->ifindex && - (!pim_addr_cmp(znh->nexthop_addr, src_ip)); + /* Are we on the correct interface? */ + if (znh->ifindex == src_ifp->ifindex) { + /* Do we have the correct NH ? */ + if (!pim_addr_cmp(znh->nexthop_addr, src_ip)) + return true; + /* + * check If the packet came from the neighbor, + * and the dst is a secondary address on the connected interface + */ + return (!pim_addr_cmp(nbr->source_addr, src_ip) && + pim_if_connected_to_source(ifp, znh->nexthop_addr)); + } + return false; } return false; } @@ -409,8 +419,19 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, if (!nbr) continue; - return nh->ifindex == src_ifp->ifindex && - (!pim_addr_cmp(nhaddr, src_ip)); + /* Are we on the correct interface? */ + if (nh->ifindex == src_ifp->ifindex) { + /* Do we have the correct NH ? */ + if (!pim_addr_cmp(nhaddr, src_ip)) + return true; + /* + * check If the packet came from the neighbor, + * and the dst is a secondary address on the connected interface + */ + return (!pim_addr_cmp(nbr->source_addr, src_ip) && + pim_if_connected_to_source(ifp, nhaddr)); + } + return false; } return false; } diff --git a/python/xref2vtysh.py b/python/xref2vtysh.py index 0cfb11e721..0e6cdcd6a7 100644 --- a/python/xref2vtysh.py +++ b/python/xref2vtysh.py @@ -450,9 +450,11 @@ class CommandEntry: graph_delete_node(node->cmdgraph, vector_slot(node->cmdgraph->nodes, 0)); vector_free(node->cmdgraph->nodes); node->cmdgraph->nodes = &gvec_{node}; -{'}'} """ ) + for cmdel in sorted(cmdels): + ofd.write(f"\tvector_set(node->cmd_vector, &{cmdel}_vtysh);\n") + ofd.write("}\n") return [node] diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 21c596bf71..c9211a152f 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -82,7 +82,7 @@ DEFPY(watch_redistribute, watch_redistribute_cmd, } DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, - "sharp watch [vrf NAME$vrf_name] <nexthop$n X:X::X:X$nhop|import$import X:X::X:X/M$inhop> [connected$connected]", + "sharp watch [vrf NAME$vrf_name] <nexthop$n X:X::X:X$nhop|import$import X:X::X:X/M$inhop> [connected$connected] [mrib$mrib]", "Sharp routing Protocol\n" "Watch for changes\n" "The vrf we would like to watch if non-default\n" @@ -91,7 +91,8 @@ DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, "The v6 nexthop to signal for watching\n" "Watch for import check changes\n" "The v6 prefix to signal for watching\n" - "Should the route be connected\n") + "Should the route be connected\n" + "In the Multicast rib\n") { struct vrf *vrf; struct prefix p; @@ -119,14 +120,13 @@ DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, } sharp_nh_tracker_get(&p); - sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, - true, !!connected); + sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, true, !!connected, !!mrib); return CMD_SUCCESS; } DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, - "sharp watch [vrf NAME$vrf_name] <nexthop$n A.B.C.D$nhop|import$import A.B.C.D/M$inhop> [connected$connected]", + "sharp watch [vrf NAME$vrf_name] <nexthop$n A.B.C.D$nhop|import$import A.B.C.D/M$inhop> [connected$connected] [mrib$mrib]", "Sharp routing Protocol\n" "Watch for changes\n" "The vrf we would like to watch if non-default\n" @@ -135,7 +135,8 @@ DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, "The v4 address to signal for watching\n" "Watch for import check changes\n" "The v4 prefix for import check to watch\n" - "Should the route be connected\n") + "Should the route be connected\n" + "In the Multicast rib\n") { struct vrf *vrf; struct prefix p; @@ -164,8 +165,7 @@ DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, } sharp_nh_tracker_get(&p); - sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, - true, !!connected); + sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, true, !!connected, !!mrib); return CMD_SUCCESS; } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 1048436b43..4447b69bf6 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -618,18 +618,19 @@ void nhg_del(uint32_t id) zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg); } -void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, - bool watch, bool connected) +void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, bool watch, + bool connected, bool mrib) { - int command; + int command = ZEBRA_NEXTHOP_REGISTER; + safi_t safi = mrib ? SAFI_MULTICAST : SAFI_UNICAST; command = ZEBRA_NEXTHOP_REGISTER; if (!watch) command = ZEBRA_NEXTHOP_UNREGISTER; - if (zclient_send_rnh(zclient, command, p, SAFI_UNICAST, connected, - false, vrf_id) == ZCLIENT_SEND_FAILURE) + if (zclient_send_rnh(zclient, command, p, safi, connected, false, vrf_id) == + ZCLIENT_SEND_FAILURE) zlog_warn("%s: Failure to send nexthop to zebra", __func__); } diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 5cbcc14665..7a86897beb 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -18,8 +18,8 @@ extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label); extern void nhg_add(uint32_t id, const struct nexthop_group *nhg, const struct nexthop_group *backup_nhg); extern void nhg_del(uint32_t id); -extern void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, - bool import, bool watch, bool connected); +extern void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, bool watch, + bool connected, bool mrib); extern void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, uint8_t instance, uint32_t nhgid, diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 627ccfee6f..a3acd0786f 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -22,7 +22,7 @@ static bool atexit_registered; static void show_meminfo_at_exit(void) { - log_memstats(stderr, "isis fuzztest"); + log_memstats(NULL, true); } static int comp_line(const void *p1, const void *p2) diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index e5a8f7a513..9c1ce3d193 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -475,7 +475,7 @@ static void vty_do_exit(int isexit) yang_terminate(); event_master_free(master); - log_memstats(stderr, "test-isis-spf"); + log_memstats(NULL, true); if (!isexit) exit(0); } diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 342a91cc79..5c23a71258 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -43,7 +43,7 @@ static void vty_do_exit(int isexit) yang_terminate(); event_master_free(master); - log_memstats(stderr, "testcli"); + log_memstats(NULL, true); if (!isexit) exit(0); } diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index fdc9e53ca3..3d700d8a19 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -427,7 +427,7 @@ static void vty_do_exit(int isexit) yang_terminate(); event_master_free(master); - log_memstats(stderr, "test-nb-oper-data"); + log_memstats(NULL, true); if (!isexit) exit(0); } diff --git a/tests/lib/test_frrlua.c b/tests/lib/test_frrlua.c index 4aa8c8a8c3..9a1c3eaab6 100644 --- a/tests/lib/test_frrlua.c +++ b/tests/lib/test_frrlua.c @@ -90,7 +90,7 @@ static void test_encode_decode(void) lua_pushin6addr(L, &in6addr_a); lua_decode_in6addr(L, -1, &in6addr_a); - assert(in6addr_cmp(&in6addr_a, &in6addr_b) == 0); + assert(memcmp(&in6addr_a, &in6addr_b, sizeof(struct in6_addr)) == 0); assert(lua_gettop(L) == 0); union sockunion su_a, su_b; diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c index 070a304335..3ce6683ae5 100644 --- a/tests/lib/test_typelist.c +++ b/tests/lib/test_typelist.c @@ -156,6 +156,6 @@ int main(int argc, char **argv) test_ATOMSORT_UNIQ(); test_ATOMSORT_NONUNIQ(); - log_memstats_stderr("test: "); + log_memstats(NULL, true); return 0; } diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c index 2cd9d47cb4..5cb518d81a 100644 --- a/tests/lib/test_zmq.c +++ b/tests/lib/test_zmq.c @@ -285,7 +285,7 @@ static void run_server(int syncfd) zmq_close(zmqsock); frrzmq_finish(); event_master_free(master); - log_memstats_stderr("test"); + log_memstats(NULL, true); } int main(void) diff --git a/tests/topotests/all_protocol_startup/r1/show_route_map.ref b/tests/topotests/all_protocol_startup/r1/show_route_map.ref index 612d0a729d..894ea59677 100644 --- a/tests/topotests/all_protocol_startup/r1/show_route_map.ref +++ b/tests/topotests/all_protocol_startup/r1/show_route_map.ref @@ -1,6 +1,6 @@ ZEBRA: -route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false - deny, sequence 10 Invoked 0 +route-map: LIES Invoked: 0 (X milliseconds total) Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 (X milliseconds total) Match clauses: interface notpresent Set clauses: @@ -8,8 +8,8 @@ route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false Action: Exit routemap RIP: -route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false - deny, sequence 10 Invoked 0 +route-map: LIES Invoked: 0 (X milliseconds total) Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 (X milliseconds total) Match clauses: interface notpresent Set clauses: @@ -17,8 +17,8 @@ route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false Action: Exit routemap RIPNG: -route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false - deny, sequence 10 Invoked 0 +route-map: LIES Invoked: 0 (X milliseconds total) Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 (X milliseconds total) Match clauses: interface notpresent Set clauses: @@ -26,8 +26,8 @@ route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false Action: Exit routemap OSPF: -route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false - deny, sequence 10 Invoked 0 +route-map: LIES Invoked: 0 (X milliseconds total) Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 (X milliseconds total) Match clauses: interface notpresent Set clauses: @@ -35,8 +35,8 @@ route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false Action: Exit routemap OSPF6: -route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false - deny, sequence 10 Invoked 0 +route-map: LIES Invoked: 0 (X milliseconds total) Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 (X milliseconds total) Match clauses: interface notpresent Set clauses: @@ -44,16 +44,16 @@ route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false Action: Exit routemap BGP: -route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false - deny, sequence 10 Invoked 0 +route-map: LIES Invoked: 0 (X milliseconds total) Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 (X milliseconds total) Match clauses: interface notpresent Set clauses: Call clause: Action: Exit routemap -route-map: bgp-map Invoked: 0 Optimization: enabled Processed Change: false - permit, sequence 10 Invoked 0 +route-map: bgp-map Invoked: 0 (X milliseconds total) Optimization: enabled Processed Change: false + permit, sequence 10 Invoked 0 (X milliseconds total) Match clauses: Set clauses: community 100:100 additive @@ -61,7 +61,7 @@ route-map: bgp-map Invoked: 0 Optimization: enabled Processed Change: false Call clause: Action: Exit routemap - permit, sequence 20 Invoked 0 + permit, sequence 20 Invoked 0 (X milliseconds total) Match clauses: Set clauses: metric 10 diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py index 80bd2505a7..0ffd762734 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -37,6 +37,7 @@ from lib.topogen import Topogen, get_topogen from lib.common_config import ( required_linux_kernel_version, ) +from lib.topolog import logger import json import functools @@ -1422,6 +1423,7 @@ def test_route_map(): .cmd('vtysh -c "show route-map" 2> /dev/null') .rstrip() ) + actual = re.sub(r"\([0-9].* milli", "(X milli", actual) actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) diff = topotest.get_textdiff( diff --git a/tests/topotests/bgp_addpath_graceful_restart/__init__.py b/tests/topotests/bgp_addpath_graceful_restart/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_addpath_graceful_restart/__init__.py diff --git a/tests/topotests/bgp_addpath_graceful_restart/r1/frr.conf b/tests/topotests/bgp_addpath_graceful_restart/r1/frr.conf new file mode 100644 index 0000000000..0644cf63a8 --- /dev/null +++ b/tests/topotests/bgp_addpath_graceful_restart/r1/frr.conf @@ -0,0 +1,23 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +int r1-eth1 + ip address 192.168.2.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as auto + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as auto + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + neighbor r1-eth1 interface remote-as auto + neighbor r1-eth1 timers 1 3 + neighbor r1-eth1 timers connect 1 + address-family ipv4 unicast + network 10.0.0.1/32 + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_graceful_restart/r2/frr.conf b/tests/topotests/bgp_addpath_graceful_restart/r2/frr.conf new file mode 100644 index 0000000000..ad236e0680 --- /dev/null +++ b/tests/topotests/bgp_addpath_graceful_restart/r2/frr.conf @@ -0,0 +1,28 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! +int r2-eth2 + ip address 192.168.3.2/24 +! +router bgp 65002 + bgp graceful-restart + bgp graceful-restart preserve-fw-state + bgp graceful-restart restart-time 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as auto + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.2.1 remote-as auto + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + neighbor 192.168.3.3 remote-as auto + neighbor 192.168.3.3 timers 1 3 + neighbor 192.168.3.3 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.3.3 addpath-tx-all-paths + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_graceful_restart/r3/frr.conf b/tests/topotests/bgp_addpath_graceful_restart/r3/frr.conf new file mode 100644 index 0000000000..e864003f25 --- /dev/null +++ b/tests/topotests/bgp_addpath_graceful_restart/r3/frr.conf @@ -0,0 +1,13 @@ +! +int r3-eth0 + ip address 192.168.3.3/24 +! +router bgp 65003 + bgp graceful-restart + bgp graceful-restart preserve-fw-state + bgp graceful-restart restart-time 10 + no bgp ebgp-requires-policy + neighbor 192.168.3.2 remote-as auto + neighbor 192.168.3.2 timers 1 3 + neighbor 192.168.3.2 timers connect 1 +! diff --git a/tests/topotests/bgp_addpath_graceful_restart/test_bgp_addpath_graceful_restart.py b/tests/topotests/bgp_addpath_graceful_restart/test_bgp_addpath_graceful_restart.py new file mode 100644 index 0000000000..9088c44bdb --- /dev/null +++ b/tests/topotests/bgp_addpath_graceful_restart/test_bgp_addpath_graceful_restart.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2024 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.common_config import kill_router_daemons + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2"), "s2": ("r1", "r2"), "s3": ("r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_addpath_graceful_restart(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast summary json")) + expected = { + "peers": { + "192.168.1.1": { + "hostname": "r1", + "remoteAs": 65001, + "localAs": 65002, + "pfxRcd": 1, + "state": "Established", + }, + "192.168.2.1": { + "hostname": "r1", + "remoteAs": 65001, + "localAs": 65002, + "pfxRcd": 1, + "state": "Established", + }, + "192.168.3.3": { + "hostname": "r3", + "remoteAs": 65003, + "localAs": 65002, + "pfxSnt": 2, + "state": "Established", + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Initial peering failed" + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + def _bgp_check_stale_routes(): + output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.0.0.1/32": [ + { + "stale": True, + "valid": True, + }, + { + "stale": True, + "valid": True, + "multipath": True, + }, + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_stale_routes, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see stale routes" + + def _bgp_check_stale_routes_cleared(): + output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.0.0.1/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_stale_routes_cleared, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see stale routes" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_addpath_llgr/__init__.py b/tests/topotests/bgp_addpath_llgr/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_addpath_llgr/__init__.py diff --git a/tests/topotests/bgp_addpath_llgr/r1/frr.conf b/tests/topotests/bgp_addpath_llgr/r1/frr.conf new file mode 100644 index 0000000000..0644cf63a8 --- /dev/null +++ b/tests/topotests/bgp_addpath_llgr/r1/frr.conf @@ -0,0 +1,23 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +int r1-eth1 + ip address 192.168.2.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as auto + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as auto + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + neighbor r1-eth1 interface remote-as auto + neighbor r1-eth1 timers 1 3 + neighbor r1-eth1 timers connect 1 + address-family ipv4 unicast + network 10.0.0.1/32 + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_llgr/r2/frr.conf b/tests/topotests/bgp_addpath_llgr/r2/frr.conf new file mode 100644 index 0000000000..7f6fff0fda --- /dev/null +++ b/tests/topotests/bgp_addpath_llgr/r2/frr.conf @@ -0,0 +1,29 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! +int r2-eth2 + ip address 192.168.3.2/24 +! +router bgp 65002 + bgp graceful-restart + bgp graceful-restart preserve-fw-state + bgp graceful-restart restart-time 0 + bgp long-lived-graceful-restart stale-time 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as auto + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.2.1 remote-as auto + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + neighbor 192.168.3.3 remote-as auto + neighbor 192.168.3.3 timers 1 3 + neighbor 192.168.3.3 timers connect 1 + address-family ipv4 unicast + neighbor 192.168.3.3 addpath-tx-all-paths + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_llgr/r3/frr.conf b/tests/topotests/bgp_addpath_llgr/r3/frr.conf new file mode 100644 index 0000000000..f36492d7e0 --- /dev/null +++ b/tests/topotests/bgp_addpath_llgr/r3/frr.conf @@ -0,0 +1,14 @@ +! +int r3-eth0 + ip address 192.168.3.3/24 +! +router bgp 65003 + bgp graceful-restart + bgp graceful-restart preserve-fw-state + bgp graceful-restart restart-time 0 + bgp long-lived-graceful-restart stale-time 10 + no bgp ebgp-requires-policy + neighbor 192.168.3.2 remote-as auto + neighbor 192.168.3.2 timers 1 3 + neighbor 192.168.3.2 timers connect 1 +! diff --git a/tests/topotests/bgp_addpath_llgr/test_bgp_addpath_llgr.py b/tests/topotests/bgp_addpath_llgr/test_bgp_addpath_llgr.py new file mode 100644 index 0000000000..fa0314304b --- /dev/null +++ b/tests/topotests/bgp_addpath_llgr/test_bgp_addpath_llgr.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2024 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.common_config import kill_router_daemons + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2"), "s2": ("r1", "r2"), "s3": ("r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_addpath_llgr(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast summary json")) + expected = { + "peers": { + "192.168.1.1": { + "hostname": "r1", + "remoteAs": 65001, + "localAs": 65002, + "pfxRcd": 1, + "state": "Established", + }, + "192.168.2.1": { + "hostname": "r1", + "remoteAs": 65001, + "localAs": 65002, + "pfxRcd": 1, + "state": "Established", + }, + "192.168.3.3": { + "hostname": "r3", + "remoteAs": 65003, + "localAs": 65002, + "pfxSnt": 2, + "state": "Established", + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Initial peering failed" + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + def _bgp_check_stale_llgr_routes(): + output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast 10.0.0.1/32 json")) + expected = { + "paths": [ + { + "stale": True, + "valid": True, + "community": {"string": "llgr-stale", "list": ["llgrStale"]}, + }, + { + "stale": True, + "valid": True, + "community": {"string": "llgr-stale", "list": ["llgrStale"]}, + }, + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_stale_llgr_routes, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see stale LLGR routes" + + def _bgp_check_stale_routes_cleared(): + output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.0.0.1/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_stale_routes_cleared, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see stale routes" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aigp_rr/r1/bgpd.conf b/tests/topotests/bgp_aigp_rr/r1/bgpd.conf index 90d34bdf83..4099a248f1 100644 --- a/tests/topotests/bgp_aigp_rr/r1/bgpd.conf +++ b/tests/topotests/bgp_aigp_rr/r1/bgpd.conf @@ -18,6 +18,7 @@ router bgp 65001 neighbor 10.0.0.4 timers connect 1 neighbor 10.0.0.4 route-reflector-client address-family ipv4 + network 10.0.1.2/32 route-map set-aigp neighbor 10.0.0.4 route-map set-nexthop out exit-address-family ! @@ -25,3 +26,7 @@ route-map set-nexthop permit 10 set ip next-hop peer-address exit ! +route-map set-aigp permit 10 + set aigp 50 + set weight 0 +! diff --git a/tests/topotests/bgp_aigp_rr/r2/bgpd.conf b/tests/topotests/bgp_aigp_rr/r2/bgpd.conf index 97059fdd33..0159dc8e7c 100644 --- a/tests/topotests/bgp_aigp_rr/r2/bgpd.conf +++ b/tests/topotests/bgp_aigp_rr/r2/bgpd.conf @@ -7,6 +7,7 @@ router bgp 65001 neighbor 10.0.0.1 timers connect 1 address-family ipv4 redistribute connected route-map connected-to-bgp + network 10.0.1.2/32 route-map set-aigp neighbor 10.0.0.1 next-hop-self exit-address-family ! @@ -16,3 +17,6 @@ route-map connected-to-bgp permit 10 match ip address prefix-list p22 set aigp 2 ! +route-map set-aigp permit 10 + set aigp 10 +! diff --git a/tests/topotests/bgp_aigp_rr/test_bgp_aigp_rr.py b/tests/topotests/bgp_aigp_rr/test_bgp_aigp_rr.py index 0079535da7..206e229b2e 100644 --- a/tests/topotests/bgp_aigp_rr/test_bgp_aigp_rr.py +++ b/tests/topotests/bgp_aigp_rr/test_bgp_aigp_rr.py @@ -101,6 +101,45 @@ def test_bgp_aigp_rr(): expected = {"paths": [{"aigpMetric": aigp, "valid": True}]} return topotest.json_cmp(output, expected) + def _bgp_check_aigp_bestpath(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.0.1.2/32 json")) + expected = { + "prefix": "10.0.1.2/32", + "paths": [ + { + "aigpMetric": 50, + "valid": True, + "sourced": True, + "local": True, + "bestpath": {"overall": True, "selectionReason": "Local Route"}, + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "metric": 0, + "accessible": True, + "used": True, + } + ], + }, + { + "aigpMetric": 10, + "valid": True, + "nexthops": [ + { + "ip": "10.0.0.2", + "hostname": "r2", + "afi": "ipv4", + "metric": 10, + "accessible": True, + "used": True, + } + ], + }, + ], + } + return topotest.json_cmp(output, expected) # r2, 10.0.2.2/32 with aigp-metric 2 test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.2.2/32", 2) @@ -122,6 +161,11 @@ def test_bgp_aigp_rr(): _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.2.2/32 is not 12" + # r1, check if the local route is favored over AIGP comparison + test_func = functools.partial(_bgp_check_aigp_bestpath) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Local route is not favored over AIGP in best-path selection" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_bfd_session/__init__.py b/tests/topotests/bgp_bfd_session/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_bfd_session/__init__.py diff --git a/tests/topotests/bgp_bfd_session/r1/frr.conf b/tests/topotests/bgp_bfd_session/r1/frr.conf new file mode 100644 index 0000000000..a1560b09fa --- /dev/null +++ b/tests/topotests/bgp_bfd_session/r1/frr.conf @@ -0,0 +1,14 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! +router bgp 65000 + neighbor 192.168.1.2 remote-as auto + neighbor 192.168.1.2 bfd + neighbor 192.168.1.2 ebgp-multihop 10 + neighbor 192.168.1.2 update-source 10.0.0.1 + neighbor 192.168.1.3 remote-as auto + neighbor 192.168.1.3 bfd + neighbor 192.168.1.3 ebgp-multihop 20 + neighbor 192.168.1.3 update-source r1-eth0 +exit diff --git a/tests/topotests/bgp_bfd_session/test_bgp_bfd_session.py b/tests/topotests/bgp_bfd_session/test_bgp_bfd_session.py new file mode 100644 index 0000000000..adf557af7b --- /dev/null +++ b/tests/topotests/bgp_bfd_session/test_bgp_bfd_session.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2024 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, get_topogen, TopoRouter +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + for _, (rname, router) in enumerate(tgen.routers().items(), 1): + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)), + [ + (TopoRouter.RD_ZEBRA, None), + (TopoRouter.RD_MGMTD, None), + (TopoRouter.RD_BFD, None), + (TopoRouter.RD_BGP, None), + ], + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_bfd_session(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bfd_session(): + output = json.loads(r1.vtysh_cmd("show bfd peers json")) + expected = [ + { + "multihop": True, + "peer": "192.168.1.2", + "local": "10.0.0.1", + "vrf": "default", + "minimum-ttl": 246, + "status": "down", + "diagnostic": "ok", + "remote-diagnostic": "ok", + "type": "dynamic", + }, + { + "multihop": True, + "peer": "192.168.1.3", + "local": "10.0.0.1", + "vrf": "default", + "minimum-ttl": 236, + "status": "down", + "diagnostic": "ok", + "remote-diagnostic": "ok", + "type": "dynamic", + } + ] + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bfd_session) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see BFD session created" + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step1.json new file mode 100644 index 0000000000..27f1fe6e27 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step1.json @@ -0,0 +1,34 @@ +{ + "loc-rib": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step2.json new file mode 100644 index 0000000000..0aa6bd4fea --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-loc-rib-step2.json @@ -0,0 +1,43 @@ +{ + "loc-rib": { + "update": { + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "label": 105, + "nxhp_ip": "192:168::2", + "nxhp_rd1": "0:0", + "nxhp_rd2": "0:0", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "555:2", + "safi": 128 + }, + "172.31.0.15/32": { + "afi": 1, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "label": 102, + "nxhp_ip": "192.168.0.2", + "nxhp_rd": "0:0", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "444:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step1.json new file mode 100644 index 0000000000..d30ef9fd32 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step1.json @@ -0,0 +1,36 @@ +{ + "post-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step2.json new file mode 100644 index 0000000000..9bf2d57769 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-post-policy-step2.json @@ -0,0 +1,45 @@ +{ + "post-policy": { + "update": { + "172.31.0.15/32": { + "afi": 1, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 102, + "nxhp_ip": "192.168.0.2", + "nxhp_rd": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "444:2", + "safi": 128 + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 105, + "nxhp_ip": "192:168::2", + "nxhp_rd1": "0:0", + "nxhp_rd2": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "555:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step1.json new file mode 100644 index 0000000000..fafe6f7c21 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step1.json @@ -0,0 +1,36 @@ +{ + "pre-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step2.json new file mode 100644 index 0000000000..95acb5903a --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-update-pre-policy-step2.json @@ -0,0 +1,45 @@ +{ + "pre-policy": { + "update": { + "172.31.0.15/32": { + "afi": 1, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 102, + "nxhp_ip": "192.168.0.2", + "nxhp_rd": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "444:2", + "safi": 128 + }, + "2001::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 105, + "nxhp_ip": "192:168::2", + "nxhp_rd1": "0:0", + "nxhp_rd2": "0:0", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "555:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step1.json new file mode 100644 index 0000000000..3891fbb15e --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step1.json @@ -0,0 +1,28 @@ +{ + "loc-rib": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step2.json new file mode 100644 index 0000000000..1e5040ba60 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-loc-rib-step2.json @@ -0,0 +1,34 @@ +{ + "loc-rib": { + "withdraw": { + "172.31.0.15/32": { + "afi": 1, + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "label": 0, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "444:2", + "safi": 128 + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "is_filtered": false, + "label": 0, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_distinguisher": "0:0", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "rd": "555:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step1.json new file mode 100644 index 0000000000..3224b525e2 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step1.json @@ -0,0 +1,30 @@ +{ + "post-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step2.json new file mode 100644 index 0000000000..9eb221d4d0 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-post-policy-step2.json @@ -0,0 +1,36 @@ +{ + "post-policy": { + "withdraw": { + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "555:2", + "safi": 128 + }, + "172.31.0.15/32": { + "afi": 1, + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy", + "rd": "444:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step1.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step1.json new file mode 100644 index 0000000000..8add68c022 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step1.json @@ -0,0 +1,30 @@ +{ + "pre-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step2.json b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step2.json new file mode 100644 index 0000000000..eea7501b22 --- /dev/null +++ b/tests/topotests/bgp_bmp/bmp1/bmp-withdraw-pre-policy-step2.json @@ -0,0 +1,36 @@ +{ + "pre-policy": { + "withdraw": { + "2001::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2001::1111/128", + "ipv6": true, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "555:2", + "safi": 128 + }, + "172.31.0.15/32": { + "afi": 1, + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "label": 0, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy", + "rd": "444:2", + "safi": 128 + } + } + } +} diff --git a/tests/topotests/bgp_bmp/r1/bgpd.conf b/tests/topotests/bgp_bmp/r1/bgpd.conf index 24505de4a8..485c217096 100644 --- a/tests/topotests/bgp_bmp/r1/bgpd.conf +++ b/tests/topotests/bgp_bmp/r1/bgpd.conf @@ -7,6 +7,18 @@ router bgp 65501 ! bmp targets bmp1 bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000 + bmp monitor ipv4 unicast pre-policy + bmp monitor ipv6 unicast pre-policy + bmp monitor ipv4 vpn pre-policy + bmp monitor ipv6 vpn pre-policy + bmp monitor ipv4 unicast post-policy + bmp monitor ipv6 unicast post-policy + bmp monitor ipv4 vpn post-policy + bmp monitor ipv6 vpn post-policy + bmp monitor ipv4 unicast loc-rib + bmp monitor ipv6 unicast loc-rib + bmp monitor ipv4 vpn loc-rib + bmp monitor ipv6 vpn loc-rib exit ! address-family ipv4 vpn @@ -28,7 +40,7 @@ router bgp 65501 neighbor 192:168::2 soft-reconfiguration inbound exit-address-family ! -router bgp 65502 vrf vrf1 +router bgp 65501 vrf vrf1 bgp router_id 192.168.0.1 bgp log-neighbor-changes address-family ipv4 unicast diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step1.json new file mode 100644 index 0000000000..038c87ca9d --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step1.json @@ -0,0 +1,21 @@ +{ + "routes": { + "172.31.0.15/32": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step2.json new file mode 100644 index 0000000000..275f7f30e9 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-update-step2.json @@ -0,0 +1,25 @@ +{ + "routes": { + "routeDistinguishers": { + "444:2": { + "172.31.0.15/32": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step1.json new file mode 100644 index 0000000000..22a6c605c1 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "2001::1111/128": null + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step2.json new file mode 100644 index 0000000000..0f930ee71f --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv4-withdraw-step2.json @@ -0,0 +1,10 @@ +{ + "routes": { + "routeDistinguishers": { + "444:2": { + "2001::1111/128": null + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step1.json new file mode 100644 index 0000000000..ed5cea68ce --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step1.json @@ -0,0 +1,27 @@ +{ + "routes": { + "2001::1111/128": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192:168::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step2.json new file mode 100644 index 0000000000..c4c0c16284 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-update-step2.json @@ -0,0 +1,25 @@ +{ + "routes": { + "routeDistinguishers": { + "555:2": { + "2001::1111/128": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192:168::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step1.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step1.json new file mode 100644 index 0000000000..22a6c605c1 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "2001::1111/128": null + } +} + diff --git a/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step2.json b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step2.json new file mode 100644 index 0000000000..7b652afca6 --- /dev/null +++ b/tests/topotests/bgp_bmp/r1/show-bgp-ipv6-withdraw-step2.json @@ -0,0 +1,10 @@ +{ + "routes": { + "routeDistinguishers": { + "555:2": { + "2001::1111/128": null + } + } + } +} + diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py index 1cf6a0e7f8..658ad2b99a 100644 --- a/tests/topotests/bgp_bmp/test_bgp_bmp.py +++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py @@ -50,6 +50,9 @@ PRE_POLICY = "pre-policy" POST_POLICY = "post-policy" LOC_RIB = "loc-rib" +UPDATE_EXPECTED_JSON = False +DEBUG_PCAP = False + def build_topo(tgen): tgen.add_router("r1") @@ -67,6 +70,12 @@ def setup_module(mod): tgen = Topogen(build_topo, mod.__name__) tgen.start_topology() + if DEBUG_PCAP: + tgen.gears["r1"].run("rm /tmp/bmp.pcap") + tgen.gears["r1"].run( + "tcpdump -nni r1-eth0 -s 0 -w /tmp/bmp.pcap &", stdout=None + ) + for rname, router in tgen.routers().items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) @@ -122,42 +131,181 @@ def get_bmp_messages(): return messages -def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None): +def update_seq(): + global SEQ + + messages = get_bmp_messages() + + if len(messages): + SEQ = messages[-1]["seq"] + + +def update_expected_files(bmp_actual, expected_prefixes, bmp_log_type, policy, step): + tgen = get_topogen() + + with open(f"/tmp/bmp-{bmp_log_type}-{policy}-step{step}.json", "w") as json_file: + json.dump(bmp_actual, json_file, indent=4) + + if step == 2: # vpn + rd = "444:2" + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv4 vpn json", isjson=True) + filtered_out = { + "routes": { + "routeDistinguishers": { + rd: { + prefix: route_info + for prefix, route_info in out["routes"] + .get("routeDistinguishers", {}) + .get(rd, {}) + .items() + if prefix in expected_prefixes + } + } + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" in pfx: + continue + filtered_out["routes"]["routeDistinguishers"][rd][pfx] = None + + # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done + with open( + f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w" + ) as json_file: + json.dump(filtered_out, json_file, indent=4) + + rd = "555:2" + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv6 vpn json", isjson=True) + filtered_out = { + "routes": { + "routeDistinguishers": { + rd: { + prefix: route_info + for prefix, route_info in out["routes"] + .get("routeDistinguishers", {}) + .get(rd, {}) + .items() + if prefix in expected_prefixes + } + } + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" not in pfx: + continue + filtered_out["routes"]["routeDistinguishers"][rd][pfx] = None + with open( + f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w" + ) as json_file: + json.dump(filtered_out, json_file, indent=4) + + return + + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv4 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" in pfx: + continue + filtered_out["routes"][pfx] = None + + # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done + with open(f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + out = tgen.gears["r1"].vtysh_cmd("show bgp ipv6 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" not in pfx: + continue + filtered_out["routes"][pfx] = None + with open(f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + +def check_for_prefixes(expected_prefixes, bmp_log_type, policy, step): """ Check for the presence of the given prefixes in the BMP server logs with the given message type and the set policy. + """ global SEQ + # we care only about the new messages messages = [ m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ ] - # get the list of pairs (prefix, policy, seq) for the given message type - prefixes = [ - m["ip_prefix"] - for m in messages - if "ip_prefix" in m.keys() - and "bmp_log_type" in m.keys() - and m["bmp_log_type"] == bmp_log_type - and m["policy"] == policy - and ( - labels is None - or ( - m["ip_prefix"] in labels.keys() and m["label"] == labels[m["ip_prefix"]] - ) - ) - ] - - # check for prefixes - for ep in expected_prefixes: - if ep not in prefixes: - msg = "The prefix {} is not present in the {} log messages." - logger.debug(msg.format(ep, bmp_log_type)) - return False - - SEQ = messages[-1]["seq"] - return True + # create empty initial files + # for step in $(seq 2); do + # for i in "update" "withdraw"; do + # for j in "pre-policy" "post-policy" "loc-rib"; do + # echo '{"null": {}}'> bmp-$i-$j-step$step.json + # done + # done + # done + + ref_file = f"{CWD}/bmp1/bmp-{bmp_log_type}-{policy}-step{step}.json" + expected = json.loads(open(ref_file).read()) + + # Build actual json from logs + actual = {} + for m in messages: + if ( + "bmp_log_type" in m.keys() + and "ip_prefix" in m.keys() + and m["ip_prefix"] in expected_prefixes + and m["bmp_log_type"] == bmp_log_type + and m["policy"] == policy + ): + policy_dict = actual.setdefault(m["policy"], {}) + bmp_log_type_dict = policy_dict.setdefault(m["bmp_log_type"], {}) + + # Add or update the ip_prefix dictionary with filtered key-value pairs + bmp_log_type_dict[m["ip_prefix"]] = { + k: v + for k, v in sorted(m.items()) + # filter out variable keys + if k not in ["timestamp", "seq", "nxhp_link-local"] + and ( + # When policy is loc-rib, the peer-distinguisher is 0:0 + # for the default VRF or the RD if any or the 0:<vrf_id>. + # 0:<vrf_id> is used to distinguished. RFC7854 says: "If the + # peer is a "Local Instance Peer", it is set to a unique, + # locally defined value." The value is not tested because it + # is variable. + k != "peer_distinguisher" + or policy != LOC_RIB + or v == "0:0" + or not v.startswith("0:") + ) + } + + # build expected JSON files + if ( + UPDATE_EXPECTED_JSON + and actual + and set(actual.get(policy, {}).get(bmp_log_type, {}).keys()) + == set(expected_prefixes) + ): + update_expected_files(actual, expected_prefixes, bmp_log_type, policy, step) + + return topotest.json_cmp(actual, expected, exact=True) def check_for_peer_message(expected_peers, bmp_log_type): @@ -188,22 +336,6 @@ def check_for_peer_message(expected_peers, bmp_log_type): return True -def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None): - """ - Configure the bmp policy. - """ - vrf = " vrf {}" if vrf else "" - cmd = [ - "con t\n", - "router bgp {}{}\n".format(asn, vrf), - "bmp targets {}\n".format(target), - "bmp monitor ipv4 {} {}\n".format(safi, policy), - "bmp monitor ipv6 {} {}\n".format(safi, policy), - "end\n", - ] - tgen.gears[node].vtysh_cmd("".join(cmd)) - - def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): """ Configure the bgp prefixes. @@ -223,67 +355,49 @@ def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): tgen.gears[node].vtysh_cmd("".join(cmd)) -def unicast_prefixes(policy): +def _test_prefixes(policy, vrf=None, step=0): """ Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. Check if the previous actions are logged in the BMP server with the right message type and the right policy. """ tgen = get_topogen() - set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy) - prefixes = ["172.31.0.15/32", "2111::1111/128"] - # add prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes) + safi = "vpn" if vrf else "unicast" - logger.info("checking for updated prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "update", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the updated prefixes has been failed !." - - # withdraw prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) - logger.info("checking for withdrawed prefxies") - # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the withdrawed prefixes has been failed !." + prefixes = ["172.31.0.15/32", "2001::1111/128"] + for type in ("update", "withdraw"): + update_seq() -def vpn_prefixes(policy): - """ - Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. - Check if the previous actions are logged in the BMP server with the right - message type and the right policy. - """ - tgen = get_topogen() - set_bmp_policy(tgen, "r1", 65501, "bmp1", "vpn", policy) - - prefixes = ["172.31.10.1/32", "2001::2222/128"] + configure_prefixes( + tgen, "r2", 65502, "unicast", prefixes, vrf=vrf, update=(type == "update") + ) - # "label vpn export" value in r2/bgpd.conf - labels = { - "172.31.10.1/32": 102, - "2001::2222/128": 105, - } + logger.info(f"checking for prefixes {type}") - # add prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, vrf="vrf1") + for ipver in [4, 6]: + if UPDATE_EXPECTED_JSON: + continue + ref_file = "{}/r1/show-bgp-ipv{}-{}-step{}.json".format( + CWD, ipver, type, step + ) + expected = json.loads(open(ref_file).read()) - logger.info("checking for updated prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "update", policy, labels=labels) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the updated prefixes has been failed !." + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r1"], + f"show bgp ipv{ipver} {safi} json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = f"r1: BGP IPv{ipver} convergence failed" + assert res is None, assertmsg - # withdraw prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, vrf="vrf1", update=False) - logger.info("checking for withdrawed prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the withdrawed prefixes has been failed !." + # check + test_func = partial(check_for_prefixes, prefixes, type, policy, step) + success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert success, "Checking the updated prefixes has failed ! %s" % res def test_bmp_server_logging(): @@ -300,7 +414,7 @@ def test_bmp_server_logging(): return False return True - success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5) + success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) assert success, "The BMP server is not logging" @@ -314,7 +428,7 @@ def test_peer_up(): logger.info("checking for BMP peers up messages") test_func = partial(check_for_peer_message, peers, "peer up") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !." @@ -323,21 +437,21 @@ def test_bmp_bgp_unicast(): Add/withdraw bgp unicast prefixes and check the bmp logs. """ logger.info("*** Unicast prefixes pre-policy logging ***") - unicast_prefixes(PRE_POLICY) + _test_prefixes(PRE_POLICY, step=1) logger.info("*** Unicast prefixes post-policy logging ***") - unicast_prefixes(POST_POLICY) + _test_prefixes(POST_POLICY, step=1) logger.info("*** Unicast prefixes loc-rib logging ***") - unicast_prefixes(LOC_RIB) + _test_prefixes(LOC_RIB, step=1) def test_bmp_bgp_vpn(): # check for the prefixes in the BMP server logging file logger.info("***** VPN prefixes pre-policy logging *****") - vpn_prefixes(PRE_POLICY) + _test_prefixes(PRE_POLICY, vrf="vrf1", step=2) logger.info("***** VPN prefixes post-policy logging *****") - vpn_prefixes(POST_POLICY) + _test_prefixes(POST_POLICY, vrf="vrf1", step=2) logger.info("***** VPN prefixes loc-rib logging *****") - vpn_prefixes(LOC_RIB) + _test_prefixes(LOC_RIB, vrf="vrf1", step=2) def test_peer_down(): @@ -353,7 +467,7 @@ def test_peer_down(): logger.info("checking for BMP peers down messages") test_func = partial(check_for_peer_message, peers, "peer down") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !." diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json new file mode 100644 index 0000000000..ba31bf1d5d --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-loc-rib-step1.json @@ -0,0 +1,32 @@ +{ + "loc-rib": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2111::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2111::1111/128", + "is_filtered": false, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json new file mode 100644 index 0000000000..d5d9d65182 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-post-policy-step1.json @@ -0,0 +1,36 @@ +{ + "post-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2111::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json new file mode 100644 index 0000000000..e11badc040 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-update-pre-policy-step1.json @@ -0,0 +1,36 @@ +{ + "pre-policy": { + "update": { + "172.31.0.15/32": { + "as_path": "65501 65502", + "bgp_nexthop": "192.168.0.2", + "bmp_log_type": "update", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2111::1111/128": { + "afi": 2, + "as_path": "65501 65502", + "bmp_log_type": "update", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "nxhp_ip": "192:168::2", + "origin": "IGP", + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json new file mode 100644 index 0000000000..37ddc09ff8 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-loc-rib-step1.json @@ -0,0 +1,26 @@ +{ + "loc-rib": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib" + }, + "2111::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2111::1111/128", + "is_filtered": false, + "peer_asn": 65501, + "peer_bgp_id": "192.168.0.1", + "peer_type": "loc-rib instance", + "policy": "loc-rib", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json new file mode 100644 index 0000000000..de84307a4e --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-post-policy-step1.json @@ -0,0 +1,30 @@ +{ + "post-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "post-policy" + }, + "2111::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "post-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json new file mode 100644 index 0000000000..1c34498b7a --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/bmp1/bmp-withdraw-pre-policy-step1.json @@ -0,0 +1,30 @@ +{ + "pre-policy": { + "withdraw": { + "172.31.0.15/32": { + "bmp_log_type": "withdraw", + "ip_prefix": "172.31.0.15/32", + "ipv6": false, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192.168.0.2", + "peer_type": "global instance", + "policy": "pre-policy" + }, + "2111::1111/128": { + "afi": 2, + "bmp_log_type": "withdraw", + "ip_prefix": "2111::1111/128", + "ipv6": true, + "peer_asn": 65502, + "peer_bgp_id": "192.168.0.2", + "peer_distinguisher": "0:0", + "peer_ip": "192:168::2", + "peer_type": "global instance", + "policy": "pre-policy", + "safi": 1 + } + } + } +} diff --git a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf b/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf index 994cdbf68e..961e20498b 100644 --- a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf @@ -7,6 +7,12 @@ router bgp 65501 vrf vrf1 ! bmp targets bmp1 bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000 + bmp monitor ipv4 unicast pre-policy + bmp monitor ipv6 unicast pre-policy + bmp monitor ipv4 unicast post-policy + bmp monitor ipv6 unicast post-policy + bmp monitor ipv4 unicast loc-rib + bmp monitor ipv6 unicast loc-rib exit ! diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json new file mode 100644 index 0000000000..038c87ca9d --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-update-step1.json @@ -0,0 +1,21 @@ +{ + "routes": { + "172.31.0.15/32": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192.168.0.2", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json new file mode 100644 index 0000000000..6a77813776 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv4-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "172.31.0.15/32": null + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json new file mode 100644 index 0000000000..db34220149 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-update-step1.json @@ -0,0 +1,27 @@ +{ + "routes": { + "2111::1111/128": [ + { + "bestpath": true, + "pathFrom": "external", + "path": "65502", + "origin": "IGP", + "nexthops": [ + { + "ip": "192:168::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json new file mode 100644 index 0000000000..93f4a75e8c --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/show-bgp-ipv6-withdraw-step1.json @@ -0,0 +1,6 @@ +{ + "routes": { + "2111::1111/128": null + } +} + diff --git a/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py b/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py index b683920d2e..d31328bdb6 100644 --- a/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py +++ b/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py @@ -51,6 +51,9 @@ PRE_POLICY = "pre-policy" POST_POLICY = "post-policy" LOC_RIB = "loc-rib" +UPDATE_EXPECTED_JSON = False +DEBUG_PCAP = False + def build_topo(tgen): tgen.add_router("r1") @@ -76,6 +79,12 @@ ip link set r1-eth1 master vrf1 """ ) + if DEBUG_PCAP: + tgen.gears["r1"].run("rm /tmp/bmp_vrf.pcap") + tgen.gears["r1"].run( + "tcpdump -nni r1-eth0 -s 0 -w /tmp/bmp_vrf.pcap &", stdout=None + ) + for rname, router in tgen.routers().items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) @@ -131,42 +140,125 @@ def get_bmp_messages(): return messages -def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None): +def update_seq(): + global SEQ + + messages = get_bmp_messages() + + if len(messages): + SEQ = messages[-1]["seq"] + + +def update_expected_files(bmp_actual, expected_prefixes, bmp_log_type, policy, step): + tgen = get_topogen() + + with open(f"/tmp/bmp-{bmp_log_type}-{policy}-step{step}.json", "w") as json_file: + json.dump(bmp_actual, json_file, indent=4) + + out = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv4 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" in pfx: + continue + filtered_out["routes"][pfx] = None + + # ls /tmp/show*json | while read file; do egrep -v 'prefix|network|metric|ocPrf|version|weight|peerId|vrf|Version|valid|Reason|fe80' $file >$(basename $file); echo >> $(basename $file); done + with open(f"/tmp/show-bgp-ipv4-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + out = tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 ipv6 json", isjson=True) + filtered_out = { + "routes": { + prefix: route_info + for prefix, route_info in out["routes"].items() + if prefix in expected_prefixes + } + } + if bmp_log_type == "withdraw": + for pfx in expected_prefixes: + if "::" not in pfx: + continue + filtered_out["routes"][pfx] = None + + with open(f"/tmp/show-bgp-ipv6-{bmp_log_type}-step{step}.json", "w") as json_file: + json.dump(filtered_out, json_file, indent=4) + + +def check_for_prefixes(expected_prefixes, bmp_log_type, policy, step): """ Check for the presence of the given prefixes in the BMP server logs with the given message type and the set policy. + """ global SEQ + # we care only about the new messages messages = [ m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ ] - # get the list of pairs (prefix, policy, seq) for the given message type - prefixes = [ - m["ip_prefix"] - for m in messages - if "ip_prefix" in m.keys() - and "bmp_log_type" in m.keys() - and m["bmp_log_type"] == bmp_log_type - and m["policy"] == policy - and ( - labels is None - or ( - m["ip_prefix"] in labels.keys() and m["label"] == labels[m["ip_prefix"]] - ) - ) - ] - - # check for prefixes - for ep in expected_prefixes: - if ep not in prefixes: - msg = "The prefix {} is not present in the {} log messages." - logger.debug(msg.format(ep, bmp_log_type)) - return False - - SEQ = messages[-1]["seq"] - return True + # create empty initial files + # for step in $(seq 1); do + # for i in "update" "withdraw"; do + # for j in "pre-policy" "post-policy" "loc-rib"; do + # echo '{"null": {}}'> bmp-$i-$j-step$step.json + # done + # done + # done + + ref_file = f"{CWD}/bmp1/bmp-{bmp_log_type}-{policy}-step{step}.json" + expected = json.loads(open(ref_file).read()) + + # Build actual json from logs + actual = {} + for m in messages: + if ( + "bmp_log_type" in m.keys() + and "ip_prefix" in m.keys() + and m["ip_prefix"] in expected_prefixes + and m["bmp_log_type"] == bmp_log_type + and m["policy"] == policy + ): + policy_dict = actual.setdefault(m["policy"], {}) + bmp_log_type_dict = policy_dict.setdefault(m["bmp_log_type"], {}) + + # Add or update the ip_prefix dictionary with filtered key-value pairs + bmp_log_type_dict[m["ip_prefix"]] = { + k: v + for k, v in sorted(m.items()) + # filter out variable keys + if k not in ["timestamp", "seq", "nxhp_link-local"] + and ( + # When policy is loc-rib, the peer-distinguisher is 0:0 + # for the default VRF or the RD if any or the 0:<vrf_id>. + # 0:<vrf_id> is used to distinguished. RFC7854 says: "If the + # peer is a "Local Instance Peer", it is set to a unique, + # locally defined value." The value is not tested because it + # is variable. + k != "peer_distinguisher" + or policy != LOC_RIB + or v == "0:0" + or not v.startswith("0:") + ) + } + + # build expected JSON files + if ( + UPDATE_EXPECTED_JSON + and actual + and set(actual.get(policy, {}).get(bmp_log_type, {}).keys()) + == set(expected_prefixes) + ): + update_expected_files(actual, expected_prefixes, bmp_log_type, policy, step) + + return topotest.json_cmp(actual, expected, exact=True) def check_for_peer_message(expected_peers, bmp_log_type): @@ -197,22 +289,6 @@ def check_for_peer_message(expected_peers, bmp_log_type): return True -def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None): - """ - Configure the bmp policy. - """ - vrf = " vrf {}".format(vrf) if vrf else "" - cmd = [ - "con t\n", - "router bgp {}{}\n".format(asn, vrf), - "bmp targets {}\n".format(target), - "bmp monitor ipv4 {} {}\n".format(safi, policy), - "bmp monitor ipv6 {} {}\n".format(safi, policy), - "end\n", - ] - tgen.gears[node].vtysh_cmd("".join(cmd)) - - def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): """ Configure the bgp prefixes. @@ -232,32 +308,48 @@ def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): tgen.gears[node].vtysh_cmd("".join(cmd)) -def unicast_prefixes(policy): +def _test_prefixes(policy, step=1): """ Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. Check if the previous actions are logged in the BMP server with the right message type and the right policy. """ tgen = get_topogen() - set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy, vrf="vrf1") prefixes = ["172.31.0.15/32", "2111::1111/128"] - # add prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes) - logger.info("checking for updated prefixes") - # check - test_func = partial(check_for_prefixes, prefixes, "update", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the updated prefixes has been failed !." + for type in ("update", "withdraw"): + update_seq() + + # add prefixes + configure_prefixes( + tgen, "r2", 65502, "unicast", prefixes, update=(type == "update") + ) + + logger.info(f"checking for prefixes {type}") + + for ipver in [4, 6]: + if UPDATE_EXPECTED_JSON: + continue + ref_file = "{}/r1/show-bgp-ipv{}-{}-step{}.json".format( + CWD, ipver, type, step + ) + expected = json.loads(open(ref_file).read()) + + test_func = partial( + topotest.router_json_cmp, + tgen.gears["r1"], + f"show bgp vrf vrf1 ipv{ipver} json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = f"r1: BGP IPv{ipver} convergence failed" + assert res is None, assertmsg - # withdraw prefixes - configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) - logger.info("checking for withdrawed prefxies") - # check - test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) - assert success, "Checking the withdrawed prefixes has been failed !." + # check + test_func = partial(check_for_prefixes, prefixes, type, policy, step) + success, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert success, "Checking the updated prefixes has been failed ! %s" % res def test_bmp_server_logging(): @@ -274,7 +366,7 @@ def test_bmp_server_logging(): return False return True - success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5) + success, _ = topotest.run_and_expect(check_for_log_file, True, count=30, wait=1) assert success, "The BMP server is not logging" @@ -288,7 +380,7 @@ def test_peer_up(): logger.info("checking for BMP peers up messages") test_func = partial(check_for_peer_message, peers, "peer up") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !." @@ -297,11 +389,11 @@ def test_bmp_bgp_unicast(): Add/withdraw bgp unicast prefixes and check the bmp logs. """ logger.info("*** Unicast prefixes pre-policy logging ***") - unicast_prefixes(PRE_POLICY) + _test_prefixes(PRE_POLICY) logger.info("*** Unicast prefixes post-policy logging ***") - unicast_prefixes(POST_POLICY) + _test_prefixes(POST_POLICY) logger.info("*** Unicast prefixes loc-rib logging ***") - unicast_prefixes(LOC_RIB) + _test_prefixes(LOC_RIB) def test_peer_down(): @@ -317,7 +409,7 @@ def test_peer_down(): logger.info("checking for BMP peers down messages") test_func = partial(check_for_peer_message, peers, "peer down") - success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1) assert success, "Checking the updated prefixes has been failed !." diff --git a/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf index d4ca392b1a..30cb6647ae 100644 --- a/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf +++ b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf @@ -11,7 +11,7 @@ router bgp 65001 exit-address-family ! route-map rmap permit 10 - set extcommunity color 1 + set extcommunity color 01:1 set extcommunity rt 80:987 - set extcommunity color 100 55555 200 + set extcommunity color 01:100 01:55555 01:200 exit diff --git a/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py index 09091198a8..94a444d5c5 100644 --- a/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py +++ b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py @@ -105,7 +105,7 @@ def test_bgp_color_extended_communities(): { "valid": True, "extendedCommunity": { - "string": "RT:80:987 Color:100 Color:200 Color:55555" + "string": "RT:80:987 Color:01:100 Color:01:200 Color:01:55555" }, } ], diff --git a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py index 7116deaea4..f7b69f21c1 100644 --- a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py +++ b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py @@ -22,7 +22,7 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest -from lib.topogen import Topogen, get_topogen +from lib.topogen import Topogen, TopoRouter, get_topogen pytestmark = [pytest.mark.bgpd] @@ -47,7 +47,14 @@ def setup_module(mod): router_list = tgen.routers() for _, (rname, router) in enumerate(router_list.items(), 1): - router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)), + [ + (TopoRouter.RD_ZEBRA, None), + (TopoRouter.RD_SHARP, None), + (TopoRouter.RD_BGP, None), + ], + ) tgen.start_router() @@ -109,6 +116,68 @@ def test_bgp_route_map_match_source_protocol(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "Failed to filter routes by source-protocol for r3" + def _bgp_check_advertised_routes_r4(): + # Remove match source-protocol towards Nbr out policy for Nbr 192.168.1.2 so it receives all routes + tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + route-map r2 permit 10 + no match source-protocol + """ + ) + + tgen.gears["r1"].vtysh_cmd( + "sharp install route 192.168.11.0 nexthop 172.16.255.1 5" + ) + + # Configure a r4 with source protocol sharp and apply it to all redistribute cmds + tgen.gears["r1"].vtysh_cmd( + """ + configure terminal + route-map r4 permit 10 + match source-protocol sharp + router bgp 65001 + address-family ipv4 unicast + redistribute connected route-map r4 + redistribute static route-map r4 + redistribute sharp route-map r4 + """ + ) + + # Since match protocol is sharp, only sharp protocol routes are to be advertised + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + + expected = { + "advertisedRoutes": { + "192.168.11.0/32": { + "valid": True, + }, + "192.168.11.1/32": { + "valid": True, + }, + "192.168.11.2/32": { + "valid": True, + }, + "192.168.11.3/32": { + "valid": True, + }, + "192.168.11.4/32": { + "valid": True, + }, + }, + "totalPrefixCounter": 5, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r4) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to match the source-protocol for redistribute cmds" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_tcp_mss/r1/bgpd.conf b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf index 07cfe2e2f1..12e5df28ee 100644 --- a/tests/topotests/bgp_tcp_mss/r1/bgpd.conf +++ b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf @@ -1,5 +1,8 @@ router bgp 65000 no bgp ebgp-requires-policy + neighbor aaa peer-group + neighbor aaa remote-as 65001 + neighbor 192.168.254.2 peer-group aaa neighbor 192.168.255.2 remote-as 65001 neighbor 192.168.255.2 timers 3 10 exit-address-family diff --git a/tests/topotests/bgp_tcp_mss/r1/zebra.conf b/tests/topotests/bgp_tcp_mss/r1/zebra.conf index 6e9b0b4a7e..57958c4420 100644 --- a/tests/topotests/bgp_tcp_mss/r1/zebra.conf +++ b/tests/topotests/bgp_tcp_mss/r1/zebra.conf @@ -2,5 +2,8 @@ interface r1-eth0 ip address 192.168.255.1/24 ! +interface r1-eth1 + ip address 192.168.254.1/24 +! ip forwarding ! diff --git a/tests/topotests/bgp_tcp_mss/r2/bgpd.conf b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf index b2d945583c..8a5a4062a4 100644 --- a/tests/topotests/bgp_tcp_mss/r2/bgpd.conf +++ b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf @@ -1,5 +1,8 @@ router bgp 65001 no bgp ebgp-requires-policy + neighbor aaa peer-group + neighbor aaa remote-as 65000 + neighbor 192.168.254.1 peer-group aaa neighbor 192.168.255.1 remote-as 65000 neighbor 192.168.255.1 timers 3 10 exit-address-family diff --git a/tests/topotests/bgp_tcp_mss/r2/zebra.conf b/tests/topotests/bgp_tcp_mss/r2/zebra.conf index 6c14de583b..f2daa523ac 100644 --- a/tests/topotests/bgp_tcp_mss/r2/zebra.conf +++ b/tests/topotests/bgp_tcp_mss/r2/zebra.conf @@ -2,5 +2,8 @@ interface r2-eth0 ip address 192.168.255.2/24 ! +interface r2-eth1 + ip address 192.168.254.2/24 +! ip forwarding ! diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py index 37949cdc99..30449a3920 100644 --- a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py +++ b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py @@ -45,6 +45,10 @@ def build_topo(tgen): switch.add_link(tgen.gears["r1"]) switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + def setup_module(mod): tgen = Topogen(build_topo, mod.__name__) @@ -78,12 +82,16 @@ def test_bgp_tcp_mss(): router2 = tgen.gears["r2"] def _bgp_converge(router): - output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + output = json.loads(router.vtysh_cmd("show ip bgp neighbor json")) expected = { "192.168.255.2": { "bgpState": "Established", "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 0}}, - } + }, + "192.168.254.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 0}}, + }, } return topotest.json_cmp(output, expected) @@ -108,7 +116,7 @@ def test_bgp_tcp_mss(): logger.info("Check if neighbor sessions are up in {}".format(router1.name)) test_func = functools.partial(_bgp_converge, router1) - _, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=15, wait=1) assert result is None, 'Failed to see BGP convergence in "{}"'.format(router1.name) logger.info("BGP neighbor session is up in {}".format(router1.name)) @@ -117,19 +125,21 @@ def test_bgp_tcp_mss(): "Configure tcp-mss 500 on {} and reset the session".format(router1.name) ) _bgp_conf_tcp_mss(router1, "65000", "192.168.255.2") + _bgp_conf_tcp_mss(router1, "65000", "aaa") _bgp_clear_session(router1) logger.info( "Configure tcp-mss 500 on {} and reset the session".format(router2.name) ) _bgp_conf_tcp_mss(router2, "65001", "192.168.255.1") + _bgp_conf_tcp_mss(router2, "65001", "aaa") _bgp_clear_session(router2) logger.info( "Check if neighbor session is up after reset in {}".format(router1.name) ) test_func = functools.partial(_bgp_converge, router1) - _, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=15, wait=1) assert result is None, 'Failed to see BGP convergence after reset in "{}"'.format( router1.name ) @@ -138,7 +148,13 @@ def test_bgp_tcp_mss(): "Verify if TCP MSS value is synced with neighbor in {}".format(router1.name) ) test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router1, "192.168.255.2") - _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=15, wait=1) + assert ( + result is None + ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router1.name) + + test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router1, "192.168.254.2") + success, result = topotest.run_and_expect(test_func, None, count=15, wait=1) assert ( result is None ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router1.name) @@ -148,7 +164,13 @@ def test_bgp_tcp_mss(): "Verify if TCP MSS value is synced with neighbor in {}".format(router2.name) ) test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router2, "192.168.255.1") - _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=15, wait=1) + assert ( + result is None + ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router2.name) + + test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router2, "192.168.254.1") + success, result = topotest.run_and_expect(test_func, None, count=15, wait=1) assert ( result is None ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router2.name) diff --git a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py index 5467cf4d84..26934b0a13 100644 --- a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py +++ b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py @@ -368,17 +368,17 @@ def test_protocols_convergence(): # check that r2 peerings are ok logger.info("Checking BGP ipv4 vpn summary for r2") - router = tgen.gears["r2"] - json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name) + r2 = tgen.gears["r2"] + json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, r2.name) expected = json.loads(open(json_file).read()) test_func = partial( topotest.router_json_cmp, - router, + r2, "show bgp ipv4 vpn summary json", expected, ) _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) - assertmsg = '"{}" JSON output mismatches'.format(router.name) + assertmsg = '"{}" JSON output mismatches'.format(r2.name) assert result is None, assertmsg @@ -400,11 +400,11 @@ def test_mpls_setup_ok(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] # diagnostic logger.info("Dumping mplsvpn nexthop table") - router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + r2.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail") vpnv4_checks = { "172.31.1.0/24": "r1", @@ -414,10 +414,10 @@ def test_mpls_setup_ok(): } logger.info( "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format( - router.name + r2.name ) ) - check_show_bgp_vpn_ok(router, vpnv4_checks) + check_show_bgp_vpn_ok(r2, vpnv4_checks) logger.info("h1, check that ping from h1 to (h2,h3) is ok") check_ping("h1", "172.31.1.10", True, 20, 0.5) @@ -436,71 +436,85 @@ def test_r3_prefixes_removed(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r3"] - logger.info("{}, keeping only 172.31.3.0/24 network".format(router.name)) - router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nshutdown\n") - router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nshutdown\n") + r3 = tgen.gears["r3"] + logger.info("{}, keeping only 172.31.3.0/24 network".format(r3.name)) + r3.vtysh_cmd( + """ +configure terminal +interface r3-eth1 vrf vrf1 + shutdown +! +interface r3-eth2 vrf vrf1 + shutdown\n +""" + ) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] logger.info( "{}, check that 'show bgp ipv4 vpn' has only 172.31.3.0/24 network from r3".format( - router.name + r2.name ) ) for prefix in ("172.31.1.0/24", "172.31.2.0/24"): test_func = functools.partial( check_show_bgp_vpn_prefix_not_found, - router, + r2, "ipv4", prefix, "444:3", ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) - assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + assert success, "{}, vpnv4 update {} still present".format(r2.name, prefix) # diagnostic logger.info("Dumping mplsvpn nexthop table") - router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + r2.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail") prefix = "172.31.3.0/24" logger.info( "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( - router.name + r2.name ) ) vpnv4_checks = { prefix: "r1", } - label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) - - router = tgen.gears["r3"] - logger.info("{}, removing {} network".format(router.name, prefix)) - router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nshutdown\n") + label_ip_entries = check_show_bgp_vpn_ok(r2, vpnv4_checks) + + r3 = tgen.gears["r3"] + logger.info("{}, removing {} network".format(r3.name, prefix)) + r3.vtysh_cmd( + """ +configure terminal +interface r3-eth3 vrf vrf1 + shutdown\n +""" + ) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] logger.info( "{}, check that 'show bgp ipv4 vpn' has not {} network from r3".format( - router.name, prefix + r2.name, prefix ) ) test_func = functools.partial( check_show_bgp_vpn_prefix_not_found, - router, + r2, "ipv4", prefix, "444:3", ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) - assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + assert success, "{}, vpnv4 update {} still present".format(r2.name, prefix) logger.info( "{}, check that 'show mpls table {}' is not present".format( - router.name, label_ip_entries[prefix] + r2.name, label_ip_entries[prefix] ) ) test_func = functools.partial( - check_show_mpls_table_entry_label_not_found, router, label_ip_entries[prefix] + check_show_mpls_table_entry_label_not_found, r2, label_ip_entries[prefix] ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "r1, mpls entry with in_label {} still present".format( @@ -517,59 +531,74 @@ def test_r3_prefixes_added_back(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r3"] + r3 = tgen.gears["r3"] prefix = "172.31.3.0/24" - logger.info("{}, restoring the {} network from r3".format(router.name, prefix)) - router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nno shutdown\n") + logger.info("{}, restoring the {} network from r3".format(r3.name, prefix)) + r3.vtysh_cmd( + """ +configure terminal + interface r3-eth3 vrf vrf1 + no shutdown +""" + ) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] logger.info( "{}, check that 'show bgp ipv4 vpn' has {} network from r3".format( - router.name, prefix + r2.name, prefix ) ) test_func = functools.partial( check_show_bgp_vpn_prefix_found, - router, + r2, "ipv4", prefix, "444:3", ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) - assert success, "{}, vpnv4 update {} not present".format(router.name, prefix) + assert success, "{}, vpnv4 update {} not present".format(r2.name, prefix) logger.info( "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( - router.name + r2.name ) ) vpnv4_checks = { prefix: "r1", } - check_show_bgp_vpn_ok(router, vpnv4_checks) + check_show_bgp_vpn_ok(r2, vpnv4_checks) - router = tgen.gears["r3"] + r3 = tgen.gears["r3"] logger.info( - "{}, restoring the redistribute connected prefixes from r3".format(router.name) + "{}, restoring the redistribute connected prefixes from r3".format(r3.name) + ) + r3.vtysh_cmd( + """ +configure terminal +interface r3-eth1 vrf vrf1 + no shutdown +! +interface r3-eth2 vrf vrf1 + no shutdown +""" ) - router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nno shutdown\n") - router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nno shutdown\n") - router = tgen.gears["r2"] + + r2 = tgen.gears["r2"] for prefix in ("172.31.1.0/24", "172.31.2.0/24"): test_func = functools.partial( check_show_bgp_vpn_prefix_found, - router, + r2, "ipv4", prefix, "444:3", ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) - assert success, "{}, vpnv4 update {} not present".format(router.name, prefix) + assert success, "{}, vpnv4 update {} not present".format(r2.name, prefix) # diagnostic logger.info("Dumping mplsvpn nexthop table") - tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + r2.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail") def test_unconfigure_nexthop_change_nexthop_self(): @@ -583,44 +612,47 @@ def test_unconfigure_nexthop_change_nexthop_self(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] vpnv4_checks = { "172.31.1.0/24": "r1", "172.31.2.0/24": "r1", "172.31.3.0/24": "r1", } logger.info( - "{}, Get the list of labels allocated for prefixes from r3".format(router.name) + "{}, Get the list of labels allocated for prefixes from r3".format(r2.name) ) - label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + label_ip_entries = check_show_bgp_vpn_ok(r2, vpnv4_checks) - logger.info( - "{}, disable next-hop-self for 192.0.2.100 neighbor".format(router.name) - ) - router = tgen.gears["r2"] - router.vtysh_cmd( - "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 next-hop-self\n" + logger.info("{}, disable next-hop-self for 192.0.2.100 neighbor".format(r2.name)) + + r2.vtysh_cmd( + """ +configure terminal +router bgp 65500 + address-family ipv4 vpn + no neighbor 192.0.2.100 next-hop-self +""" ) for prefix, label in label_ip_entries.items(): logger.info( "{}, check mpls entry for {} with in_label {} is not present'".format( - router.name, prefix, label + r2.name, prefix, label ) ) test_func = functools.partial( - check_show_mpls_table_entry_label_not_found, router, label + check_show_mpls_table_entry_label_not_found, r2, label ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "r1, mpls entry for {} with in_label {} still present".format( prefix, label ) - router = tgen.gears["r1"] + r1 = tgen.gears["r1"] for prefix, label in label_ip_entries.items(): test_func = functools.partial( check_show_bgp_vpn_prefix_not_found, - router, + r1, "ipv4", prefix, "444:3", @@ -628,12 +660,12 @@ def test_unconfigure_nexthop_change_nexthop_self(): ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "{}, mpls vpn update {} label {} is present".format( - router.name, prefix, label + r1.name, prefix, label ) for prefix, label in label_ip_entries.items(): test_func = functools.partial( check_show_bgp_vpn_prefix_found, - router, + r1, "ipv4", prefix, "444:3", @@ -641,12 +673,12 @@ def test_unconfigure_nexthop_change_nexthop_self(): ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "{}, mpls vpn update {} label {} is present".format( - router.name, prefix, label + r1.name, prefix, label ) # diagnostic logger.info("Dumping mplsvpn nexthop table") - tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail") def test_reconfigure_nexthop_change_nexthop_self(): @@ -660,10 +692,15 @@ def test_reconfigure_nexthop_change_nexthop_self(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] - logger.info("{}, enable next-hop-self for 192.0.2.100 neighbor".format(router.name)) - router.vtysh_cmd( - "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 next-hop-self\n" + r2 = tgen.gears["r2"] + logger.info("{}, enable next-hop-self for 192.0.2.100 neighbor".format(r2.name)) + r2.vtysh_cmd( + """ +configure terminal +router bgp 65500 + address-family ipv4 vpn + neighbor 192.0.2.100 next-hop-self +""" ) vpnv4_checks = { "172.31.1.0/24": "r1", @@ -672,17 +709,17 @@ def test_reconfigure_nexthop_change_nexthop_self(): } logger.info( "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( - router.name + r2.name ) ) - check_show_bgp_vpn_ok(router, vpnv4_checks) + check_show_bgp_vpn_ok(r2, vpnv4_checks) logger.info("h1, check that ping from h1 to (h2,h3) is ok") check_ping("h1", "172.31.1.10", True, 20, 0.5) check_ping("h1", "172.31.2.10", True, 20, 0.5) # diagnostic logger.info("Dumping mplsvpn nexthop table") - router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + r2.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail") def test_declare_vpn_network_with_different_label(): @@ -696,20 +733,21 @@ def test_declare_vpn_network_with_different_label(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r3"] + r3 = tgen.gears["r3"] logger.info( - "{}, declare static 33.33.33.33/32 network rd 33:33 label 33".format( - router.name - ) - ) - router.vtysh_cmd( - "configure terminal\nrouter bgp 65501\nno bgp network import-check\n" + "{}, declare static 33.33.33.33/32 network rd 33:33 label 33".format(r3.name) ) - router.vtysh_cmd( - "configure terminal\nrouter bgp 65501\naddress-family ipv4 vpn\nnetwork 33.33.33.33/32 rd 444:3 label 33\n" + r3.vtysh_cmd( + """ +configure terminal +router bgp 65501 + no bgp network import-check\n" + address-family ipv4 vpn + network 33.33.33.33/32 rd 444:3 label 33 +""" ) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] vpnv4_entries = { "172.31.1.0/24": None, "172.31.2.0/24": None, @@ -720,7 +758,7 @@ def test_declare_vpn_network_with_different_label(): for prefix, label in vpnv4_entries.items(): test_func = functools.partial( check_show_bgp_vpn_prefix_found, - router, + r2, "ipv4", prefix, "444:3", @@ -729,7 +767,7 @@ def test_declare_vpn_network_with_different_label(): ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "{}, vpnv4 update {}, label {} not present".format( - router.name, prefix, label + r2.name, prefix, label ) vpnv4_checks = { @@ -740,10 +778,10 @@ def test_declare_vpn_network_with_different_label(): } logger.info( "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( - router.name + r2.name ) ) - check_show_bgp_vpn_ok(router, vpnv4_checks) + check_show_bgp_vpn_ok(r2, vpnv4_checks) def test_filter_vpn_network_from_r1(): @@ -757,52 +795,51 @@ def test_filter_vpn_network_from_r1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] vpnv4_checks = { "172.31.0.0/24": "r3", } logger.info( "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r3".format( - router.name + r2.name ) ) - label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + label_ip_entries = check_show_bgp_vpn_ok(r2, vpnv4_checks) for prefix, label in label_ip_entries.items(): - logger.info("{}, filter prefix {} from r1".format(router.name, prefix)) - router.vtysh_cmd( - "configure terminal\nroute-map rmap deny 1\nmatch ip next-hop address 192.0.2.1\n" - ) - router.vtysh_cmd( - "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 route-map rmap in\n" - ) - logger.info( - "{}, check that prefix {} is not present".format(router.name, prefix) + logger.info("{}, filter prefix {} from r1".format(r2.name, prefix)) + r2.vtysh_cmd( + """ +configure terminal +route-map rmap deny 1 + match ip next-hop address 192.0.2.1 +! +router bgp 65500 + address-family ipv4 vpn + neighbor 192.0.2.100 route-map rmap in +""" ) + logger.info("{}, check that prefix {} is not present".format(r2.name, prefix)) test_func = functools.partial( check_show_bgp_vpn_prefix_not_found, - router, + r2, "ipv4", "172.31.0.0/24", "444:1", ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) - assert success, "{}, vpnv4 update {}, is still present".format( - router.name, prefix - ) + assert success, "{}, vpnv4 update {}, is still present".format(r2.name, prefix) # diagnostic logger.info("Dumping mplsvpn nexthop table") - router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + r2.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail") logger.info( - "{}, check that show mpls table {} is not present".format( - router.name, label - ) + "{}, check that show mpls table {} is not present".format(r2.name, label) ) test_func = functools.partial( - check_show_mpls_table_entry_label_not_found, router, int(label) + check_show_mpls_table_entry_label_not_found, r2, int(label) ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert success, "r1, mpls entry for {} with in_label {} still present".format( @@ -821,34 +858,34 @@ def test_unfilter_vpn_network_from_r1(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - router = tgen.gears["r2"] + r2 = tgen.gears["r2"] prefix = "172.31.0.0/24" - logger.info("{}, filter prefix {} from r1".format(router.name, prefix)) - router.vtysh_cmd( + logger.info("{}, filter prefix {} from r1".format(r2.name, prefix)) + r2.vtysh_cmd( "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 route-map rmap in\n" ) - logger.info("{}, check that prefix {} is present".format(router.name, prefix)) + logger.info("{}, check that prefix {} is present".format(r2.name, prefix)) test_func = functools.partial( - check_show_bgp_vpn_prefix_found, router, "ipv4", prefix, "444:1" + check_show_bgp_vpn_prefix_found, r2, "ipv4", prefix, "444:1" ) success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) - assert success, "{}, vpnv4 update {}, is not present".format(router.name, prefix) + assert success, "{}, vpnv4 update {}, is not present".format(r2.name, prefix) vpnv4_checks = { "172.31.0.0/24": "r3", } logger.info( "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format( - router.name + r2.name ) ) - check_show_bgp_vpn_ok(router, vpnv4_checks) + check_show_bgp_vpn_ok(r2, vpnv4_checks) # diagnostic logger.info("Dumping mplsvpn nexthop table") - router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + r2.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail") def test_memory_leak(): diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py index 146687d588..6cceb02bca 100644 --- a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py @@ -187,7 +187,7 @@ def bgp_vpnv4_table_check( test_func = functools.partial( check_bgp_vpnv4_prefix_presence, router, prefix, table_version ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "{}, prefix ipv4 vpn {} is not installed yet".format( router.name, prefix ) @@ -321,7 +321,7 @@ def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None): test_func = functools.partial( check_show_mpls_table, router, blacklist, label_list, whitelist ) - success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "{}, MPLS labels check fail: {}".format(router.name, result) @@ -347,10 +347,37 @@ def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): return topotest.json_cmp(output, expected) -def check_show_mpls_table_entry_label_found(router, inlabel, interface): - output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) +def check_show_mpls_table_entry_label_found_nexthop( + expected_router, get_router, network, nexthop +): + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) + expected = { + "inLabel": label, + "installed": True, + "nexthops": [{"nexthop": nexthop}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found( + expected_router, get_router, interface, network=None, label=None +): + if not label: + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) expected = { - "inLabel": inlabel, + "inLabel": label, "installed": True, "nexthops": [{"interface": interface}], } @@ -395,6 +422,17 @@ def mpls_entry_get_interface(router, label): return outgoing_interface +def get_mpls_label(router, network): + label = router.vtysh_cmd( + "show ip route vrf vrf1 %s json" % network, + isjson=True, + ) + label = label.get(f"{network}", [{}])[0].get("nexthops", [{}])[0] + label = int(label.get("labels", [-1])[0]) + + return label + + def test_protocols_convergence(): """ Assert that all protocols have converged @@ -415,7 +453,7 @@ def test_protocols_convergence(): "show bgp vrf vrf1 ipv4 json", expected, ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=3) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg @@ -429,7 +467,7 @@ def test_protocols_convergence(): "show bgp ipv4 vpn json", expected, ) - _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=3) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg @@ -471,7 +509,7 @@ def test_flapping_bgp_vrf_down(): test_func = functools.partial( _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv4", "172.31.0.11/32" ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert ( success ), "r1, prefix 172.31.0.11/32 from r11 did not disappear. r11 still connected to rr ?" @@ -509,7 +547,7 @@ def test_flapping_bgp_vrf_up(): "172.31.0.11/32", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert ( success ), "r2, prefix 172.31.0.11/32 from r11 not present. r11 still disconnected from rr ?" @@ -539,7 +577,7 @@ def test_recursive_route(): "172.31.0.30/32", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, vpnv4 update 172.31.0.30 not found" bgp_vpnv4_table_check(tgen.gears["r2"], group=PREFIXES_R11 + ["172.31.0.30/32"]) @@ -565,7 +603,7 @@ def test_recursive_route(): "172.31.0.30/32", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, vpnv4 update 172.31.0.30 still present" @@ -591,7 +629,7 @@ def test_prefix_changes_interface(): "172.31.0.50/32", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, vpnv4 update 172.31.0.50 not found" # diagnostic @@ -637,7 +675,7 @@ def test_prefix_changes_interface(): "444:1", label=oldlabel, ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert ( success ), "r2, vpnv4 update 172.31.0.50 with old label {0} still present".format(oldlabel) @@ -654,7 +692,7 @@ def test_prefix_changes_interface(): "172.31.0.50/32", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, vpnv4 update 172.31.0.50 not found" label_list = set() @@ -719,9 +757,13 @@ def test_changing_default_label_value(): "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" ) test_func = functools.partial( - check_show_mpls_table_entry_label_found, router, 222, "vrf1" + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + router, + "vrf1", + label=222, ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r1, mpls entry with label 222 not found" # check label repartition is ok @@ -769,7 +811,7 @@ def test_unconfigure_allocation_mode_nexthop(): test_func = functools.partial( check_show_mpls_table_entry_label_not_found, router, 17 ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r1, mpls entry with label 17 still present" # Check vpnv4 routes from r1 @@ -821,7 +863,7 @@ def test_reconfigure_allocation_mode_nexthop(): test_func = functools.partial( check_show_mpls_table_entry_label_not_found, router, 17 ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r1, mpls entry with label 17 still present" # Check vpnv4 routes from r1 @@ -837,6 +879,126 @@ def test_reconfigure_allocation_mode_nexthop(): mpls_table_check(router, label_list=label_list) +def test_network_command(): + """ + Test with network declaration + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Disabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno redistribute static\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + for p in ["172.31.0.24/32", "172.31.0.15/32"]: + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, tgen.gears["r2"], "ipv4", p, "444:1" + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network %s should not present on r2" % p + + logger.info("Use network command for host networks declared in static instead") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nnetwork 172.31.0.14/32\n", + isjson=False, + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nnetwork 172.31.0.15/32\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + bgp_vpnv4_table_check(tgen.gears["r2"], group=["172.31.0.12/32", "172.31.0.15/32"]) + bgp_vpnv4_table_check(tgen.gears["r2"], group=["172.31.0.14/32"]) + mpls_table_check(tgen.gears["r1"], whitelist=["192.0.2.14"]) + + logger.info(" Remove network to 172.31.0.14/32") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno network 172.31.0.14/32\n", + isjson=False, + ) + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.14/32", + "444:1", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network 172.31.0.14/32 should not present on r2" + mpls_table_check(tgen.gears["r1"], blacklist=["192.0.2.14"]) + + logger.info("Disabling redistribute connected and enabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "redistribute static\n no redistribute connected", + isjson=False, + ) + logger.info("Use network command for connect network 192.168.255.0/24 in vrf") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "network 192.168.255.0/24\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + bgp_vpnv4_table_check(tgen.gears["r2"], group=["192.168.255.0/24"]) + logger.info("Checking no mpls entry associated to 192.168.255.0/24") + mpls_table_check(tgen.gears["r1"], blacklist=["192.168.255.0"]) + logger.info("Checking 192.168.255.0/24 fallback to vrf") + test_func = functools.partial( + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + tgen.gears["r2"], + "vrf1", + network="192.168.255.0/24", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "192.168.255.0/24 does not fallback to vrf" + + logger.info( + "Use network command for statically routed network 192.168.3.0/24 in vrf" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\n" "ip route 192.168.3.0/24 192.0.2.11\n" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "network 192.168.3.0/24\n", + isjson=False, + ) + logger.info("Checking 192.168.3.0/24 route on r2") + test_func = functools.partial( + check_show_mpls_table_entry_label_found_nexthop, + tgen.gears["r1"], + tgen.gears["r2"], + "192.168.3.0/24", + "192.0.2.11", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "label from r2 is not present on r1 for 192.168.3.0/24" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping bgp network import-check-table") + tgen.gears["r1"].vtysh_cmd( + "show bgp vrf vrf1 import-check-table detail", isjson=False + ) + + logger.info("Restoring 172.31.0.14 prefix on r1") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nnetwork 172.31.0.14/32\n", + isjson=False, + ) + logger.info("Restoring redistribute connected") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "no redistribute static\n redistribute connected", + isjson=False, + ) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py index 5fef6e9e60..8c24656f5d 100644 --- a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py @@ -186,7 +186,7 @@ def bgp_vpnv6_table_check( test_func = functools.partial( check_bgp_vpnv6_prefix_presence, router, prefix, table_version ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "{}, prefix ipv6 vpn {} is not installed yet".format( router.name, prefix ) @@ -316,7 +316,7 @@ def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None): test_func = functools.partial( check_show_mpls_table, router, blacklist, label_list, whitelist ) - success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "{}, MPLS labels check fail: {}".format(router.name, result) @@ -342,10 +342,37 @@ def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): return topotest.json_cmp(output, expected) -def check_show_mpls_table_entry_label_found(router, inlabel, interface): - output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) +def check_show_mpls_table_entry_label_found_nexthop( + expected_router, get_router, network, nexthop +): + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) expected = { - "inLabel": inlabel, + "inLabel": label, + "installed": True, + "nexthops": [{"nexthop": nexthop}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found( + expected_router, get_router, interface, network=None, label=None +): + if not label: + label = get_mpls_label(get_router, network) + if label < 0: + return False + + output = json.loads( + expected_router.vtysh_cmd("show mpls table {} json".format(label)) + ) + expected = { + "inLabel": label, "installed": True, "nexthops": [{"interface": interface}], } @@ -366,6 +393,17 @@ def get_table_version(router): return table["tableVersion"] +def get_mpls_label(router, network): + label = router.vtysh_cmd( + "show ipv6 route vrf vrf1 %s json" % network, + isjson=True, + ) + label = label.get(f"{network}", [{}])[0].get("nexthops", [{}])[0] + label = int(label.get("labels", [-1])[0]) + + return label + + def mpls_entry_get_interface(router, label): """ Assert that the label is in MPLS table @@ -411,7 +449,7 @@ def test_protocols_convergence(): "show bgp vrf vrf1 ipv6 json", expected, ) - _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=3) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg @@ -425,7 +463,7 @@ def test_protocols_convergence(): "show bgp ipv6 vpn json", expected, ) - _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=3) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg @@ -467,7 +505,7 @@ def test_flapping_bgp_vrf_down(): test_func = functools.partial( _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv6", "172:31::11/128" ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert ( success ), "r1, prefix 172:31::11/128 from r11 did not disappear. r11 still connected to rr ?" @@ -509,7 +547,7 @@ def test_flapping_bgp_vrf_up(): "172:31::11/128", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert ( success ), "r2, prefix 172:31::11/128 from r11 not present. r11 still disconnected from rr ?" @@ -547,7 +585,7 @@ def test_recursive_route(): # Check r2 received vpnv6 update with 172:31::30 test_func = functools.partial(_prefix30_found, tgen.gears["r2"]) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, VPNv6 update 172:31::30 not found" # that route should be sent along with label for 192::2:11 @@ -570,7 +608,7 @@ def test_recursive_route(): # Check r2 removed 172:31::30 vpnv6 update test_func = functools.partial(_prefix30_not_found, tgen.gears["r2"]) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, VPNv6 update 172:31::30 still present" @@ -596,7 +634,7 @@ def test_prefix_changes_interface(): "172:31::50/128", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, VPNv6 update 172:31::50 not found" # diagnostic @@ -642,7 +680,7 @@ def test_prefix_changes_interface(): "444:1", label=oldlabel, ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert ( success ), "r2, vpnv6 update 172:31::50 with old label {0} still present".format(oldlabel) @@ -659,7 +697,7 @@ def test_prefix_changes_interface(): "172:31::50/128", "444:1", ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r2, vpnv6 update 172:31::50 not found" label_list = set() @@ -724,9 +762,13 @@ def test_changing_default_label_value(): "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" ) test_func = functools.partial( - check_show_mpls_table_entry_label_found, router, 222, "vrf1" + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + router, + "vrf1", + label=222, ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r1, mpls entry with label 222 not found" # check label repartition is ok @@ -772,7 +814,7 @@ def test_unconfigure_allocation_mode_nexthop(): test_func = functools.partial( check_show_mpls_table_entry_label_not_found, router, 17 ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r1, mpls entry with label 17 still present" # Check vpnv6 routes from r1 @@ -821,7 +863,7 @@ def test_reconfigure_allocation_mode_nexthop(): test_func = functools.partial( check_show_mpls_table_entry_label_not_found, router, 17 ) - success, _ = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) assert success, "r1, mpls entry with label 17 still present" # Check vpnv6 routes from r1 @@ -837,6 +879,133 @@ def test_reconfigure_allocation_mode_nexthop(): mpls_table_check(router, label_list=label_list) +def test_network_command(): + """ + Test with network declaration + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Disabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nno redistribute static\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + for p in ["172:31::14/128", "172:31::15/128"]: + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, tgen.gears["r2"], "ipv6", p, "444:1" + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network %s should not present on r2" % p + + logger.info("Use network command for host networks declared in static instead") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nnetwork 172:31::14/128\n", + isjson=False, + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nnetwork 172:31::15/128\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + # 172:31::12 uses LL as nexthop + # 172:31::16 uses GA as nexthop + bgp_vpnv6_table_check(tgen.gears["r2"], group=["172:31::12/128"]) + bgp_vpnv6_table_check(tgen.gears["r2"], group=["172:31::15/128"]) + + bgp_vpnv6_table_check(tgen.gears["r2"], group=["172:31::14/128"]) + mpls_table_check(tgen.gears["r1"], whitelist=["192:2::14"]) + + logger.info(" Remove network to 172:31::14/128") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nno network 172:31::14/128\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv6", + "172:31::14/128", + "444:1", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "network %s should not present on r2" % p + mpls_table_check(tgen.gears["r1"], blacklist=["192:2::14"]) + + logger.info("Disabling redistribute connected and enabling redistribute static") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\n" + "redistribute static\n no redistribute connected", + isjson=False, + ) + logger.info( + "Use network command for connected host network 192:168::255:0/112 in vrf" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\n" + "network 192:168::255:0/112\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + bgp_vpnv6_table_check(tgen.gears["r2"], group=["192:168::255:0/112"]) + logger.info("Checking no mpls entry associated to 192:168::255:0/112") + mpls_table_check(tgen.gears["r1"], blacklist=["192:168:255::0"]) + logger.info("Checking 192:168::255:0/112 fallback to vrf") + test_func = functools.partial( + check_show_mpls_table_entry_label_found, + tgen.gears["r1"], + tgen.gears["r2"], + "vrf1", + network="192:168::255:0/112", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "192:168::255:0/112 does not fallback to vrf" + + logger.info( + "Use network command for statically routed network 192:168::3:0/112 in vrf" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\n" "ipv6 route 192:168::3:0/112 192:2::11\n" + ) + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\n" + "network 192:168::3:0/112\n", + isjson=False, + ) + logger.info("Checking 192:168::3:0/112 route on r2") + test_func = functools.partial( + check_show_mpls_table_entry_label_found_nexthop, + tgen.gears["r1"], + tgen.gears["r2"], + "192:168::3:0/112", + "192:2::11", + ) + success, _ = topotest.run_and_expect(test_func, None, count=10, wait=3) + assert success, "label from r2 is not present on r1 for 192:168::3:0/112" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping bgp network import-check-table") + tgen.gears["r1"].vtysh_cmd( + "show bgp vrf vrf1 import-check-table detail", isjson=False + ) + + logger.info("Restoring 172:31::14 prefix on r1") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nnetwork 172:31::14/128\n", + isjson=False, + ) + logger.info("Restoring redistribute connected") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\n" + "no redistribute static\n redistribute connected", + isjson=False, + ) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 44536e945e..dafd19c283 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -522,9 +522,14 @@ def pytest_configure(config): is_xdist = True is_worker = True - resource.setrlimit( - resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY) - ) + try: + resource.setrlimit( + resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY) + ) + except ValueError: + # The hard limit cannot be raised. Raise the soft limit to previous hard limit + core_rlimits = resource.getrlimit(resource.RLIMIT_CORE) + resource.setrlimit(resource.RLIMIT_CORE, (core_rlimits[1], core_rlimits[1])) # ----------------------------------------------------- # Set some defaults for the pytest.ini [pytest] section # --------------------------------------------------- diff --git a/tests/topotests/high_ecmp/r1/frr.conf b/tests/topotests/high_ecmp/r1/frr.conf new file mode 100644 index 0000000000..4382f94cf1 --- /dev/null +++ b/tests/topotests/high_ecmp/r1/frr.conf @@ -0,0 +1,267 @@ +int lo + ip addr 192.168.1.1/32 +router bgp 1001 + timers bgp 5 20 + no bgp ebgp-requires-policy + neighbor r1-eth0 interface remote-as external + neighbor r1-eth1 interface remote-as external + neighbor r1-eth2 interface remote-as external + neighbor r1-eth3 interface remote-as external + neighbor r1-eth4 interface remote-as external + neighbor r1-eth5 interface remote-as external + neighbor r1-eth6 interface remote-as external + neighbor r1-eth7 interface remote-as external + neighbor r1-eth8 interface remote-as external + neighbor r1-eth9 interface remote-as external + neighbor r1-eth10 interface remote-as external + neighbor r1-eth11 interface remote-as external + neighbor r1-eth12 interface remote-as external + neighbor r1-eth13 interface remote-as external + neighbor r1-eth14 interface remote-as external + neighbor r1-eth15 interface remote-as external + neighbor r1-eth16 interface remote-as external + neighbor r1-eth17 interface remote-as external + neighbor r1-eth18 interface remote-as external + neighbor r1-eth19 interface remote-as external + neighbor r1-eth20 interface remote-as external + neighbor r1-eth21 interface remote-as external + neighbor r1-eth22 interface remote-as external + neighbor r1-eth23 interface remote-as external + neighbor r1-eth24 interface remote-as external + neighbor r1-eth25 interface remote-as external + neighbor r1-eth26 interface remote-as external + neighbor r1-eth27 interface remote-as external + neighbor r1-eth28 interface remote-as external + neighbor r1-eth29 interface remote-as external + neighbor r1-eth30 interface remote-as external + neighbor r1-eth31 interface remote-as external + neighbor r1-eth32 interface remote-as external + neighbor r1-eth33 interface remote-as external + neighbor r1-eth34 interface remote-as external + neighbor r1-eth35 interface remote-as external + neighbor r1-eth36 interface remote-as external + neighbor r1-eth37 interface remote-as external + neighbor r1-eth38 interface remote-as external + neighbor r1-eth39 interface remote-as external + neighbor r1-eth40 interface remote-as external + neighbor r1-eth41 interface remote-as external + neighbor r1-eth42 interface remote-as external + neighbor r1-eth43 interface remote-as external + neighbor r1-eth44 interface remote-as external + neighbor r1-eth45 interface remote-as external + neighbor r1-eth46 interface remote-as external + neighbor r1-eth47 interface remote-as external + neighbor r1-eth48 interface remote-as external + neighbor r1-eth49 interface remote-as external + neighbor r1-eth50 interface remote-as external + neighbor r1-eth51 interface remote-as external + neighbor r1-eth52 interface remote-as external + neighbor r1-eth53 interface remote-as external + neighbor r1-eth54 interface remote-as external + neighbor r1-eth55 interface remote-as external + neighbor r1-eth56 interface remote-as external + neighbor r1-eth57 interface remote-as external + neighbor r1-eth58 interface remote-as external + neighbor r1-eth59 interface remote-as external + neighbor r1-eth60 interface remote-as external + neighbor r1-eth61 interface remote-as external + neighbor r1-eth62 interface remote-as external + neighbor r1-eth63 interface remote-as external + neighbor r1-eth64 interface remote-as external + neighbor r1-eth65 interface remote-as external + neighbor r1-eth66 interface remote-as external + neighbor r1-eth67 interface remote-as external + neighbor r1-eth68 interface remote-as external + neighbor r1-eth69 interface remote-as external + neighbor r1-eth70 interface remote-as external + neighbor r1-eth71 interface remote-as external + neighbor r1-eth72 interface remote-as external + neighbor r1-eth73 interface remote-as external + neighbor r1-eth74 interface remote-as external + neighbor r1-eth75 interface remote-as external + neighbor r1-eth76 interface remote-as external + neighbor r1-eth77 interface remote-as external + neighbor r1-eth78 interface remote-as external + neighbor r1-eth79 interface remote-as external + neighbor r1-eth80 interface remote-as external + neighbor r1-eth81 interface remote-as external + neighbor r1-eth82 interface remote-as external + neighbor r1-eth83 interface remote-as external + neighbor r1-eth84 interface remote-as external + neighbor r1-eth85 interface remote-as external + neighbor r1-eth86 interface remote-as external + neighbor r1-eth87 interface remote-as external + neighbor r1-eth88 interface remote-as external + neighbor r1-eth89 interface remote-as external + neighbor r1-eth90 interface remote-as external + neighbor r1-eth91 interface remote-as external + neighbor r1-eth92 interface remote-as external + neighbor r1-eth93 interface remote-as external + neighbor r1-eth94 interface remote-as external + neighbor r1-eth95 interface remote-as external + neighbor r1-eth96 interface remote-as external + neighbor r1-eth97 interface remote-as external + neighbor r1-eth98 interface remote-as external + neighbor r1-eth99 interface remote-as external + neighbor r1-eth100 interface remote-as external + neighbor r1-eth101 interface remote-as external + neighbor r1-eth102 interface remote-as external + neighbor r1-eth103 interface remote-as external + neighbor r1-eth104 interface remote-as external + neighbor r1-eth105 interface remote-as external + neighbor r1-eth106 interface remote-as external + neighbor r1-eth107 interface remote-as external + neighbor r1-eth108 interface remote-as external + neighbor r1-eth109 interface remote-as external + neighbor r1-eth110 interface remote-as external + neighbor r1-eth111 interface remote-as external + neighbor r1-eth112 interface remote-as external + neighbor r1-eth113 interface remote-as external + neighbor r1-eth114 interface remote-as external + neighbor r1-eth115 interface remote-as external + neighbor r1-eth116 interface remote-as external + neighbor r1-eth117 interface remote-as external + neighbor r1-eth118 interface remote-as external + neighbor r1-eth119 interface remote-as external + neighbor r1-eth120 interface remote-as external + neighbor r1-eth121 interface remote-as external + neighbor r1-eth122 interface remote-as external + neighbor r1-eth123 interface remote-as external + neighbor r1-eth124 interface remote-as external + neighbor r1-eth125 interface remote-as external + neighbor r1-eth126 interface remote-as external + neighbor r1-eth127 interface remote-as external + neighbor r1-eth128 interface remote-as external + neighbor r1-eth129 interface remote-as external + neighbor r1-eth130 interface remote-as external + neighbor r1-eth131 interface remote-as external + neighbor r1-eth132 interface remote-as external + neighbor r1-eth133 interface remote-as external + neighbor r1-eth134 interface remote-as external + neighbor r1-eth135 interface remote-as external + neighbor r1-eth136 interface remote-as external + neighbor r1-eth137 interface remote-as external + neighbor r1-eth138 interface remote-as external + neighbor r1-eth139 interface remote-as external + neighbor r1-eth140 interface remote-as external + neighbor r1-eth141 interface remote-as external + neighbor r1-eth142 interface remote-as external + neighbor r1-eth143 interface remote-as external + neighbor r1-eth144 interface remote-as external + neighbor r1-eth145 interface remote-as external + neighbor r1-eth146 interface remote-as external + neighbor r1-eth147 interface remote-as external + neighbor r1-eth148 interface remote-as external + neighbor r1-eth149 interface remote-as external + neighbor r1-eth150 interface remote-as external + neighbor r1-eth151 interface remote-as external + neighbor r1-eth152 interface remote-as external + neighbor r1-eth153 interface remote-as external + neighbor r1-eth154 interface remote-as external + neighbor r1-eth155 interface remote-as external + neighbor r1-eth156 interface remote-as external + neighbor r1-eth157 interface remote-as external + neighbor r1-eth158 interface remote-as external + neighbor r1-eth159 interface remote-as external + neighbor r1-eth160 interface remote-as external + neighbor r1-eth161 interface remote-as external + neighbor r1-eth162 interface remote-as external + neighbor r1-eth163 interface remote-as external + neighbor r1-eth164 interface remote-as external + neighbor r1-eth165 interface remote-as external + neighbor r1-eth166 interface remote-as external + neighbor r1-eth167 interface remote-as external + neighbor r1-eth168 interface remote-as external + neighbor r1-eth169 interface remote-as external + neighbor r1-eth170 interface remote-as external + neighbor r1-eth171 interface remote-as external + neighbor r1-eth172 interface remote-as external + neighbor r1-eth173 interface remote-as external + neighbor r1-eth174 interface remote-as external + neighbor r1-eth175 interface remote-as external + neighbor r1-eth176 interface remote-as external + neighbor r1-eth177 interface remote-as external + neighbor r1-eth178 interface remote-as external + neighbor r1-eth179 interface remote-as external + neighbor r1-eth180 interface remote-as external + neighbor r1-eth181 interface remote-as external + neighbor r1-eth182 interface remote-as external + neighbor r1-eth183 interface remote-as external + neighbor r1-eth184 interface remote-as external + neighbor r1-eth185 interface remote-as external + neighbor r1-eth186 interface remote-as external + neighbor r1-eth187 interface remote-as external + neighbor r1-eth188 interface remote-as external + neighbor r1-eth189 interface remote-as external + neighbor r1-eth190 interface remote-as external + neighbor r1-eth191 interface remote-as external + neighbor r1-eth192 interface remote-as external + neighbor r1-eth193 interface remote-as external + neighbor r1-eth194 interface remote-as external + neighbor r1-eth195 interface remote-as external + neighbor r1-eth196 interface remote-as external + neighbor r1-eth197 interface remote-as external + neighbor r1-eth198 interface remote-as external + neighbor r1-eth199 interface remote-as external + neighbor r1-eth200 interface remote-as external + neighbor r1-eth201 interface remote-as external + neighbor r1-eth202 interface remote-as external + neighbor r1-eth203 interface remote-as external + neighbor r1-eth204 interface remote-as external + neighbor r1-eth205 interface remote-as external + neighbor r1-eth206 interface remote-as external + neighbor r1-eth207 interface remote-as external + neighbor r1-eth208 interface remote-as external + neighbor r1-eth209 interface remote-as external + neighbor r1-eth210 interface remote-as external + neighbor r1-eth211 interface remote-as external + neighbor r1-eth212 interface remote-as external + neighbor r1-eth213 interface remote-as external + neighbor r1-eth214 interface remote-as external + neighbor r1-eth215 interface remote-as external + neighbor r1-eth216 interface remote-as external + neighbor r1-eth217 interface remote-as external + neighbor r1-eth218 interface remote-as external + neighbor r1-eth219 interface remote-as external + neighbor r1-eth220 interface remote-as external + neighbor r1-eth221 interface remote-as external + neighbor r1-eth222 interface remote-as external + neighbor r1-eth223 interface remote-as external + neighbor r1-eth224 interface remote-as external + neighbor r1-eth225 interface remote-as external + neighbor r1-eth226 interface remote-as external + neighbor r1-eth227 interface remote-as external + neighbor r1-eth228 interface remote-as external + neighbor r1-eth229 interface remote-as external + neighbor r1-eth230 interface remote-as external + neighbor r1-eth231 interface remote-as external + neighbor r1-eth232 interface remote-as external + neighbor r1-eth233 interface remote-as external + neighbor r1-eth234 interface remote-as external + neighbor r1-eth235 interface remote-as external + neighbor r1-eth236 interface remote-as external + neighbor r1-eth237 interface remote-as external + neighbor r1-eth238 interface remote-as external + neighbor r1-eth239 interface remote-as external + neighbor r1-eth240 interface remote-as external + neighbor r1-eth241 interface remote-as external + neighbor r1-eth242 interface remote-as external + neighbor r1-eth243 interface remote-as external + neighbor r1-eth244 interface remote-as external + neighbor r1-eth245 interface remote-as external + neighbor r1-eth246 interface remote-as external + neighbor r1-eth247 interface remote-as external + neighbor r1-eth248 interface remote-as external + neighbor r1-eth249 interface remote-as external + neighbor r1-eth250 interface remote-as external + neighbor r1-eth251 interface remote-as external + neighbor r1-eth252 interface remote-as external + neighbor r1-eth253 interface remote-as external + neighbor r1-eth254 interface remote-as external + neighbor r1-eth255 interface remote-as external + neighbor r1-eth256 interface remote-as external + neighbor r1-eth257 interface remote-as external + neighbor r1-eth258 interface remote-as external + neighbor r1-eth259 interface remote-as external + address-family ipv4 uni + redistribute sharp diff --git a/tests/topotests/high_ecmp/r2/frr.conf b/tests/topotests/high_ecmp/r2/frr.conf new file mode 100644 index 0000000000..a9d65360f5 --- /dev/null +++ b/tests/topotests/high_ecmp/r2/frr.conf @@ -0,0 +1,267 @@ +int lo + ip addr 192.168.1.2/32 +! +router bgp 1002 + timers bgp 5 20 + no bgp ebgp-requires-policy + read-quanta 1 + neighbor r2-eth0 interface remote-as external + neighbor r2-eth1 interface remote-as external + neighbor r2-eth2 interface remote-as external + neighbor r2-eth3 interface remote-as external + neighbor r2-eth4 interface remote-as external + neighbor r2-eth5 interface remote-as external + neighbor r2-eth6 interface remote-as external + neighbor r2-eth7 interface remote-as external + neighbor r2-eth8 interface remote-as external + neighbor r2-eth9 interface remote-as external + neighbor r2-eth10 interface remote-as external + neighbor r2-eth11 interface remote-as external + neighbor r2-eth12 interface remote-as external + neighbor r2-eth13 interface remote-as external + neighbor r2-eth14 interface remote-as external + neighbor r2-eth15 interface remote-as external + neighbor r2-eth16 interface remote-as external + neighbor r2-eth17 interface remote-as external + neighbor r2-eth18 interface remote-as external + neighbor r2-eth19 interface remote-as external + neighbor r2-eth20 interface remote-as external + neighbor r2-eth21 interface remote-as external + neighbor r2-eth22 interface remote-as external + neighbor r2-eth23 interface remote-as external + neighbor r2-eth24 interface remote-as external + neighbor r2-eth25 interface remote-as external + neighbor r2-eth26 interface remote-as external + neighbor r2-eth27 interface remote-as external + neighbor r2-eth28 interface remote-as external + neighbor r2-eth29 interface remote-as external + neighbor r2-eth30 interface remote-as external + neighbor r2-eth31 interface remote-as external + neighbor r2-eth32 interface remote-as external + neighbor r2-eth33 interface remote-as external + neighbor r2-eth34 interface remote-as external + neighbor r2-eth35 interface remote-as external + neighbor r2-eth36 interface remote-as external + neighbor r2-eth37 interface remote-as external + neighbor r2-eth38 interface remote-as external + neighbor r2-eth39 interface remote-as external + neighbor r2-eth40 interface remote-as external + neighbor r2-eth41 interface remote-as external + neighbor r2-eth42 interface remote-as external + neighbor r2-eth43 interface remote-as external + neighbor r2-eth44 interface remote-as external + neighbor r2-eth45 interface remote-as external + neighbor r2-eth46 interface remote-as external + neighbor r2-eth47 interface remote-as external + neighbor r2-eth48 interface remote-as external + neighbor r2-eth49 interface remote-as external + neighbor r2-eth50 interface remote-as external + neighbor r2-eth51 interface remote-as external + neighbor r2-eth52 interface remote-as external + neighbor r2-eth53 interface remote-as external + neighbor r2-eth54 interface remote-as external + neighbor r2-eth55 interface remote-as external + neighbor r2-eth56 interface remote-as external + neighbor r2-eth57 interface remote-as external + neighbor r2-eth58 interface remote-as external + neighbor r2-eth59 interface remote-as external + neighbor r2-eth60 interface remote-as external + neighbor r2-eth61 interface remote-as external + neighbor r2-eth62 interface remote-as external + neighbor r2-eth63 interface remote-as external + neighbor r2-eth64 interface remote-as external + neighbor r2-eth65 interface remote-as external + neighbor r2-eth66 interface remote-as external + neighbor r2-eth67 interface remote-as external + neighbor r2-eth68 interface remote-as external + neighbor r2-eth69 interface remote-as external + neighbor r2-eth70 interface remote-as external + neighbor r2-eth71 interface remote-as external + neighbor r2-eth72 interface remote-as external + neighbor r2-eth73 interface remote-as external + neighbor r2-eth74 interface remote-as external + neighbor r2-eth75 interface remote-as external + neighbor r2-eth76 interface remote-as external + neighbor r2-eth77 interface remote-as external + neighbor r2-eth78 interface remote-as external + neighbor r2-eth79 interface remote-as external + neighbor r2-eth80 interface remote-as external + neighbor r2-eth81 interface remote-as external + neighbor r2-eth82 interface remote-as external + neighbor r2-eth83 interface remote-as external + neighbor r2-eth84 interface remote-as external + neighbor r2-eth85 interface remote-as external + neighbor r2-eth86 interface remote-as external + neighbor r2-eth87 interface remote-as external + neighbor r2-eth88 interface remote-as external + neighbor r2-eth89 interface remote-as external + neighbor r2-eth90 interface remote-as external + neighbor r2-eth91 interface remote-as external + neighbor r2-eth92 interface remote-as external + neighbor r2-eth93 interface remote-as external + neighbor r2-eth94 interface remote-as external + neighbor r2-eth95 interface remote-as external + neighbor r2-eth96 interface remote-as external + neighbor r2-eth97 interface remote-as external + neighbor r2-eth98 interface remote-as external + neighbor r2-eth99 interface remote-as external + neighbor r2-eth100 interface remote-as external + neighbor r2-eth101 interface remote-as external + neighbor r2-eth102 interface remote-as external + neighbor r2-eth103 interface remote-as external + neighbor r2-eth104 interface remote-as external + neighbor r2-eth105 interface remote-as external + neighbor r2-eth106 interface remote-as external + neighbor r2-eth107 interface remote-as external + neighbor r2-eth108 interface remote-as external + neighbor r2-eth109 interface remote-as external + neighbor r2-eth110 interface remote-as external + neighbor r2-eth111 interface remote-as external + neighbor r2-eth112 interface remote-as external + neighbor r2-eth113 interface remote-as external + neighbor r2-eth114 interface remote-as external + neighbor r2-eth115 interface remote-as external + neighbor r2-eth116 interface remote-as external + neighbor r2-eth117 interface remote-as external + neighbor r2-eth118 interface remote-as external + neighbor r2-eth119 interface remote-as external + neighbor r2-eth120 interface remote-as external + neighbor r2-eth121 interface remote-as external + neighbor r2-eth122 interface remote-as external + neighbor r2-eth123 interface remote-as external + neighbor r2-eth124 interface remote-as external + neighbor r2-eth125 interface remote-as external + neighbor r2-eth126 interface remote-as external + neighbor r2-eth127 interface remote-as external + neighbor r2-eth128 interface remote-as external + neighbor r2-eth129 interface remote-as external + neighbor r2-eth130 interface remote-as external + neighbor r2-eth131 interface remote-as external + neighbor r2-eth132 interface remote-as external + neighbor r2-eth133 interface remote-as external + neighbor r2-eth134 interface remote-as external + neighbor r2-eth135 interface remote-as external + neighbor r2-eth136 interface remote-as external + neighbor r2-eth137 interface remote-as external + neighbor r2-eth138 interface remote-as external + neighbor r2-eth139 interface remote-as external + neighbor r2-eth140 interface remote-as external + neighbor r2-eth141 interface remote-as external + neighbor r2-eth142 interface remote-as external + neighbor r2-eth143 interface remote-as external + neighbor r2-eth144 interface remote-as external + neighbor r2-eth145 interface remote-as external + neighbor r2-eth146 interface remote-as external + neighbor r2-eth147 interface remote-as external + neighbor r2-eth148 interface remote-as external + neighbor r2-eth149 interface remote-as external + neighbor r2-eth150 interface remote-as external + neighbor r2-eth151 interface remote-as external + neighbor r2-eth152 interface remote-as external + neighbor r2-eth153 interface remote-as external + neighbor r2-eth154 interface remote-as external + neighbor r2-eth155 interface remote-as external + neighbor r2-eth156 interface remote-as external + neighbor r2-eth157 interface remote-as external + neighbor r2-eth158 interface remote-as external + neighbor r2-eth159 interface remote-as external + neighbor r2-eth160 interface remote-as external + neighbor r2-eth161 interface remote-as external + neighbor r2-eth162 interface remote-as external + neighbor r2-eth163 interface remote-as external + neighbor r2-eth164 interface remote-as external + neighbor r2-eth165 interface remote-as external + neighbor r2-eth166 interface remote-as external + neighbor r2-eth167 interface remote-as external + neighbor r2-eth168 interface remote-as external + neighbor r2-eth169 interface remote-as external + neighbor r2-eth170 interface remote-as external + neighbor r2-eth171 interface remote-as external + neighbor r2-eth172 interface remote-as external + neighbor r2-eth173 interface remote-as external + neighbor r2-eth174 interface remote-as external + neighbor r2-eth175 interface remote-as external + neighbor r2-eth176 interface remote-as external + neighbor r2-eth177 interface remote-as external + neighbor r2-eth178 interface remote-as external + neighbor r2-eth179 interface remote-as external + neighbor r2-eth180 interface remote-as external + neighbor r2-eth181 interface remote-as external + neighbor r2-eth182 interface remote-as external + neighbor r2-eth183 interface remote-as external + neighbor r2-eth184 interface remote-as external + neighbor r2-eth185 interface remote-as external + neighbor r2-eth186 interface remote-as external + neighbor r2-eth187 interface remote-as external + neighbor r2-eth188 interface remote-as external + neighbor r2-eth189 interface remote-as external + neighbor r2-eth190 interface remote-as external + neighbor r2-eth191 interface remote-as external + neighbor r2-eth192 interface remote-as external + neighbor r2-eth193 interface remote-as external + neighbor r2-eth194 interface remote-as external + neighbor r2-eth195 interface remote-as external + neighbor r2-eth196 interface remote-as external + neighbor r2-eth197 interface remote-as external + neighbor r2-eth198 interface remote-as external + neighbor r2-eth199 interface remote-as external + neighbor r2-eth200 interface remote-as external + neighbor r2-eth201 interface remote-as external + neighbor r2-eth202 interface remote-as external + neighbor r2-eth203 interface remote-as external + neighbor r2-eth204 interface remote-as external + neighbor r2-eth205 interface remote-as external + neighbor r2-eth206 interface remote-as external + neighbor r2-eth207 interface remote-as external + neighbor r2-eth208 interface remote-as external + neighbor r2-eth209 interface remote-as external + neighbor r2-eth210 interface remote-as external + neighbor r2-eth211 interface remote-as external + neighbor r2-eth212 interface remote-as external + neighbor r2-eth213 interface remote-as external + neighbor r2-eth214 interface remote-as external + neighbor r2-eth215 interface remote-as external + neighbor r2-eth216 interface remote-as external + neighbor r2-eth217 interface remote-as external + neighbor r2-eth218 interface remote-as external + neighbor r2-eth219 interface remote-as external + neighbor r2-eth220 interface remote-as external + neighbor r2-eth221 interface remote-as external + neighbor r2-eth222 interface remote-as external + neighbor r2-eth223 interface remote-as external + neighbor r2-eth224 interface remote-as external + neighbor r2-eth225 interface remote-as external + neighbor r2-eth226 interface remote-as external + neighbor r2-eth227 interface remote-as external + neighbor r2-eth228 interface remote-as external + neighbor r2-eth229 interface remote-as external + neighbor r2-eth230 interface remote-as external + neighbor r2-eth231 interface remote-as external + neighbor r2-eth232 interface remote-as external + neighbor r2-eth233 interface remote-as external + neighbor r2-eth234 interface remote-as external + neighbor r2-eth235 interface remote-as external + neighbor r2-eth236 interface remote-as external + neighbor r2-eth237 interface remote-as external + neighbor r2-eth238 interface remote-as external + neighbor r2-eth239 interface remote-as external + neighbor r2-eth240 interface remote-as external + neighbor r2-eth241 interface remote-as external + neighbor r2-eth242 interface remote-as external + neighbor r2-eth243 interface remote-as external + neighbor r2-eth244 interface remote-as external + neighbor r2-eth245 interface remote-as external + neighbor r2-eth246 interface remote-as external + neighbor r2-eth247 interface remote-as external + neighbor r2-eth248 interface remote-as external + neighbor r2-eth249 interface remote-as external + neighbor r2-eth250 interface remote-as external + neighbor r2-eth251 interface remote-as external + neighbor r2-eth252 interface remote-as external + neighbor r2-eth253 interface remote-as external + neighbor r2-eth254 interface remote-as external + neighbor r2-eth255 interface remote-as external + neighbor r2-eth256 interface remote-as external + neighbor r2-eth257 interface remote-as external + neighbor r2-eth258 interface remote-as external + neighbor r2-eth259 interface remote-as external diff --git a/tests/topotests/high_ecmp/test_high_ecmp.py b/tests/topotests/high_ecmp/test_high_ecmp.py new file mode 100644 index 0000000000..d28a1ee069 --- /dev/null +++ b/tests/topotests/high_ecmp/test_high_ecmp.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_high_ecmp.py +# +# Copyright (c) 2024 by +# Nvidia Corporation +# Donald Sharp +# + +""" +test_high_ecmp.py: Testing two routers with 256 interfaces and BGP setup + on it. + +""" + +import os +import re +import sys +import pytest +import json + +pytestmark = [pytest.mark.bgpd] + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + + tgen.add_router("r1") + tgen.add_router("r2") + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + # Let's create 257 interfaces between the two switches + for switch in range(1, 257): + switch = tgen.add_switch("sw{}".format(switch)) + switch.add_link(r1) + switch.add_link(r2) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)), + [ + (TopoRouter.RD_ZEBRA, "-s 180000000"), + (TopoRouter.RD_BGP, None), + (TopoRouter.RD_SHARP, None), + ], + ) + + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_nothing(): + "Do Nothing" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 1cec2f16f0..fe3d865565 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -237,9 +237,9 @@ def test_isis_summary_json(): assertmsg = "Test isis summary json failed in '{}' data '{}'".format( rname, json_output ) - assert json_output["vrf"] == "default", assertmsg - assert json_output["areas"][0]["area"] == "1", assertmsg - assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg + assert json_output["vrfs"][0]["vrf"] == "default", assertmsg + assert json_output["vrfs"][0]["areas"][0]["area"] == "1", assertmsg + assert json_output["vrfs"][0]["areas"][0]["levels"][0]["id"] != "3", assertmsg def test_isis_interface_json(): diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py index f3c0be49c5..ac8af02844 100644 --- a/tests/topotests/lib/bmp_collector/bmp.py +++ b/tests/topotests/lib/bmp_collector/bmp.py @@ -10,11 +10,13 @@ BMP main module: - XXX: more bmp messages types to dissect - XXX: complete bgp message dissection """ -import datetime import ipaddress import json import os import struct +import sys + +from datetime import datetime from bgp.update import BGPUpdate from bgp.update.rd import RouteDistinguisher @@ -48,6 +50,13 @@ def log2file(logs, log_file): f.write(json.dumps(logs) + "\n") +def timestamp_print(message, file=sys.stderr): + """Helper function to timestamp_print messages with timestamps.""" + + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + print(f"[{current_time}] {message}", file=file) + + # ------------------------------------------------------------------------------ class BMPCodes: """ @@ -196,14 +205,18 @@ class BMPMsg: data = data[msglen:] if version != BMPCodes.VERSION: - # XXX: log something + timestamp_print( + f"Expected BMP version {BMPCodes.VERSION} but got version {version}." + ) return data msg_cls = cls.lookup_msg_type(msgtype) if msg_cls == cls.UNKNOWN_TYPE: - # XXX: log something + timestamp_print(f"Got unknown message type ") return data + timestamp_print(f"Got message type: {msg_cls}") + msg_cls.MSG_LEN = msglen - cls.MIN_LEN logs = msg_cls.dissect(msg_data) logs["seq"] = SEQ @@ -281,7 +294,7 @@ class BMPPerPeerMessage: "peer_distinguisher": str(RouteDistinguisher(peer_distinguisher)), "peer_asn": peer_asn, "peer_bgp_id": peer_bgp_id, - "timestamp": str(datetime.datetime.fromtimestamp(timestamp)), + "timestamp": str(datetime.fromtimestamp(timestamp)), } ) diff --git a/tests/topotests/lib/bmp_collector/bmpserver b/tests/topotests/lib/bmp_collector/bmpserver index 5257df7530..56d85fc74b 100755 --- a/tests/topotests/lib/bmp_collector/bmpserver +++ b/tests/topotests/lib/bmp_collector/bmpserver @@ -5,43 +5,105 @@ # Authored by Farid Mihoub <farid.mihoub@6wind.com> # import argparse + # XXX: something more reliable should be used "Twisted" a great choice. +import signal import socket import sys +from datetime import datetime + from bmp import BMPMsg BGP_MAX_SIZE = 4096 +# Global variable to track shutdown signal +shutdown = False + + parser = argparse.ArgumentParser() parser.add_argument("-a", "--address", type=str, default="0.0.0.0") parser.add_argument("-p", "--port", type=int, default=1789) parser.add_argument("-l", "--logfile", type=str, default="/var/log/bmp.log") + +def handle_signal(signum, frame): + global shutdown + timestamp_print(f"Received signal {signum}, shutting down.") + shutdown = True + + +def timestamp_print(message, file=sys.stderr): + """Helper function to timestamp_print messages with timestamps.""" + + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + print(f"[{current_time}] {message}", file=file) + + def main(): + global shutdown + + # Set up signal handling for SIGTERM and SIGINT + signal.signal(signal.SIGTERM, handle_signal) + signal.signal(signal.SIGINT, handle_signal) + args = parser.parse_args() ADDRESS, PORT = args.address, args.port LOG_FILE = args.logfile + timestamp_print(f"Starting bmpserver on {args.address}:{args.port}") + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind((ADDRESS, PORT)) - s.listen() - connection, _ = s.accept() - try: - while True: - data = connection.recv(BGP_MAX_SIZE) - while len(data) > BMPMsg.MIN_LEN: - data = BMPMsg.dissect(data, log_file=LOG_FILE) + s.bind((ADDRESS, PORT)) + s.listen() + timestamp_print(f"Listening on TCP {args.address}:{args.port}") + + connection, client_address = s.accept() + timestamp_print(f"TCP session opened from {client_address}") + + try: + while not shutdown: # Check for shutdown signal + data = connection.recv(BGP_MAX_SIZE) + if shutdown: + break + + if not data: + # connection closed + break + + timestamp_print( + f"Data received from {client_address}: length {len(data)}" + ) + + while len(data) > BMPMsg.MIN_LEN: + data = BMPMsg.dissect(data, log_file=LOG_FILE) + + timestamp_print( + f"Finished dissecting data from {client_address}" + ) + + except Exception as e: + timestamp_print(f"{e}") + pass + except KeyboardInterrupt: + timestamp_print(f"Got Keyboard Interrupt.") + pass + finally: + timestamp_print(f"TCP session closed with {client_address}") + connection.close() + except socket.error as sock_err: + timestamp_print(f"Socket error: {e}") except Exception as e: - # XXX: do something - pass - except KeyboardInterrupt: - # XXX: do something - pass + timestamp_print(f"{e}") finally: - connection.close() + timestamp_print(f"Server shutting down on {ADDRESS}:{PORT}") + if __name__ == "__main__": - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + logging.info("BMP server was interrupted and is shutting down.") + sys.exit(0) diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 14dd61b077..d04d7b4257 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -833,6 +833,8 @@ class TopoRouter(TopoGear): Loads the unified configuration file source Start the daemons in the list If daemons is None, try to infer daemons from the config file + `daemons` is a tuple (daemon, param) of daemons to start, e.g.: + (TopoRouter.RD_ZEBRA, "-s 90000000"). """ source_path = self.load_config(self.RD_FRR, source) if not daemons: @@ -849,8 +851,9 @@ class TopoRouter(TopoGear): if result: self.load_config(daemon, "") else: - for daemon in daemons: - self.load_config(daemon, "") + for item in daemons: + daemon, param = item + self.load_config(daemon, "", param) def load_config(self, daemon, source=None, param=None): """Loads daemon configuration from the specified source @@ -1273,16 +1276,24 @@ class TopoBMPCollector(TopoHost): return gear def start(self, log_file=None): + log_dir = os.path.join(self.logdir, self.name) + self.run("chmod 777 {}".format(log_dir)) + + log_err = os.path.join(log_dir, "bmpserver.log") + log_arg = "-l {}".format(log_file) if log_file else "" - self.run( - "{}/bmp_collector/bmpserver -a {} -p {} {}&".format( - CWD, self.ip, self.port, log_arg - ), - stdout=None, - ) + + with open(log_err, "w") as err: + self.run( + "{}/bmp_collector/bmpserver -a {} -p {} {}&".format( + CWD, self.ip, self.port, log_arg + ), + stdout=None, + stderr=err, + ) def stop(self): - self.run("pkill -9 -f bmpserver") + self.run("pkill -f bmpserver") return "" diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index bd98958355..42d51853e0 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1903,7 +1903,7 @@ class Router(Node): tail_log_files = [] check_daemon_files = [] - def start_daemon(daemon, extra_opts=None): + def start_daemon(daemon): daemon_opts = self.daemons_options.get(daemon, "") # get pid and vty filenames and remove the files @@ -1998,9 +1998,6 @@ class Router(Node): "{}/{}/{}.log".format(self.logdir, self.name, daemon) ) - if extra_opts: - cmdopt += " " + extra_opts - if do_gdb_or_rr(True) and do_gdb_or_rr(False): logger.warning("cant' use gdb and rr at same time") @@ -2237,7 +2234,7 @@ class Router(Node): # Start Zebra after mgmtd if "zebra" in daemons_list: - start_daemon("zebra", "-s 90000000") + start_daemon("zebra") while "zebra" in daemons_list: daemons_list.remove("zebra") diff --git a/tests/topotests/nhrp_topo/r1/nhrp_shortcut_present.json b/tests/topotests/nhrp_topo/r1/nhrp_shortcut_present.json new file mode 100644 index 0000000000..96632d8463 --- /dev/null +++ b/tests/topotests/nhrp_topo/r1/nhrp_shortcut_present.json @@ -0,0 +1,14 @@ +{ + "attr":{ + "entriesCount":1 + }, + "table":[ + { + "type":"dynamic", + "prefix":"192.168.4.0\/24", + "via":"10.255.255.4", + "identity":"" + } + ] +} + diff --git a/tests/topotests/nhrp_topo/r1/zebra.conf b/tests/topotests/nhrp_topo/r1/zebra.conf index b45670fcb2..c8a216335f 100644 --- a/tests/topotests/nhrp_topo/r1/zebra.conf +++ b/tests/topotests/nhrp_topo/r1/zebra.conf @@ -10,3 +10,4 @@ exit interface r1-eth1 ip address 192.168.1.1/24 ! +ip route 0.0.0.0/0 10.255.255.2 diff --git a/tests/topotests/nhrp_topo/r2/nhrp4_cache.json b/tests/topotests/nhrp_topo/r2/nhrp4_cache.json index 34558e0c28..ee122c59e5 100644 --- a/tests/topotests/nhrp_topo/r2/nhrp4_cache.json +++ b/tests/topotests/nhrp_topo/r2/nhrp4_cache.json @@ -1,10 +1,21 @@ { "attr":{ - "entriesCount":2 + "entriesCount":3 }, "table":[ { "interface":"r2-gre0", + "type":"dynamic", + "protocol":"10.255.255.4", + "nbma":"10.1.1.4", + "claimed_nbma":"10.1.1.4", + "used":false, + "timeout":true, + "auth":false, + "identity":"" + }, + { + "interface":"r2-gre0", "type":"local", "protocol":"10.255.255.2", "nbma":"10.2.1.2", diff --git a/tests/topotests/nhrp_topo/r2/nhrp_route4.json b/tests/topotests/nhrp_topo/r2/nhrp_route4.json index 7393cba893..876b24a9b1 100644 --- a/tests/topotests/nhrp_topo/r2/nhrp_route4.json +++ b/tests/topotests/nhrp_topo/r2/nhrp_route4.json @@ -12,7 +12,31 @@ "installed":true, "internalNextHopNum":1, "internalNextHopActiveNum":1, - "nexthops":[ + "nexthops": [ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-gre0", + "active":true + } + ] + } + ], + "10.255.255.4\/32": [ + { + "prefix":"10.255.255.4\/32", + "prefixLen":32, + "protocol":"nhrp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":10, + "metric":0, + "installed":true, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops": [ { "fib":true, "directlyConnected":true, diff --git a/tests/topotests/nhrp_topo/r2/zebra.conf b/tests/topotests/nhrp_topo/r2/zebra.conf index 9f40d4d72e..756cc6d8c8 100644 --- a/tests/topotests/nhrp_topo/r2/zebra.conf +++ b/tests/topotests/nhrp_topo/r2/zebra.conf @@ -1,3 +1,4 @@ +ip forwarding interface r2-eth0 ip address 10.2.1.2/24 ! @@ -10,3 +11,5 @@ interface r2-gre0 interface r2-eth1 ip address 192.168.2.2/24 ! +ip route 192.168.4.4/24 10.255.255.4 +ip route 192.168.1.1/24 10.255.255.1 diff --git a/tests/topotests/nhrp_topo/r4/nhrp4_cache.json b/tests/topotests/nhrp_topo/r4/nhrp4_cache.json new file mode 100644 index 0000000000..19074e4e6d --- /dev/null +++ b/tests/topotests/nhrp_topo/r4/nhrp4_cache.json @@ -0,0 +1,30 @@ +{ + "attr":{ + "entriesCount":2 + }, + "table":[ + { + "interface":"r4-gre0", + "type":"local", + "protocol":"10.255.255.4", + "nbma":"10.1.1.4", + "claimed_nbma":"10.1.1.4", + "used":false, + "timeout":false, + "auth":false, + "identity":"-" + }, + { + "interface":"r4-gre0", + "type":"nhs", + "protocol":"10.255.255.2", + "nbma":"10.2.1.2", + "claimed_nbma":"10.2.1.2", + "used":false, + "timeout":true, + "auth":false, + "identity":"" + } + ] +} + diff --git a/tests/topotests/nhrp_topo/r4/nhrp_route4.json b/tests/topotests/nhrp_topo/r4/nhrp_route4.json new file mode 100644 index 0000000000..01d627c977 --- /dev/null +++ b/tests/topotests/nhrp_topo/r4/nhrp_route4.json @@ -0,0 +1,26 @@ +{ + "10.255.255.2\/32": [ + { + "prefix": "10.255.255.2\/32", + "prefixLen": 32, + "protocol": "nhrp", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 10, + "metric": 0, + "installed": true, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "fib": true, + "directlyConnected": true, + "interfaceName": "r4-gre0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/nhrp_topo/r4/nhrpd.conf b/tests/topotests/nhrp_topo/r4/nhrpd.conf new file mode 100644 index 0000000000..df9700c22b --- /dev/null +++ b/tests/topotests/nhrp_topo/r4/nhrpd.conf @@ -0,0 +1,11 @@ +log stdout debugging +debug nhrp all +interface r4-gre0 + ip nhrp authentication secret + ip nhrp holdtime 10 + ip nhrp shortcut + ip nhrp network-id 42 + ip nhrp nhs dynamic nbma 10.2.1.2 + ip nhrp registration no-unique + tunnel source r4-eth0 +exit diff --git a/tests/topotests/nhrp_topo/r4/zebra.conf b/tests/topotests/nhrp_topo/r4/zebra.conf new file mode 100644 index 0000000000..b517dbb05e --- /dev/null +++ b/tests/topotests/nhrp_topo/r4/zebra.conf @@ -0,0 +1,13 @@ +interface r4-eth0 + ip address 10.1.1.4/24 +! +ip route 10.2.1.0/24 10.1.1.3 +interface r4-gre0 + ip address 10.255.255.4/32 + no link-detect + ipv6 nd suppress-ra +exit +interface r4-eth1 + ip address 192.168.4.4/24 +! +ip route 0.0.0.0/0 10.255.255.2 diff --git a/tests/topotests/nhrp_topo/test_nhrp_topo.py b/tests/topotests/nhrp_topo/test_nhrp_topo.py index 8833003107..90e793f273 100644 --- a/tests/topotests/nhrp_topo/test_nhrp_topo.py +++ b/tests/topotests/nhrp_topo/test_nhrp_topo.py @@ -33,18 +33,52 @@ from lib.common_config import required_linux_kernel_version, retry # Required to instantiate the topology builder class. pytestmark = [pytest.mark.nhrpd] +TOPOLOGY = """ + 192.168.2.0/24 + -----+----- + | + | + | + +----------+ + | | + | R2 | + | NHS | + +----------+ + | .2 + | + | + | + GRE P2MP Between + 10.2.1.0/24 + Between Spokes and Hub | + | + 10.255.255.x/32 +----+-----+ + | | + | R3 | + | | + +----+-----+ + |.3 + | + | + +----------+ | +---------+ + | | | | | | | + | |R1 | | | R4 | | +192.168.1.0/24 +-------------|NHC +---------+----------| NHC | ------+ 192.168.4.0/24 + | | |.1 .4| | | + | +----------+ 10.1.1.0/24 +---------+ | +""" def build_topo(tgen): "Build function" - # Create 3 routers. - for routern in range(1, 4): + # Create 4 routers. + 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["r3"]) + switch.add_link(tgen.gears["r4"]) switch = tgen.add_switch("s2") switch.add_link(tgen.gears["r2"]) switch.add_link(tgen.gears["r3"]) @@ -53,6 +87,9 @@ def build_topo(tgen): switch = tgen.add_switch("s4") switch.add_link(tgen.gears["r1"]) + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r4"]) + def _populate_iface(): tgen = get_topogen() @@ -62,6 +99,7 @@ def _populate_iface(): "echo 0 > /proc/sys/net/ipv4/ip_forward_use_pmtu", "echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/disable_ipv6", "echo 1 > /proc/sys/net/ipv6/conf/{0}-gre0/disable_ipv6", + "iptables -A FORWARD -i {0}-gre0 -o {0}-gre0 -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128", ] cmds_tot = [ @@ -84,10 +122,27 @@ def _populate_iface(): output = tgen.net["r1"].cmd(input) logger.info("output: " + output) + input = cmd.format("r4", "4") + logger.info("input: " + input) + output = tgen.net["r4"].cmd(input) + logger.info("output: " + output) + + +def _verify_iptables(): + tgen = get_topogen() + # Verify iptables is installed + # This is needed for creating shortcuts + for rname in ("r1", "r4"): + rc, _, _ = tgen.net[rname].cmd_status("iptables --version") + if rc == 127: + return False + return True + def setup_module(mod): "Sets up the pytest environment" + logger.info("NHRP Topology : \n {}".format(TOPOLOGY)) result = required_linux_kernel_version("5.0") if result is not True: pytest.skip("Kernel requirements are not met") @@ -103,7 +158,7 @@ def setup_module(mod): TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), ) - if rname in ("r1", "r2"): + if rname in ("r1", "r2", "r4"): router.load_config( TopoRouter.RD_NHRP, os.path.join(CWD, "{}/nhrpd.conf".format(rname)) ) @@ -226,10 +281,10 @@ def test_nhrp_connection(): # force session to reinitialize def relink_session(): - for r in ["r1", "r2"]: + for r in ["r1", "r2", "r4"]: tgen.gears[r].vtysh_cmd("clear ip nhrp cache") - tgen.net[r].cmd("ip l del {}-gre0".format(r)); - _populate_iface(); + tgen.net[r].cmd("ip l del {}-gre0".format(r)) + _populate_iface() @retry(retry_timeout=40, initial_wait=5) def verify_same_password(): @@ -255,24 +310,29 @@ def test_nhrp_connection(): ### Passwords are different logger.info("Modify password and send ping again, should drop") - hubrouter.vtysh_cmd(""" + hubrouter.vtysh_cmd( + """ configure interface r2-gre0 ip nhrp authentication secret12 - """) + """ + ) relink_session() verify_mismatched_password() - + ### Passwords are the same - again logger.info("Recover password and verify conectivity is back") - hubrouter.vtysh_cmd(""" + hubrouter.vtysh_cmd( + """ configure interface r2-gre0 ip nhrp authentication secret - """) + """ + ) relink_session() verify_same_password() + def test_route_install(): "Test use of NHRP routes by other protocols (sharpd here)." tgen = get_topogen() @@ -305,6 +365,134 @@ def test_route_install(): assert result is None, assertmsg +# Initial wait of 30 second because that is +# what the default purge time is for nhrp - +# here we are testing that all of the expected +# retries are sent and logged before a +# shortcut is purged +@retry(retry_timeout=10, initial_wait=30) +def check_retry_debug_info(pingspoke=None): + tgen = get_topogen() + r1 = tgen.gears["r1"] + if pingspoke == None: + pingspoke = r1 + logger.info(f"Check retries are being sent from {pingspoke.name}") + output = pingspoke.cmd("grep -c 'Retrying Resolution Request' nhrpd.log") + # Making sure that we see all expected retries for a 30 second purge time + assertmsg = f"Did not see all expected retries on {pingspoke.name}" + assert output.strip() == "6", assertmsg + logger.info("Check retries are being sent OK") + + +# Helper function to ping between spokes and +# check for either complete or incomplete shortcut +# based on whichever one you are expecting - +# expect_succesful_shortcut inidcates whether +# you are expecting to find a complete shortcut +# (True) or incomplete shortcut (False) as a +# result of the ping +@retry(retry_timeout=10, initial_wait=10) +def create_shortcut(expect_successful_shortcut=True, pingspoke=None, peer_addr=None): + tgen = get_topogen() + r1 = tgen.gears["r1"] + if pingspoke == None: + pingspoke = r1 + if peer_addr == None: + peer_addr = "192.168.4.4" + # Pinging the other spoke in an attempt to create specified type of shortcut + output = pingspoke.cmd(f"ping -c 10 -i .5 {peer_addr}") + print(output) + output = pingspoke.vtysh_cmd("show ip nhrp shortcut") + if expect_successful_shortcut: + logger.info(f"Check shortcut creation from {pingspoke.name} to {peer_addr}") + else: + logger.info( + f"Check incomplete shortcut creation from {pingspoke.name} to {peer_addr}" + ) + + output = pingspoke.vtysh_cmd("show ip nhrp shortcut") + print(output) + if expect_successful_shortcut: + json_file = "{}/{}/nhrp_shortcut_present.json".format(CWD, pingspoke.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, pingspoke, "show ip nhrp shortcut json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + + if result is not None: + assertmsg = ( + "Shortcut is not being made between spoke {} and peer {}".format( + pingspoke.name, peer_addr + ) + ) + assert 0, assertmsg + else: + logger.info("Shortcut creation between spokes OK") + else: + # Currentlly, 'show ip nhrp shortcut json' does not show incomplete shortcuts + # so an explicit check for for the 'incompete' keyword needed here + if "incomplete" not in output: + assertmsg = ( + "Incomplete shortcut between spoke {} and peer {} is not seen".format( + pingspoke.name, peer_addr + ) + ) + assert 0, assertmsg + else: + logger.info("Incomplete shortcut creation between spokes OK") + + +# This function tests the NHRP resolution request retries by dropping +# incoming packets (including the NHRP resolution request packets) +# from a receiving spoke in order to stop the NHRP resolution +# responses from ever being sent from that receiving spoke - and in turn +# resolution responses will not reach the sending spoke. +# This will trigger the NHRP resolution request retries which +# can be viewed through log messages. +def test_nhrp_retry_resolution(): + """ " + Verify resolution requests are retried when resolution responses + are not received by a spoke + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # iptables used to create shortcuts + # and subsequent resolution request retries + if not _verify_iptables(): + pytest.skip("iptables is not installed") + + r1 = tgen.gears["r1"] + r4 = tgen.gears["r4"] + + logger.info("Testing retrying resolution request functionality") + # Make sure that shortcut creation between spokes work + create_shortcut(expect_successful_shortcut=True) + # Clearing shortcut information for spokes + r1.vtysh_cmd("clear ip nhrp shortcut") + r4.vtysh_cmd("clear ip nhrp shortcut") + + # Setting iptables rules to stop incoming packets on r4 + # This should stop resolution requests from reaching + # the receiving router (r4) and hence stop the + # creation of a complete shortcut + r4.cmd("iptables -A INPUT -i r4-eth0 -j DROP") + + # Make sure that nhrp debugging is enabled to read the retry logs + r1.vtysh_cmd( + """ + configure + debug nhrp all + """ + ) + create_shortcut(expect_successful_shortcut=False) + # Look for retry logging output for resolution request retries + check_retry_debug_info() + # Undo iptables rule + r4.cmd("iptables -D INPUT -i r4-eth0 -j DROP") + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/pim_cand_rp_bsr/r1/frr.conf b/tests/topotests/pim_cand_rp_bsr/r1/frr.conf index d0aa3d529f..899e9c0684 100644 --- a/tests/topotests/pim_cand_rp_bsr/r1/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r1/frr.conf @@ -5,21 +5,45 @@ log file /tmp/r1-frr.log ! !debug pim packet !debug pim bsm +!debug pimv6 bsm ! -ip route 0.0.0.0/0 10.0.0.4 +! +interface lo + ip address 10.0.0.1/32 + ipv6 address fd00:0:0:0::1/128 + ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r1-eth0 ip address 10.0.0.1/24 + ipv6 address fd00:0:0:0::1/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r1-eth1 ip address 10.0.1.1/24 + ipv6 address fd00:0:0:1::1/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim bsr candidate-bsr priority 200 source address 10.0.0.1 ! +router pim6 + bsr candidate-bsr priority 200 source address fd00:0:0:0::1 +! +router ospf + ospf router-id 10.0.0.1 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.0.1 +! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r2/frr.conf b/tests/topotests/pim_cand_rp_bsr/r2/frr.conf index 741c839f19..85af461d5e 100644 --- a/tests/topotests/pim_cand_rp_bsr/r2/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r2/frr.conf @@ -3,20 +3,46 @@ hostname r2 password zebra log file /tmp/r2-frr.log ! -ip route 0.0.0.0/0 10.0.0.4 +!debug pim packet +!debug pim bsm +!debug pimv6 bsm +! +interface lo + ip address 10.0.0.2/32 + ipv6 address fd00:0:0:0::2/128 + ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r2-eth0 ip address 10.0.0.2/24 + ipv6 address fd00:0:0:0::2/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r2-eth1 ip address 10.0.2.2/24 + ipv6 address fd00:0:0:2::2/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim - bsr candidate-bsr priority 100 source address 10.0.0.2 + bsr candidate-bsr priority 100 +! +router pim6 + bsr candidate-bsr priority 100 +! +router ospf + ospf router-id 10.0.0.2 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.0.2 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r3/frr.conf b/tests/topotests/pim_cand_rp_bsr/r3/frr.conf index bd5c8ce93f..022c44ea58 100644 --- a/tests/topotests/pim_cand_rp_bsr/r3/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r3/frr.conf @@ -5,28 +5,48 @@ log file /tmp/r3-frr.log ! !debug pim packet !debug pim bsm +!debug pimv6 bsm ! -ip route 0.0.0.0/0 10.0.3.4 -ip route 10.0.6.0/24 10.0.3.6 ! interface r3-eth0 ip address 10.0.1.3/24 + ipv6 address fd00:0:0:1::3/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r3-eth1 ip address 10.0.3.3/24 + ipv6 address fd00:0:0:3::3/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r3-eth2 ip address 10.0.4.3/24 + ipv6 address fd00:0:0:4::3/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim bsr candidate-rp group 239.0.0.0/16 bsr candidate-rp priority 10 source address 10.0.3.3 ! +router pim6 + bsr candidate-rp group ffbb::/64 + bsr candidate-rp priority 10 source address fd00:0:0:3::3 +! +router ospf + ospf router-id 10.0.1.3 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.1.3 +! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r4/frr.conf b/tests/topotests/pim_cand_rp_bsr/r4/frr.conf index 825b227728..2d0a035f9a 100644 --- a/tests/topotests/pim_cand_rp_bsr/r4/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r4/frr.conf @@ -3,35 +3,64 @@ hostname r4 password zebra log file /tmp/r4-frr.log ! -ip route 10.0.1.0/24 10.0.0.1 -ip route 10.0.4.0/24 10.0.3.3 -ip route 10.0.6.0/24 10.0.3.6 +! +interface lo + ip address 10.0.3.4/32 + ipv6 address fd00:0:0:3::4/64 + ip pim + ipv6 pim ! interface r4-eth0 ip address 10.0.2.4/24 + ipv6 address fd00:0:0:2::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r4-eth1 ip address 10.0.3.4/24 + ipv6 address fd00:0:0:3::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r4-eth2 ip address 10.0.5.4/24 + ipv6 address fd00:0:0:5::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r4-eth3 ip address 10.0.0.4/24 + ipv6 address fd00:0:0:0::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim bsr candidate-rp group 239.0.0.0/24 bsr candidate-rp group 239.0.0.0/16 bsr candidate-rp group 239.0.0.0/8 - bsr candidate-rp priority 20 source address 10.0.3.4 + bsr candidate-rp priority 20 +! +router pim6 + bsr candidate-rp group ffbb::/124 + bsr candidate-rp group ffbb::/64 + bsr candidate-rp group ffbb::/108 + bsr candidate-rp priority 20 +! +router ospf + ospf router-id 10.0.2.4 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.2.4 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r5/frr.conf b/tests/topotests/pim_cand_rp_bsr/r5/frr.conf index c934717d08..552e51f417 100644 --- a/tests/topotests/pim_cand_rp_bsr/r5/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r5/frr.conf @@ -3,15 +3,32 @@ hostname r5 password zebra log file /tmp/r5-frr.log ! -ip route 0.0.0.0/0 10.0.4.3 ! interface r5-eth0 ip address 10.0.4.5/24 + ipv6 address fd00:0:0:4::5/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r5-eth1 ip address 10.0.6.5/24 + ipv6 address fd00:0:0:6::5/64 + ip igmp + ip pim + ipv6 pim + ipv6 ospf6 area 0 +! +router pim6 +! +router ospf + ospf router-id 10.0.4.5 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.4.5 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r6/frr.conf b/tests/topotests/pim_cand_rp_bsr/r6/frr.conf index fd9d1eb5c4..20955a12c7 100644 --- a/tests/topotests/pim_cand_rp_bsr/r6/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r6/frr.conf @@ -3,20 +3,40 @@ hostname r6 password zebra log file /tmp/r6-frr.log ! -ip route 0.0.0.0/0 10.0.6.6 ! interface r6-eth0 ip address 10.0.5.6/24 + ipv6 address fd00:0:0:5::6/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r6-eth1 ip address 10.0.6.6/24 + ipv6 address fd00:0:0:6::6/64 + ip igmp + ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r6-eth2 ip address 10.0.3.6/24 + ipv6 address fd00:0:0:3::6/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 +! +router pim6 +! +router ospf + ospf router-id 10.0.5.6 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.5.6 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py b/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py index ce7bc9dc56..96a3241a2b 100644 --- a/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py +++ b/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py @@ -59,7 +59,12 @@ CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, "../")) # Required to instantiate the topology builder class. -pytestmark = [pytest.mark.pimd] +pytestmark = [ + pytest.mark.pimd, + pytest.mark.pim6d, + pytest.mark.ospfd, + pytest.mark.ospf6d, +] def build_topo(tgen): @@ -102,6 +107,7 @@ def build_topo(tgen): sw1.add_link(tgen.gears["r4"]) sw3.add_link(tgen.gears["r6"]) + def setup_module(mod): logger.info("PIM Candidate RP/BSR:\n {}".format(TOPOLOGY)) @@ -125,6 +131,7 @@ def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() + def test_pim_bsr_election_r1(request): "Test PIM BSR Election" tgen = get_topogen() @@ -137,19 +144,18 @@ def test_pim_bsr_election_r1(request): r2 = tgen.gears["r2"] # r1 should be the BSR winner because it has higher priority expected = { - "bsr":"10.0.0.1", - "priority":200, - "state":"ACCEPT_PREFERRED", + "bsr": "10.0.0.1", + "priority": 200, + "state": "ACCEPT_PREFERRED", } - test_func = partial( - topotest.router_json_cmp, r2, "show ip pim bsr json", expected - ) + test_func = partial(topotest.router_json_cmp, r2, "show ip pim bsr json", expected) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2: r1 was not elected, bsr election mismatch" assert result is None, assertmsg + def test_pim_bsr_cand_bsr_r1(request): "Test PIM BSR candidate BSR" tgen = get_topogen() @@ -162,11 +168,7 @@ def test_pim_bsr_cand_bsr_r1(request): r2 = tgen.gears["r2"] # r2 is a candidate bsr with low priority: elected = False - expected = { - "address": "10.0.0.2", - "priority": 100, - "elected": False - } + expected = {"address": "10.0.0.2", "priority": 100, "elected": False} test_func = partial( topotest.router_json_cmp, r2, "show ip pim bsr candidate-bsr json", expected ) @@ -175,6 +177,7 @@ def test_pim_bsr_cand_bsr_r1(request): assertmsg = "r2: candidate bsr mismatch " assert result is None, assertmsg + def test_pim_bsr_cand_rp(request): "Test PIM BSR candidate RP" tgen = get_topogen() @@ -187,10 +190,7 @@ def test_pim_bsr_cand_rp(request): r3 = tgen.gears["r3"] # r3 is a candidate rp - expected = { - "address":"10.0.3.3", - "priority":10 - } + expected = {"address": "10.0.3.3", "priority": 10} test_func = partial( topotest.router_json_cmp, r3, "show ip pim bsr candidate-rp json", expected ) @@ -211,29 +211,95 @@ def test_pim_bsr_rp_info(request): # At this point, all nodes, including r5 should have synced the RP state step("Verify rp-info on r5 from BSR") - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/16", None, "10.0.3.3", - "BSR", False, "ipv4", True, retry_timeout = 90) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/16", + None, + "10.0.3.3", + "BSR", + False, + "ipv4", + True, + retry_timeout=90, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/8", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 30) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/8", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=30, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/24", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 30) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/24", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=30, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Verify rp-info on the BSR node itself r1") - result = verify_pim_rp_info(tgen, None, "r1", "239.0.0.0/16", None, "10.0.3.3", - "BSR", False, "ipv4", True, retry_timeout = 10) + result = verify_pim_rp_info( + tgen, + None, + "r1", + "239.0.0.0/16", + None, + "10.0.3.3", + "BSR", + False, + "ipv4", + True, + retry_timeout=10, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r1", "239.0.0.0/8", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 10) + result = verify_pim_rp_info( + tgen, + None, + "r1", + "239.0.0.0/8", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=10, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r1", "239.0.0.0/24", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 10) + result = verify_pim_rp_info( + tgen, + None, + "r1", + "239.0.0.0/24", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=10, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) @@ -253,7 +319,8 @@ def test_pim_bsr_election_fallback_r2(request): configure router pim no bsr candidate-bsr priority 200 source address 10.0.0.1 - """) + """ + ) step("Verify r1 is no longer a BSR candidate") expected = {} @@ -269,16 +336,14 @@ def test_pim_bsr_election_fallback_r2(request): r2 = tgen.gears["r2"] # We should fall back to r2 as the BSR expected = { - "bsr":"10.0.0.2", - "priority":100, - "state":"BSR_ELECTED", + "bsr": "10.0.0.2", + "priority": 100, + "state": "BSR_ELECTED", } step("Verify that we fallback to r2 as the new BSR") - test_func = partial( - topotest.router_json_cmp, r2, "show ip pim bsr json", expected - ) + test_func = partial(topotest.router_json_cmp, r2, "show ip pim bsr json", expected) _, result = topotest.run_and_expect(test_func, None, count=180, wait=1) assertmsg = "r2: failed to fallback to r2 as a BSR" @@ -301,12 +366,129 @@ def test_pim_bsr_rp_info_fallback(request): configure router pim no bsr candidate-rp group 239.0.0.0/16 - """) + """ + ) step("Verify falling back to r4 as the new RP for 239.0.0.0/16") - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/16", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 30) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/16", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=30, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + +def test_pimv6_bsr_election_r1(request): + "Test PIMv6 BSR Election" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r2 = tgen.gears["r2"] + # r1 should be the BSR winner because it has higher priority + expected = { + "bsr": "fd00::1", + "priority": 200, + "state": "ACCEPT_PREFERRED", + } + + test_func = partial( + topotest.router_json_cmp, r2, "show ipv6 pim bsr json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r2: r1 was not elected, IPv6 bsr election mismatch" + assert result is None, assertmsg + + +def test_pimv6_bsr_cand_rp(request): + "Test PIMv6 BSR candidate RP" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r3 = tgen.gears["r3"] + + # r3 is a candidate rp + expected = {"address": "fd00:0:0:3::3", "priority": 10} + test_func = partial( + topotest.router_json_cmp, r3, "show ipv6 pim bsr candidate-rp json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r3: bsr candidate rp mismatch" + assert result is None, assertmsg + + +def test_pimv6_bsr_rp_info(request): + "Test IPv6 RP info state" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # At this point, all nodes, including r5 should have synced the RP state + step("Verify rp-info on r5 from BSR") + result = verify_pim_rp_info( + tgen, + None, + "r5", + "ffbb::0/64", + None, + "fd00:0:0:3::3", + "BSR", + False, + "ipv6", + True, + retry_timeout=90, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_pim_rp_info( + tgen, + None, + "r5", + "ffbb::0/124", + None, + "fd00:0:0:3::4", + "BSR", + False, + "ipv6", + True, + retry_timeout=30, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_pim_rp_info( + tgen, + None, + "r5", + "ffbb::0/108", + None, + "fd00:0:0:3::4", + "BSR", + False, + "ipv6", + True, + retry_timeout=30, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index b234a84252..ef95ea1b95 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -32,7 +32,7 @@ log_file_date_format = %Y-%m-%d %H:%M:%S junit_logging = all junit_log_passing_tests = true -norecursedirs = .git example_munet example_test example_topojson_test lib munet docker +norecursedirs = .git example_munet example_test example_topojson_test lib munet docker high_ecmp # Directory to store test results and run logs in, default shown # rundir = /tmp/topotests diff --git a/tests/topotests/zebra_multiple_connected/r1/ip_route_kernel_blackhole.json b/tests/topotests/zebra_multiple_connected/r1/ip_route_kernel_blackhole.json new file mode 100644 index 0000000000..c8ea930370 --- /dev/null +++ b/tests/topotests/zebra_multiple_connected/r1/ip_route_kernel_blackhole.json @@ -0,0 +1,24 @@ +{ + "0.0.0.0/0":[ + { + "prefix":"0.0.0.0/0", + "prefixLen":0, + "protocol":"kernel", + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "fib":true, + "unreachable":true, + "blackhole":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py index 7dbeb6f1cc..eda8c88706 100644 --- a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py +++ b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py @@ -202,6 +202,27 @@ def test_zebra_kernel_route_add(): assert result, "Connected Route should have been added\n{}".format(_) +def test_zebra_kernel_route_blackhole_add(): + "Test that a blackhole route is not affected by interface's link change" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + router.run("ip route add blackhole default") + router.run("ip link set dev r1-eth1 down") + + kernel = "{}/{}/ip_route_kernel_blackhole.json".format(CWD, router.name) + expected = json.loads(open(kernel).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show ip route 0.0.0.0/0 json", expected + ) + result, _ = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result, "Blackhole Route should have not been removed\n{}".format(_) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_rib/r1/frr-import.conf b/tests/topotests/zebra_rib/r1/frr-import.conf new file mode 100644 index 0000000000..d07433144f --- /dev/null +++ b/tests/topotests/zebra_rib/r1/frr-import.conf @@ -0,0 +1,18 @@ +! +hostname r1 +password zebra +log file /tmp/r1-frr.log +! +interface r1-eth0 + ip address 10.0.0.1/24 +! +interface r1-eth1 + ip address 10.10.0.1/24 +! +ip route 10.1.0.0/24 10.0.0.2 table 10 +ip route 10.2.0.0/24 10.0.0.2 table 10 +ip route 10.3.0.0/24 10.10.0.2 table 10 +ip route 10.4.0.0/24 10.10.0.2 table 10 +! +ip forwarding +!
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_init_mrib_table.json b/tests/topotests/zebra_rib/r1/import_init_mrib_table.json new file mode 100644 index 0000000000..80c19dacf2 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_init_mrib_table.json @@ -0,0 +1,110 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_init_table.json b/tests/topotests/zebra_rib/r1/import_init_table.json new file mode 100644 index 0000000000..80c19dacf2 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_init_table.json @@ -0,0 +1,110 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_2.json b/tests/topotests/zebra_rib/r1/import_mrib_table_2.json new file mode 100644 index 0000000000..61aaaede6e --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_2.json @@ -0,0 +1,226 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.1.0.0/24": [ + { + "prefix": "10.1.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.2.0.0/24": [ + { + "prefix": "10.2.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.3.0.0/24": [ + { + "prefix": "10.3.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.4.0.0/24": [ + { + "prefix": "10.4.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_3.json b/tests/topotests/zebra_rib/r1/import_mrib_table_3.json new file mode 100644 index 0000000000..27a0b9f264 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_3.json @@ -0,0 +1,255 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.1.0.0/24": [ + { + "prefix": "10.1.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.2.0.0/24": [ + { + "prefix": "10.2.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.3.0.0/24": [ + { + "prefix": "10.3.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.4.0.0/24": [ + { + "prefix": "10.4.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.20.0.0/24": [ + { + "prefix": "10.20.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_4.json b/tests/topotests/zebra_rib/r1/import_mrib_table_4.json new file mode 100644 index 0000000000..5a8f0eecd5 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_4.json @@ -0,0 +1,226 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.1.0.0/24": [ + { + "prefix": "10.1.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.2.0.0/24": [ + { + "prefix": "10.2.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.3.0.0/24": [ + { + "prefix": "10.3.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.4.0.0/24": [ + { + "prefix": "10.4.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_table_2.json b/tests/topotests/zebra_rib/r1/import_table_2.json new file mode 100644 index 0000000000..61aaaede6e --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_table_2.json @@ -0,0 +1,226 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.1.0.0/24": [ + { + "prefix": "10.1.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.2.0.0/24": [ + { + "prefix": "10.2.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.3.0.0/24": [ + { + "prefix": "10.3.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.4.0.0/24": [ + { + "prefix": "10.4.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_table_3.json b/tests/topotests/zebra_rib/r1/import_table_3.json new file mode 100644 index 0000000000..27a0b9f264 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_table_3.json @@ -0,0 +1,255 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.1.0.0/24": [ + { + "prefix": "10.1.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.2.0.0/24": [ + { + "prefix": "10.2.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.3.0.0/24": [ + { + "prefix": "10.3.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.4.0.0/24": [ + { + "prefix": "10.4.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.20.0.0/24": [ + { + "prefix": "10.20.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 15, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/import_table_4.json b/tests/topotests/zebra_rib/r1/import_table_4.json new file mode 100644 index 0000000000..5a8f0eecd5 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/import_table_4.json @@ -0,0 +1,226 @@ +{ + "10.0.0.0/24": [ + { + "prefix": "10.0.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.0.0.1/32": [ + { + "prefix": "10.0.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.1.0.0/24": [ + { + "prefix": "10.1.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.2.0.0/24": [ + { + "prefix": "10.2.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.0.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "10.3.0.0/24": [ + { + "prefix": "10.3.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.4.0.0/24": [ + { + "prefix": "10.4.0.0/24", + "prefixLen": 24, + "protocol": "table", + "instance": 10, + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 123, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.10.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.0/24": [ + { + "prefix": "10.10.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ], + "10.10.0.1/32": [ + { + "prefix": "10.10.0.1/32", + "prefixLen": 32, + "protocol": "local", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true, + "weight": 1 + } + ] + } + ] +}
\ No newline at end of file diff --git a/tests/topotests/zebra_rib/r1/sharp_rmap.ref b/tests/topotests/zebra_rib/r1/sharp_rmap.ref index 47a9eb6a49..d80a5f661e 100644 --- a/tests/topotests/zebra_rib/r1/sharp_rmap.ref +++ b/tests/topotests/zebra_rib/r1/sharp_rmap.ref @@ -1,6 +1,6 @@ ZEBRA: -route-map: sharp Invoked: 500 Optimization: enabled Processed Change: false - permit, sequence 10 Invoked 244 +route-map: sharp Invoked: 500 (X milliseconds total) Optimization: enabled Processed Change: false + permit, sequence 10 Invoked 244 (X milliseconds total) Match clauses: ip address 10 Set clauses: @@ -8,7 +8,7 @@ route-map: sharp Invoked: 500 Optimization: enabled Processed Change: false Call clause: Action: Exit routemap - permit, sequence 20 Invoked 256 + permit, sequence 20 Invoked 256 (X milliseconds total) Match clauses: Set clauses: src 192.168.213.1 diff --git a/tests/topotests/zebra_rib/r1/static_rmap.ref b/tests/topotests/zebra_rib/r1/static_rmap.ref index 2de98bd514..68e4a5f7b6 100644 --- a/tests/topotests/zebra_rib/r1/static_rmap.ref +++ b/tests/topotests/zebra_rib/r1/static_rmap.ref @@ -1,6 +1,6 @@ ZEBRA: -route-map: static Invoked: 2 Optimization: enabled Processed Change: false - permit, sequence 10 Invoked 2 +route-map: static Invoked: 2 (X milliseconds total) Optimization: enabled Processed Change: false + permit, sequence 10 Invoked 2 (X milliseconds total) Match clauses: Set clauses: src 192.168.215.1 diff --git a/tests/topotests/zebra_rib/test_zebra_import.py b/tests/topotests/zebra_rib/test_zebra_import.py new file mode 100644 index 0000000000..7819548ad2 --- /dev/null +++ b/tests/topotests/zebra_rib/test_zebra_import.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_zebra_import.py +# +# Copyright (c) 2024 ATCorp +# Nathan Bahr +# + +import os +import sys +from functools import partial +import pytest +import json +import platform + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger +from lib.common_config import step, write_test_header + +""" +test_zebra_import.py: Test zebra table import functionality +""" + +TOPOLOGY = """ + Single router zebra functionality + + +---+---+ + 10.0.0.1/24 | | 10.10.0.1/24 + <--->+ R1 +<---> + | | + +---+---+ +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +pytestmark = [pytest.mark.sharpd] +krel = platform.release() + +def build_topo(tgen): + "Build function" + + tgen.add_router("r1") + sw1 = tgen.add_switch("sw1") + sw2 = tgen.add_switch("sw2") + sw1.add_link(tgen.gears["r1"], "r1-eth0") + sw2.add_link(tgen.gears["r1"], "r1-eth1") + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + router.load_frr_config(os.path.join(CWD, "{}/frr-import.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + for router in router_list.values(): + if router.has_version("<", "4.0"): + tgen.set_error("unsupported version") + + +def teardown_module(): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_zebra_urib_import(request): + "Verify router starts with the initial URIB" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + step("Verify initial main routing table") + initial_json_file = "{}/r1/import_init_table.json".format(CWD) + expected = json.loads(open(initial_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + r1.vtysh_cmd( + """ + conf term + ip import-table 10 + """) + + import_json_file = "{}/r1/import_table_2.json".format(CWD) + expected = json.loads(open(import_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Add a new static route and verify it gets added") + r1.vtysh_cmd( + """ + conf term + ip route 10.20.0.0/24 10.10.0.2 table 10 + """ + ) + + sync_json_file = "{}/r1/import_table_3.json".format(CWD) + expected = json.loads(open(sync_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Remove the static route and verify it gets removed") + r1.vtysh_cmd( + """ + conf term + no ip route 10.20.0.0/24 10.10.0.2 table 10 + """ + ) + + expected = json.loads(open(import_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Disable table import and verify it goes back to the initial table") + r1.vtysh_cmd( + """ + conf term + no ip import-table 10 + """ + ) + + expected = json.loads(open(initial_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Re-import with distance and verify correct distance") + r1.vtysh_cmd( + """ + conf term + ip import-table 10 distance 123 + """) + + import_json_file = "{}/r1/import_table_4.json".format(CWD) + expected = json.loads(open(import_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + +def test_zebra_mrib_import(request): + "Verify router starts with the initial MRIB" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + step("Verify initial main MRIB routing table") + initial_json_file = "{}/r1/import_init_mrib_table.json".format(CWD) + expected = json.loads(open(initial_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip rpf json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + r1.vtysh_cmd( + """ + conf term + ip import-table 10 mrib + """) + + import_json_file = "{}/r1/import_mrib_table_2.json".format(CWD) + expected = json.loads(open(import_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip rpf json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Add a new static route and verify it gets added") + r1.vtysh_cmd( + """ + conf term + ip route 10.20.0.0/24 10.10.0.2 table 10 + """ + ) + + sync_json_file = "{}/r1/import_mrib_table_3.json".format(CWD) + expected = json.loads(open(sync_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip rpf json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Remove the static route and verify it gets removed") + r1.vtysh_cmd( + """ + conf term + no ip route 10.20.0.0/24 10.10.0.2 table 10 + """ + ) + + expected = json.loads(open(import_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip rpf json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Disable table import and verify it goes back to the initial table") + r1.vtysh_cmd( + """ + conf term + no ip import-table 10 mrib + """ + ) + + expected = json.loads(open(initial_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip rpf json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + step("Re-import with distance and verify correct distance") + r1.vtysh_cmd( + """ + conf term + ip import-table 10 mrib distance 123 + """) + + import_json_file = "{}/r1/import_mrib_table_4.json".format(CWD) + expected = json.loads(open(import_json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip rpf json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, '"r1" JSON output mismatches' + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index c0a79ed79d..d1aee46b40 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -246,6 +246,7 @@ def test_route_map_usage(): def check_static_map_correct_runs(): actual = r1.vtysh_cmd("show route-map static") + actual = re.sub(r"\([0-9].* milli", "(X milli", actual) actual = ("\n".join(actual.splitlines()) + "\n").rstrip() return topotest.get_textdiff( actual, @@ -266,6 +267,7 @@ def test_route_map_usage(): def check_sharp_map_correct_runs(): actual = r1.vtysh_cmd("show route-map sharp") + actual = re.sub(r"\([0-9].* milli", "(X milli", actual) actual = ("\n".join(actual.splitlines()) + "\n").rstrip() return topotest.get_textdiff( actual, diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 4ed80d7d07..5f701d514c 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -532,7 +532,7 @@ identity set-extcommunity-color { typedef color-list { type string { - pattern '((429496729[0-5]|42949672[0-8][0-9]|' + pattern '((00|01|10|11):(429496729[0-5]|42949672[0-8][0-9]|' + '4294967[0-1][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}|' @@ -540,10 +540,11 @@ identity set-extcommunity-color { + '[1-9][0-9]{0,8})(\s*))+'; } description - "The color-list type represent a set of colors of value (1..4294967295) + "The color-list type represent a set of colors of value (examples 00:200 01:200 10:200) values are separated by white spaces"; reference - "RFC 9012 - The BGP Tunnel Encapsulation Attribute"; + "RFC 9012 - The BGP Tunnel Encapsulation Attribute. + RFC 9256 - Segment Routing Policy Architecture."; } augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:rmap-match-condition/frr-route-map:match-condition" { diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 60914b0be9..a3e073f626 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -1922,7 +1922,7 @@ module frr-isisd { if set to disable, ISISEXPLICITNULLFlag will override the value of ISISPHPFlag"; leaf algo { - type uint32 { + type uint8 { range "128..255"; } description diff --git a/zebra/debug.c b/zebra/debug.c index cf1701be19..7b6a19fa1d 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -29,6 +29,7 @@ unsigned long zebra_debug_evpn_mh; unsigned long zebra_debug_pbr; unsigned long zebra_debug_neigh; unsigned long zebra_debug_tc; +unsigned long zebra_debug_srv6; DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); @@ -121,6 +122,9 @@ DEFUN_NOSH (show_debugging_zebra, if (IS_ZEBRA_DEBUG_PBR) vty_out(vty, " Zebra PBR debugging is on\n"); + if (IS_ZEBRA_DEBUG_SRV6) + vty_out(vty, " Zebra SRv6 is on\n"); + hook_call(zebra_debug_show_debugging, vty); cmd_show_lib_debugs(vty); @@ -372,6 +376,21 @@ DEFUN (debug_zebra_tc, return CMD_SUCCESS; } +DEFPY(debug_zebra_srv6, + debug_zebra_srv6_cmd, + "[no$no] debug zebra srv6", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra SRv6 events\n") +{ + if (no) + UNSET_FLAG(zebra_debug_srv6, ZEBRA_DEBUG_SRV6); + else + SET_FLAG(zebra_debug_srv6, ZEBRA_DEBUG_SRV6); + return CMD_SUCCESS; +} + DEFPY (debug_zebra_mlag, debug_zebra_mlag_cmd, "[no$no] debug zebra mlag", @@ -754,6 +773,11 @@ static int config_write_debug(struct vty *vty) write++; } + if (IS_ZEBRA_DEBUG_SRV6) { + vty_out(vty, "debug zebra srv6\n"); + write++; + } + return write; } @@ -793,6 +817,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_rib_cmd); install_element(ENABLE_NODE, &debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &debug_zebra_dplane_cmd); + install_element(ENABLE_NODE, &debug_zebra_srv6_cmd); install_element(ENABLE_NODE, &debug_zebra_mlag_cmd); install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd); install_element(ENABLE_NODE, &debug_zebra_pbr_cmd); @@ -845,6 +870,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &no_debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &no_debug_zebra_dplane_cmd); install_element(CONFIG_NODE, &no_debug_zebra_pbr_cmd); + install_element(CONFIG_NODE, &debug_zebra_srv6_cmd); install_element(CONFIG_NODE, &debug_zebra_mlag_cmd); install_element(CONFIG_NODE, &debug_zebra_evpn_mh_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index 075d903c6b..b4e5ee4b5b 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -62,6 +62,8 @@ extern "C" { #define ZEBRA_DEBUG_TC 0x01 +#define ZEBRA_DEBUG_SRV6 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -122,6 +124,8 @@ extern "C" { #define IS_ZEBRA_DEBUG_TC (zebra_debug_tc & ZEBRA_DEBUG_TC) +#define IS_ZEBRA_DEBUG_SRV6 (zebra_debug_srv6 & ZEBRA_DEBUG_SRV6) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; @@ -139,6 +143,7 @@ extern unsigned long zebra_debug_evpn_mh; extern unsigned long zebra_debug_pbr; extern unsigned long zebra_debug_neigh; extern unsigned long zebra_debug_tc; +extern unsigned long zebra_debug_srv6; extern void zebra_debug_init(void); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 8a967978cb..e6b4af3674 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -1712,6 +1712,16 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov) * anyway. */ if (fnc->socket != -1 && fnc->connecting == false) { + enum dplane_op_e op = dplane_ctx_get_op(ctx); + + /* + * Just skip multicast routes and let them flow through + */ + if ((op == DPLANE_OP_ROUTE_DELETE || op == DPLANE_OP_ROUTE_INSTALL || + op == DPLANE_OP_ROUTE_UPDATE) && + dplane_ctx_get_safi(ctx) == SAFI_MULTICAST) + goto skip; + frr_with_mutex (&fnc->ctxqueue_mutex) { dplane_ctx_enqueue_tail(&fnc->ctxqueue, ctx); cur_queue = @@ -1722,7 +1732,7 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov) peak_queue = cur_queue; continue; } - +skip: dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); dplane_provider_enqueue_out_ctx(prov, ctx); } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 62b665682f..7ef3fa2e61 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1568,6 +1568,15 @@ static int netlink_request_tunneldump(struct zebra_ns *zns, int family, return netlink_request(&zns->netlink_cmd, &req); } +/* Prototype for tunneldump walker */ +static int tunneldump_walk_cb(struct interface *ifp, void *arg); + +struct tunneldump_ctx { + struct zebra_ns *zns; + struct zebra_dplane_info *dp_info; + int ret; +}; + /* * Currently we only ask for vxlan l3svd vni information. * In the future this can be expanded. @@ -1575,39 +1584,48 @@ static int netlink_request_tunneldump(struct zebra_ns *zns, int family, int netlink_tunneldump_read(struct zebra_ns *zns) { int ret = 0; + struct tunneldump_ctx ctx = {}; struct zebra_dplane_info dp_info; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct nlsock *netlink_cmd = &zns->netlink_cmd; zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; + /* Set up context and call iterator */ + ctx.zns = zns; + ctx.dp_info = &dp_info; - ret = netlink_request_tunneldump(zns, PF_BRIDGE, - tmp_if->ifindex); - if (ret < 0) { - route_unlock_node(rn); - return ret; - } + zebra_ns_ifp_walk(zns, tunneldump_walk_cb, &ctx); - ret = netlink_parse_info(netlink_link_change, netlink_cmd, - &dp_info, 0, true); + ret = ctx.ret; - if (ret < 0) { - route_unlock_node(rn); - return ret; - } + return ret; +} + +static int tunneldump_walk_cb(struct interface *ifp, void *arg) +{ + int ret; + struct tunneldump_ctx *ctx = arg; + struct zebra_if *zif; + + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + goto done; + + ret = netlink_request_tunneldump(ctx->zns, PF_BRIDGE, ifp->ifindex); + if (ret < 0) { + ctx->ret = ret; + return NS_WALK_STOP; } - return 0; + ret = netlink_parse_info(netlink_link_change, &(ctx->zns->netlink_cmd), + ctx->dp_info, 0, true); + + if (ret < 0) { + ctx->ret = ret; + return NS_WALK_STOP; + } + +done: + return NS_WALK_CONTINUE; } static uint8_t netlink_get_dplane_vlan_state(uint8_t state) diff --git a/zebra/interface.c b/zebra/interface.c index f1f1b17209..86de5dbae6 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -43,7 +43,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information"); DEFINE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), (vty, ifp)); -DEFINE_MTYPE(ZEBRA, ZIF_DESC, "Intf desc"); +DEFINE_MTYPE_STATIC(ZEBRA, ZIF_DESC, "Intf desc"); static void if_down_del_nbr_connected(struct interface *ifp); @@ -215,6 +215,8 @@ static int if_zebra_delete_hook(struct interface *ifp) if_nhg_dependents_release(ifp); nhg_connected_tree_free(&zebra_if->nhg_dependents); + zebra_ns_unlink_ifp(ifp); + XFREE(MTYPE_ZIF_DESC, zebra_if->desc); EVENT_OFF(zebra_if->speed_update); @@ -225,62 +227,14 @@ static int if_zebra_delete_hook(struct interface *ifp) return 0; } -/* Build the table key */ -static void if_build_key(uint32_t ifindex, struct prefix *p) -{ - p->family = AF_INET; - p->prefixlen = IPV4_MAX_BITLEN; - p->u.prefix4.s_addr = ifindex; -} - -/* Link an interface in a per NS interface tree */ -struct interface *if_link_per_ns(struct zebra_ns *ns, struct interface *ifp) -{ - struct prefix p; - struct route_node *rn; - - if (ifp->ifindex == IFINDEX_INTERNAL) - return NULL; - - if_build_key(ifp->ifindex, &p); - rn = route_node_get(ns->if_table, &p); - if (rn->info) { - ifp = (struct interface *)rn->info; - route_unlock_node(rn); /* get */ - return ifp; - } - - rn->info = ifp; - ifp->node = rn; - - return ifp; -} - -/* Delete a VRF. This is called in vrf_terminate(). */ -void if_unlink_per_ns(struct interface *ifp) -{ - if (!ifp->node) - return; - - ifp->node->info = NULL; - route_unlock_node(ifp->node); - ifp->node = NULL; -} - /* Look up an interface by identifier within a NS */ struct interface *if_lookup_by_index_per_ns(struct zebra_ns *ns, uint32_t ifindex) { - struct prefix p; - struct route_node *rn; struct interface *ifp = NULL; - if_build_key(ifindex, &p); - rn = route_node_lookup(ns->if_table, &p); - if (rn) { - ifp = (struct interface *)rn->info; - route_unlock_node(rn); /* lookup */ - } + ifp = zebra_ns_lookup_ifp(ns, ifindex); + return ifp; } @@ -288,18 +242,11 @@ struct interface *if_lookup_by_index_per_ns(struct zebra_ns *ns, struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns, const char *ifname) { - struct route_node *rn; struct interface *ifp; - for (rn = route_top(ns->if_table); rn; rn = route_next(rn)) { - ifp = (struct interface *)rn->info; - if (ifp && strcmp(ifp->name, ifname) == 0) { - route_unlock_node(rn); - return (ifp); - } - } + ifp = zebra_ns_lookup_ifp_name(ns, ifname); - return NULL; + return ifp; } struct interface *if_lookup_by_index_per_nsid(ns_id_t ns_id, uint32_t ifindex) @@ -571,7 +518,8 @@ void if_add_update(struct interface *ifp) zns = zvrf->zns; else zns = zebra_ns_lookup(NS_DEFAULT); - if_link_per_ns(zns, ifp); + + zebra_ns_link_ifp(zns, ifp); if_data = ifp->info; assert(if_data); @@ -776,7 +724,7 @@ void if_delete_update(struct interface **pifp) /* Send out notification on interface delete. */ zebra_interface_delete_update(ifp); - if_unlink_per_ns(ifp); + zebra_ns_unlink_ifp(ifp); /* Update ifindex after distributing the delete message. This is in case any client needs to have the old value of ifindex available @@ -784,7 +732,6 @@ void if_delete_update(struct interface **pifp) for setting ifindex to IFINDEX_INTERNAL after processing the interface deletion message. */ if_set_index(ifp, IFINDEX_INTERNAL); - ifp->node = NULL; UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); @@ -1082,50 +1029,52 @@ void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, } /* - * during initial link dump kernel does not order lower devices before - * upper devices so we need to fixup link dependencies at the end of dump + * Callback for per-ns link fixup iteration */ -void zebra_if_update_all_links(struct zebra_ns *zns) +static int zif_link_fixup_cb(struct interface *ifp, void *arg) { - struct route_node *rn; - struct interface *ifp; struct zebra_if *zif; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_info("fixup link dependencies"); + zif = ifp->info; + /* update bond-member to bond linkages */ + if ((IS_ZEBRA_IF_BOND_SLAVE(ifp)) && + (zif->bondslave_info.bond_ifindex != IFINDEX_INTERNAL) && + !zif->bondslave_info.bond_if) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("bond mbr %s map to bond %d", zif->ifp->name, + zif->bondslave_info.bond_ifindex); + zebra_l2_map_slave_to_bond(zif, ifp->vrf->vrf_id); + } - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - ifp = (struct interface *)rn->info; - if (!ifp) - continue; - zif = ifp->info; - /* update bond-member to bond linkages */ - if ((IS_ZEBRA_IF_BOND_SLAVE(ifp)) - && (zif->bondslave_info.bond_ifindex != IFINDEX_INTERNAL) - && !zif->bondslave_info.bond_if) { - if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("bond mbr %s map to bond %d", - zif->ifp->name, - zif->bondslave_info.bond_ifindex); - zebra_l2_map_slave_to_bond(zif, ifp->vrf->vrf_id); - } + /* update SVI linkages */ + if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) { + zif->link = if_lookup_by_index_per_nsid(zif->link_nsid, + zif->link_ifindex); + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("interface %s/%d's lower fixup to %s/%d", + ifp->name, ifp->ifindex, + zif->link ? zif->link->name : "unk", + zif->link_ifindex); + } - /* update SVI linkages */ - if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) { - zif->link = if_lookup_by_index_per_nsid( - zif->link_nsid, zif->link_ifindex); - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("interface %s/%d's lower fixup to %s/%d", - ifp->name, ifp->ifindex, - zif->link?zif->link->name:"unk", - zif->link_ifindex); - } + /* Update VLAN<=>SVI map */ + if (IS_ZEBRA_IF_VLAN(ifp)) + zebra_evpn_acc_bd_svi_set(zif, NULL, + !!if_is_operative(ifp)); - /* Update VLAN<=>SVI map */ - if (IS_ZEBRA_IF_VLAN(ifp)) - zebra_evpn_acc_bd_svi_set(zif, NULL, - !!if_is_operative(ifp)); - } + return NS_WALK_CONTINUE; +} + +/* + * during initial link dump kernel does not order lower devices before + * upper devices so we need to fixup link dependencies at the end of dump + */ +void zebra_if_update_all_links(struct zebra_ns *zns) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("fixup link dependencies"); + + zebra_ns_ifp_walk(zns, zif_link_fixup_cb, NULL); } static bool if_ignore_set_protodown(const struct interface *ifp, bool new_down, diff --git a/zebra/interface.h b/zebra/interface.h index 8d19c1838f..2c7a079bf4 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -94,9 +94,6 @@ enum zebra_if_flags { #define ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) \ ((zif)->protodown_rc == ZEBRA_PROTODOWN_EXTERNAL) -/* Mem type for zif desc */ -DECLARE_MTYPE(ZIF_DESC); - /* `zebra' daemon local interface structure. */ struct zebra_if { /* back pointer to the interface */ @@ -215,6 +212,9 @@ struct zebra_if { char neigh_mac[6]; struct in6_addr v6_2_v4_ll_addr6; + /* Linkage for per-vrf/per-NS ifp container */ + struct ifp_tree_link *ns_tree_link; + /* The description of the interface */ char *desc; }; @@ -262,12 +262,10 @@ extern void zebra_if_init(void); extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, uint32_t); extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *, const char *); -extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *); extern struct interface *if_lookup_by_index_per_nsid(ns_id_t nsid, uint32_t ifindex); extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int); -extern void if_unlink_per_ns(struct interface *); extern void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *fip, char mac[6], struct in6_addr *address, diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 2148d131ec..0c607dfa67 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -932,7 +932,7 @@ static int netlink_recv_msg(struct nlsock *nl, struct msghdr *msg) } while (status == -1 && errno == EINTR); if (status == -1) { - if (errno == EWOULDBLOCK || errno == EAGAIN) + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EMSGSIZE) return 0; flog_err(EC_ZEBRA_RECVMSG_OVERRUN, "%s recvmsg overrun: %s", nl->name, safe_strerror(errno)); diff --git a/zebra/main.c b/zebra/main.c index dd6249eff5..138a955bc3 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -54,6 +54,8 @@ #define ZEBRA_PTM_SUPPORT +char *zserv_path; + /* process id. */ pid_t pid; @@ -314,17 +316,46 @@ FRR_DAEMON_INFO(zebra, ZEBRA, ); /* clang-format on */ +void zebra_main_router_started(void) +{ + /* + * Clean up zebra-originated routes. The requests will be sent to OS + * immediately, so originating PID in notifications from kernel + * will be equal to the current getpid(). To know about such routes, + * we have to have route_read() called before. + * If FRR is gracefully restarting, we either wait for clients + * (e.g., BGP) to signal GR is complete else we wait for specified + * duration. + */ + zrouter.startup_time = monotime(NULL); + zrouter.rib_sweep_time = 0; + zrouter.graceful_restart = zebra_di.graceful_restart; + if (!zrouter.graceful_restart) + event_add_timer(zrouter.master, rib_sweep_route, NULL, 0, NULL); + else { + int gr_cleanup_time; + + gr_cleanup_time = zebra_di.gr_cleanup_time ? zebra_di.gr_cleanup_time + : ZEBRA_GR_DEFAULT_RIB_SWEEP_TIME; + event_add_timer(zrouter.master, rib_sweep_route, NULL, gr_cleanup_time, + &zrouter.t_rib_sweep); + } + + zserv_start(zserv_path); +} + /* Main startup routine. */ int main(int argc, char **argv) { // int batch_mode = 0; - char *zserv_path = NULL; struct sockaddr_storage dummy; socklen_t dummylen; bool asic_offload = false; bool v6_with_v4_nexthop = false; bool notify_on_ack = true; + zserv_path = NULL; + vrf_configure_backend(VRF_BACKEND_VRF_LITE); frr_preinit(&zebra_di, argc, argv); @@ -341,7 +372,7 @@ int main(int argc, char **argv) " -e, --ecmp Specify ECMP to use.\n" " -r, --retain When program terminates, retain added route by zebra.\n" " -A, --asic-offload FRR is interacting with an asic underneath the linux kernel\n" - " --v6-with-v4-nexthops Underlying dataplane supports v6 routes with v4 nexthops" + " --v6-with-v4-nexthops Underlying dataplane supports v6 routes with v4 nexthops\n" #ifdef HAVE_NETLINK " -s, --nl-bufsize Set netlink receive buffer size\n" " -n, --vrfwnetns Use NetNS as VRF backend\n" @@ -475,31 +506,11 @@ int main(int argc, char **argv) */ frr_config_fork(); - /* After we have successfully acquired the pidfile, we can be sure - * about being the only copy of zebra process, which is submitting - * changes to the FIB. - * Clean up zebra-originated routes. The requests will be sent to OS - * immediately, so originating PID in notifications from kernel - * will be equal to the current getpid(). To know about such routes, - * we have to have route_read() called before. - * If FRR is gracefully restarting, we either wait for clients - * (e.g., BGP) to signal GR is complete else we wait for specified - * duration. - */ - zrouter.startup_time = monotime(NULL); - zrouter.rib_sweep_time = 0; - zrouter.graceful_restart = zebra_di.graceful_restart; - if (!zrouter.graceful_restart) - event_add_timer(zrouter.master, rib_sweep_route, NULL, 0, NULL); - else { - int gr_cleanup_time; - - gr_cleanup_time = zebra_di.gr_cleanup_time - ? zebra_di.gr_cleanup_time - : ZEBRA_GR_DEFAULT_RIB_SWEEP_TIME; - event_add_timer(zrouter.master, rib_sweep_route, NULL, - gr_cleanup_time, &zrouter.t_rib_sweep); - } + /* + * After we have successfully acquired the pidfile, we can be sure + * about being the only copy of zebra process, which is submitting + * changes to the FIB. + */ /* Needed for BSD routing socket. */ pid = getpid(); @@ -510,9 +521,6 @@ int main(int argc, char **argv) /* Start the ted module, before zserv */ zebra_opaque_start(); - /* Start Zebra API server */ - zserv_start(zserv_path); - /* Init label manager */ label_manager_init(); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 2de0917a7e..66dc5b4b5f 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -35,10 +35,10 @@ /* array holding redistribute info about table redistribution */ /* bit AFI is set if that AFI is redistributing routes from this table */ -static int zebra_import_table_used[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; -static uint32_t zebra_import_table_distance[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; +static int zebra_import_table_used[AFI_MAX][SAFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; +static uint32_t zebra_import_table_distance[AFI_MAX][SAFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; -int is_zebra_import_table_enabled(afi_t afi, vrf_id_t vrf_id, uint32_t table_id) +int is_zebra_import_table_enabled(afi_t afi, safi_t safi, vrf_id_t vrf_id, uint32_t table_id) { /* * Make sure that what we are called with actualy makes sense @@ -46,9 +46,12 @@ int is_zebra_import_table_enabled(afi_t afi, vrf_id_t vrf_id, uint32_t table_id) if (afi == AFI_MAX) return 0; + if (safi == SAFI_MAX) + return 0; + if (is_zebra_valid_kernel_table(table_id) && table_id < ZEBRA_KERNEL_TABLE_MAX) - return zebra_import_table_used[afi][table_id]; + return zebra_import_table_used[afi][safi][table_id]; return 0; } @@ -687,7 +690,7 @@ void zebra_interface_vrf_update_add(struct interface *ifp, vrf_id_t old_vrf_id) } } -int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, +int zebra_add_import_table_entry(struct zebra_vrf *zvrf, safi_t safi, struct route_node *rn, struct route_entry *re, const char *rmap_name) { struct route_entry *newre; @@ -705,7 +708,7 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, if (ret != RMAP_PERMITMATCH) { UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); - zebra_del_import_table_entry(zvrf, rn, re); + zebra_del_import_table_entry(zvrf, safi, rn, re); return 0; } @@ -724,26 +727,26 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, if (same) { UNSET_FLAG(same->flags, ZEBRA_FLAG_SELECTED); - zebra_del_import_table_entry(zvrf, rn, same); + zebra_del_import_table_entry(zvrf, safi, rn, same); } UNSET_FLAG(re->flags, ZEBRA_FLAG_RR_USE_DISTANCE); - newre = zebra_rib_route_entry_new( - 0, ZEBRA_ROUTE_TABLE, re->table, re->flags, re->nhe_id, - zvrf->table_id, re->metric, re->mtu, - zebra_import_table_distance[afi][re->table], re->tag); + newre = zebra_rib_route_entry_new(0, ZEBRA_ROUTE_TABLE, re->table, re->flags, re->nhe_id, + zvrf->table_id, re->metric, re->mtu, + zebra_import_table_distance[afi][safi][re->table], + re->tag); ng = nexthop_group_new(); copy_nexthops(&ng->nexthop, re->nhe->nhg.nexthop, NULL); - rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre, ng, false); + rib_add_multipath(afi, safi, &p, NULL, newre, ng, false); nexthop_group_delete(&ng); return 0; } -int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, +int zebra_del_import_table_entry(struct zebra_vrf *zvrf, safi_t safi, struct route_node *rn, struct route_entry *re) { struct prefix p; @@ -752,17 +755,16 @@ int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, afi = family2afi(rn->p.family); prefix_copy(&p, &rn->p); - rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE, - re->table, re->flags, &p, NULL, re->nhe->nhg.nexthop, - re->nhe_id, zvrf->table_id, re->metric, re->distance, + rib_delete(afi, safi, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE, re->table, re->flags, &p, NULL, + re->nhe->nhg.nexthop, re->nhe_id, zvrf->table_id, re->metric, re->distance, false); return 0; } /* Assuming no one calls this with the main routing table */ -int zebra_import_table(afi_t afi, vrf_id_t vrf_id, uint32_t table_id, - uint32_t distance, const char *rmap_name, int add) +int zebra_import_table(afi_t afi, safi_t safi, vrf_id_t vrf_id, uint32_t table_id, + uint32_t distance, const char *rmap_name, bool add) { struct route_table *table; struct route_entry *re; @@ -776,38 +778,39 @@ int zebra_import_table(afi_t afi, vrf_id_t vrf_id, uint32_t table_id, if (afi >= AFI_MAX) return -1; + if (safi >= SAFI_MAX) + return -1; + + /* Always import from the URIB sub-table */ table = zebra_vrf_get_table_with_table_id(afi, SAFI_UNICAST, vrf_id, table_id); if (table == NULL) { return 0; } else if (IS_ZEBRA_DEBUG_RIB) { - zlog_debug("%s routes from table %d", - add ? "Importing" : "Unimporting", table_id); + zlog_debug("%s routes from table %d into %s", add ? "Importing" : "Unimporting", + table_id, safi2str(safi)); } if (add) { if (rmap_name) - zebra_add_import_table_route_map(afi, rmap_name, - table_id); + zebra_add_import_table_route_map(afi, safi, rmap_name, table_id); else { - rmap_name = - zebra_get_import_table_route_map(afi, table_id); + rmap_name = zebra_get_import_table_route_map(afi, safi, table_id); if (rmap_name) { - zebra_del_import_table_route_map(afi, table_id); + zebra_del_import_table_route_map(afi, safi, table_id); rmap_name = NULL; } } - zebra_import_table_used[afi][table_id] = 1; - zebra_import_table_distance[afi][table_id] = distance; + zebra_import_table_used[afi][safi][table_id] = 1; + zebra_import_table_distance[afi][safi][table_id] = distance; } else { - zebra_import_table_used[afi][table_id] = 0; - zebra_import_table_distance[afi][table_id] = - ZEBRA_TABLE_DISTANCE_DEFAULT; + zebra_import_table_used[afi][safi][table_id] = 0; + zebra_import_table_distance[afi][safi][table_id] = ZEBRA_TABLE_DISTANCE_DEFAULT; - rmap_name = zebra_get_import_table_route_map(afi, table_id); + rmap_name = zebra_get_import_table_route_map(afi, safi, table_id); if (rmap_name) { - zebra_del_import_table_route_map(afi, table_id); + zebra_del_import_table_route_map(afi, safi, table_id); rmap_name = NULL; } } @@ -831,10 +834,9 @@ int zebra_import_table(afi_t afi, vrf_id_t vrf_id, uint32_t table_id, if (((afi == AFI_IP) && (rn->p.family == AF_INET)) || ((afi == AFI_IP6) && (rn->p.family == AF_INET6))) { if (add) - zebra_add_import_table_entry(zvrf, rn, re, - rmap_name); + zebra_add_import_table_entry(zvrf, safi, rn, re, rmap_name); else - zebra_del_import_table_entry(zvrf, rn, re); + zebra_del_import_table_entry(zvrf, safi, rn, re); } } return 0; @@ -844,26 +846,27 @@ int zebra_import_table_config(struct vty *vty, vrf_id_t vrf_id) { int i; afi_t afi; + safi_t safi; int write = 0; char afi_str[AFI_MAX][10] = {"", "ip", "ipv6", "ethernet"}; const char *rmap_name; - for (afi = AFI_IP; afi < AFI_MAX; afi++) { + FOREACH_AFI_SAFI (afi, safi) { for (i = 1; i < ZEBRA_KERNEL_TABLE_MAX; i++) { - if (!is_zebra_import_table_enabled(afi, vrf_id, i)) + if (!is_zebra_import_table_enabled(afi, safi, vrf_id, i)) continue; - if (zebra_import_table_distance[afi][i] - != ZEBRA_TABLE_DISTANCE_DEFAULT) { - vty_out(vty, "%s import-table %d distance %d", - afi_str[afi], i, - zebra_import_table_distance[afi][i]); + if (zebra_import_table_distance[afi][safi][i] != + ZEBRA_TABLE_DISTANCE_DEFAULT) { + vty_out(vty, "%s import-table %d %sdistance %d", afi_str[afi], i, + (safi == SAFI_MULTICAST ? "mrib " : ""), + zebra_import_table_distance[afi][safi][i]); } else { - vty_out(vty, "%s import-table %d", afi_str[afi], - i); + vty_out(vty, "%s import-table %d%s", afi_str[afi], i, + (safi == SAFI_MULTICAST ? " mrib" : "")); } - rmap_name = zebra_get_import_table_route_map(afi, i); + rmap_name = zebra_get_import_table_route_map(afi, safi, i); if (rmap_name) vty_out(vty, " route-map %s", rmap_name); @@ -875,21 +878,19 @@ int zebra_import_table_config(struct vty *vty, vrf_id_t vrf_id) return write; } -static void zebra_import_table_rm_update_vrf_afi(struct zebra_vrf *zvrf, - afi_t afi, int table_id, - const char *rmap) +static void zebra_import_table_rm_update_vrf_afi(struct zebra_vrf *zvrf, afi_t afi, safi_t safi, + int table_id, const char *rmap) { struct route_table *table; struct route_entry *re; struct route_node *rn; const char *rmap_name; - rmap_name = zebra_get_import_table_route_map(afi, table_id); + rmap_name = zebra_get_import_table_route_map(afi, safi, table_id); if ((!rmap_name) || (strcmp(rmap_name, rmap) != 0)) return; - table = zebra_vrf_get_table_with_table_id(afi, SAFI_UNICAST, - zvrf->vrf->vrf_id, table_id); + table = zebra_vrf_get_table_with_table_id(afi, safi, zvrf->vrf->vrf_id, table_id); if (!table) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug("%s: Table id=%d not found", __func__, @@ -916,7 +917,7 @@ static void zebra_import_table_rm_update_vrf_afi(struct zebra_vrf *zvrf, if (((afi == AFI_IP) && (rn->p.family == AF_INET)) || ((afi == AFI_IP6) && (rn->p.family == AF_INET6))) - zebra_add_import_table_entry(zvrf, rn, re, rmap_name); + zebra_add_import_table_entry(zvrf, safi, rn, re, rmap_name); } return; @@ -926,16 +927,15 @@ static void zebra_import_table_rm_update_vrf(struct zebra_vrf *zvrf, const char *rmap) { afi_t afi; + safi_t safi; int i; - for (afi = AFI_IP; afi < AFI_MAX; afi++) { + FOREACH_AFI_SAFI (afi, safi) { for (i = 1; i < ZEBRA_KERNEL_TABLE_MAX; i++) { - if (!is_zebra_import_table_enabled( - afi, zvrf->vrf->vrf_id, i)) + if (!is_zebra_import_table_enabled(afi, safi, zvrf->vrf->vrf_id, i)) continue; - zebra_import_table_rm_update_vrf_afi(zvrf, afi, i, - rmap); + zebra_import_table_rm_update_vrf_afi(zvrf, afi, safi, i, rmap); } } } diff --git a/zebra/redistribute.h b/zebra/redistribute.h index 4347454eb7..7fb31f01cf 100644 --- a/zebra/redistribute.h +++ b/zebra/redistribute.h @@ -56,19 +56,14 @@ extern void zebra_interface_vrf_update_del(struct interface *ifp, extern void zebra_interface_vrf_update_add(struct interface *ifp, vrf_id_t old_vrf_id); -extern int zebra_import_table(afi_t afi, vrf_id_t vrf_id, - uint32_t table_id, uint32_t distance, - const char *rmap_name, int add); +extern int zebra_import_table(afi_t afi, safi_t safi, vrf_id_t vrf_id, uint32_t table_id, + uint32_t distance, const char *rmap_name, bool add); -extern int zebra_add_import_table_entry(struct zebra_vrf *zvrf, - struct route_node *rn, - struct route_entry *re, - const char *rmap_name); -extern int zebra_del_import_table_entry(struct zebra_vrf *zvrf, - struct route_node *rn, +extern int zebra_add_import_table_entry(struct zebra_vrf *zvrf, safi_t safi, struct route_node *rn, + struct route_entry *re, const char *rmap_name); +extern int zebra_del_import_table_entry(struct zebra_vrf *zvrf, safi_t safi, struct route_node *rn, struct route_entry *re); -extern int is_zebra_import_table_enabled(afi_t, vrf_id_t vrf_id, - uint32_t table_id); +extern int is_zebra_import_table_enabled(afi_t, safi_t safi, vrf_id_t vrf_id, uint32_t table_id); extern int zebra_import_table_config(struct vty *, vrf_id_t vrf_id); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index dc679ed495..ab07ef8d21 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -3178,6 +3178,9 @@ netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) } else return FRR_NETLINK_ERROR; + if (dplane_ctx_get_safi(ctx) == SAFI_MULTICAST) + return FRR_NETLINK_SUCCESS; + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) return FRR_NETLINK_SUCCESS; diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 0bfcd518ca..4444eda94b 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -317,12 +317,12 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) frr_with_privs(&zserv_privs) { if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { - if (!RSYSTEM_ROUTE(type)) + if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) { - if (!RSYSTEM_ROUTE(type)) + if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); @@ -330,12 +330,12 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) /* Must do delete and add separately - * no update available */ - if (!RSYSTEM_ROUTE(old_type)) + if (!RSYSTEM_ROUTE(old_type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), dplane_ctx_get_old_ng(ctx), dplane_ctx_get_old_metric(ctx)); - if (!RSYSTEM_ROUTE(type)) + if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); diff --git a/zebra/table_manager.h b/zebra/table_manager.h index 21691994cb..f16efdd626 100644 --- a/zebra/table_manager.h +++ b/zebra/table_manager.h @@ -24,6 +24,7 @@ extern "C" { #if !defined(GNU_LINUX) /* BSD systems */ +#define RT_TABLE_ID_MAIN 0 #else /* Linux Systems */ diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index a733b5917f..0ad42ebd5b 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -610,70 +610,47 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, return; } -static int zebra_evpn_map_vlan_ns(struct ns *ns, - void *_in_param, - void **_p_zevpn) +/* Callback for per-NS ifp walk */ +static int zebra_evpn_map_vlan_ns(struct interface *tmp_if, void *_in_param) { - int found = 0; - struct zebra_ns *zns = ns->info; - struct route_node *rn; + bool found = false; struct interface *br_if; - struct zebra_evpn **p_zevpn = (struct zebra_evpn **)_p_zevpn; struct zebra_evpn *zevpn; - struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_from_svi_param *in_param = - (struct zebra_from_svi_param *)_in_param; - vlanid_t vid; + struct zebra_from_svi_param *in_param = _in_param; vni_t vni_id = 0; - uint8_t bridge_vlan_aware; - assert(p_zevpn && in_param); + assert(in_param); br_if = in_param->br_if; assert(br_if); zif = in_param->zif; assert(zif); - vid = in_param->vid; - bridge_vlan_aware = in_param->bridge_vlan_aware; - if (bridge_vlan_aware) { - vni_id = zebra_l2_bridge_if_vni_find(zif, vid); - if (vni_id) - found = 1; - } else { - /* - * See if this interface (or interface plus VLAN Id) maps to a - * VxLAN - */ - /* TODO: Optimize with a hash. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - - if (zif->brslave_info.br_if != br_if) - continue; - - vni_id = zebra_vxlan_if_access_vlan_vni_find(zif, br_if); - if (vni_id) { - found = 1; - route_unlock_node(rn); - break; - } - } - } + /* + * See if this interface (or interface plus VLAN Id) maps to a + * VxLAN + */ + /* TODO: Optimize with a hash. */ + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + goto done; + if (!if_is_operative(tmp_if)) + goto done; + + if (zif->brslave_info.br_if != br_if) + goto done; + + vni_id = zebra_vxlan_if_access_vlan_vni_find(zif, br_if); + if (vni_id) + found = true; +done: if (!found) return NS_WALK_CONTINUE; zevpn = zebra_evpn_lookup(vni_id); - *p_zevpn = zevpn; + in_param->zevpn = zevpn; return NS_WALK_STOP; } @@ -685,42 +662,39 @@ struct zebra_evpn *zebra_evpn_map_vlan(struct interface *ifp, struct interface *br_if, vlanid_t vid) { struct zebra_if *zif; - struct zebra_evpn **p_zevpn; - struct zebra_evpn *zevpn = NULL; - struct zebra_from_svi_param in_param; + struct zebra_from_svi_param in_param = {}; + vni_t vni_id = 0; /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); - in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif); + + /* Special case for vlan */ + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif)) { + vni_id = zebra_l2_bridge_if_vni_find(zif, vid); + if (vni_id) + return zebra_evpn_lookup(vni_id); + } + in_param.vid = vid; in_param.br_if = br_if; in_param.zif = zif; - p_zevpn = &zevpn; - ns_walk_func(zebra_evpn_map_vlan_ns, (void *)&in_param, (void **)p_zevpn); - return zevpn; + zebra_ns_ifp_walk_all(zebra_evpn_map_vlan_ns, &in_param); + + return in_param.zevpn; } -static int zebra_evpn_from_svi_ns(struct ns *ns, - void *_in_param, - void **_p_zevpn) +/* Callback for from_svi ifp walker */ +static int zebra_evpn_from_svi_ns(struct interface *tmp_if, void *_in_param) { - struct zebra_ns *zns = ns->info; - struct route_node *rn; struct interface *br_if; - struct zebra_evpn **p_zevpn = (struct zebra_evpn **)_p_zevpn; struct zebra_evpn *zevpn; - struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_if *br_zif; - struct zebra_l2_bridge_vlan *bvlan; - struct zebra_from_svi_param *in_param = - (struct zebra_from_svi_param *)_in_param; - int found = 0; + struct zebra_from_svi_param *in_param = _in_param; + bool found = false; vni_t vni_id = 0; - vlanid_t vid = 0; - uint8_t bridge_vlan_aware; if (!in_param) return NS_WALK_STOP; @@ -728,48 +702,30 @@ static int zebra_evpn_from_svi_ns(struct ns *ns, br_if = in_param->br_if; zif = in_param->zif; assert(zif); - bridge_vlan_aware = in_param->bridge_vlan_aware; - vid = in_param->vid; br_zif = br_if->info; assert(br_zif); - if (bridge_vlan_aware) { - bvlan = zebra_l2_bridge_if_vlan_find(br_zif, vid); - if (bvlan && bvlan->access_bd && bvlan->access_bd->vni) { - found = 1; - vni_id = bvlan->access_bd->vni; - } - } else { - /* TODO: Optimize with a hash. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - - if (zif->brslave_info.br_if != br_if) - continue; - - vni_id = - zebra_vxlan_if_access_vlan_vni_find(zif, br_if); - if (vni_id) { - found = 1; - route_unlock_node(rn); - break; - } - } - } + if (!tmp_if) + goto done; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + goto done; + if (!if_is_operative(tmp_if)) + goto done; + + if (zif->brslave_info.br_if != br_if) + goto done; + + vni_id = zebra_vxlan_if_access_vlan_vni_find(zif, br_if); + if (vni_id) + found = true; +done: if (!found) return NS_WALK_CONTINUE; zevpn = zebra_evpn_lookup(vni_id); - if (p_zevpn) - *p_zevpn = zevpn; + in_param->zevpn = zevpn; return NS_WALK_STOP; } @@ -780,17 +736,21 @@ static int zebra_evpn_from_svi_ns(struct ns *ns, struct zebra_evpn *zebra_evpn_from_svi(struct interface *ifp, struct interface *br_if) { - struct zebra_evpn *zevpn = NULL; - struct zebra_evpn **p_zevpn; struct zebra_if *zif; - struct zebra_from_svi_param in_param; + struct zebra_l2_bridge_vlan *bvlan; + struct zebra_from_svi_param in_param = {}; + vni_t vni_id = 0; + struct zebra_evpn *zevpn; + struct zebra_l2info_vlan *vl; if (!br_if) return NULL; /* Make sure the linked interface is a bridge. */ - if (!IS_ZEBRA_IF_BRIDGE(br_if)) + if (!IS_ZEBRA_IF_BRIDGE(br_if)) { + zlog_debug("%s: br_if NOT a bridge", __func__); return NULL; + } /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; @@ -798,57 +758,60 @@ struct zebra_evpn *zebra_evpn_from_svi(struct interface *ifp, in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif); in_param.vid = 0; + /* Don't need to search in this case */ if (in_param.bridge_vlan_aware) { - struct zebra_l2info_vlan *vl; - if (!IS_ZEBRA_IF_VLAN(ifp)) return NULL; + zevpn = NULL; + zif = ifp->info; assert(zif); vl = &zif->l2info.vl; in_param.vid = vl->vid; + + bvlan = zebra_l2_bridge_if_vlan_find(br_if->info, vl->vid); + if (bvlan && bvlan->access_bd && bvlan->access_bd->vni) { + vni_id = bvlan->access_bd->vni; + zevpn = zebra_evpn_lookup(vni_id); + } + + return zevpn; } + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN: + * search all NSes + */ in_param.br_if = br_if; in_param.zif = zif; - p_zevpn = &zevpn; - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - ns_walk_func(zebra_evpn_from_svi_ns, (void *)&in_param, - (void **)p_zevpn); - return zevpn; + zebra_ns_ifp_walk_all(zebra_evpn_from_svi_ns, &in_param); + + return in_param.zevpn; } -static int zvni_map_to_macvlan_ns(struct ns *ns, void *_in_param, void **_p_ifp) +static int zvni_map_to_macvlan_ns(struct interface *tmp_if, void *_in_param) { - struct zebra_ns *zns = ns->info; - struct zebra_from_svi_param *in_param = - (struct zebra_from_svi_param *)_in_param; - struct interface **p_ifp = (struct interface **)_p_ifp; - struct route_node *rn; - struct interface *tmp_if = NULL; + struct zebra_from_svi_param *in_param = _in_param; struct zebra_if *zif; - assert(in_param && p_ifp); + assert(in_param); /* Identify corresponding VLAN interface. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) - continue; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + goto done; - if (zif->link == in_param->svi_if) { - *p_ifp = tmp_if; - route_unlock_node(rn); - return NS_WALK_STOP; - } + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) + goto done; + + if (zif->link == in_param->svi_if) { + in_param->ret_ifp = tmp_if; + return NS_WALK_STOP; } +done: return NS_WALK_CONTINUE; } @@ -857,17 +820,16 @@ static int zvni_map_to_macvlan_ns(struct ns *ns, void *_in_param, void **_p_ifp) struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, struct interface *svi_if) { - struct interface *tmp_if = NULL; struct zebra_if *zif; - struct interface **p_ifp; - struct zebra_from_svi_param in_param; + struct zebra_from_svi_param in_param = {}; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; if (!svi_if) { - zlog_debug("svi_if is not passed."); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: svi_if is not passed.", __func__); return NULL; } @@ -879,11 +841,10 @@ struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, in_param.br_if = br_if; in_param.zif = NULL; in_param.svi_if = svi_if; - p_ifp = &tmp_if; /* Identify corresponding VLAN interface. */ - ns_walk_func(zvni_map_to_macvlan_ns, (void *)&in_param, (void **)p_ifp); - return tmp_if; + zebra_ns_ifp_walk_all(zvni_map_to_macvlan_ns, &in_param); + return in_param.ret_ifp; } /* diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index c946425dd5..0ffebdd277 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -116,7 +116,10 @@ struct zebra_evpn { struct zebra_from_svi_param { struct interface *br_if; struct interface *svi_if; + struct interface *ret_ifp; struct zebra_if *zif; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; uint8_t bridge_vlan_aware; vlanid_t vid; }; diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index ffd749fcf1..8edfc9f3f6 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -28,9 +28,185 @@ extern struct zebra_privs_t zserv_privs; DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_NS, "Zebra Name Space"); +DEFINE_MTYPE_STATIC(ZEBRA, ZNS_IFP, "Zebra NS Ifp"); + +static int ifp_tree_cmp(const struct ifp_tree_link *a, const struct ifp_tree_link *b); + +DECLARE_RBTREE_UNIQ(ifp_tree, struct ifp_tree_link, link, ifp_tree_cmp); static struct zebra_ns *dzns; +static int ifp_tree_cmp(const struct ifp_tree_link *a, const struct ifp_tree_link *b) +{ + return (a->ifindex - b->ifindex); +} + +/* + * Link an ifp into its parent NS + */ +void zebra_ns_link_ifp(struct zebra_ns *zns, struct interface *ifp) +{ + struct zebra_if *zif; + struct ifp_tree_link *link, tlink = {}; + + zif = ifp->info; + assert(zif != NULL); + + if (zif->ns_tree_link) { + assert(zif->ns_tree_link->zns == zns); + assert(zif->ns_tree_link->ifp == ifp); + return; + } + + /* Lookup first - already linked? */ + tlink.ifindex = ifp->ifindex; + link = ifp_tree_find(&zns->ifp_tree, &tlink); + if (link) { + assert(link->ifp == ifp); + return; + } + + /* Allocate new linkage struct and add */ + link = XCALLOC(MTYPE_ZNS_IFP, sizeof(struct ifp_tree_link)); + link->ifp = ifp; + link->ifindex = ifp->ifindex; + link->zns = zns; + + ifp_tree_add(&zns->ifp_tree, link); + + zif->ns_tree_link = link; +} + +/* + * Unlink an ifp from its parent NS (probably because the ifp is being deleted) + */ +void zebra_ns_unlink_ifp(struct interface *ifp) +{ + struct zebra_if *zif; + struct ifp_tree_link *link; + struct zebra_ns *zns; + + zif = ifp->info; + if (zif && zif->ns_tree_link) { + link = zif->ns_tree_link; + zns = link->zns; + + ifp_tree_del(&zns->ifp_tree, link); + + zif->ns_tree_link = NULL; + + XFREE(MTYPE_ZNS_IFP, link); + } +} + +/* + * ifp lookup apis + */ +struct interface *zebra_ns_lookup_ifp(struct zebra_ns *zns, uint32_t ifindex) +{ + struct interface *ifp = NULL; + struct ifp_tree_link *link, tlink = {}; + + /* Init temp struct for lookup */ + tlink.ifindex = ifindex; + + link = ifp_tree_find(&zns->ifp_tree, &tlink); + if (link) + ifp = link->ifp; + + return ifp; +} + +static int lookup_ifp_name_cb(struct interface *ifp, void *arg); + +struct ifp_name_ctx { + const char *ifname; + struct interface *ifp; +}; + +struct interface *zebra_ns_lookup_ifp_name(struct zebra_ns *zns, const char *ifname) +{ + struct ifp_name_ctx ctx = {}; + + /* Hand context struct into walker function for use in its callback */ + ctx.ifname = ifname; + zebra_ns_ifp_walk(zns, lookup_ifp_name_cb, &ctx); + + return ctx.ifp; +} + +static int lookup_ifp_name_cb(struct interface *ifp, void *arg) +{ + struct ifp_name_ctx *pctx = arg; + + if (strcmp(ifp->name, pctx->ifname) == 0) { + pctx->ifp = ifp; + return NS_WALK_STOP; + } + + return NS_WALK_CONTINUE; +} + +/* Iterate collection of ifps, calling application's callback. Callback uses + * return semantics from lib/ns.h: return NS_WALK_STOP to stop the iteration. + * Caller's 'arg' is included in each callback. + */ +int zebra_ns_ifp_walk(struct zebra_ns *zns, + int (*func)(struct interface *ifp, void *arg), void *arg) +{ + struct ifp_tree_link *link; + int ret = NS_WALK_CONTINUE; + + frr_each (ifp_tree, &zns->ifp_tree, link) { + ret = (func)(link->ifp, arg); + if (ret == NS_WALK_STOP) + break; + } + + if (ret == NS_WALK_STOP) + return NS_WALK_STOP; + else + return NS_WALK_CONTINUE; +} + +/* + * Walk all NSes, and all ifps for each NS. + */ +struct ns_ifp_walk_ctx { + int (*func)(struct interface *ifp, void *arg); + void *arg; + int ret; +}; + +static int ns_ifp_walker(struct ns *ns, void *in_param, void **unused); + +void zebra_ns_ifp_walk_all(int (*func)(struct interface *ifp, void *arg), void *arg) +{ + struct ns_ifp_walk_ctx ctx = {}; + + ctx.func = func; + ctx.arg = arg; + + ns_walk_func(ns_ifp_walker, &ctx, NULL); +} + +static int ns_ifp_walker(struct ns *ns, void *in_param, void **unused) +{ + struct zebra_ns *zns; + struct ns_ifp_walk_ctx *ctx = in_param; + int ret = NS_WALK_CONTINUE; + + zns = ns->info; + if (zns == NULL) + goto done; + + ret = zebra_ns_ifp_walk(zns, ctx->func, ctx->arg); + +done: + + return ret; +} + static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete); struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) @@ -58,7 +234,7 @@ static int zebra_ns_new(struct ns *ns) zns->ns_id = ns->ns_id; /* Do any needed per-NS data structure allocation. */ - zns->if_table = route_table_init(); + ifp_tree_init(&zns->ifp_tree); return 0; } @@ -66,11 +242,22 @@ static int zebra_ns_new(struct ns *ns) static int zebra_ns_delete(struct ns *ns) { struct zebra_ns *zns = (struct zebra_ns *)ns->info; + struct zebra_if *zif; + struct ifp_tree_link *link; if (IS_ZEBRA_DEBUG_EVENT) zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id); if (!zns) return 0; + + /* Clean up ifp tree */ + while ((link = ifp_tree_pop(&zns->ifp_tree)) != NULL) { + zif = link->ifp->info; + + zif->ns_tree_link = NULL; + XFREE(MTYPE_ZNS_IFP, link); + } + XFREE(MTYPE_ZEBRA_NS, ns->info); return 0; } @@ -123,6 +310,17 @@ void zebra_ns_startup_continue(struct zebra_dplane_ctx *ctx) vlan_read(zns); kernel_read_pbr_rules(zns); kernel_read_tc_qdisc(zns); + + /* + * At this point FRR has requested and read a bunch + * of data from the dplane about initial state of + * the system. Zebra now needs to initialize + * the gr subsystem ( or the route sweeping + * subsystem ) to allow that to properly work. + * This must be done *immediately* after the + * load of all data from the underlying dplane. + */ + zebra_main_router_started(); break; } } @@ -146,10 +344,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) */ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) { - if (zns->if_table) - route_table_finish(zns->if_table); - zns->if_table = NULL; - zebra_dplane_ns_enable(zns, false /*Disable*/); kernel_terminate(zns, complete); diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index d5fd5869bc..0e40cebbe0 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -32,6 +32,20 @@ struct nlsock { }; #endif +/* Tree of interfaces: external linkage struct, and rbtree */ +PREDECL_RBTREE_UNIQ(ifp_tree); + +struct ifp_tree_link { + struct ifp_tree_item link; + + ifindex_t ifindex; + + struct interface *ifp; + + /* Backpointer */ + struct zebra_ns *zns; +}; + struct zebra_ns { /* net-ns name. */ char name[VRF_NAMSIZ]; @@ -53,7 +67,8 @@ struct zebra_ns { struct nlsock ge_netlink_cmd; /* command channel for generic netlink */ #endif - struct route_table *if_table; + /* Tree of interfaces in this ns */ + struct ifp_tree_head ifp_tree; /* Back pointer */ struct ns *ns; @@ -61,6 +76,23 @@ struct zebra_ns { struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); +/* Manage collection of ifps per-NS */ +void zebra_ns_link_ifp(struct zebra_ns *zns, struct interface *ifp); +void zebra_ns_unlink_ifp(struct interface *ifp); +struct interface *zebra_ns_lookup_ifp(struct zebra_ns *zns, uint32_t ifindex); +struct interface *zebra_ns_lookup_ifp_name(struct zebra_ns *zns, const char *ifname); + +/* Iterate collection of ifps, calling application's callback. Callback uses + * return semantics from lib/ns.h: return NS_WALK_STOP to stop the iteration. + * Caller's 'arg' is included in each callback. + * The iterator returns STOP or CONTINUE also. + */ +int zebra_ns_ifp_walk(struct zebra_ns *zns, + int (*func)(struct interface *ifp, void *arg), void *arg); + +/* Walk all NSes, and all ifps for each NS. */ +void zebra_ns_ifp_walk_all(int (*func)(struct interface *ifp, void *arg), void *arg); + int zebra_ns_init(void); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disabled(struct ns *ns); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 4c1401124b..b2543ca0e8 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -637,19 +637,12 @@ int zebra_rib_labeled_unicast(struct route_entry *re) void rib_install_kernel(struct route_node *rn, struct route_entry *re, struct route_entry *old) { - struct nexthop *nexthop; struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); enum zebra_dplane_result ret; rib_dest_t *dest = rib_dest_from_rnode(rn); - if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - return; - } - /* * Install the resolved nexthop object first. */ @@ -716,17 +709,8 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, /* Uninstall the route from kernel. */ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) { - struct nexthop *nexthop; - struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - if (info->safi != SAFI_UNICAST) { - UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - return; - } - /* * Make sure we update the FPM any time we send new information to * the dataplane. @@ -1264,8 +1248,15 @@ static void rib_process(struct route_node *rn) struct zebra_vrf *zvrf = NULL; struct vrf *vrf; struct route_entry *proto_re_changed = NULL; - vrf_id_t vrf_id = VRF_UNKNOWN; + safi_t safi = SAFI_UNICAST; + + if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct rib_table_info *info = srcdest_rnode_table_info(rn); + + assert(info); + safi = info->safi; + } assert(rn); @@ -1291,9 +1282,8 @@ static void rib_process(struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB_DETAILED) { struct route_entry *re = re_list_first(&dest->routes); - zlog_debug("%s(%u:%u):%pRN: Processing rn %p", - VRF_LOGNAME(vrf), vrf_id, re->table, rn, - rn); + zlog_debug("%s(%u:%u:%u):%pRN: Processing rn %p", VRF_LOGNAME(vrf), vrf_id, + re->table, safi, rn, rn); } old_fib = dest->selected_fib; @@ -1303,15 +1293,12 @@ static void rib_process(struct route_node *rn) char flags_buf[128]; char status_buf[128]; - zlog_debug( - "%s(%u:%u):%pRN: Examine re %p (%s) status: %sflags: %sdist %d metric %d", - VRF_LOGNAME(vrf), vrf_id, re->table, rn, re, - zebra_route_string(re->type), - _dump_re_status(re, status_buf, - sizeof(status_buf)), - zclient_dump_route_flags(re->flags, flags_buf, - sizeof(flags_buf)), - re->distance, re->metric); + zlog_debug("%s(%u:%u:%u):%pRN: Examine re %p (%s) status: %sflags: %sdist %d metric %d", + VRF_LOGNAME(vrf), vrf_id, re->table, safi, rn, re, + zebra_route_string(re->type), + _dump_re_status(re, status_buf, sizeof(status_buf)), + zclient_dump_route_flags(re->flags, flags_buf, sizeof(flags_buf)), + re->distance, re->metric); } /* Currently selected re. */ @@ -1435,11 +1422,10 @@ static void rib_process(struct route_node *rn) : old_fib ? old_fib : new_fib ? new_fib : NULL; - zlog_debug( - "%s(%u:%u):%pRN: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", - VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, rn, - (void *)old_selected, (void *)new_selected, - (void *)old_fib, (void *)new_fib); + zlog_debug("%s(%u:%u:%u):%pRN: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, safi, rn, + (void *)old_selected, (void *)new_selected, (void *)old_fib, + (void *)new_fib); } /* Buffer ROUTE_ENTRY_CHANGED here, because it will get cleared if @@ -4028,6 +4014,7 @@ static void rib_link(struct route_node *rn, struct route_entry *re, int process) { rib_dest_t *dest; afi_t afi; + safi_t safi; const char *rmap_name; assert(re && rn); @@ -4045,11 +4032,13 @@ static void rib_link(struct route_node *rn, struct route_entry *re, int process) afi = (rn->p.family == AF_INET) ? AFI_IP : (rn->p.family == AF_INET6) ? AFI_IP6 : AFI_MAX; - if (is_zebra_import_table_enabled(afi, re->vrf_id, re->table)) { - struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + if (is_zebra_import_table_enabled(afi, safi, re->vrf_id, re->table)) { + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - rmap_name = zebra_get_import_table_route_map(afi, re->table); - zebra_add_import_table_entry(zvrf, rn, re, rmap_name); + rmap_name = zebra_get_import_table_route_map(afi, safi, re->table); + zebra_add_import_table_entry(zvrf, safi, rn, re, rmap_name); + } } if (process) @@ -4107,6 +4096,7 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) void rib_delnode(struct route_node *rn, struct route_entry *re) { afi_t afi; + safi_t safi; if (IS_ZEBRA_DEBUG_RIB) rnode_debug(rn, re->vrf_id, "rn %p, re %p, removing", @@ -4120,15 +4110,17 @@ void rib_delnode(struct route_node *rn, struct route_entry *re) afi = (rn->p.family == AF_INET) ? AFI_IP : (rn->p.family == AF_INET6) ? AFI_IP6 : AFI_MAX; - if (is_zebra_import_table_enabled(afi, re->vrf_id, re->table)) { - struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + if (is_zebra_import_table_enabled(afi, safi, re->vrf_id, re->table)) { + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - zebra_del_import_table_entry(zvrf, rn, re); - /* Just clean up if non main table */ - if (IS_ZEBRA_DEBUG_RIB) - 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)); + zebra_del_import_table_entry(zvrf, safi, rn, re); + /* Just clean up if non main table */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s %s(%u):%pRN: Freeing route rn %p, re %p (%s)", + safi2str(safi), vrf_id_to_name(re->vrf_id), re->vrf_id, + rn, rn, re, zebra_route_string(re->type)); + } } rib_queue_add(rn); @@ -4563,7 +4555,7 @@ rib_update_handle_kernel_route_down_possibility(struct route_node *rn, struct interface *ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (ifp && if_is_up(ifp)) { + if ((ifp && if_is_up(ifp)) || nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { alive = true; break; } diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 35486a4cd0..640e6551a7 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1271,7 +1271,7 @@ void show_nexthop_json_helper(json_object *json_nexthop, bool display_vrfid = false; uint8_t rn_family; - if (re == NULL || nexthop->vrf_id != re->vrf_id) + if ((re == NULL || nexthop->vrf_id != re->vrf_id) && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) display_vrfid = true; if (rn) @@ -1292,7 +1292,7 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_node *rn, bool display_vrfid = false; uint8_t rn_family; - if (re == NULL || nexthop->vrf_id != re->vrf_id) + if ((re == NULL || nexthop->vrf_id != re->vrf_id) && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) display_vrfid = true; if (rn) diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 46afbcecfa..29bbf6023d 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -29,7 +29,7 @@ static uint32_t zebra_rmap_update_timer = ZEBRA_RMAP_DEFAULT_UPDATE_TIMER; static struct event *zebra_t_rmap_update = NULL; -char *zebra_import_table_routemap[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; +char *zebra_import_table_routemap[AFI_MAX][SAFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; struct zebra_rmap_obj { struct nexthop *nexthop; @@ -1235,21 +1235,19 @@ route_map_result_t zebra_route_map_check(afi_t family, struct route_entry *re, return (ret); } -char *zebra_get_import_table_route_map(afi_t afi, uint32_t table) +char *zebra_get_import_table_route_map(afi_t afi, safi_t safi, uint32_t table) { - return zebra_import_table_routemap[afi][table]; + return zebra_import_table_routemap[afi][safi][table]; } -void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name, - uint32_t table) +void zebra_add_import_table_route_map(afi_t afi, safi_t safi, const char *rmap_name, uint32_t table) { - zebra_import_table_routemap[afi][table] = - XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); + zebra_import_table_routemap[afi][safi][table] = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); } -void zebra_del_import_table_route_map(afi_t afi, uint32_t table) +void zebra_del_import_table_route_map(afi_t afi, safi_t safi, uint32_t table) { - XFREE(MTYPE_ROUTE_MAP_NAME, zebra_import_table_routemap[afi][table]); + XFREE(MTYPE_ROUTE_MAP_NAME, zebra_import_table_routemap[afi][safi][table]); } route_map_result_t zebra_import_table_route_map_check(int family, diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index 2039e80e3a..fca9752285 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -14,10 +14,10 @@ extern "C" { #endif extern void zebra_route_map_init(void); -extern char *zebra_get_import_table_route_map(afi_t afi, uint32_t table); -extern void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name, +extern char *zebra_get_import_table_route_map(afi_t afi, safi_t safi, uint32_t table); +extern void zebra_add_import_table_route_map(afi_t afi, safi_t safi, const char *rmap_name, uint32_t table); -extern void zebra_del_import_table_route_map(afi_t afi, uint32_t table); +extern void zebra_del_import_table_route_map(afi_t afi, safi_t safi, uint32_t table); extern route_map_result_t zebra_import_table_route_map_check( int family, struct route_entry *re, const struct prefix *p, diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index c86c6be1ef..a637c3214e 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -329,6 +329,8 @@ static inline uint8_t if_netlink_get_frr_protodown_r_bit(void) return zrouter.protodown_r_bit; } +extern void zebra_main_router_started(void); + /* zebra_northbound.c */ extern const struct frr_yang_module_info frr_zebra_info; diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 082d4609aa..624f60e815 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -214,7 +214,7 @@ void zebra_srv6_locator_format_set(struct srv6_locator *locator, locator->sid_format = format; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s format has changed, old=%s new=%s", __func__, locator->name, locator->sid_format ? ((struct srv6_sid_format *) @@ -237,7 +237,7 @@ void zebra_srv6_locator_format_set(struct srv6_locator *locator, zebra_srv6_sid_ctx_free(ctx); } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s format has changed, send SRV6_LOCATOR_DEL notification to zclients", __func__, locator->name); @@ -269,7 +269,7 @@ void zebra_srv6_locator_format_set(struct srv6_locator *locator, block_new->refcnt++; locator->sid_block = block_new; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s format has changed, send SRV6_LOCATOR_ADD notification to zclients", __func__, locator->name); @@ -293,13 +293,13 @@ void zebra_srv6_sid_format_changed_cb(struct srv6_sid_format *format) struct listnode *node, *nnode; struct zebra_srv6_sid_ctx *ctx; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: SID format %s has changed. Notifying zclients.", __func__, format->name); for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator)) { if (locator->sid_format == format) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s has changed because its format (%s) has been modified. Notifying zclients.", __func__, locator->name, format->name); @@ -801,7 +801,7 @@ static int zebra_srv6_manager_get_locator_chunk(struct srv6_locator **loc, if (!*loc) zlog_err("Unable to assign locator chunk to %s instance %u", zebra_route_string(client->proto), client->instance); - else if (IS_ZEBRA_DEBUG_PACKET) + else if (IS_ZEBRA_DEBUG_SRV6) zlog_info("Assigned locator chunk %s to %s instance %u", (*loc)->name, zebra_route_string(client->proto), client->instance); @@ -835,7 +835,7 @@ static int release_srv6_locator_chunk(uint8_t proto, uint16_t instance, if (!loc) return -1; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Releasing srv6-locator on %s", __func__, locator_name); @@ -892,7 +892,7 @@ int release_daemon_srv6_locator_chunks(struct zserv *client) struct srv6_locator *loc; struct srv6_locator_chunk *chunk; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u", __func__, zebra_route_string(client->proto), client->instance, client->session_id); @@ -912,7 +912,7 @@ int release_daemon_srv6_locator_chunks(struct zserv *client) } } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Released %d srv6-locator chunks", __func__, count); @@ -1159,7 +1159,7 @@ static bool alloc_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to allocate explicit SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1305,7 +1305,7 @@ static bool alloc_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, block->u.uncompressed.num_func_allocated++; } - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated explicit SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1331,7 +1331,7 @@ static bool alloc_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to allocate dynamic SID function from block %pFX", __func__, &block->prefix); @@ -1465,7 +1465,7 @@ static bool alloc_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, block->u.uncompressed.num_func_allocated++; } - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated dynamic SID function %u from block %pFX", __func__, *sid_func, &block->prefix); @@ -1510,7 +1510,7 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, * return the existing SID */ if (sid_same(&s->sid->value, sid_value)) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: returning existing SRv6 SID %pI6 ctx %s", __func__, &s->sid->value, srv6_sid_ctx2str(buf, @@ -1569,7 +1569,7 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, * deallocate the current SID function before allocating the new one */ if (zctx->sid) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: ctx %s already associated with a dynamic SID %pI6, releasing dynamic SID", __func__, srv6_sid_ctx2str(buf, sizeof(buf), @@ -1595,7 +1595,7 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, zctx->sid = *sid; listnode_add(srv6->sids, zctx); - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated explicit SRv6 SID %pI6 for context %s", __func__, &(*sid)->value, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); @@ -1648,7 +1648,7 @@ static int get_srv6_sid_dynamic(struct zebra_srv6_sid **sid, } } if (memcmp(&s->ctx, ctx, sizeof(struct srv6_sid_ctx)) == 0) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: returning existing SID %s %pI6", __func__, srv6_sid_ctx2str(buf, sizeof(buf), @@ -1695,7 +1695,7 @@ static int get_srv6_sid_dynamic(struct zebra_srv6_sid **sid, zctx->sid = *sid; listnode_add(srv6->sids, zctx); - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated new dynamic SRv6 SID %pI6 for context %s", __func__, &(*sid)->value, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); @@ -1733,7 +1733,7 @@ int get_srv6_sid(struct zebra_srv6_sid **sid, struct srv6_sid_ctx *ctx, (sid_value) ? SRV6_SID_ALLOC_MODE_EXPLICIT : SRV6_SID_ALLOC_MODE_DYNAMIC; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: received SRv6 SID alloc request: SID ctx %s (%pI6), mode=%s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value, srv6_sid_alloc_mode2str(alloc_mode)); @@ -1794,7 +1794,7 @@ static bool release_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to release explicit SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1918,7 +1918,7 @@ static bool release_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, zebra_srv6_sid_func_free(sid_func_ptr); } - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: released explicit SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1944,7 +1944,7 @@ static int release_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to release dynamic SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -2107,7 +2107,7 @@ static int release_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, } } - if (ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: released dynamic SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -2129,7 +2129,7 @@ int release_srv6_sid(struct zserv *client, struct zebra_srv6_sid_ctx *zctx) if (!zctx || !zctx->sid) return -1; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: releasing SRv6 SID %pI6 associated with ctx %s (proto=%u, instance=%u)", __func__, &zctx->sid->value, srv6_sid_ctx2str(buf, sizeof(buf), &zctx->ctx), @@ -2145,7 +2145,7 @@ int release_srv6_sid(struct zserv *client, struct zebra_srv6_sid_ctx *zctx) /* Remove the client from the list of clients using the SID */ listnode_delete(zctx->sid->client_list, client); - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: released SRv6 SID %pI6 associated with ctx %s (proto=%u, instance=%u)", __func__, &zctx->sid->value, srv6_sid_ctx2str(buf, sizeof(buf), &zctx->ctx), @@ -2156,7 +2156,7 @@ int release_srv6_sid(struct zserv *client, struct zebra_srv6_sid_ctx *zctx) * and remove it from the SRv6 database. */ if (listcount(zctx->sid->client_list) == 0) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: SRv6 SID %pI6 associated with ctx %s is no longer in use, removing it from SRv6 database", __func__, &zctx->sid->value, srv6_sid_ctx2str(buf, sizeof(buf), @@ -2251,7 +2251,7 @@ static int srv6_manager_get_sid_internal(struct zebra_srv6_sid **sid, struct zserv *c; char buf[256]; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: getting SRv6 SID for ctx %s, sid_value=%pI6, locator_name=%s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value ? sid_value : &in6addr_any, locator_name); @@ -2266,7 +2266,8 @@ static int srv6_manager_get_sid_internal(struct zebra_srv6_sid **sid, zsend_srv6_sid_notify(client, ctx, sid_value, 0, 0, NULL, ZAPI_SRV6_SID_FAIL_ALLOC); } else if (ret == 0) { - if (IS_ZEBRA_DEBUG_PACKET) + assert(*sid); + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: got existing SRv6 SID for ctx %s: sid_value=%pI6 (func=%u) (proto=%u, instance=%u, sessionId=%u), notify client", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), @@ -2281,7 +2282,7 @@ static int srv6_manager_get_sid_internal(struct zebra_srv6_sid **sid, : NULL, ZAPI_SRV6_SID_ALLOCATED); } else { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: got new SRv6 SID for ctx %s: sid_value=%pI6 (func=%u) (proto=%u, instance=%u, sessionId=%u), notifying all clients", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), @@ -2318,7 +2319,7 @@ int release_daemon_srv6_sids(struct zserv *client) int count = 0; int ret; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: releasing SRv6 SIDs for client proto %s, instance %d, session %u", __func__, zebra_route_string(client->proto), client->instance, client->session_id); @@ -2333,7 +2334,7 @@ int release_daemon_srv6_sids(struct zserv *client) count++; } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: released %d SRv6 SIDs", __func__, count); return count; @@ -2356,7 +2357,7 @@ static int srv6_manager_release_sid_internal(struct zserv *client, char buf[256]; const char *locator_name = NULL; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: releasing SRv6 SID associated with ctx %s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); @@ -2370,7 +2371,7 @@ static int srv6_manager_release_sid_internal(struct zserv *client, break; } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: no SID associated with ctx %s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index e464e47b1f..2b3cfc8766 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -398,6 +398,7 @@ vrf_id_t zebra_vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { zvrf = vrf->info; + if (zvrf == NULL) continue; /* case vrf with netns : match the netnsid */ @@ -408,6 +409,7 @@ vrf_id_t zebra_vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) /* VRF is VRF_BACKEND_VRF_LITE */ if (zvrf->table_id != table_id) continue; + return zvrf_id(zvrf); } } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 3bf20ff42e..b65097e725 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -370,7 +370,7 @@ static void show_nexthop_detail_helper(struct vty *vty, break; } - if (re->vrf_id != nexthop->vrf_id) { + if (re->vrf_id != nexthop->vrf_id && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); vty_out(vty, "(vrf %s)", VRF_LOGNAME(vrf)); @@ -858,6 +858,27 @@ static void vty_show_ip_route_detail_json(struct vty *vty, vty_json(vty, json); } +static void zebra_vty_display_vrf_header(struct vty *vty, struct zebra_vrf *zvrf, uint32_t tableid) +{ + if (!tableid) + vty_out(vty, "VRF %s:\n", zvrf_name(zvrf)); + else { + if (vrf_is_backend_netns()) + vty_out(vty, "VRF %s table %u:\n", zvrf_name(zvrf), tableid); + else { + vrf_id_t vrf = zebra_vrf_lookup_by_table(tableid, zvrf->zns->ns_id); + + if (vrf == VRF_DEFAULT && tableid != RT_TABLE_ID_MAIN) + vty_out(vty, "table %u:\n", tableid); + else { + struct zebra_vrf *zvrf2 = zebra_vrf_lookup_by_id(vrf); + + vty_out(vty, "VRF %s table %u:\n", zvrf_name(zvrf2), tableid); + } + } + } +} + static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, struct route_table *table, afi_t afi, bool use_fib, route_tag_t tag, @@ -937,17 +958,9 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, } if (ctx->multi && ctx->header_done) vty_out(vty, "\n"); - if (ctx->multi || zvrf_id(zvrf) != VRF_DEFAULT - || tableid) { - if (!tableid) - vty_out(vty, "VRF %s:\n", - zvrf_name(zvrf)); - else - vty_out(vty, - "VRF %s table %u:\n", - zvrf_name(zvrf), - tableid); - } + if (ctx->multi || zvrf_id(zvrf) != VRF_DEFAULT || tableid) + zebra_vty_display_vrf_header(vty, zvrf, tableid); + ctx->header_done = true; first = 0; } @@ -3594,54 +3607,34 @@ static int zebra_ip_config(struct vty *vty) return write; } -DEFUN (ip_zebra_import_table_distance, +DEFPY (ip_zebra_import_table_distance, ip_zebra_import_table_distance_cmd, - "ip import-table (1-252) [distance (1-255)] [route-map RMAP_NAME]", + "ip import-table (1-252)$table_id [mrib]$mrib [distance (1-255)$distance] [route-map RMAP_NAME$rmap]", IP_STR "import routes from non-main kernel table\n" "kernel routing table id\n" + "Import into the MRIB instead of the URIB\n" "Distance for imported routes\n" "Default distance value\n" "route-map for filtering\n" "route-map name\n") { - uint32_t table_id = 0; - - table_id = strtoul(argv[2]->arg, NULL, 10); - int distance = ZEBRA_TABLE_DISTANCE_DEFAULT; - char *rmap = - strmatch(argv[argc - 2]->text, "route-map") - ? XSTRDUP(MTYPE_ROUTE_MAP_NAME, argv[argc - 1]->arg) - : NULL; - int ret; + safi_t safi = mrib ? SAFI_MULTICAST : SAFI_UNICAST; - if (argc == 7 || (argc == 5 && !rmap)) - distance = strtoul(argv[4]->arg, NULL, 10); + if (distance_str == NULL) + distance = ZEBRA_TABLE_DISTANCE_DEFAULT; if (!is_zebra_valid_kernel_table(table_id)) { - vty_out(vty, - "Invalid routing table ID, %d. Must be in range 1-252\n", - table_id); - if (rmap) - XFREE(MTYPE_ROUTE_MAP_NAME, rmap); + vty_out(vty, "Invalid routing table ID, %ld. Must be in range 1-252\n", table_id); return CMD_WARNING; } if (is_zebra_main_routing_table(table_id)) { - vty_out(vty, - "Invalid routing table ID, %d. Must be non-default table\n", - table_id); - if (rmap) - XFREE(MTYPE_ROUTE_MAP_NAME, rmap); + vty_out(vty, "Invalid routing table ID, %ld. Must be non-default table\n", table_id); return CMD_WARNING; } - ret = zebra_import_table(AFI_IP, VRF_DEFAULT, table_id, - distance, rmap, 1); - if (rmap) - XFREE(MTYPE_ROUTE_MAP_NAME, rmap); - - return ret; + return zebra_import_table(AFI_IP, safi, VRF_DEFAULT, table_id, distance, rmap, true); } DEFUN_HIDDEN (zebra_packet_process, @@ -3700,20 +3693,20 @@ DEFUN_HIDDEN (no_zebra_workqueue_timer, return CMD_SUCCESS; } -DEFUN (no_ip_zebra_import_table, +DEFPY (no_ip_zebra_import_table, no_ip_zebra_import_table_cmd, - "no ip import-table (1-252) [distance (1-255)] [route-map NAME]", + "no ip import-table (1-252)$table_id [mrib]$mrib [distance (1-255)] [route-map NAME]", NO_STR IP_STR "import routes from non-main kernel table\n" "kernel routing table id\n" + "Import into the MRIB instead of the URIB\n" "Distance for imported routes\n" "Default distance value\n" "route-map for filtering\n" "route-map name\n") { - uint32_t table_id = 0; - table_id = strtoul(argv[3]->arg, NULL, 10); + safi_t safi = mrib ? SAFI_MULTICAST : SAFI_UNICAST; if (!is_zebra_valid_kernel_table(table_id)) { vty_out(vty, @@ -3722,16 +3715,14 @@ DEFUN (no_ip_zebra_import_table, } if (is_zebra_main_routing_table(table_id)) { - vty_out(vty, - "Invalid routing table ID, %d. Must be non-default table\n", - table_id); + vty_out(vty, "Invalid routing table ID, %ld. Must be non-default table\n", table_id); return CMD_WARNING; } - if (!is_zebra_import_table_enabled(AFI_IP, VRF_DEFAULT, table_id)) + if (!is_zebra_import_table_enabled(AFI_IP, safi, VRF_DEFAULT, table_id)) return CMD_SUCCESS; - return (zebra_import_table(AFI_IP, VRF_DEFAULT, table_id, 0, NULL, 0)); + return (zebra_import_table(AFI_IP, safi, VRF_DEFAULT, table_id, 0, NULL, false)); } DEFPY (zebra_nexthop_group_keep, @@ -3802,6 +3793,10 @@ static int config_write_protocol(struct vty *vty) if (!zebra_nhg_recursive_use_backups()) vty_out(vty, "no zebra nexthop resolve-via-backup\n"); +#ifdef HAVE_SCRIPTING + frrscript_names_config_write(vty); +#endif + if (rnh_get_hide_backups()) vty_out(vty, "ip nht hide-backup-events\n"); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 0f72259951..8a1664440f 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -859,39 +859,30 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) vty_out(vty, "\n"); } -static int zvni_map_to_svi_ns(struct ns *ns, - void *_in_param, - void **_p_ifp) +static int zvni_map_to_svi_ns(struct interface *tmp_if, void *_in_param) { - struct zebra_ns *zns = ns->info; - struct route_node *rn; - struct zebra_from_svi_param *in_param = - (struct zebra_from_svi_param *)_in_param; + struct zebra_from_svi_param *in_param = _in_param; struct zebra_l2info_vlan *vl; - struct interface *tmp_if = NULL; - struct interface **p_ifp = (struct interface **)_p_ifp; struct zebra_if *zif; - assert(in_param && p_ifp); + assert(in_param); /* TODO: Optimize with a hash. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VLAN - || zif->link != in_param->br_if) - continue; - vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; - - if (vl->vid == in_param->vid) { - *p_ifp = tmp_if; - route_unlock_node(rn); - return NS_WALK_STOP; - } + + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + goto done; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VLAN || zif->link != in_param->br_if) + goto done; + vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; + + if (vl->vid == in_param->vid) { + in_param->ret_ifp = tmp_if; + return NS_WALK_STOP; } + +done: return NS_WALK_CONTINUE; } @@ -904,10 +895,9 @@ static int zvni_map_to_svi_ns(struct ns *ns, */ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { - struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_from_svi_param in_param; - struct interface **p_ifp; + struct zebra_from_svi_param in_param = {}; + /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; @@ -922,12 +912,11 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) in_param.vid = vid; in_param.br_if = br_if; - in_param.zif = NULL; - p_ifp = &tmp_if; + /* Identify corresponding VLAN interface. */ - ns_walk_func(zvni_map_to_svi_ns, (void *)&in_param, - (void **)p_ifp); - return tmp_if; + zebra_ns_ifp_walk_all(zvni_map_to_svi_ns, &in_param); + + return in_param.ret_ifp; } int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn) @@ -1007,9 +996,9 @@ static int zevpn_build_vni_hash_table(struct zebra_if *zif, */ zevpn = zebra_evpn_lookup(vni); if (zevpn) { - zlog_debug( - "EVPN hash already present for IF %s(%u) L2-VNI %u", - ifp->name, ifp->ifindex, vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("EVPN hash already present for IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); /* * Inform BGP if intf is up and mapped to @@ -1072,48 +1061,32 @@ static int zevpn_build_vni_hash_table(struct zebra_if *zif, return 0; } -static int zevpn_build_hash_table_zns(struct ns *ns, - void *param_in __attribute__((unused)), - void **param_out __attribute__((unused))) +static int zevpn_build_hash_table_zns(struct interface *ifp, void *arg) { - struct zebra_ns *zns = ns->info; - struct route_node *rn; - struct interface *ifp; - struct zebra_vrf *zvrf; - - zvrf = zebra_vrf_get_evpn(); + struct zebra_vrf *zvrf = arg; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; - /* Walk VxLAN interfaces and create EVPN hash. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + goto done; - ifp = (struct interface *)rn->info; - if (!ifp) - continue; - zif = ifp->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; + vxl = &zif->l2info.vxl; + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) link not in same namespace as BGP EVPN core instance", + ifp->name, ifp->ifindex); + goto done; + } - vxl = &zif->l2info.vxl; - /* link of VXLAN interface should be in zebra_evpn_vrf */ - if (zvrf->zns->ns_id != vxl->link_nsid) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Intf %s(%u) link not in same " - "namespace than BGP EVPN core instance ", - ifp->name, ifp->ifindex); - continue; - } + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Building vni table for %s-if %s", + IS_ZEBRA_VXLAN_IF_VNI(zif) ? "vni" : "svd", ifp->name); - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Building vni table for %s-if %s", - IS_ZEBRA_VXLAN_IF_VNI(zif) ? "vni" : "svd", - ifp->name); + zebra_vxlan_if_vni_iterate(zif, zevpn_build_vni_hash_table, NULL); - zebra_vxlan_if_vni_iterate(zif, zevpn_build_vni_hash_table, - NULL); - } +done: return NS_WALK_CONTINUE; } @@ -1124,7 +1097,13 @@ static int zevpn_build_hash_table_zns(struct ns *ns, static void zevpn_build_hash_table(void) { - ns_walk_func(zevpn_build_hash_table_zns, NULL, NULL); + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + if (zvrf == NULL) + return; + + zebra_ns_ifp_walk_all(zevpn_build_hash_table_zns, zvrf); } /* @@ -1968,70 +1947,63 @@ static int zl3vni_del(struct zebra_l3vni *zl3vni) return 0; } -static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, - void *_zl3vni, - void **_pifp) -{ - struct zebra_ns *zns = ns->info; - struct zebra_l3vni *zl3vni = (struct zebra_l3vni *)_zl3vni; - struct route_node *rn = NULL; - struct interface *ifp = NULL; +/* Context arg for zl3vni map iteration */ +struct zl3vni_map_arg { struct zebra_vrf *zvrf; + struct zebra_l3vni *zl3vni; + struct interface *ret_ifp; +}; - zvrf = zebra_vrf_get_evpn(); - - assert(_pifp); - - /* loop through all vxlan-interface */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { +static int zl3vni_map_to_vxlan_if_ns(struct interface *ifp, void *arg) +{ + struct zl3vni_map_arg *ctx = arg; + struct zebra_l3vni *zl3vni = ctx->zl3vni; + struct zebra_vrf *zvrf = ctx->zvrf; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl; + struct zebra_vxlan_vni *vni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl; - struct zebra_vxlan_vni *vni = NULL; + /* look for vxlan-interface */ - ifp = (struct interface *)rn->info; - if (!ifp) - continue; - - zif = ifp->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + goto done; - vxl = &zif->l2info.vxl; - vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni); - if (!vni || vni->vni != zl3vni->vni) - continue; + vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni); + if (!vni || vni->vni != zl3vni->vni) + goto done; - /* link of VXLAN interface should be in zebra_evpn_vrf */ - if (zvrf->zns->ns_id != vxl->link_nsid) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Intf %s(%u) VNI %u, link not in same " - "namespace than BGP EVPN core instance ", - ifp->name, ifp->ifindex, vni->vni); - continue; - } + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) VNI %u, link not in same namespace as BGP EVPN core instance", + ifp->name, ifp->ifindex, vni->vni); + goto done; + } + zl3vni->local_vtep_ip = zif->l2info.vxl.vtep_ip; + ctx->ret_ifp = ifp; - zl3vni->local_vtep_ip = zif->l2info.vxl.vtep_ip; - *_pifp = (void *)ifp; - route_unlock_node(rn); - return NS_WALK_STOP; - } + return NS_WALK_STOP; +done: return NS_WALK_CONTINUE; } struct interface *zl3vni_map_to_vxlan_if(struct zebra_l3vni *zl3vni) { - struct interface **p_ifp; - struct interface *ifp = NULL; + struct zl3vni_map_arg arg = {}; - p_ifp = &ifp; + arg.zl3vni = zl3vni; + arg.zvrf = zebra_vrf_get_evpn(); - ns_walk_func(zl3vni_map_to_vxlan_if_ns, - (void *)zl3vni, (void **)p_ifp); - return ifp; + if (arg.zvrf == NULL) + return NULL; + + zebra_ns_ifp_walk_all(zl3vni_map_to_vxlan_if_ns, &arg); + + return arg.ret_ifp; } struct interface *zl3vni_map_to_svi_if(struct zebra_l3vni *zl3vni) @@ -2086,57 +2058,35 @@ struct zebra_l3vni *zl3vni_from_vrf(vrf_id_t vrf_id) return zl3vni_lookup(zvrf->l3vni); } -static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni) +/* loop through all vxlan-interface */ +static int zl3vni_from_svi_ns(struct interface *tmp_if, void *_in_param) { int found = 0; vni_t vni_id = 0; - struct zebra_ns *zns = ns->info; - struct zebra_l3vni **p_zl3vni = (struct zebra_l3vni **)_p_zl3vni; - struct zebra_from_svi_param *in_param = - (struct zebra_from_svi_param *)_in_param; - struct route_node *rn = NULL; - struct interface *tmp_if = NULL; + struct zebra_from_svi_param *in_param = _in_param; struct zebra_if *zif = NULL; - struct zebra_if *br_zif = NULL; - assert(in_param && p_zl3vni); + assert(in_param); - br_zif = in_param->br_if->info; - assert(br_zif); + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + goto done; + if (!if_is_operative(tmp_if)) + goto done; - if (in_param->bridge_vlan_aware) { - vni_id = zebra_l2_bridge_if_vni_find(br_zif, in_param->vid); - if (vni_id) - found = 1; - } else { - /* loop through all vxlan-interface */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - - if (zif->brslave_info.br_if != in_param->br_if) - continue; - - vni_id = zebra_vxlan_if_access_vlan_vni_find( - zif, in_param->br_if); - if (vni_id) { - found = 1; - route_unlock_node(rn); - break; - } - } + if (zif->brslave_info.br_if != in_param->br_if) + goto done; + + vni_id = zebra_vxlan_if_access_vlan_vni_find(zif, in_param->br_if); + if (vni_id) { + in_param->zl3vni = zl3vni_lookup(vni_id); + found = 1; } +done: if (!found) return NS_WALK_CONTINUE; - *p_zl3vni = zl3vni_lookup(vni_id); return NS_WALK_STOP; } @@ -2147,10 +2097,11 @@ static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni) static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp, struct interface *br_if) { - struct zebra_l3vni *zl3vni = NULL; struct zebra_if *zif = NULL; + vni_t vni_id = 0; + struct zebra_if *br_zif = NULL; struct zebra_from_svi_param in_param = {}; - struct zebra_l3vni **p_zl3vni; + struct zebra_l2info_vlan *vl; if (!br_if) return NULL; @@ -2158,15 +2109,15 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp, /* Make sure the linked interface is a bridge. */ if (!IS_ZEBRA_IF_BRIDGE(br_if)) return NULL; + in_param.br_if = br_if; /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif); - if (in_param.bridge_vlan_aware) { - struct zebra_l2info_vlan *vl; + br_zif = br_if->info; + assert(br_zif); + in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif); + if (in_param.bridge_vlan_aware) { if (!IS_ZEBRA_IF_VLAN(ifp)) return NULL; @@ -2174,15 +2125,18 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp, assert(zif); vl = &zif->l2info.vl; in_param.vid = vl->vid; + + vni_id = zebra_l2_bridge_if_vni_find(br_zif, in_param.vid); + if (vni_id) + return zl3vni_lookup(vni_id); } /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ /* TODO: Optimize with a hash. */ - p_zl3vni = &zl3vni; + zebra_ns_ifp_walk_all(zl3vni_from_svi_ns, &in_param); - ns_walk_func(zl3vni_from_svi_ns, (void *)&in_param, (void **)p_zl3vni); - return zl3vni; + return in_param.zl3vni; } vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if) @@ -2336,6 +2290,36 @@ static void zevpn_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) listnode_add_sort(zl3vni->l2vnis, zevpn); } +/* Helper for vni transition iterator */ + +struct vni_trans_ctx { + vni_t vni; + struct zebra_vxlan_vni *vnip; + struct interface *ret_ifp; +}; + +static int vni_trans_cb(struct interface *ifp, void *arg) +{ + struct vni_trans_ctx *ctx = arg; + struct zebra_if *zif; + struct zebra_vxlan_vni *vnip; + + /* Find VxLAN interface for this VNI. */ + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + goto done; + + vnip = zebra_vxlan_if_vni_find(zif, ctx->vni); + if (vnip) { + ctx->ret_ifp = ifp; + ctx->vnip = vnip; + return NS_WALK_STOP; + } + +done: + return NS_WALK_CONTINUE; +} + /* * Handle transition of vni from l2 to l3 and vice versa. * This function handles only the L2VNI add/delete part of @@ -2386,39 +2370,25 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, return -1; } } else { - struct zebra_ns *zns; - struct route_node *rn; - struct interface *ifp; - struct zebra_if *zif; struct zebra_vxlan_vni *vnip; struct zebra_l2info_vxlan *vxl; struct interface *vlan_if; - bool found = false; + struct zebra_if *zif; + struct zebra_ns *zns; + struct vni_trans_ctx ctx = {}; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Adding L2-VNI %u - transition from L3-VNI", vni); - /* Find VxLAN interface for this VNI. */ zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - ifp = (struct interface *)rn->info; - if (!ifp) - continue; - zif = ifp->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - - vxl = &zif->l2info.vxl; - vnip = zebra_vxlan_if_vni_find(zif, vni); - if (vnip) { - found = true; - route_unlock_node(rn); - break; - } - } - if (!found) { + ctx.vni = vni; + + /* Find VxLAN interface for this VNI. */ + zebra_ns_ifp_walk(zns, vni_trans_cb, &ctx); + + if (ctx.ret_ifp == NULL) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_err( "Adding L2-VNI - Failed to find VxLAN interface for VNI %u", @@ -2431,6 +2401,10 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, if (zevpn) return 0; + zif = ctx.ret_ifp->info; + vnip = ctx.vnip; + vxl = &zif->l2info.vxl; + zevpn = zebra_evpn_add(vni); /* Find bridge interface for the VNI */ @@ -2443,13 +2417,13 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); } - zevpn->vxlan_if = ifp; + zevpn->vxlan_if = ctx.ret_ifp; zevpn->local_vtep_ip = vxl->vtep_ip; /* Inform BGP if the VNI is up and mapped to a bridge. */ - if (if_is_operative(ifp) && zif->brslave_info.br_if) { + if (if_is_operative(ctx.ret_ifp) && zif->brslave_info.br_if) { zebra_evpn_send_add_to_client(zevpn); - zebra_evpn_read_mac_neigh(zevpn, ifp); + zebra_evpn_read_mac_neigh(zevpn, ctx.ret_ifp); } } |
