diff options
64 files changed, 1830 insertions, 136 deletions
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 b3518ac696..58e1ffa500 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -689,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", @@ -699,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 { @@ -716,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", @@ -725,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; } } } 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_route.c b/bgpd/bgp_route.c index 7a87739479..988be7f4de 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4388,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. */ @@ -5038,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, @@ -5056,7 +5053,7 @@ 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", @@ -5092,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, @@ -5120,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, @@ -5191,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( @@ -5301,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); @@ -5341,7 +5338,7 @@ 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); @@ -5558,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)); @@ -5568,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)); @@ -6292,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 { @@ -6321,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; } } } @@ -9056,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); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index ecb45ff697..e86d1dc656 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -18732,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 */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 22f1cf8414..7e98735c14 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3031,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. */ @@ -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[] = { @@ -6097,9 +6101,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 +6130,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/doc/user/bgp.rst b/doc/user/bgp.rst index 20a9ef84fa..0c7fcecb9b 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2928,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/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/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/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 f175bbf9e4..1350487898 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -106,7 +106,7 @@ int tv2msec(struct timeval tv) int msecs; msecs = tv.tv_sec * 1000; - msecs += tv.tv_usec / 1000; + msecs += (tv.tv_usec + 1000) / 1000; return msecs; } @@ -126,7 +126,12 @@ int ospf_lsa_refresh_delay(struct ospf *ospf, struct ospf_lsa *lsa) zlog_debug("LSA[Type%d:%pI4]: Refresh timer delay %d milliseconds", lsa->data->type, &lsa->data->id, delay); - assert(delay > 0); + 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_vty.c b/ospfd/ospf_vty.c index 428d92dd0c..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) @@ -13603,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); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 7638e979a2..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; 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_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/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/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/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/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_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/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_rib.c b/zebra/zebra_rib.c index 59e7696ed2..b2543ca0e8 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4555,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_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 8c0d6df68a..b65097e725 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -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; } |
