summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_fsm.c2
-rw-r--r--bgpd/bgp_network.c29
-rw-r--r--bgpd/bgp_nexthop.c2
-rw-r--r--bgpd/bgp_nht.c40
-rw-r--r--bgpd/bgp_packet.c11
-rw-r--r--bgpd/bgp_route.c28
-rw-r--r--bgpd/bgp_zebra.c2
-rw-r--r--bgpd/bgpd.c47
-rw-r--r--bgpd/bgpd.h10
-rw-r--r--configure.ac5
-rw-r--r--isisd/isisd.h4
-rw-r--r--lib/command.h1
-rw-r--r--lib/darr.h17
-rw-r--r--lib/northbound_oper.c20
-rw-r--r--lib/zclient.c7
-rw-r--r--lib/zclient.h6
-rw-r--r--ospf6d/ospf6_area.c32
-rw-r--r--ospf6d/ospf6_area.h1
-rw-r--r--ospf6d/ospf6_interface.c3
-rw-r--r--ospfd/ospf_dump.c14
-rw-r--r--staticd/static_routes.c30
-rw-r--r--staticd/static_routes.h1
-rw-r--r--staticd/static_zebra.c6
-rw-r--r--tests/lib/cli/test_cli.refout.in4
-rw-r--r--tests/lib/northbound/test_oper_exists.c6
-rw-r--r--tests/lib/test_darr.c4
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/__init__.py0
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/r1/frr.conf38
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/r2/frr.conf19
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/r3/frr.conf20
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/r4/frr.conf21
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/r5/frr.conf20
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/r6/frr.conf40
-rw-r--r--tests/topotests/bgp_addpath_disable_rx/test_bgp_addpath_disable_rx.py131
-rw-r--r--tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py41
-rw-r--r--tests/topotests/bgp_ecomm_list_match/test_bgp_ecomm_list_match.py43
-rw-r--r--tests/topotests/bgp_vpn_5549_route_map/test_bgp_vpn_5549_route_map.py125
-rwxr-xr-xtests/topotests/lib/bmp_collector/bmpserver.py15
-rw-r--r--tests/topotests/lib/pim.py18
-rw-r--r--tests/topotests/lib/topotest.py42
-rw-r--r--tests/topotests/zebra_rib/r1/import_mrib_table_2.json8
-rw-r--r--tests/topotests/zebra_rib/r1/import_mrib_table_3.json10
-rw-r--r--tests/topotests/zebra_rib/r1/import_mrib_table_4.json8
-rw-r--r--tests/topotests/zebra_rib/r1/import_table_2.json8
-rw-r--r--tests/topotests/zebra_rib/r1/import_table_3.json10
-rw-r--r--tests/topotests/zebra_rib/r1/import_table_4.json8
-rw-r--r--tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json54
-rw-r--r--tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt3
-rw-r--r--tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json115
-rw-r--r--tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt6
-rw-r--r--tests/topotests/zebra_rib/test_zebra_rib.py150
-rw-r--r--vrrpd/vrrp_vty.c3
-rw-r--r--yang/frr-bgp-common.yang143
-rw-r--r--zebra/fpm_listener.c61
-rw-r--r--zebra/interface.c19
-rw-r--r--zebra/interface.h4
-rw-r--r--zebra/rtadv.c61
-rw-r--r--zebra/zebra_neigh.c10
-rw-r--r--zebra/zebra_nhg.c62
-rw-r--r--zebra/zebra_nhg.h1
-rw-r--r--zebra/zebra_rib.c11
-rw-r--r--zebra/zebra_srv6.c4
-rw-r--r--zebra/zebra_vrf.c106
-rw-r--r--zebra/zebra_vrf.h2
-rw-r--r--zebra/zebra_vxlan_if.c29
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;
}