diff options
65 files changed, 1553 insertions, 248 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 764b6b8716..607e34e8d3 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -325,7 +325,7 @@ void bgp_timer_set(struct peer_connection *connection) /* First entry point of peer's finite state machine. In Idle status start timer is on unless peer is shutdown or peer is inactive. All other timer must be turned off */ - if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(connection) || + if (BGP_PEER_START_SUPPRESSED(peer) || peer_active(connection) != BGP_PEER_ACTIVE || peer->bgp->vrf_id == VRF_UNKNOWN) { EVENT_OFF(connection->t_start); } else { diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 4bcb41b8dc..1f4f4d52a2 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -389,6 +389,23 @@ static void bgp_socket_set_buffer_size(const int fd) setsockopt_so_recvbuf(fd, bm->socket_buffer); } +static const char *bgp_peer_active2str(enum bgp_peer_active active) +{ + switch (active) { + case BGP_PEER_ACTIVE: + return "active"; + case BGP_PEER_CONNECTION_UNSPECIFIED: + return "unspecified connection"; + case BGP_PEER_BFD_DOWN: + return "BFD down"; + case BGP_PEER_AF_UNCONFIGURED: + return "no AF activated"; + } + + assert(!"We should never get here this is a dev escape"); + return "ERROR"; +} + /* Accept bgp connection. */ static void bgp_accept(struct event *thread) { @@ -400,6 +417,7 @@ static void bgp_accept(struct event *thread) struct peer_connection *connection, *incoming; char buf[SU_ADDRSTRLEN]; struct bgp *bgp = NULL; + enum bgp_peer_active active; sockunion_init(&su); @@ -508,7 +526,7 @@ static void bgp_accept(struct event *thread) bgp_fsm_change_status(incoming, Active); EVENT_OFF(incoming->t_start); - if (peer_active(incoming)) { + if (peer_active(incoming) == BGP_PEER_ACTIVE) { if (CHECK_FLAG(dynamic_peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) BGP_EVENT_ADD(incoming, TCP_connection_open_w_delay); else @@ -559,10 +577,11 @@ static void bgp_accept(struct event *thread) } /* Check that at least one AF is activated for the peer. */ - if (!peer_active(connection)) { + active = peer_active(connection); + if (active != BGP_PEER_ACTIVE) { if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s - incoming conn rejected - no AF activated for peer", - peer->host); + zlog_debug("%s - incoming conn rejected - %s", peer->host, + bgp_peer_active2str(active)); close(bgp_sock); return; } @@ -662,7 +681,7 @@ static void bgp_accept(struct event *thread) bgp_event_update(connection, TCP_connection_closed); } - if (peer_active(incoming)) { + if (peer_active(incoming) == BGP_PEER_ACTIVE) { if (CHECK_FLAG(doppelganger->flags, PEER_FLAG_TIMER_DELAYOPEN)) BGP_EVENT_ADD(incoming, TCP_connection_open_w_delay); else diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index ed689c8bac..db7580c283 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -444,7 +444,7 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc) !peer_established(peer->connection) && !CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) { connection = peer->connection; - if (peer_active(connection)) + if (peer_active(connection) == BGP_PEER_ACTIVE) BGP_EVENT_ADD(connection, BGP_Stop); BGP_EVENT_ADD(connection, BGP_Start); } diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 3be6ee48b8..268d804699 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -763,10 +763,6 @@ static void bgp_nht_ifp_table_handle(struct bgp *bgp, struct interface *ifp, bool up) { struct bgp_nexthop_cache *bnc; - struct nexthop *nhop; - uint16_t other_nh_count; - bool nhop_ll_found = false; - bool nhop_found = false; if (ifp->ifindex == IFINDEX_INTERNAL) { zlog_warn("%s: The interface %s ignored", __func__, ifp->name); @@ -774,42 +770,9 @@ static void bgp_nht_ifp_table_handle(struct bgp *bgp, } frr_each (bgp_nexthop_cache, table, bnc) { - other_nh_count = 0; - nhop_ll_found = bnc->ifindex_ipv6_ll == ifp->ifindex; - for (nhop = bnc->nexthop; nhop; nhop = nhop->next) { - if (nhop->ifindex == bnc->ifindex_ipv6_ll) - continue; - - if (nhop->ifindex != ifp->ifindex) { - other_nh_count++; - continue; - } - if (nhop->vrf_id != ifp->vrf->vrf_id) { - other_nh_count++; - continue; - } - nhop_found = true; - } - - if (!nhop_found && !nhop_ll_found) - /* The event interface does not match the nexthop cache - * entry */ - continue; - - if (!up && other_nh_count > 0) - /* Down event ignored in case of multiple next-hop - * interfaces. The other might interfaces might be still - * up. The cases where all interfaces are down or a bnc - * is invalid are processed by a separate zebra rnh - * messages. - */ + if (bnc->ifindex_ipv6_ll != ifp->ifindex) continue; - if (!nhop_ll_found) { - evaluate_paths(bnc); - continue; - } - bnc->last_update = monotime(NULL); bnc->change_flags = 0; @@ -822,7 +785,6 @@ static void bgp_nht_ifp_table_handle(struct bgp *bgp, if (up) { SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); SET_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED); - /* change nexthop number only for ll */ bnc->nexthop_num = 1; } else { UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index ec8cc0465b..47c5193268 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -658,17 +658,12 @@ struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t loc ext_opt_params = true; (void)bgp_open_capability(s, peer, ext_opt_params); } else { - struct stream *tmp = stream_new(STREAM_SIZE(s)); + size_t endp = stream_get_endp(s); - stream_copy(tmp, s); - if (bgp_open_capability(tmp, peer, ext_opt_params) > - BGP_OPEN_NON_EXT_OPT_LEN) { - stream_free(tmp); + if (bgp_open_capability(s, peer, ext_opt_params) > BGP_OPEN_NON_EXT_OPT_LEN) { + stream_set_endp(s, endp); ext_opt_params = true; (void)bgp_open_capability(s, peer, ext_opt_params); - } else { - stream_copy(s, tmp); - stream_free(tmp); } } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a7825165e7..082c2a737a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3406,13 +3406,14 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp, safi_t safi, uint32_t addpath_tx_id) { const struct prefix *p; - struct peer *onlypeer; + struct peer *onlypeer, *peer; struct attr attr = { 0 }, *pattr = &attr; struct bgp *bgp; bool advertise; p = bgp_dest_get_prefix(dest); bgp = SUBGRP_INST(subgrp); + peer = SUBGRP_PEER(subgrp); onlypeer = ((SUBGRP_PCOUNT(subgrp) == 1) ? (SUBGRP_PFIRST(subgrp))->peer : NULL); @@ -3447,6 +3448,26 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp, pattr, selected)) bgp_attr_flush(pattr); + + /* Remove paths from Adj-RIB-Out if it's not a best (selected) path. + * Why should we keep Adj-RIB-Out with stale paths? + */ + if (!bgp_addpath_encode_tx(peer, afi, safi)) { + struct bgp_adj_out *adj, *adj_next; + + RB_FOREACH_SAFE (adj, bgp_adj_out_rb, + &dest->adj_out, adj_next) { + if (adj->subgroup != subgrp) + continue; + + if (!adj->adv && + adj->addpath_tx_id != addpath_tx_id) { + bgp_adj_out_unset_subgroup(dest, + subgrp, 1, + adj->addpath_tx_id); + } + } + } } else { bgp_adj_out_unset_subgroup( dest, subgrp, 1, addpath_tx_id); @@ -7141,7 +7162,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p, &pi->extra->labels->label[0]); } #endif - if (pi->extra && pi->extra->vrfleak->bgp_orig) + if (pi->extra && pi->extra->vrfleak && pi->extra->vrfleak->bgp_orig) bgp_nexthop = pi->extra->vrfleak->bgp_orig; bgp_nexthop_reachability_check(afi, safi, pi, p, dest, @@ -12768,6 +12789,9 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, * though then we must display Advertised to on a path-by-path basis. */ if (!bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (peer->group) + continue; + if (bgp_adj_out_lookup(peer, dest, 0)) { if (json && !json_adv_to) json_adv_to = json_object_new_object(); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 84ff88be16..b953da57ce 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -137,7 +137,7 @@ static void bgp_start_interface_nbrs(struct bgp *bgp, struct interface *ifp) for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (peer->conf_if && (strcmp(peer->conf_if, ifp->name) == 0) && !peer_established(peer->connection)) { - if (peer_active(peer->connection)) + if (peer_active(peer->connection) == BGP_PEER_ACTIVE) BGP_EVENT_ADD(peer->connection, BGP_Stop); BGP_EVENT_ADD(peer->connection, BGP_Start); } diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 6a56eb4598..2fda41a8a9 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1960,7 +1960,7 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, enum peer_asn_type as_type, struct peer_group *group, bool config_node, const char *as_str) { - int active; + enum bgp_peer_active active; struct peer *peer; char buf[SU_ADDRSTRLEN]; afi_t afi; @@ -2014,7 +2014,7 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, } active = peer_active(peer->connection); - if (!active) { + if (active != BGP_PEER_ACTIVE) { if (peer->connection->su.sa.sa_family == AF_UNSPEC) peer->last_reset = PEER_DOWN_NBR_ADDR; else @@ -2046,7 +2046,7 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, if (bgp->autoshutdown) peer_flag_set(peer, PEER_FLAG_SHUTDOWN); /* Set up peer's events and timers. */ - else if (!active && peer_active(peer->connection)) { + else if (active != BGP_PEER_ACTIVE && peer_active(peer->connection) == BGP_PEER_ACTIVE) { if (peer->last_reset == PEER_DOWN_NOAFI_ACTIVATED) peer->last_reset = 0; bgp_timer_set(peer->connection); @@ -2419,7 +2419,7 @@ static void peer_group2peer_config_copy_af(struct peer_group *group, static int peer_activate_af(struct peer *peer, afi_t afi, safi_t safi) { - int active; + enum bgp_peer_active active; struct peer *other; if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { @@ -2447,7 +2447,7 @@ static int peer_activate_af(struct peer *peer, afi_t afi, safi_t safi) if (peer->group) peer_group2peer_config_copy_af(peer->group, peer, afi, safi); - if (!active && peer_active(peer->connection)) { + if (active != BGP_PEER_ACTIVE && peer_active(peer->connection) == BGP_PEER_ACTIVE) { bgp_timer_set(peer->connection); } else { peer->last_reset = PEER_DOWN_AF_ACTIVATE; @@ -3391,7 +3391,7 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, } /* Set up peer's events and timers. */ - if (peer_active(peer->connection)) + if (peer_active(peer->connection) == BGP_PEER_ACTIVE) bgp_timer_set(peer->connection); } @@ -4199,7 +4199,18 @@ int bgp_delete(struct bgp *bgp) while (listcount(bgp->peer)) { peer = listnode_head(bgp->peer); - peer_delete(peer); + if (peer->ifp || CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)) + bgp_zebra_terminate_radv(peer->bgp, peer); + + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP configured Graceful-Restart, skipping unconfig notification", + peer); + peer_delete(peer); + } else { + peer_notify_unconfig(peer->connection); + peer_delete(peer); + } } if (bgp->peer_self && !IS_BGP_INSTANCE_HIDDEN(bgp)) { @@ -4709,16 +4720,16 @@ bool bgp_path_attribute_treat_as_withdraw(struct peer *peer, char *buf, } /* If peer is configured at least one address family return 1. */ -bool peer_active(struct peer_connection *connection) +enum bgp_peer_active peer_active(struct peer_connection *connection) { struct peer *peer = connection->peer; if (BGP_CONNECTION_SU_UNSPEC(connection)) - return false; + return BGP_PEER_CONNECTION_UNSPECIFIED; if (peer->bfd_config) { if (bfd_session_is_down(peer->bfd_config->session)) - return false; + return BGP_PEER_BFD_DOWN; } if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST] @@ -4732,8 +4743,9 @@ bool peer_active(struct peer_connection *connection) || peer->afc[AFI_IP6][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_FLOWSPEC] || peer->afc[AFI_L2VPN][SAFI_EVPN]) - return true; - return false; + return BGP_PEER_ACTIVE; + + return BGP_PEER_AF_UNCONFIGURED; } /* If peer is negotiated at least one address family return 1. */ @@ -6417,7 +6429,7 @@ int peer_timers_connect_set(struct peer *peer, uint32_t connect) /* Skip peer-group mechanics for regular peers. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { if (!peer_established(peer->connection)) { - if (peer_active(peer->connection)) + if (peer_active(peer->connection) == BGP_PEER_ACTIVE) BGP_EVENT_ADD(peer->connection, BGP_Stop); BGP_EVENT_ADD(peer->connection, BGP_Start); } @@ -6438,7 +6450,7 @@ int peer_timers_connect_set(struct peer *peer, uint32_t connect) member->v_connect = connect; if (!peer_established(member->connection)) { - if (peer_active(member->connection)) + if (peer_active(member->connection) == BGP_PEER_ACTIVE) BGP_EVENT_ADD(member->connection, BGP_Stop); BGP_EVENT_ADD(member->connection, BGP_Start); } @@ -6471,7 +6483,7 @@ int peer_timers_connect_unset(struct peer *peer) /* Skip peer-group mechanics for regular peers. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { if (!peer_established(peer->connection)) { - if (peer_active(peer->connection)) + if (peer_active(peer->connection) == BGP_PEER_ACTIVE) BGP_EVENT_ADD(peer->connection, BGP_Stop); BGP_EVENT_ADD(peer->connection, BGP_Start); } @@ -6492,7 +6504,7 @@ int peer_timers_connect_unset(struct peer *peer) member->v_connect = peer->bgp->default_connect_retry; if (!peer_established(member->connection)) { - if (peer_active(member->connection)) + if (peer_active(member->connection) == BGP_PEER_ACTIVE) BGP_EVENT_ADD(member->connection, BGP_Stop); BGP_EVENT_ADD(member->connection, BGP_Start); } @@ -8771,7 +8783,8 @@ static int peer_unshut_after_cfg(struct bgp *bgp) peer->host); peer->shut_during_cfg = false; - if (peer_active(peer->connection) && peer->connection->status != Established) { + if (peer_active(peer->connection) == BGP_PEER_ACTIVE && + peer->connection->status != Established) { if (peer->connection->status != Idle) BGP_EVENT_ADD(peer->connection, BGP_Stop); BGP_EVENT_ADD(peer->connection, BGP_Start); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 9b13270ca7..25f533631c 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -2299,6 +2299,14 @@ enum bgp_martian_type { BGP_MARTIAN_SOO, /* bgp->evpn_info->macvrf_soo */ }; +/* Distinguish the reason why the peer is not active. */ +enum bgp_peer_active { + BGP_PEER_ACTIVE, + BGP_PEER_CONNECTION_UNSPECIFIED, + BGP_PEER_BFD_DOWN, + BGP_PEER_AF_UNCONFIGURED, +}; + extern const struct message bgp_martian_type_str[]; extern const char *bgp_martian_type2str(enum bgp_martian_type mt); @@ -2347,7 +2355,7 @@ extern struct peer *peer_unlock_with_caller(const char *, struct peer *); extern enum bgp_peer_sort peer_sort(struct peer *peer); extern enum bgp_peer_sort peer_sort_lookup(struct peer *peer); -extern bool peer_active(struct peer_connection *connection); +extern enum bgp_peer_active peer_active(struct peer_connection *connection); extern bool peer_active_nego(struct peer *); extern bool peer_afc_received(struct peer *peer); extern bool peer_afc_advertised(struct peer *peer); diff --git a/configure.ac b/configure.ac index 163772904d..f9f3286563 100644 --- a/configure.ac +++ b/configure.ac @@ -1054,6 +1054,11 @@ AC_MSG_FAILURE([Please specify a number from 0-12 for log precision ARG]) ;; esac with_log_timestamp_precision=${with_log_timestamp_precision:-0} +if test "${with_log_timestamp_precision}" != 0; then +AC_SUBST([LOG_TIMESTAMP_PRECISION_CLI], [" +log timestamp precision ${with_log_timestamp_precision}"]) +AM_SUBST_NOTMAKE([LOG_TIMESTAMP_PRECISION_CLI]) +fi AC_DEFINE_UNQUOTED([LOG_TIMESTAMP_PRECISION], [${with_log_timestamp_precision}], [Startup zlog timestamp precision]) AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use]) diff --git a/isisd/isisd.h b/isisd/isisd.h index ae39502023..48f6a5832d 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -76,9 +76,9 @@ struct isis_master { struct event_loop *master; /* Various global options */ uint8_t options; -#define ISIS_OPT_DUMMY_AS_LOOPBACK (1 << 0) +#define F_ISIS_UNIT_TEST (1 << 0) +#define ISIS_OPT_DUMMY_AS_LOOPBACK (1 << 1) }; -#define F_ISIS_UNIT_TEST 0x01 #define ISIS_DEFAULT_MAX_AREA_ADDRESSES 3 diff --git a/lib/command.h b/lib/command.h index dfd732893b..36ea73811d 100644 --- a/lib/command.h +++ b/lib/command.h @@ -485,7 +485,6 @@ struct cmd_node { /* Graceful Restart cli help strings */ #define GR_CMD "Global Graceful Restart command\n" #define NO_GR_CMD "Undo Global Graceful Restart command\n" -#define GR "Global Graceful Restart - GR Mode\n" #define GR_DISABLE "Global Graceful Restart - Disable Mode\n" #define NO_GR_DISABLE "Undo Global Graceful Restart - Disable Mode\n" #define GR_DEBUG "Graceful Restart - Enable Debug Logs\n" diff --git a/lib/darr.h b/lib/darr.h index 084c2a103a..4638b904d1 100644 --- a/lib/darr.h +++ b/lib/darr.h @@ -62,6 +62,7 @@ * - darr_strdup * - darr_strdup_cap * - darr_strlen + * - darr_strlen_fixup * - darr_strnul * - darr_sprintf, darr_vsprintf */ @@ -753,6 +754,22 @@ void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt); }) /** + * Fixup darr_len (and thus darr_strlen) for `S` based on its strlen(S) + * (i.e., scan for NUL byte). The dynamic array length will be set to strlen(S) + 1. + * + * Args: + * S: The dynamic array with a NUL terminated string, cannot be NULL. + * + * Return: + * The calculated strlen() value. + */ +#define darr_strlen_fixup(S) \ + ({ \ + _darr_len(S) = strlen(S) + 1; \ + darr_strlen(S); \ + }) + +/** * darr_vsprintf() - vsprintf into a new dynamic array. * * Args: diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c index ad495b6f9c..626c37082e 100644 --- a/lib/northbound_oper.c +++ b/lib/northbound_oper.c @@ -394,10 +394,17 @@ static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in, char **xpath_out struct lyd_node **trunk) { char *xpath = NULL; + uint32_t llopts = 0; enum nb_error ret = NB_OK; LY_ERR err; + /* + * Try to instantiate ever shortened paths until one succeeds, suppress + * libyang logs for the expected errors along the way. + */ darr_in_strdup(xpath, xpath_in); + + ly_temp_log_options(&llopts); for (;;) { err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, LYD_NEW_PATH_UPDATE, NULL, trunk); @@ -407,7 +414,10 @@ static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in, char **xpath_out ret = yang_xpath_pop_node(xpath); if (ret != NB_OK) break; + darr_strlen_fixup(xpath); } + ly_temp_log_options(NULL); + if (ret == NB_OK) *xpath_out = xpath; else @@ -893,9 +903,12 @@ static enum nb_error nb_op_iter_leaf(struct nb_op_yield_state *ys, return nb_op_libyang_cb_get(ys, nb_node, ni->inner, xpath); /* Check for new simple get */ - if (nb_node->cbs.get) + if (nb_node->cbs.get) { /* XXX: need to run through translator */ + DEBUGD(&nb_dbg_cbs_state, "northbound callback (get): xpath [%s] list_entry [%p]", + xpath, ni->list_entry); return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner); + } data = nb_callback_get_elem(nb_node, xpath, ni->list_entry); if (data == NULL) @@ -931,9 +944,12 @@ static enum nb_error nb_op_iter_leaflist(struct nb_op_yield_state *ys, return NB_OK; /* Check for new simple get */ - if (nb_node->cbs.get) + if (nb_node->cbs.get) { /* XXX: need to run through translator */ + DEBUGD(&nb_dbg_cbs_state, "northbound callback (get): xpath [%s] list_entry [%p]", + xpath, ni->list_entry); return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner); + } if (CHECK_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE)) /* XXX: need to run through translator */ diff --git a/lib/zclient.c b/lib/zclient.c index f0476867be..bdb62befb9 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1362,6 +1362,8 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_putc(s, api->type); stream_putw(s, api->instance); + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + SET_FLAG(api->flags, ZEBRA_FLAG_TABLEID); stream_putl(s, api->flags); stream_putl(s, api->message); @@ -1421,6 +1423,8 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + SET_FLAG(api->flags, ZEBRA_FLAG_TABLEID); if (zapi_nexthop_encode(s, api_nh, api->flags, api->message) != 0) @@ -1460,6 +1464,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + SET_FLAG(api->flags, ZEBRA_FLAG_TABLEID); + if (zapi_nexthop_encode(s, api_nh, api->flags, api->message) != 0) diff --git a/lib/zclient.h b/lib/zclient.h index 43521d6e2e..8719af1d03 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -578,6 +578,12 @@ struct zapi_route { * kernel (NLM_F_APPEND at the very least ) */ #define ZEBRA_FLAG_OUTOFSYNC 0x400 +/* + * This flag lets us know that the route entry is + * associated to the table ID and must remain when the + * table ID is de-associated from a VRF. + */ +#define ZEBRA_FLAG_TABLEID 0x800 /* The older XXX_MESSAGE flags live here */ uint32_t message; diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index cf5479e18c..f512e60ef2 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -360,12 +360,25 @@ void ospf6_area_delete(struct ospf6_area *oa) ospf6_route_table_delete(oa->summary_router); listnode_delete(oa->ospf6->area_list, oa); + + if (oa->ospf6->backbone == oa) + oa->ospf6->backbone = NULL; oa->ospf6 = NULL; /* free area */ XFREE(MTYPE_OSPF6_AREA, oa); } +void ospf6_area_no_config_delete(struct ospf6_area *oa) +{ + if ((oa->if_list->count == 0) && (oa->range_table->count == 0) && + (oa->nssa_range_table->count == 0) && !IS_AREA_STUB(oa) && !IS_AREA_NSSA(oa) && + !PREFIX_NAME_IN(oa) && !PREFIX_NAME_OUT(oa) && !IMPORT_NAME(oa) && !EXPORT_NAME(oa)) { + ospf6_area_disable(oa); + ospf6_area_delete(oa); + } +} + struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id) { struct ospf6_area *oa; @@ -409,6 +422,7 @@ void ospf6_area_disable(struct ospf6_area *oa) struct listnode *node, *nnode; struct ospf6_interface *oi; + UNSET_FLAG(oa->flag, OSPF6_AREA_ACTIVE); UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) @@ -646,6 +660,9 @@ DEFUN (no_area_range, } ospf6_route_remove(range, oa->range_table); + /* Delete area if no interfaces or configuration. */ + ospf6_area_no_config_delete(oa); + return CMD_SUCCESS; } @@ -814,6 +831,9 @@ DEFUN (no_area_filter_list, if (ospf6_check_and_set_router_abr(area->ospf6)) ospf6_schedule_abr_task(ospf6); + /* Delete area if no interfaces or configuration. */ + ospf6_area_no_config_delete(area); + return CMD_SUCCESS; } @@ -939,6 +959,9 @@ DEFUN (no_area_import_list, if (ospf6_check_and_set_router_abr(area->ospf6)) ospf6_schedule_abr_task(ospf6); + /* Delete area if no interfaces or configuration. */ + ospf6_area_no_config_delete(area); + return CMD_SUCCESS; } @@ -1002,6 +1025,9 @@ DEFUN (no_area_export_list, if (ospf6_check_and_set_router_abr(area->ospf6)) ospf6_schedule_abr_task(ospf6); + /* Delete area if no interfaces or configuration. */ + ospf6_area_no_config_delete(area); + return CMD_SUCCESS; } @@ -1338,6 +1364,9 @@ DEFUN (no_ospf6_area_stub_no_summary, ospf6_area_stub_unset(ospf6, area); ospf6_area_no_summary_unset(ospf6, area); + /* Delete area if no interfaces or configuration. */ + ospf6_area_no_config_delete(area); + return CMD_SUCCESS; } @@ -1418,6 +1447,9 @@ DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd, ospf6_area_no_summary_unset(ospf6, area); ospf6_nssa_default_originate_unset(ospf6, area); + /* Delete area if no interfaces or configuration. */ + ospf6_area_no_config_delete(area); + return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index d9afd65d81..2ed69cc597 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -144,6 +144,7 @@ extern int ospf6_area_cmp(void *va, void *vb); extern struct ospf6_area *ospf6_area_create(uint32_t area_id, struct ospf6 *ospf6, int df); extern void ospf6_area_delete(struct ospf6_area *oa); +extern void ospf6_area_no_config_delete(struct ospf6_area *oa); extern struct ospf6_area *ospf6_area_lookup(uint32_t area_id, struct ospf6 *ospf6); extern struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 60f92385dd..1022101eb3 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1823,6 +1823,9 @@ void ospf6_interface_stop(struct ospf6_interface *oi) if (oa->if_list->count == 0) { UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); ospf6_abr_disable_area(oa); + + /* Delete area if no interfaces or configuration. */ + ospf6_area_no_config_delete(oa); } } diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 124b38219d..c143173155 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -1509,16 +1509,16 @@ DEFPY (debug_ospf_gr, if (vty->node == CONFIG_NODE) { if (no) - CONF_DEBUG_OFF(gr, GR); + DEBUG_OFF(gr, GR); else - CONF_DEBUG_ON(gr, GR); + DEBUG_ON(gr, GR); + } else { + if (no) + TERM_DEBUG_OFF(gr, GR); + else + TERM_DEBUG_ON(gr, GR); } - if (no) - TERM_DEBUG_OFF(gr, GR); - else - TERM_DEBUG_ON(gr, GR); - return CMD_SUCCESS; } diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 82eabd8d56..d549df70fe 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -368,6 +368,36 @@ static void static_ifindex_update_nh(struct interface *ifp, bool up, static_install_path(pn); } +void static_install_nexthops_on_startup(void) +{ + struct route_table *stable; + struct route_node *rn; + struct static_nexthop *nh; + struct static_path *pn; + struct static_vrf *svrf; + struct static_route_info *si; + afi_t afi; + safi_t safi; + + RB_FOREACH (svrf, svrf_name_head, &svrfs) { + FOREACH_AFI_SAFI (afi, safi) { + stable = static_vrf_static_table(afi, safi, svrf); + if (!stable) + continue; + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each (static_path_list, &si->path_list, pn) { + frr_each (static_nexthop_list, &pn->nexthop_list, nh) { + static_zebra_nht_register(nh, true); + } + } + } + } + } +} + static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi, safi_t safi) { diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 2e2e4986c3..7f4936e0b9 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -264,6 +264,7 @@ extern void static_bfd_initialize(struct zclient *zc, struct event_loop *tm); extern void static_bfd_show(struct vty *vty, bool isjson); +extern void static_install_nexthops_on_startup(void); #ifdef __cplusplus } #endif diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index a6521cccc6..3ed525f386 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -181,6 +181,12 @@ static void zebra_connected(struct zclient *zclient) vrf = vrf_lookup_by_id(VRF_DEFAULT); assert(vrf); static_fixup_vrf_ids(vrf); + + /* + * It's possible that staticd connected after config was read + * in. + */ + static_install_nexthops_on_startup(); } /* API to check whether the configured nexthop address is diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in index 222abcdade..31b5a8c237 100644 --- a/tests/lib/cli/test_cli.refout.in +++ b/tests/lib/cli/test_cli.refout.in @@ -405,7 +405,7 @@ frr version @PACKAGE_VERSION@ frr defaults @DFLT_NAME@
!
hostname test
-domainname test.domain
+domainname test.domain@LOG_TIMESTAMP_PRECISION_CLI@ !
!
!
@@ -420,7 +420,7 @@ frr version @PACKAGE_VERSION@ frr defaults @DFLT_NAME@
!
hostname foohost
-domainname test.domain
+domainname test.domain@LOG_TIMESTAMP_PRECISION_CLI@ !
!
!
diff --git a/tests/lib/northbound/test_oper_exists.c b/tests/lib/northbound/test_oper_exists.c index 17afcc7fd4..52ecae2fef 100644 --- a/tests/lib/northbound/test_oper_exists.c +++ b/tests/lib/northbound/test_oper_exists.c @@ -157,13 +157,15 @@ const char *data_json = "\n" "}\n"; -static const struct lyd_node *test_oper_get_tree_locked(const char *xpath) +static const struct lyd_node *test_oper_get_tree_locked(const char *xpath __attribute__((unused)), + void **lock __attribute__((unused))) { ++data_tree_lock; return data_tree; } -static void test_oper_unlock_tree(const struct lyd_node *tree __attribute__((unused))) +static void test_oper_unlock_tree(const struct lyd_node *tree __attribute__((unused)), + void *lock __attribute__((unused))) { data_tree_lock--; } diff --git a/tests/lib/test_darr.c b/tests/lib/test_darr.c index 87f9e3e564..be319db1c1 100644 --- a/tests/lib/test_darr.c +++ b/tests/lib/test_darr.c @@ -48,6 +48,7 @@ * [x] - darr_strdup * [x] - darr_strdup_cap * [x] - darr_strlen + * [x] - darr_strlen_fixup * [x] - darr_strnul * [ ] - darr_vsprintf */ @@ -406,6 +407,9 @@ static void test_string(void) assert(!strcmp(da1, "0123456789: DEADBEEF")); assert(darr_strlen(da1) == 20); assert(darr_cap(da1) == 128); + + da1[5] = 0; + assert(darr_strlen_fixup(da1) == 5); darr_free(da1); da1 = darr_sprintf("0123456789: %08x", 0xDEADBEEF); diff --git a/tests/topotests/bgp_addpath_disable_rx/__init__.py b/tests/topotests/bgp_addpath_disable_rx/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/__init__.py diff --git a/tests/topotests/bgp_addpath_disable_rx/r1/frr.conf b/tests/topotests/bgp_addpath_disable_rx/r1/frr.conf new file mode 100644 index 0000000000..0c3fdad671 --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/r1/frr.conf @@ -0,0 +1,38 @@ +! +int r1-eth0 + ip address 192.168.137.1/24 +! +router bgp 65000 + bgp router-id 192.168.137.1 + no bgp ebgp-requires-policy + no bgp enforce-first + neighbor AS65100-V4 peer-group + neighbor AS65100-V4 remote-as 65100 + neighbor AS65100-V4 timers 1 3 + neighbor AS65200-V4 peer-group + neighbor AS65200-V4 remote-as 65200 + neighbor AS65200-V4 timers 1 3 + neighbor AS65600-V4 peer-group + neighbor AS65600-V4 remote-as 65600 + neighbor AS65600-V4 timers 1 3 + neighbor 192.168.137.100 peer-group AS65100-V4 + neighbor 192.168.137.201 peer-group AS65200-V4 + neighbor 192.168.137.202 peer-group AS65200-V4 + neighbor 192.168.137.60 peer-group AS65600-V4 + ! + address-family ipv4 unicast + neighbor AS65100-V4 addpath-tx-bestpath-per-AS + neighbor AS65100-V4 route-server-client + neighbor AS65200-V4 addpath-tx-bestpath-per-AS + neighbor AS65200-V4 disable-addpath-rx + neighbor AS65200-V4 route-server-client + neighbor AS65600-V4 addpath-tx-bestpath-per-AS + neighbor AS65600-V4 disable-addpath-rx + neighbor AS65600-V4 route-server-client + neighbor 192.168.137.201 disable-addpath-rx + neighbor 192.168.137.202 disable-addpath-rx + neighbor 192.168.137.60 disable-addpath-rx + exit-address-family +! +exit +! diff --git a/tests/topotests/bgp_addpath_disable_rx/r2/frr.conf b/tests/topotests/bgp_addpath_disable_rx/r2/frr.conf new file mode 100644 index 0000000000..daa252dc4b --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/r2/frr.conf @@ -0,0 +1,19 @@ +! +int r2-eth0 + ip address 192.168.137.60/24 +! +router bgp 65600 + bgp router-id 192.168.137.60 + no bgp ebgp-requires-policy + no bgp enforce-first-as + neighbor 192.168.137.1 remote-as 65000 + ! + address-family ipv4 unicast + neighbor 192.168.137.1 disable-addpath-rx + neighbor 192.168.137.1 prefix-list out out + exit-address-family +! +exit +! +ip prefix-list out seq 100 deny any +! diff --git a/tests/topotests/bgp_addpath_disable_rx/r3/frr.conf b/tests/topotests/bgp_addpath_disable_rx/r3/frr.conf new file mode 100644 index 0000000000..120c59fa21 --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/r3/frr.conf @@ -0,0 +1,20 @@ +! +int r3-eth0 + ip address 192.168.137.100/24 +! +int r3-eth1 + ip address 192.168.44.1/24 +! +router bgp 65100 + bgp router-id 192.168.137.100 + no bgp ebgp-requires-policy + no bgp enforce-first + neighbor 192.168.44.44 remote-as 65444 + neighbor 192.168.137.1 remote-as 65000 + ! + address-family ipv4 unicast + neighbor 192.168.44.44 disable-addpath-rx + exit-address-family +! +exit +! diff --git a/tests/topotests/bgp_addpath_disable_rx/r4/frr.conf b/tests/topotests/bgp_addpath_disable_rx/r4/frr.conf new file mode 100644 index 0000000000..b116d1c16a --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/r4/frr.conf @@ -0,0 +1,21 @@ +! +int r4-eth0 + ip address 192.168.137.201/24 +! +int r4-eth1 + ip address 192.168.54.21/24 +! +router bgp 65200 + bgp router-id 192.168.137.201 + no bgp enforce-first + no bgp ebgp-requires-policy + neighbor 192.168.54.44 remote-as 65444 + neighbor 192.168.137.1 remote-as 65000 + ! + address-family ipv4 unicast + neighbor 192.168.54.44 disable-addpath-rx + neighbor 192.168.137.1 disable-addpath-rx + exit-address-family +! +exit +! diff --git a/tests/topotests/bgp_addpath_disable_rx/r5/frr.conf b/tests/topotests/bgp_addpath_disable_rx/r5/frr.conf new file mode 100644 index 0000000000..926dd6fbf8 --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/r5/frr.conf @@ -0,0 +1,20 @@ +int r5-eth0 + ip address 192.168.137.202/24 +! +int r5-eth1 + ip address 192.168.54.22/24 +! +router bgp 65200 + bgp router-id 192.168.137.202 + no bgp ebgp-requires-policy + no bgp enforce-first + neighbor 192.168.54.44 remote-as 65444 + neighbor 192.168.137.1 remote-as 65000 + ! + address-family ipv4 unicast + neighbor 192.168.54.44 disable-addpath-rx + neighbor 192.168.137.1 disable-addpath-rx + exit-address-family +! +exit +! diff --git a/tests/topotests/bgp_addpath_disable_rx/r6/frr.conf b/tests/topotests/bgp_addpath_disable_rx/r6/frr.conf new file mode 100644 index 0000000000..3ef826da38 --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/r6/frr.conf @@ -0,0 +1,40 @@ +! +int r6-eth0 + ip address 192.168.44.44/24 +! +int r6-eth1 + ip address 192.168.54.44/24 +! +router bgp 65444 + bgp router-id 192.168.54.44 + no bgp network import-check + no bgp ebgp-requires-policy + neighbor 192.168.44.1 remote-as 65100 + neighbor 192.168.54.21 remote-as 65200 + neighbor 192.168.54.22 remote-as 65200 + ! + address-family ipv4 unicast + network 10.0.0.0/24 + neighbor 192.168.44.1 disable-addpath-rx + neighbor 192.168.44.1 route-map AS65100-OUT out + neighbor 192.168.54.21 disable-addpath-rx + neighbor 192.168.54.21 route-map AS65200-OUT out + neighbor 192.168.54.22 disable-addpath-rx + neighbor 192.168.54.22 route-map AS65200-OUT out + exit-address-family +! +exit +! +ip prefix-list out seq 2 permit 10.0.0.0/24 +ip prefix-list out seq 100 deny any +ip prefix-list v4_our_to65200 seq 100 deny any +! +route-map AS65200-OUT permit 10 + match ip address prefix-list v4_our_to65200 +exit +! +route-map AS65100-OUT permit 10 + match ip address prefix-list out + set as-path prepend 65444 +exit +! diff --git a/tests/topotests/bgp_addpath_disable_rx/test_bgp_addpath_disable_rx.py b/tests/topotests/bgp_addpath_disable_rx/test_bgp_addpath_disable_rx.py new file mode 100644 index 0000000000..421dc85af7 --- /dev/null +++ b/tests/topotests/bgp_addpath_disable_rx/test_bgp_addpath_disable_rx.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2025 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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + r3 = tgen.add_router("r3") + r4 = tgen.add_router("r4") + r5 = tgen.add_router("r5") + r6 = tgen.add_router("r6") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(r2) + switch.add_link(r3) + switch.add_link(r4) + switch.add_link(r5) + + switch = tgen.add_switch("s2") + switch.add_link(r3) + switch.add_link(r6) + + switch = tgen.add_switch("s3") + switch.add_link(r4) + switch.add_link(r5) + switch.add_link(r6) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + for _, (rname, router) in enumerate(tgen.routers().items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_addpath_disable_rx(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r6 = tgen.gears["r6"] + + def _bgp_check_aspath(aspath): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 10.0.0.0/24 json")) + expected = { + "paths": [ + { + "aspath": { + "string": aspath, + }, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_aspath, "65100 65444 65444") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.0.0.0/24 should have aspath (65100 65444 65444)" + + # Send 10.0.0.0/24 from r6 towards r2. + # This should be the best path for r2 without any prepends. + r6.vtysh_cmd( + """ + configure terminal + ip prefix-list v4_our_to65200 seq 2 permit 10.0.0.0/24 + """ + ) + + test_func = functools.partial(_bgp_check_aspath, "65200 65444") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.0.0.0/24 should have aspath (65200 65444)" + + # Drop 10.0.0.0/24 from r6 towards r2. + # This should be removed from r2 and the old path (65100 65444 65444) + # should be used. + r6.vtysh_cmd( + """ + configure terminal + no ip prefix-list v4_our_to65200 seq 2 permit 10.0.0.0/24 + """ + ) + + test_func = functools.partial(_bgp_check_aspath, "65100 65444 65444") + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.0.0.0/24 should have aspath (65100 65444 65444)" + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py index c14ef6b8c3..0d4c8c8564 100644 --- a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py +++ b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py @@ -154,6 +154,11 @@ def test_bgp_comm_list_limit_match(): "vrfName": "default", "routerId": "192.168.1.3", "localAS": 65003, + "routes": { + "172.16.255.2/32": [{"prefix": "172.16.255.2", "prefixLen": 32}], + "172.16.255.5/32": [{"prefix": "172.16.255.5", "prefixLen": 32}], + "192.168.0.0/24": [{"prefix": "192.168.0.0", "prefixLen": 24}], + }, "totalRoutes": 3, "totalPaths": 3, } @@ -165,6 +170,42 @@ def test_bgp_comm_list_limit_match(): assert result is None, "Failed to check that 3 routes have been received on R3" +def test_bgp_comm_list_limit_match_no_community(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + router.vtysh_cmd( + """ + configure terminal + route-map r1 permit 20 + match community-limit 0 + """ + ) + + def _bgp_count(): + output = json.loads(router.vtysh_cmd("show bgp ipv4 json")) + expected = { + "vrfName": "default", + "routerId": "192.168.1.3", + "localAS": 65003, + "routes": { + "172.16.255.2/32": [{"prefix": "172.16.255.2", "prefixLen": 32}], + "192.168.0.0/24": [{"prefix": "192.168.0.0", "prefixLen": 24}], + }, + "totalRoutes": 2, + "totalPaths": 2, + } + return topotest.json_cmp(output, expected) + + step("Check that 2 routes have been received on R3") + test_func = functools.partial(_bgp_count) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to check that 2 routes have been received on R3" + + def test_bgp_comm_list_reset_limit_match(): tgen = get_topogen() diff --git a/tests/topotests/bgp_ecomm_list_match/test_bgp_ecomm_list_match.py b/tests/topotests/bgp_ecomm_list_match/test_bgp_ecomm_list_match.py index 670a600a8c..c3277c5d40 100644 --- a/tests/topotests/bgp_ecomm_list_match/test_bgp_ecomm_list_match.py +++ b/tests/topotests/bgp_ecomm_list_match/test_bgp_ecomm_list_match.py @@ -150,6 +150,11 @@ def test_bgp_extcomm_list_limit_match(): "vrfName": "default", "routerId": "192.168.1.3", "localAS": 65003, + "routes": { + "172.16.255.2/32": [{"prefix": "172.16.255.2", "prefixLen": 32}], + "172.16.255.5/32": [{"prefix": "172.16.255.5", "prefixLen": 32}], + "192.168.0.0/24": [{"prefix": "192.168.0.0", "prefixLen": 24}], + }, "totalRoutes": 3, "totalPaths": 3, } @@ -161,7 +166,43 @@ def test_bgp_extcomm_list_limit_match(): assert result is None, "Failed to check that 3 routes have been received on R3" -def test_bgp_comm_list_reset_limit_match(): +def test_bgp_extcomm_list_limit_match_no_community(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + router.vtysh_cmd( + """ + configure terminal + route-map r1 permit 20 + match extcommunity-limit 0 + """ + ) + + def _bgp_count(): + output = json.loads(router.vtysh_cmd("show bgp ipv4 json")) + expected = { + "vrfName": "default", + "routerId": "192.168.1.3", + "localAS": 65003, + "routes": { + "172.16.255.2/32": [{"prefix": "172.16.255.2", "prefixLen": 32}], + "192.168.0.0/24": [{"prefix": "192.168.0.0", "prefixLen": 24}], + }, + "totalRoutes": 2, + "totalPaths": 2, + } + return topotest.json_cmp(output, expected) + + step("Check that 2 routes have been received on R3") + test_func = functools.partial(_bgp_count) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to check that 2 routes have been received on R3" + + +def test_bgp_extcomm_list_reset_limit_match(): tgen = get_topogen() if tgen.routers_have_failure(): diff --git a/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py index 08d6e140a1..93764642d8 100644 --- a/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py +++ b/tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py @@ -165,6 +165,131 @@ def test_bgp_vpn_5549(): assert result is None, "IPv6 nexthop is invalid" +def check_show_interface_rtadv_params_found(router): + output = json.loads(router.vtysh_cmd("show interface json")) + expected = { + "pe1-eth1": { + "ndAdvertisedReachableTimeMsecs": 0, + "ndAdvertisedRetransmitIntervalMsecs": 0, + "ndAdvertisedHopCountLimitHops": 64, + "ndRouterAdvertisementsIntervalSecs": 10, + "ndRouterAdvertisementsDoNotUseFastRetransmit": False, + "ndRouterAdvertisementsLifetimeTracksRaInterval": True, + "ndRouterAdvertisementDefaultRouterPreference": "medium", + "hostsUseStatelessAutoconfigForAddresses": True, + } + } + return topotest.json_cmp(output, expected) + + +def test_show_interface_rtadv_params_found(): + tgen = get_topogen() + + router = tgen.gears["pe1"] + test_func = functools.partial(check_show_interface_rtadv_params_found, router) + success, _ = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success, "rtadv output is invalid" + + +def check_show_interface_rtadv_params_not_found(router): + output = json.loads(router.vtysh_cmd("show interface json")) + expected = { + "pe1-eth1": { + "ndAdvertisedReachableTimeMsecs": 0, + "ndAdvertisedRetransmitIntervalMsecs": 0, + } + } + + ret = topotest.json_cmp(output, expected) + if ret is None: + return "Unexpected: interface rtadv parameters found" + return None + + +def test_show_interface_rtadv_params_not_found(): + tgen = get_topogen() + + router = tgen.gears["pe1"] + router.vtysh_cmd( + "configure \n \ + router bgp 65001 \n \ + no neighbor 2001:db8:1::2 \n \ + exit \n \ + exit" + ) + + test_func = functools.partial(check_show_interface_rtadv_params_not_found, router) + success, _ = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success, "not good" + + +def check_show_interface_rtadv_params_found_reapply(router): + output = json.loads(router.vtysh_cmd("show interface json")) + expected = { + "pe1-eth1": { + "ndAdvertisedReachableTimeMsecs": 0, + "ndAdvertisedRetransmitIntervalMsecs": 0, + } + } + return topotest.json_cmp(output, expected) + + +def test_show_interface_rtadv_params_found_reapply(): + tgen = get_topogen() + + router = tgen.gears["pe1"] + router.vtysh_cmd( + "configure \n \ + router bgp 65001 \n \ + neighbor 2001:db8:1::2 remote-as internal \n \ + neighbor 2001:db8:1::2 update-source 2001:db8:1::1 \n \ + neighbor 2001:db8:1::2 timers 1 3 \n \ + neighbor 2001:db8:1::2 timers connect 1 \n \ + neighbor 2001:db8:1::2 capability extended-nexthop \n \ + exit \n \ + exit" + ) + + test_func = functools.partial( + check_show_interface_rtadv_params_found_reapply, router + ) + success, _ = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success, "rtadv output is invalid" + + +def check_show_interface_rtadv_params_not_found_after_reapply(router): + output = json.loads(router.vtysh_cmd("show interface json")) + + expected = { + "pe1-eth1": { + "ndAdvertisedReachableTimeMsecs": 0, + "ndAdvertisedRetransmitIntervalMsecs": 0, + } + } + + ret = topotest.json_cmp(output, expected) + if ret is None: + return "Unexpected: interface rtadv parameters found" + return None + + +def test_show_interface_rtadv_params_not_found_after_reapply(): + tgen = get_topogen() + + router = tgen.gears["pe1"] + router.vtysh_cmd( + "configure \n \ + no router bgp 65001 vrf RED \n \ + no router bgp 65001 \n \ + exit" + ) + test_func = functools.partial( + check_show_interface_rtadv_params_not_found_after_reapply, router + ) + success, _ = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success, "not good" + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/bmp_collector/bmpserver.py b/tests/topotests/lib/bmp_collector/bmpserver.py index c42c387563..f19f63bd8a 100755 --- a/tests/topotests/lib/bmp_collector/bmpserver.py +++ b/tests/topotests/lib/bmp_collector/bmpserver.py @@ -73,7 +73,7 @@ def savepid(): pid = open(pid_file, "r").readline().strip() if check_pid(int(pid)): timestamp_print( - "PID file already exists and program still running %s\n" % pid_file + f"PID file already exists and program still running {pid_file}\n" ) return False else: @@ -81,8 +81,7 @@ def savepid(): fd = os.open(pid_file, flags ^ os.O_EXCL, mode) except (OSError, IOError, ValueError): timestamp_print( - "issue accessing PID file %s (most likely permission or ownership)\n" - % pid_file + f"issue accessing PID file {pid_file} (most likely permission or ownership)\n" ) return False @@ -93,9 +92,9 @@ def savepid(): f.close() saved_pid = True except IOError: - timestamp_print("Can not create PID file %s\n" % pid_file) + timestamp_print(f"Can not create PID file {pid_file}\n") return False - timestamp_print("Created PID file %s with value %d\n" % (pid_file, ownid)) + timestamp_print(f"Created PID file {pid_file} with value {ownid}\n") return True @@ -106,9 +105,9 @@ def removepid(): if exc.errno == errno.ENOENT: pass else: - timestamp_print("Can not remove PID file %s\n" % pid_file) + timestamp_print(f"Can not remove PID file {pid_file}\n") return - timestamp_print("Removed PID file %s\n" % pid_file) + timestamp_print(f"Removed PID file {pid_file}\n") def main(): @@ -168,7 +167,7 @@ def main(): timestamp_print(f"TCP session closed with {client_address}") connection.close() except socket.error as sock_err: - timestamp_print(f"Socket error: {e}") + timestamp_print(f"Socket error: {sock_err}") except Exception as e: timestamp_print(f"{e}") finally: diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 349b82aab4..30f214d7cb 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -53,20 +53,20 @@ def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True "pim": { "join-prune-interval": "5", "rp": [{ - "rp_addr" : "1.0.3.17". - "keep-alive-timer": "100" - "group_addr_range": ["224.1.1.0/24", "225.1.1.0/24"] - "prefix-list": "pf_list_1" + "rp_addr" : "1.0.3.17", + "keep-alive-timer": "100", + "group_addr_range": ["224.1.1.0/24", "225.1.1.0/24"], + "prefix-list": "pf_list_1", "delete": True }] }, "pim6": { "disable" : ["l1-i1-eth1"], "rp": [{ - "rp_addr" : "2001:db8:f::5:17". - "keep-alive-timer": "100" - "group_addr_range": ["FF00::/8"] - "prefix-list": "pf_list_1" + "rp_addr" : "2001:db8:f::5:17", + "keep-alive-timer": "100", + "group_addr_range": ["FF00::/8"], + "prefix-list": "pf_list_1", "delete": True }] } @@ -268,7 +268,7 @@ def create_igmp_config(tgen, topo, input_dict=None, build=False): "r1-r0-eth0" :{ "igmp":{ "version": "2", - "delete": True + "delete": True, "query": { "query-interval" : 100, "query-max-response-time": 200 diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index e2c70cdccd..07033698d0 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1960,7 +1960,13 @@ class Router(Node): ) else: binary = os.path.join(self.daemondir, daemon) - check_daemon_files.extend([runbase + ".pid", runbase + ".vty"]) + if daemon == "zebra": + zapi_base = "/var/run/{}/zserv.api".format(self.routertype) + check_daemon_files.extend( + [runbase + ".pid", runbase + ".vty", zapi_base] + ) + else: + check_daemon_files.extend([runbase + ".pid", runbase + ".vty"]) cmdenv = "ASAN_OPTIONS=" if asan_abort: @@ -2244,17 +2250,39 @@ class Router(Node): else: logger.debug("%s: %s %s started", self, self.routertype, daemon) + # Check if the daemons are running + def _check_daemons_running(check_daemon_files): + wait_time = 30 if (gdb_routers or gdb_daemons) else 10 + timeout = Timeout(wait_time) + for remaining in timeout: + if not check_daemon_files: + break + check = check_daemon_files[0] + if self.path_exists(check): + check_daemon_files.pop(0) + continue + self.logger.debug( + "Waiting {}s for {} to appear".format(remaining, check) + ) + time.sleep(0.5) + # Start mgmtd first if "mgmtd" in daemons_list: start_daemon("mgmtd") while "mgmtd" in daemons_list: daemons_list.remove("mgmtd") + # Wait till mgmtd is up and running to some + # very small extent before moving on + _check_daemons_running(check_daemon_files) # Start Zebra after mgmtd if "zebra" in daemons_list: start_daemon("zebra") while "zebra" in daemons_list: daemons_list.remove("zebra") + # Wait till zebra is up and running to some + # very small extent before moving on + _check_daemons_running(check_daemon_files) # Start staticd next if required if "staticd" in daemons_list: @@ -2290,17 +2318,7 @@ class Router(Node): start_daemon(daemon) # Check if daemons are running. - wait_time = 30 if (gdb_routers or gdb_daemons) else 10 - timeout = Timeout(wait_time) - for remaining in timeout: - if not check_daemon_files: - break - check = check_daemon_files[0] - if self.path_exists(check): - check_daemon_files.pop(0) - continue - self.logger.debug("Waiting {}s for {} to appear".format(remaining, check)) - time.sleep(0.5) + _check_daemons_running(check_daemon_files) if check_daemon_files: assert False, "Timeout({}) waiting for {} to appear on {}".format( diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_2.json b/tests/topotests/zebra_rib/r1/import_mrib_table_2.json index 61aaaede6e..03e4163269 100644 --- a/tests/topotests/zebra_rib/r1/import_mrib_table_2.json +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_2.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_3.json b/tests/topotests/zebra_rib/r1/import_mrib_table_3.json index 27a0b9f264..dd1774cd84 100644 --- a/tests/topotests/zebra_rib/r1/import_mrib_table_3.json +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_3.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -238,7 +238,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_4.json b/tests/topotests/zebra_rib/r1/import_mrib_table_4.json index 5a8f0eecd5..9a318f9f00 100644 --- a/tests/topotests/zebra_rib/r1/import_mrib_table_4.json +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_4.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_table_2.json b/tests/topotests/zebra_rib/r1/import_table_2.json index 61aaaede6e..03e4163269 100644 --- a/tests/topotests/zebra_rib/r1/import_table_2.json +++ b/tests/topotests/zebra_rib/r1/import_table_2.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_table_3.json b/tests/topotests/zebra_rib/r1/import_table_3.json index 27a0b9f264..dd1774cd84 100644 --- a/tests/topotests/zebra_rib/r1/import_table_3.json +++ b/tests/topotests/zebra_rib/r1/import_table_3.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -238,7 +238,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_table_4.json b/tests/topotests/zebra_rib/r1/import_table_4.json index 5a8f0eecd5..9a318f9f00 100644 --- a/tests/topotests/zebra_rib/r1/import_table_4.json +++ b/tests/topotests/zebra_rib/r1/import_table_4.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json new file mode 100644 index 0000000000..70398d80b1 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json @@ -0,0 +1,54 @@ +{ + "0.0.0.0/0": [ + { + "protocol": "kernel", + "vrfName": "default", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.0.0.0/24": [ + { + "protocol": "static", + "vrfName": "default", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.1.0.0/24": [ + { + "protocol": "static", + "vrfName": "default", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.211.254", + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], + "10.2.0.0/24": null, + "10.3.0.0/24": null, + "192.168.210.0/24": null, + "192.168.210.1/32": null +} diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt new file mode 100644 index 0000000000..f0fcb73d4d --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt @@ -0,0 +1,3 @@ +blackhole default +blackhole 10.0.0.0/24 proto XXXX metric 20 +10.1.0.0/24 via 192.168.211.254 dev r1-eth1 proto XXXX metric 20 diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json new file mode 100644 index 0000000000..5949a69a73 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json @@ -0,0 +1,115 @@ +{ + "0.0.0.0/0": [ + { + "protocol": "kernel", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.0.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.1.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.211.254", + "interfaceName": "r1-eth1", + "vrf": "default", + "active": true + } + ] + } + ], + "10.2.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.210.254", + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "10.3.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.212.254", + "interfaceName": "r1-eth2", + "vrf": "default", + "active": true + } + ] + } + ], + "192.168.210.0/24": [ + { + "protocol": "connected", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "192.168.210.1/32": [ + { + "protocol": "local", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt new file mode 100644 index 0000000000..fb3d034689 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt @@ -0,0 +1,6 @@ +blackhole default +blackhole 10.0.0.0/24 proto XXXX metric 20 +10.1.0.0/24 via 192.168.211.254 dev r1-eth1 proto XXXX metric 20 +10.2.0.0/24 via 192.168.210.254 dev r1-eth0 proto XXXX metric 20 +10.3.0.0/24 via 192.168.212.254 dev r1-eth2 proto XXXX metric 20 +192.168.210.0/24 dev r1-eth0 proto XXXX scope link src 192.168.210.1 diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index d1aee46b40..6dc316752a 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -27,12 +27,13 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers from lib import topotest +from lib.common_config import step from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from time import sleep -pytestmark = [pytest.mark.sharpd] +pytestmark = [pytest.mark.sharpd, pytest.mark.staticd] krel = platform.release() @@ -64,6 +65,7 @@ def setup_module(mod): router.load_config( TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) ) + router.load_config(TopoRouter.RD_STATIC, "/dev/null") # Macvlan interface for protodown func test */ config_macvlan(tgen, "r1", "r1-eth0", "r1-eth0-macvlan") @@ -77,14 +79,54 @@ def teardown_module(): tgen.stop_topology() +def check_routes_installed(expected, table=None): + tgen = get_topogen() + r1 = tgen.gears["r1"] + + cmd = "ip route show" + if table: + cmd += " table {}".format(table) + actual = r1.run(cmd) + actual = ("\n".join(actual.splitlines()) + "\n").rstrip() + actual = re.sub(r" nhid [0-9][0-9]", "", actual) + actual = re.sub(r" proto sharp", " proto XXXX", actual) + actual = re.sub(r" proto static", " proto XXXX", actual) + actual = re.sub(r" proto 194", " proto XXXX", actual) + actual = re.sub(r" proto 196", " proto XXXX", actual) + actual = re.sub(r" proto kernel", " proto XXXX", actual) + actual = re.sub(r" proto 2", " proto XXXX", actual) + # Some platforms have double spaces? Why?????? + actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) + actual = re.sub(r" metric", " metric", actual) + actual = re.sub(r" link ", " link ", actual) + actual = actual.splitlines() + actual = [ + line.rstrip() + for line in actual + if not line.startswith("broadcast") and not line.startswith("local") + ] + + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + expected = expected.splitlines() + expected = [line.rstrip() for line in expected] + + return topotest.get_textdiff( + actual, + expected, + title1="Actual ip route show", + title2="Expected ip route show", + ) + + def test_zebra_kernel_route_vrf(): "Test kernel routes should be removed after interface changes vrf" logger.info("Test kernel routes should be removed after interface changes vrf") vrf = "RED" + table_id = 1 tgen = get_topogen() r1 = tgen.gears["r1"] - # Add kernel routes, the interface is initially in default vrf + step("Add kernel routes, the interface is initially in default vrf") r1.run("ip route add 3.5.1.0/24 via 192.168.210.1 dev r1-eth0") json_file = "{}/r1/v4_route_1_vrf_before.json".format(CWD) expected = json.loads(open(json_file).read()) @@ -94,11 +136,69 @@ def test_zebra_kernel_route_vrf(): _, result = topotest.run_and_expect(test_func, None, count=5, wait=1) assert result is None, '"r1" JSON output mismatches' - # Change the interface's vrf - r1.run("ip link add {} type vrf table 1".format(vrf)) + step("Add routes in table 1") + r1.run("ip route add blackhole default table {}".format(table_id)) + + r1.vtysh_cmd( + """ +configure terminal + ip route 10.0.0.0/24 blackhole table {} + ip route 10.1.0.0/24 192.168.211.254 nexthop-vrf default table {} +""".format( + table_id, table_id + ) + ) + + json_file = "{}/r1/v4_route_table_1_no_vrf.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route table 1 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + ipfile = "{}/r1/v4_route_table_1_no_vrf.txt".format(CWD) + expected = open(ipfile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + + test_func = partial(check_routes_installed, expected, table=1) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) + assert ok, result + + step("Add VRF {} and assign it r1-eth0 interface".format(vrf)) + r1.run("ip link add {} type vrf table {}".format(vrf, table_id)) r1.run("ip link set {} up".format(vrf)) r1.run("ip link set dev r1-eth0 master {}".format(vrf)) + step("Add static routes to VRF {}".format(vrf)) + r1.vtysh_cmd( + """ +configure terminal + vrf {} + ip route 10.2.0.0/24 192.168.210.254 + ip route 10.3.0.0/24 192.168.212.254 nexthop-vrf default +""".format( + vrf + ) + ) + + json_file = "{}/r1/v4_route_table_1_vrf_red.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route table 1 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + ipfile = "{}/r1/v4_route_table_1_vrf_red.txt".format(CWD) + expected = open(ipfile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + + test_func = partial(check_routes_installed, expected, table=1) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) + assert ok, result + + step("check 3.5.1.0/24 absence on VRF default") expected = "{}" test_func = partial( topotest.router_output_cmp, r1, "show ip route 3.5.1.0/24 json", expected @@ -107,10 +207,26 @@ def test_zebra_kernel_route_vrf(): assertmsg = "{} should not have the kernel route.\n{}".format('"r1"', diff) assert result, assertmsg - # Clean up + step("Remove VRF {}".format(vrf)) r1.run("ip link set dev r1-eth0 nomaster") r1.run("ip link del dev {}".format(vrf)) + json_file = "{}/r1/v4_route_table_1_no_vrf.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route table 1 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + ipfile = "{}/r1/v4_route_table_1_no_vrf.txt".format(CWD) + expected = open(ipfile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + + test_func = partial(check_routes_installed, expected, table=1) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) + assert ok, result + def test_zebra_kernel_admin_distance(): "Test some basic kernel routes added that should be accepted" @@ -295,28 +411,8 @@ def test_route_map_usage(): expected = open(sharp_ipfile).read().rstrip() expected = ("\n".join(expected.splitlines()) + "\n").rstrip() - def check_routes_installed(): - actual = r1.run("ip route show") - actual = ("\n".join(actual.splitlines()) + "\n").rstrip() - actual = re.sub(r" nhid [0-9][0-9]", "", actual) - actual = re.sub(r" proto sharp", " proto XXXX", actual) - actual = re.sub(r" proto static", " proto XXXX", actual) - actual = re.sub(r" proto 194", " proto XXXX", actual) - actual = re.sub(r" proto 196", " proto XXXX", actual) - actual = re.sub(r" proto kernel", " proto XXXX", actual) - actual = re.sub(r" proto 2", " proto XXXX", actual) - # Some platforms have double spaces? Why?????? - actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) - actual = re.sub(r" metric", " metric", actual) - actual = re.sub(r" link ", " link ", actual) - return topotest.get_textdiff( - actual, - expected, - title1="Actual ip route show", - title2="Expected ip route show", - ) - - ok, result = topotest.run_and_expect(check_routes_installed, "", count=5, wait=1) + test_func = partial(check_routes_installed, expected) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) assert ok, result diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 59794d9297..22542d8f8b 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -66,8 +66,7 @@ void cli_show_vrrp(struct vty *vty, const struct lyd_node *dnode, bool show_defa const char *ver = yang_dnode_get_string(dnode, "version"); vty_out(vty, " vrrp %s", vrid); - if (show_defaults || !yang_dnode_is_default(dnode, "version")) - vty_out(vty, " version %s", ver); + vty_out(vty, " version %s", ver); vty_out(vty, "\n"); } diff --git a/yang/frr-bgp-common.yang b/yang/frr-bgp-common.yang index 9d21597304..bb78c475d1 100644 --- a/yang/frr-bgp-common.yang +++ b/yang/frr-bgp-common.yang @@ -73,65 +73,116 @@ submodule frr-bgp-common { revision 2019-12-03 { description "Initial revision."; + reference "FRRouting"; } grouping rmap-policy-import { + description + "Grouping for route map policy import configuration"; + leaf rmap-import { type frr-route-map:route-map-ref; + description + "Reference to route map for import policy"; } } grouping rmap-policy-export { + description + "Grouping for route map policy export configuration"; + leaf rmap-export { type frr-route-map:route-map-ref; + description + "Reference to route map for export policy"; } } grouping unsuppress-map-policy-import { + description + "Grouping for unsuppress map policy import configuration"; + leaf unsuppress-map-import { type frr-route-map:route-map-ref; + description + "Reference to unsuppress map for import policy"; } } grouping unsuppress-map-policy-export { + description + "Grouping for unsuppress map policy export configuration"; + leaf unsuppress-map-export { type frr-route-map:route-map-ref; + description + "Reference to unsuppress map for export policy"; } } grouping plist-policy-import { + description + "Grouping for prefix list policy import configuration"; + leaf plist-import { type frr-bt:plist-ref; + description + "Reference to prefix list for import policy"; } } grouping plist-policy-export { + description + "Grouping for prefix list policy export configuration"; + leaf plist-export { type frr-bt:plist-ref; + description + "Reference to prefix list for export policy"; } } grouping access-list-policy-import { + description + "Grouping for access list policy import configuration"; + leaf access-list-import { type frr-bt:access-list-ref; + description + "Reference to access list for import policy"; } } grouping access-list-policy-export { + description + "Grouping for access list policy export configuration"; + leaf access-list-export { type frr-bt:access-list-ref; + description + "Reference to access list for export policy"; } } grouping as-path-filter-list-policy-import { + description + "Grouping for AS path filter list policy import configuration"; + leaf as-path-filter-list-import { type frr-bt:as-path-filter-ref; + description + "Reference to AS path filter list for import policy"; } } grouping as-path-filter-list-policy-export { + description + "Grouping for AS path filter list policy export configuration"; + leaf as-path-filter-list-export { type frr-bt:as-path-filter-ref; + description + "Reference to AS path filter list for export policy"; } } @@ -227,6 +278,9 @@ submodule frr-bgp-common { description "Configuration relating to MED."; container med-config { + description + "Container for MED configuration"; + leaf enable-med-admin { type boolean; default "false"; @@ -312,6 +366,9 @@ submodule frr-bgp-common { } grouping global-bgp-config { + description + "Grouping for global BGP configuration"; + leaf instance-type-view { type boolean; default "false"; @@ -322,15 +379,21 @@ submodule frr-bgp-common { leaf as-notation { type enumeration { - enum "plain" { value 0; } - enum "dot" { value 1; } - enum "dot+" { value 2; } + enum "plain" { + value 0; + description "Use plain format for all AS values"; + } + enum "dot" { + value 1; + description "Use 'AA.BB' format for AS 4 byte values"; + } + enum "dot+" { + value 2; + description "Use 'AA.BB' format for all AS values"; + } } description - "The as-notation type: - - plain: use plain format for all AS values - - dot: use 'AA.BB' format for AS 4 byte values. - - dot+: use 'AA.BB' format for all AS values."; + "The as-notation type"; } leaf ebgp-multihop-connected-route-check { @@ -403,7 +466,13 @@ submodule frr-bgp-common { } grouping global-neighbor-config { + description + "Grouping for global neighbor configuration"; + container global-neighbor-config { + description + "Container for global neighbor configuration"; + leaf dynamic-neighbors-limit { type uint32 { range "1..65535"; @@ -421,6 +490,9 @@ submodule frr-bgp-common { } container packet-quanta-config { + description + "Container for packet quanta configuration"; + leaf wpkt-quanta { type uint32 { range "1..64"; @@ -443,7 +515,13 @@ submodule frr-bgp-common { } grouping global-update-group-config { + description + "Grouping for global update group configuration"; + container global-update-group-config { + description + "Container for global update group configuration"; + leaf subgroup-pkt-queue-size { type uint32 { range "20..100"; @@ -466,6 +544,9 @@ submodule frr-bgp-common { } grouping global-network-config { + description + "Grouping for global network configuration"; + leaf import-check { type boolean; default "true"; @@ -480,6 +561,9 @@ submodule frr-bgp-common { } grouping neighbor-timers { + description + "Grouping for neighbor timers configuration"; + leaf hold-time { type uint16 { range "0 | 3..65535"; @@ -539,7 +623,13 @@ submodule frr-bgp-common { } grouping global-config-timers { + description + "Grouping for global configuration timers."; + container global-config-timers { + description + "Container for global configuration timers."; + leaf rmap-delay-time { type uint16 { range "0..600"; @@ -590,6 +680,9 @@ submodule frr-bgp-common { description "Configuration parameters relating to BGP graceful restart."; choice mode { + description + "Choice for graceful restart mode."; + case graceful-restart-mode { leaf enabled { type boolean; @@ -732,6 +825,9 @@ submodule frr-bgp-common { "List of route redistribution per AFI."; list redistribution-list { key "route-type route-instance"; + description + "List of route types to be redistributed."; + leaf route-type { type frr-rt-type:frr-route-types; description @@ -763,6 +859,9 @@ submodule frr-bgp-common { } grouping mp-afi-safi-network-config { + description + "Grouping for AFI/SAFI network configuration."; + leaf label-index { type rt-types:mpls-label; description @@ -778,6 +877,9 @@ submodule frr-bgp-common { } grouping mp-afi-safi-agg-route-config { + description + "Grouping for AFI/SAFI aggregate route configuration."; + leaf as-set { type boolean; default "false"; @@ -847,6 +949,9 @@ submodule frr-bgp-common { } grouping admin-distance { + description + "Grouping for administrative distance configuration."; + container admin-distance { description "Administrative distance (or preference) assigned to @@ -885,6 +990,9 @@ submodule frr-bgp-common { } grouping distance-per-route-config { + description + "Grouping for administrative distance configuration per route."; + leaf distance { type uint8 { range "1..255"; @@ -902,6 +1010,9 @@ submodule frr-bgp-common { } grouping route-flap-dampening { + description + "Grouping for route flap dampening configuration."; + container route-flap-dampening { description "Dampening feature"; @@ -973,6 +1084,9 @@ submodule frr-bgp-common { } grouping flow-spec-config { + description + "Grouping for flow spec configuration."; + container flow-spec-config { description "Flow spec feature."; @@ -1027,6 +1141,9 @@ submodule frr-bgp-common { description "Label value for VRF."; choice label-allocation-mode { + description + "Choice for label allocation mode (manual or auto)."; + case manual { leaf label { type rt-types:mpls-label; @@ -1082,6 +1199,9 @@ submodule frr-bgp-common { } choice rt-direction { + description + "Choice for route target direction (import, export, or both)."; + case import-export { uses rt-list; } @@ -1132,7 +1252,13 @@ submodule frr-bgp-common { } grouping global-afi-safi-vpn-config { + description + "Grouping for global AFI/SAFI VPN configuration."; + container vpn-config { + description + "VPN configuration container."; + leaf rd { type string; description @@ -1150,6 +1276,9 @@ submodule frr-bgp-common { } grouping global-afi-safi-vpn-network-config { + description + "Grouping for global AFI/SAFI VPN network configuration."; + list network-config { key "rd"; description diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c index 140cfa77cf..43ca6e47b8 100644 --- a/zebra/fpm_listener.c +++ b/zebra/fpm_listener.c @@ -44,6 +44,7 @@ struct glob { int sock; bool reflect; bool dump_hex; + FILE *output_file; }; struct glob glob_space; @@ -123,13 +124,13 @@ static int accept_conn(int listen_sock) while (1) { char buf[120]; - fprintf(stdout, "Waiting for client connection...\n"); + fprintf(glob->output_file, "Waiting for client connection...\n"); client_len = sizeof(client_addr); sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len); if (sock >= 0) { - fprintf(stdout, "Accepted client %s\n", + fprintf(glob->output_file, "Accepted client %s\n", inet_ntop(AF_INET, &client_addr.sin_addr, buf, sizeof(buf))); return sock; } @@ -172,8 +173,7 @@ read_fpm_msg(char *buf, size_t buf_len) bytes_read = read(glob->sock, cur, need_len); if (bytes_read == 0) { - fprintf(stdout, - "Socket closed as that read returned 0\n"); + fprintf(glob->output_file, "Socket closed as that read returned 0\n"); return NULL; } @@ -567,7 +567,7 @@ addr_to_s(unsigned char family, void *addr) } /* - * netlink_msg_ctx_print + * netlink_msg_ctx_snprint */ static int netlink_msg_ctx_snprint(struct netlink_msg_ctx *ctx, char *buf, size_t buf_len) @@ -628,7 +628,7 @@ static void print_netlink_msg_ctx(struct netlink_msg_ctx *ctx) char buf[1024]; netlink_msg_ctx_snprint(ctx, buf, sizeof(buf)); - printf("%s\n", buf); + fprintf(glob->output_file, "%s\n", buf); } static void fpm_listener_hexdump(const void *mem, size_t len) @@ -641,7 +641,7 @@ static void fpm_listener_hexdump(const void *mem, size_t len) return; if (len == 0) { - printf("%016lx: (zero length / no data)\n", (long)src); + fprintf(glob->output_file, "%016lx: (zero length / no data)\n", (long)src); return; } @@ -654,14 +654,14 @@ static void fpm_listener_hexdump(const void *mem, size_t len) const uint8_t *lineend = src + 8; uint32_t line_bytes = 0; - printf("%016lx: ", (long)src); + fprintf(glob->output_file, "%016lx: ", (long)src); while (src < lineend && src < end) { - printf("%02x ", *src++); + fprintf(glob->output_file, "%02x ", *src++); line_bytes++; } if (line_bytes < 8) - printf("%*s", (8 - line_bytes) * 3, ""); + fprintf(glob->output_file, "%*s", (8 - line_bytes) * 3, ""); src -= line_bytes; while (src < lineend && src < end && fb.pos < fb.buf + fb.len) { @@ -672,7 +672,7 @@ static void fpm_listener_hexdump(const void *mem, size_t len) else *fb.pos++ = '.'; } - printf("\n"); + fprintf(glob->output_file, "\n"); } } @@ -711,20 +711,17 @@ static void parse_netlink_msg(char *buf, size_t buf_len, fpm_msg_hdr_t *fpm) if (glob->reflect && hdr->nlmsg_type == RTM_NEWROUTE && ctx->rtmsg->rtm_protocol > RTPROT_STATIC) { - printf(" Route %s(%u) reflecting back\n", - netlink_prot_to_s( - ctx->rtmsg->rtm_protocol), - ctx->rtmsg->rtm_protocol); + fprintf(glob->output_file, " Route %s(%u) reflecting back\n", + netlink_prot_to_s(ctx->rtmsg->rtm_protocol), + ctx->rtmsg->rtm_protocol); ctx->rtmsg->rtm_flags |= RTM_F_OFFLOAD; write(glob->sock, fpm, fpm_msg_len(fpm)); } break; default: - fprintf(stdout, - "Ignoring netlink message - Type: %s(%d)\n", - netlink_msg_type_to_s(hdr->nlmsg_type), - hdr->nlmsg_type); + fprintf(glob->output_file, "Ignoring netlink message - Type: %s(%d)\n", + netlink_msg_type_to_s(hdr->nlmsg_type), hdr->nlmsg_type); } } } @@ -734,8 +731,8 @@ static void parse_netlink_msg(char *buf, size_t buf_len, fpm_msg_hdr_t *fpm) */ static void process_fpm_msg(fpm_msg_hdr_t *hdr) { - fprintf(stdout, "FPM message - Type: %d, Length %d\n", hdr->msg_type, - ntohs(hdr->msg_len)); + fprintf(glob->output_file, "FPM message - Type: %d, Length %d\n", hdr->msg_type, + ntohs(hdr->msg_len)); if (hdr->msg_type != FPM_MSG_TYPE_NETLINK) { fprintf(stderr, "Unknown fpm message type %u\n", hdr->msg_type); @@ -770,12 +767,12 @@ int main(int argc, char **argv) pid_t daemon; int r; bool fork_daemon = false; - - setbuf(stdout, NULL); + const char *output_file = NULL; memset(glob, 0, sizeof(*glob)); + glob->output_file = stdout; - while ((r = getopt(argc, argv, "rdv")) != -1) { + while ((r = getopt(argc, argv, "rdvo:")) != -1) { switch (r) { case 'r': glob->reflect = true; @@ -786,9 +783,23 @@ int main(int argc, char **argv) case 'v': glob->dump_hex = true; break; + case 'o': + output_file = optarg; + break; } } + if (output_file) { + glob->output_file = fopen(output_file, "w"); + if (!glob->output_file) { + fprintf(stderr, "Failed to open output file %s: %s\n", output_file, + strerror(errno)); + exit(1); + } + } + + setbuf(glob->output_file, NULL); + if (fork_daemon) { daemon = fork(); @@ -805,7 +816,7 @@ int main(int argc, char **argv) while (1) { glob->sock = accept_conn(glob->server_sock); fpm_serve(); - fprintf(stdout, "Done serving client"); + fprintf(glob->output_file, "Done serving client\n"); } } #else diff --git a/zebra/interface.c b/zebra/interface.c index b7a790382d..fd1ea380a5 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -40,8 +40,8 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information"); #define ZEBRA_PTM_SUPPORT -DEFINE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), - (vty, ifp)); +DEFINE_HOOK(zebra_if_extra_info, (struct vty * vty, json_object *json_if, struct interface *ifp), + (vty, json_if, ifp)); DEFINE_MTYPE_STATIC(ZEBRA, ZIF_DESC, "Intf desc"); @@ -1728,7 +1728,7 @@ interface_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx, dplane_ctx_get_ifp_vxlan_vni_array(ctx); struct zebra_vxlan_vni vni_start, vni_end; struct hash *vni_table = NULL; - struct zebra_vxlan_vni vni, *vnip; + struct zebra_vxlan_vni vni; vni_t vni_id; vlanid_t vid; int i; @@ -1762,11 +1762,8 @@ interface_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx, vni_start.vni, vni_end.vni, ifp->name, ifp->ifindex); - if (!vni_table) { + if (!vni_table) vni_table = zebra_vxlan_vni_table_create(); - if (!vni_table) - return; - } for (vid = vni_start.access_vlan, vni_id = vni_start.vni; vid <= vni_end.access_vlan; vid++, vni_id++) { @@ -1774,9 +1771,7 @@ interface_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx, memset(&vni, 0, sizeof(vni)); vni.vni = vni_id; vni.access_vlan = vid; - vnip = hash_get(vni_table, &vni, zebra_vxlan_vni_alloc); - if (!vnip) - return; + (void)hash_get(vni_table, &vni, zebra_vxlan_vni_alloc); } memset(&vni_start, 0, sizeof(vni_start)); @@ -2851,7 +2846,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) &iflp->rmt_ip, iflp->rmt_as); } - hook_call(zebra_if_extra_info, vty, ifp); + hook_call(zebra_if_extra_info, vty, NULL, ifp); if (listhead(ifp->nbr_connected)) vty_out(vty, " Neighbor address(s):\n"); @@ -3257,6 +3252,8 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp, json_object_int_add(json_te, "neighborAsbrAs", iflp->rmt_as); } + hook_call(zebra_if_extra_info, vty, json_if, ifp); + if (listhead(ifp->nbr_connected)) { json_object *json_nbr_addrs; diff --git a/zebra/interface.h b/zebra/interface.h index 0f5c997403..5b45656bd5 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -223,8 +223,8 @@ struct zebra_if { char *desc; }; -DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), - (vty, ifp)); +DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, json_object *json_if, struct interface *ifp), + (vty, json_if, ifp)); #define IS_ZEBRA_IF_VRF(ifp) \ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VRF) diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 467bcb6b16..a767bda72e 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -1726,7 +1726,7 @@ int rtadv_dnssl_encode(uint8_t *out, const char *in) } /* Dump interface ND information to vty. */ -static int nd_dump_vty(struct vty *vty, struct interface *ifp) +static int nd_dump_vty(struct vty *vty, json_object *json_if, struct interface *ifp) { struct zebra_if *zif; struct rtadvconf *rtadv; @@ -1735,7 +1735,7 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) zif = (struct zebra_if *)ifp->info; rtadv = &zif->rtadv; - if (rtadv->AdvSendAdvertisements) { + if (!json_if && rtadv->AdvSendAdvertisements) { vty_out(vty, " ND advertised reachable time is %d milliseconds\n", rtadv->AdvReachableTime); @@ -1792,6 +1792,63 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " ND router advertisements with Adv. Interval option.\n"); } + + if (json_if && rtadv->AdvSendAdvertisements) { + json_object_int_add(json_if, "ndAdvertisedReachableTimeMsecs", + rtadv->AdvReachableTime); + json_object_int_add(json_if, "ndAdvertisedRetransmitIntervalMsecs", + rtadv->AdvRetransTimer); + json_object_int_add(json_if, "ndAdvertisedHopCountLimitHops", rtadv->AdvCurHopLimit); + json_object_int_add(json_if, "ndRouterAdvertisementsSent", zif->ra_sent); + json_object_int_add(json_if, "ndRouterAdvertisementsRcvd", zif->ra_rcvd); + + interval = rtadv->MaxRtrAdvInterval; + if (interval % 1000) + json_object_int_add(json_if, "ndRouterAdvertisementsIntervalMsecs", + interval); + else + json_object_int_add(json_if, "ndRouterAdvertisementsIntervalSecs", + interval / 1000); + + json_object_boolean_add(json_if, "ndRouterAdvertisementsDoNotUseFastRetransmit", + !rtadv->UseFastRexmit); + + if (rtadv->AdvDefaultLifetime != -1) + json_object_int_add(json_if, "ndRouterAdvertisementsLiveForSecs", + rtadv->AdvDefaultLifetime); + else + json_object_boolean_add(json_if, + "ndRouterAdvertisementsLifetimeTracksRaInterval", + true); + + json_object_string_add(json_if, "ndRouterAdvertisementDefaultRouterPreference", + rtadv_pref_strs[rtadv->DefaultPreference]); + + if (rtadv->AdvManagedFlag) + json_object_boolean_add(json_if, "hostsUseDhcpToObtainRoutableAddresses", + true); + else + json_object_boolean_add(json_if, "hostsUseStatelessAutoconfigForAddresses", + true); + + if (rtadv->AdvHomeAgentFlag) { + json_object_boolean_add(json_if, + "ndRouterAdvertisementsWithHomeAgentFlagBit", true); + if (rtadv->HomeAgentLifetime != -1) + json_object_int_add(json_if, "homeAgentLifetimeSecs", + rtadv->HomeAgentLifetime); + else + json_object_boolean_add(json_if, + "homeAgentLifetimeTracksRaLifetime", true); + + json_object_int_add(json_if, "homeAgentPreference", + rtadv->HomeAgentLifetime); + } + if (rtadv->AdvIntervalOption) + json_object_boolean_add(json_if, + "ndRouterAdvertisementsWithAdvIntervalOption", true); + } + return 0; } diff --git a/zebra/zebra_neigh.c b/zebra/zebra_neigh.c index a222e7f6e8..8a91f2719b 100644 --- a/zebra/zebra_neigh.c +++ b/zebra/zebra_neigh.c @@ -153,14 +153,18 @@ void zebra_neigh_del(struct interface *ifp, struct ipaddr *ip) /* kernel neigh delete all for a given interface */ void zebra_neigh_del_all(struct interface *ifp) { - struct zebra_neigh_ent *n, *nn; + struct zebra_neigh_ent *n, *next; if (IS_ZEBRA_DEBUG_NEIGH) zlog_debug("zebra neigh delete all for interface %s/%d", ifp->name, ifp->ifindex); - RB_FOREACH_SAFE (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, nn) - zebra_neigh_del(ifp, &n->ip); + RB_FOREACH_SAFE (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, next) { + if (n->ifindex == ifp->ifindex) { + /* Free the neighbor directly instead of looking it up again */ + zebra_neigh_free(n); + } + } } /* kernel neigh add */ diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index f5141c8f23..5b7452a79e 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2930,6 +2930,68 @@ static uint32_t proto_nhg_nexthop_active_update(struct nexthop_group *nhg) return curr_active; } +void nexthop_vrf_update(struct route_node *rn, struct route_entry *re, vrf_id_t vrf_id) +{ + struct nhg_hash_entry *curr_nhe, *new_nhe; + afi_t rt_afi = family2afi(rn->p.family); + struct nexthop *nexthop; + + re->vrf_id = vrf_id; + + /* Make a local copy of the existing nhe, so we don't work on/modify + * the shared nhe. + */ + curr_nhe = zebra_nhe_copy(re->nhe, re->nhe->id); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p nhe %p (%pNG), curr_nhe %p", __func__, re, re->nhe, re->nhe, + curr_nhe); + + /* Clear the existing id, if any: this will avoid any confusion + * if the id exists, and will also force the creation + * of a new nhe reflecting the changes we may make in this local copy. + */ + curr_nhe->id = 0; + + curr_nhe->vrf_id = vrf_id; + for (ALL_NEXTHOPS(curr_nhe->nhg, nexthop)) { + if (!nexthop->ifindex) + /* change VRF ID of nexthop without interfaces + * (eg. blackhole) + */ + nexthop->vrf_id = vrf_id; + } + + if (zebra_nhg_get_backup_nhg(curr_nhe)) { + for (ALL_NEXTHOPS(curr_nhe->backup_info->nhe->nhg, nexthop)) { + if (!nexthop->ifindex) + /* change VRF ID of nexthop without interfaces + * (eg. blackhole) + */ + nexthop->vrf_id = vrf_id; + } + } + + /* + * Ref or create an nhe that matches the current state of the + * nexthop(s). + */ + new_nhe = zebra_nhg_rib_find_nhe(curr_nhe, rt_afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p CHANGED: nhe %p (%pNG) => new_nhe %p (%pNG)", __func__, re, + re->nhe, re->nhe, new_nhe, new_nhe); + + route_entry_update_nhe(re, new_nhe); + + /* + * Do not need the old / copied nhe anymore since it + * was either copied over into a new nhe or not + * used at all. + */ + zebra_nhg_free(curr_nhe); +} + /* * This function takes the start of two comparable nexthops from two different * nexthop groups and walks them to see if they can be considered the same diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 0f90627a0d..de6f6123aa 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -401,6 +401,7 @@ extern void zebra_nhg_mark_keep(void); /* Nexthop resolution processing */ struct route_entry; /* Forward ref to avoid circular includes */ +extern void nexthop_vrf_update(struct route_node *rn, struct route_entry *re, vrf_id_t vrf_id); extern int nexthop_active_update(struct route_node *rn, struct route_entry *re, struct route_entry *old_re); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8cea605f41..20ec25a431 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -903,6 +903,11 @@ void zebra_rtable_node_cleanup(struct route_table *table, rib_unlink(node, re); } + zebra_node_info_cleanup(node); +} + +void zebra_node_info_cleanup(struct route_node *node) +{ if (node->info) { rib_dest_t *dest = node->info; @@ -4498,6 +4503,12 @@ rib_update_handle_kernel_route_down_possibility(struct route_node *rn, bool alive = false; for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { + if (!nexthop->ifindex) { + /* blackhole nexthops have no interfaces */ + alive = true; + break; + } + struct interface *ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 51efcceb75..f9b5dd8808 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -1236,7 +1236,7 @@ static bool alloc_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, zlog_warn("%s: function %u is outside ELIB [%u/%u] and EWLIB alloc ranges [%u/%u]", __func__, sid_func, elib_start, elib_end, ewlib_start, ewlib_end); - return -1; + return false; } } else if (format->type == SRV6_SID_FORMAT_TYPE_UNCOMPRESSED) { uint32_t explicit_start = @@ -1395,7 +1395,7 @@ static bool alloc_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, dynamic_end) { zlog_warn("%s: SRv6: Warning, SRv6 SID Dynamic alloc space is depleted", __func__); - return NULL; + return false; } /* diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 7bfe07b4cf..d652c57388 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -162,6 +162,69 @@ static int zebra_vrf_enable(struct vrf *vrf) return 0; } +/* update the VRF ID of a routing table and their routing entries */ +static void zebra_vrf_disable_update_vrfid(struct zebra_vrf *zvrf, afi_t afi, safi_t safi) +{ + struct rib_table_info *info; + struct route_entry *re, *nre; + struct route_node *rn, *nrn; + bool empty_table = true; + bool rn_delete; + + /* Assign the table to the default VRF. + * Although the table is not technically owned by the default VRF, + * the code assumes that unassigned routing tables are + * associated with the default VRF. + */ + info = route_table_get_info(zvrf->table[afi][safi]); + info->zvrf = vrf_info_lookup(VRF_DEFAULT); + + rn = route_top(zvrf->table[afi][safi]); + if (rn) + empty_table = false; + while (rn) { + if (!rn->info) { + rn = route_next(rn); + continue; + } + + /* Assign the kernel route entries to the default VRF, + * even though they are not actually owned by it. + * + * Remove route nodes that were created by FRR daemons, + * unless they are associated with the table rather than the VRF. + * Routes associated with the VRF are not needed once the VRF is + * disabled. + */ + rn_delete = true; + RNODE_FOREACH_RE_SAFE (rn, re, nre) { + if (re->type == ZEBRA_ROUTE_KERNEL || + CHECK_FLAG(re->flags, ZEBRA_FLAG_TABLEID)) { + nexthop_vrf_update(rn, re, VRF_DEFAULT); + if (CHECK_FLAG(re->flags, ZEBRA_FLAG_TABLEID)) + /* reinstall routes */ + rib_install_kernel(rn, re, NULL); + rn_delete = false; + } else + rib_unlink(rn, re); + } + if (rn_delete) { + nrn = route_next(rn); + zebra_node_info_cleanup(rn); + rn->info = NULL; + route_unlock_node(rn); + rn = nrn; + } else { + empty_table = false; + rn = route_next(rn); + } + } + + if (empty_table) + zebra_router_release_table(zvrf, zvrf->table_id, afi, safi); + zvrf->table[afi][safi] = NULL; +} + /* Callback upon disabling a VRF. */ static int zebra_vrf_disable(struct vrf *vrf) { @@ -224,9 +287,13 @@ static int zebra_vrf_disable(struct vrf *vrf) * we no-longer need this pointer. */ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { - zebra_router_release_table(zvrf, zvrf->table_id, afi, - safi); - zvrf->table[afi][safi] = NULL; + if (!zvrf->table[afi][safi] || vrf->vrf_id == VRF_DEFAULT) { + zebra_router_release_table(zvrf, zvrf->table_id, afi, safi); + zvrf->table[afi][safi] = NULL; + continue; + } + + zebra_vrf_disable_update_vrfid(zvrf, afi, safi); } } @@ -357,19 +424,50 @@ static void zebra_rnhtable_node_cleanup(struct route_table *table, static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi, safi_t safi) { + vrf_id_t vrf_id = zvrf->vrf->vrf_id; + struct rib_table_info *info; + struct route_entry *re; struct route_node *rn; struct prefix p; assert(!zvrf->table[afi][safi]); + /* Attempt to retrieve the Linux routing table using zvrf->table_id. + * If the table was created before the VRF, it will already exist. + * Otherwise, create a new table. + */ zvrf->table[afi][safi] = zebra_router_get_table(zvrf, zvrf->table_id, afi, safi); + /* If the table existed before the VRF was created, info->zvrf was + * referring to the default VRF. + * Assign the table to the new VRF. + * Note: FRR does not allow multiple VRF interfaces to be created with the + * same table ID. + */ + info = route_table_get_info(zvrf->table[afi][safi]); + info->zvrf = zvrf; + + /* If the table existed before the VRF was created, their routing entries + * was owned by the default VRF. + * Re-assign all the routing entries to the new VRF. + */ + for (rn = route_top(zvrf->table[afi][safi]); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + RNODE_FOREACH_RE (rn, re) + nexthop_vrf_update(rn, re, vrf_id); + } + memset(&p, 0, sizeof(p)); p.family = afi2family(afi); + /* create a fake default route or get the existing one */ rn = srcdest_rnode_get(zvrf->table[afi][safi], &p, NULL); - zebra_rib_create_dest(rn); + if (!rn->info) + /* do not override the existing default route */ + zebra_rib_create_dest(rn); } /* Allocate new zebra VRF. */ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 334bb93684..289a8fcc47 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -263,6 +263,8 @@ extern void zebra_vrf_init(void); extern void zebra_rtable_node_cleanup(struct route_table *table, struct route_node *node); +extern void zebra_node_info_cleanup(struct route_node *node); + #ifdef __cplusplus } diff --git a/zebra/zebra_vxlan_if.c b/zebra/zebra_vxlan_if.c index ea0be2f644..4fd9cb6587 100644 --- a/zebra/zebra_vxlan_if.c +++ b/zebra/zebra_vxlan_if.c @@ -54,6 +54,8 @@ #include "zebra/zebra_evpn_vxlan.h" #include "zebra/zebra_router.h" +DEFINE_MTYPE_STATIC(ZEBRA, L2_VNI, "L2 VNI"); + static unsigned int zebra_vxlan_vni_hash_keymake(const void *p) { const struct zebra_vxlan_vni *vni; @@ -527,11 +529,7 @@ static int zebra_vxlan_if_add_update_vni(struct zebra_if *zif, old_vni->access_vlan, vni->vni, vni->access_vlan); - zebra_evpn_vl_vxl_deref(old_vni->access_vlan, old_vni->vni, - zif); - zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif); - zebra_vxlan_if_update_vni(zif->ifp, vni, ctx); - zebra_vxlan_vni_free(old_vni); + } else { int ret; @@ -544,19 +542,20 @@ static int zebra_vxlan_if_add_update_vni(struct zebra_if *zif, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("%s vxlan %s vni %u has error accessing bridge table.", __func__, zif->ifp->name, vni->vni); + + return 0; } else if (ret == 0) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("%s vxlan %s vni (%u, %u) not present in bridge table", - __func__, zif->ifp->name, vni->vni, - vni->access_vlan); - zebra_evpn_vl_vxl_deref(old_vni->access_vlan, - old_vni->vni, zif); - zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif); - zebra_vxlan_if_update_vni(zif->ifp, vni, ctx); - zebra_vxlan_vni_free(old_vni); + __func__, zif->ifp->name, vni->vni, vni->access_vlan); } } + zebra_evpn_vl_vxl_deref(old_vni->access_vlan, old_vni->vni, zif); + zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif); + zebra_vxlan_if_update_vni(zif->ifp, vni, ctx); + zebra_vxlan_vni_free(old_vni); + return 0; } @@ -608,7 +607,7 @@ void zebra_vxlan_vni_free(void *arg) vni = (struct zebra_vxlan_vni *)arg; - XFREE(MTYPE_TMP, vni); + XFREE(MTYPE_L2_VNI, vni); } void *zebra_vxlan_vni_alloc(void *p) @@ -617,7 +616,7 @@ void *zebra_vxlan_vni_alloc(void *p) const struct zebra_vxlan_vni *vnip; vnip = (const struct zebra_vxlan_vni *)p; - vni = XCALLOC(MTYPE_TMP, sizeof(*vni)); + vni = XCALLOC(MTYPE_L2_VNI, sizeof(*vni)); vni->vni = vnip->vni; vni->access_vlan = vnip->access_vlan; vni->mcast_grp = vnip->mcast_grp; @@ -659,8 +658,6 @@ int zebra_vxlan_if_vni_table_create(struct zebra_if *zif) vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); vni_info->vni_table = zebra_vxlan_vni_table_create(); - if (!vni_info->vni_table) - return -ENOMEM; return 0; } |
