diff options
61 files changed, 2047 insertions, 1330 deletions
diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c index aada6e555f..030db4b28e 100644 --- a/bgpd/bgp_addpath.c +++ b/bgpd/bgp_addpath.c @@ -10,8 +10,6 @@  #include "bgp_addpath.h"  #include "bgp_route.h" -#include "bgp_open.h" -#include "bgp_packet.h"  static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = {  	{ @@ -361,30 +359,6 @@ void bgp_addpath_type_changed(struct bgp *bgp)  	}  } -int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type, uint16_t paths) -{ -	int action = CAPABILITY_ACTION_UNSET; - -	switch (addpath_type) { -	case BGP_ADDPATH_ALL: -	case BGP_ADDPATH_BEST_PER_AS: -		action = CAPABILITY_ACTION_SET; -		break; -	case BGP_ADDPATH_BEST_SELECTED: -		if (paths) -			action = CAPABILITY_ACTION_SET; -		else -			action = CAPABILITY_ACTION_UNSET; -		break; -	case BGP_ADDPATH_NONE: -	case BGP_ADDPATH_MAX: -		action = CAPABILITY_ACTION_UNSET; -		break; -	} - -	return action; -} -  /*   * Change the addpath type assigned to a peer, or peer group. In addition to   * adjusting the counts, peer sessions will be reset as needed to make the @@ -398,7 +372,6 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,  	struct listnode *node, *nnode;  	struct peer *tmp_peer;  	struct peer_group *group; -	int action = bgp_addpath_capability_action(addpath_type, paths);  	if (safi == SAFI_LABELED_UNICAST)  		safi = SAFI_UNICAST; @@ -456,12 +429,9 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,  			}  		}  	} else { -		if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) && -		    !CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) -			peer_change_action(peer, afi, safi, peer_change_reset); +		peer_change_action(peer, afi, safi, peer_change_reset);  	} -	bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action);  }  /* diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h index f1ff98ea7a..c136671ea4 100644 --- a/bgpd/bgp_addpath.h +++ b/bgpd/bgp_addpath.h @@ -15,11 +15,7 @@  #include "bgpd/bgp_table.h"  #include "lib/json.h" -struct bgp_addpath_capability { -	uint16_t afi; -	uint8_t safi; -	uint8_t flags; -}; +#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1  struct bgp_paths_limit_capability {  	uint16_t afi; @@ -27,8 +23,6 @@ struct bgp_paths_limit_capability {  	uint16_t paths_limit;  } __attribute__((packed)); -#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1 -  void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d);  bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi, @@ -68,5 +62,4 @@ void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi,  			    safi_t safi);  void bgp_addpath_type_changed(struct bgp *bgp); -extern int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type, uint16_t paths);  #endif diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 0e3ed9f0d1..1a30cb37f4 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -2163,6 +2163,9 @@ bgp_establish(struct peer_connection *connection)  	peer->established++;  	bgp_fsm_change_status(connection, Established); +	if (peer->last_reset == PEER_DOWN_WAITING_OPEN) +		peer->last_reset = 0; +  	/* bgp log-neighbor-changes of neighbor Up */  	if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) {  		struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 164e2300c0..2ef7ec97e3 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -1278,6 +1278,25 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc)  	}  	LIST_FOREACH (path, &(bnc->paths), nh_thread) { +		/* +		 * Currently when a peer goes down, bgp immediately +		 * sees this via the interface events( if it is directly +		 * connected).  And in this case it takes and puts on +		 * a special peer queue all path info's associated with +		 * but these items are not yet processed typically when +		 * the nexthop is being handled here.  Thus we end +		 * up in a situation where the process Queue for BGP +		 * is being asked to look at the same path info multiple +		 * times.  Let's just cut to the chase here and if +		 * the bnc has a peer associated with it and the path info +		 * being looked at uses that peer and the peer is no +		 * longer established we know the path_info is being +		 * handled elsewhere and we do not need to process +		 * it here at all since the pathinfo is going away +		 */ +		if (peer && path->peer == peer && !peer_established(peer->connection)) +			continue; +  		if (path->type == ZEBRA_ROUTE_BGP &&  		    (path->sub_type == BGP_ROUTE_NORMAL ||  		     path->sub_type == BGP_ROUTE_STATIC || diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index ca2e8de041..f8726ffff9 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1218,7 +1218,6 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,  	uint16_t len;  	uint32_t gr_restart_time;  	uint8_t addpath_afi_safi_count = 0; -	bool adv_addpath_tx = false;  	unsigned long number_of_orfs_p;  	uint8_t number_of_orfs = 0;  	const char *capability = lookup_msg(capcode_str, capability_code, @@ -1226,6 +1225,9 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,  	const char *hostname = cmd_hostname_get();  	const char *domainname = cmd_domainname_get(); +	if (!peer) +		return; +  	if (!peer_established(peer->connection))  		return; @@ -1383,87 +1385,6 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,  		COND_FLAG(peer->cap, PEER_CAP_LLGR_ADV,  			  action == CAPABILITY_ACTION_SET);  		break; -	case CAPABILITY_CODE_ADDPATH: -		FOREACH_AFI_SAFI (afi, safi) { -			if (peer->afc[afi][safi]) { -				addpath_afi_safi_count++; - -				/* Only advertise addpath TX if a feature that -				* will use it is -				* configured */ -				if (peer->addpath_type[afi][safi] != -				    BGP_ADDPATH_NONE) -					adv_addpath_tx = true; - -				/* If we have enabled labeled unicast, we MUST check -				* against unicast SAFI because addpath IDs are -				* allocated under unicast SAFI, the same as the RIB -				* is managed in unicast SAFI. -				*/ -				if (safi == SAFI_LABELED_UNICAST) -					if (peer->addpath_type[afi][SAFI_UNICAST] != -					    BGP_ADDPATH_NONE) -						adv_addpath_tx = true; -			} -		} - -		stream_putc(s, action); -		stream_putc(s, CAPABILITY_CODE_ADDPATH); -		stream_putc(s, CAPABILITY_CODE_ADDPATH_LEN * -				       addpath_afi_safi_count); - -		FOREACH_AFI_SAFI (afi, safi) { -			if (peer->afc[afi][safi]) { -				bool adv_addpath_rx = -					!CHECK_FLAG(peer->af_flags[afi][safi], -						    PEER_FLAG_DISABLE_ADDPATH_RX); -				uint8_t flags = 0; - -				/* Convert AFI, SAFI to values for packet. */ -				bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, -							  &pkt_safi); - -				stream_putw(s, pkt_afi); -				stream_putc(s, pkt_safi); - -				if (adv_addpath_rx) { -					SET_FLAG(flags, BGP_ADDPATH_RX); -					SET_FLAG(peer->af_cap[afi][safi], -						 PEER_CAP_ADDPATH_AF_RX_ADV); -				} else { -					UNSET_FLAG(peer->af_cap[afi][safi], -						   PEER_CAP_ADDPATH_AF_RX_ADV); -				} - -				if (adv_addpath_tx) { -					SET_FLAG(flags, BGP_ADDPATH_TX); -					SET_FLAG(peer->af_cap[afi][safi], -						 PEER_CAP_ADDPATH_AF_TX_ADV); -					if (safi == SAFI_LABELED_UNICAST) -						SET_FLAG(peer->af_cap[afi] -								     [SAFI_UNICAST], -							 PEER_CAP_ADDPATH_AF_TX_ADV); -				} else { -					UNSET_FLAG(peer->af_cap[afi][safi], -						   PEER_CAP_ADDPATH_AF_TX_ADV); -				} - -				stream_putc(s, flags); -			} -		} - -		if (bgp_debug_neighbor_events(peer)) -			zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", -				   peer, -				   action == CAPABILITY_ACTION_SET -					   ? "Advertising" -					   : "Removing", -				   capability, iana_afi2str(pkt_afi), -				   iana_safi2str(pkt_safi)); - -		COND_FLAG(peer->cap, PEER_CAP_ADDPATH_ADV, -			  action == CAPABILITY_ACTION_SET); -		break;  	case CAPABILITY_CODE_PATHS_LIMIT:  		FOREACH_AFI_SAFI (afi, safi) {  			if (!peer->afc[afi][safi]) @@ -1619,6 +1540,7 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,  	case CAPABILITY_CODE_REFRESH:  	case CAPABILITY_CODE_AS4:  	case CAPABILITY_CODE_DYNAMIC: +	case CAPABILITY_CODE_ADDPATH:  	case CAPABILITY_CODE_ENHANCED_RR:  	case CAPABILITY_CODE_EXT_MESSAGE:  		break; @@ -3174,102 +3096,6 @@ static int bgp_route_refresh_receive(struct peer_connection *connection,  	return BGP_PACKET_NOOP;  } -static void bgp_dynamic_capability_addpath(uint8_t *pnt, int action, -					   struct capability_header *hdr, -					   struct peer *peer) -{ -	uint8_t *data = pnt + 3; -	uint8_t *end = data + hdr->length; -	size_t len = end - data; -	afi_t afi; -	safi_t safi; - -	if (action == CAPABILITY_ACTION_SET) { -		if (len % CAPABILITY_CODE_ADDPATH_LEN) { -			flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, -				  "Add Path: Received invalid length %zu, non-multiple of 4", -				  len); -			return; -		} - -		SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); - -		while (data + CAPABILITY_CODE_ADDPATH_LEN <= end) { -			afi_t afi; -			safi_t safi; -			iana_afi_t pkt_afi; -			iana_safi_t pkt_safi; -			struct bgp_addpath_capability bac; - -			memcpy(&bac, data, sizeof(bac)); -			pkt_afi = ntohs(bac.afi); -			pkt_safi = safi_int2iana(bac.safi); - -			/* If any other value (other than 1-3) is received, -			 * then the capability SHOULD be treated as not -			 * understood and ignored. -			 */ -			if (!bac.flags || bac.flags > 3) { -				flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, -					  "Add Path: Received invalid send/receive value %u in Add Path capability", -					  bac.flags); -				goto ignore; -			} - -			if (bgp_debug_neighbor_events(peer)) -				zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s%s%s", -					   peer->host, lookup_msg(capcode_str, hdr->code, NULL), -					   iana_afi2str(pkt_afi), iana_safi2str(pkt_safi), -					   CHECK_FLAG(bac.flags, BGP_ADDPATH_RX) ? ", receive" : "", -					   CHECK_FLAG(bac.flags, BGP_ADDPATH_TX) ? ", transmit" -										 : ""); - -			if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, -						      &safi)) { -				if (bgp_debug_neighbor_events(peer)) -					zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Addpath Attribute for this AFI/SAFI", -						   peer->host, -						   iana_afi2str(pkt_afi), -						   iana_safi2str(pkt_safi)); -				goto ignore; -			} else if (!peer->afc[afi][safi]) { -				if (bgp_debug_neighbor_events(peer)) -					zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the AddPath capability for this AFI/SAFI", -						   peer->host, -						   iana_afi2str(pkt_afi), -						   iana_safi2str(pkt_safi)); -				goto ignore; -			} - -			if (CHECK_FLAG(bac.flags, BGP_ADDPATH_RX)) -				SET_FLAG(peer->af_cap[afi][safi], -					 PEER_CAP_ADDPATH_AF_RX_RCV); -			else -				UNSET_FLAG(peer->af_cap[afi][safi], -					   PEER_CAP_ADDPATH_AF_RX_RCV); - -			if (CHECK_FLAG(bac.flags, BGP_ADDPATH_TX)) -				SET_FLAG(peer->af_cap[afi][safi], -					 PEER_CAP_ADDPATH_AF_TX_RCV); -			else -				UNSET_FLAG(peer->af_cap[afi][safi], -					   PEER_CAP_ADDPATH_AF_TX_RCV); - -ignore: -			data += CAPABILITY_CODE_ADDPATH_LEN; -		} -	} else { -		FOREACH_AFI_SAFI (afi, safi) { -			UNSET_FLAG(peer->af_cap[afi][safi], -				   PEER_CAP_ADDPATH_AF_RX_RCV); -			UNSET_FLAG(peer->af_cap[afi][safi], -				   PEER_CAP_ADDPATH_AF_TX_RCV); -		} - -		UNSET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); -	} -} -  static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action,  					       struct capability_header *hdr,  					       struct peer *peer) @@ -4030,9 +3856,6 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,  		case CAPABILITY_CODE_LLGR:  			bgp_dynamic_capability_llgr(pnt, action, hdr, peer);  			break; -		case CAPABILITY_CODE_ADDPATH: -			bgp_dynamic_capability_addpath(pnt, action, hdr, peer); -			break;  		case CAPABILITY_CODE_PATHS_LIMIT:  			bgp_dynamic_capability_paths_limit(pnt, action, hdr,  							   peer); @@ -4046,6 +3869,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,  		case CAPABILITY_CODE_REFRESH:  		case CAPABILITY_CODE_AS4:  		case CAPABILITY_CODE_DYNAMIC: +		case CAPABILITY_CODE_ADDPATH:  		case CAPABILITY_CODE_ENHANCED_RR:  		case CAPABILITY_CODE_EXT_MESSAGE:  			break; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 672c43b37c..f2e61e1e7f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10951,6 +10951,12 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,  		else  			vty_out(vty, ", (stale)");  	} +	if (bgp_path_suppressed(path)) { +		if (json_paths) +			json_object_boolean_true_add(json_path, "suppressed"); +		else +			vty_out(vty, ", (suppressed)"); +	}  	if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR))) {  		if (json_paths) { @@ -15239,7 +15245,7 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,  				if (type == bgp_show_adj_route_advertised ||  				    type == bgp_show_adj_route_received) {  					if (first) { -						vty_out(vty, "\"%s\":", rd_str); +						vty_out(vty, "{\"%s\":", rd_str);  						first = false;  					} else {  						vty_out(vty, ",\"%s\":", rd_str); @@ -15253,6 +15259,8 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,  			output_count += output_count_per_rd;  			filtered_count += filtered_count_per_rd;  		} +		if (first == false && json_routes) +			vty_out(vty, "}");  	} else {  		show_adj_route(vty, peer, table, afi, safi, type, rmap_name,  			       json, json_ar, show_flags, &header1, &header2, diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 2b3e11929b..046b18f224 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -9206,21 +9206,12 @@ DEFUN(neighbor_disable_addpath_rx,  	struct peer *peer;  	afi_t afi = bgp_node_afi(vty);  	safi_t safi = bgp_node_safi(vty); -	int ret; -	int action;  	peer = peer_and_group_lookup_vty(vty, peer_str);  	if (!peer)  		return CMD_WARNING_CONFIG_FAILED; -	action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0); - -	ret = peer_af_flag_set_vty(vty, peer_str, afi, safi, -				   PEER_FLAG_DISABLE_ADDPATH_RX); - -	bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); - -	return ret; +	return peer_af_flag_set_vty(vty, peer_str, afi, safi, PEER_FLAG_DISABLE_ADDPATH_RX);  }  DEFUN(no_neighbor_disable_addpath_rx, @@ -9235,21 +9226,12 @@ DEFUN(no_neighbor_disable_addpath_rx,  	struct peer *peer;  	afi_t afi = bgp_node_afi(vty);  	safi_t safi = bgp_node_safi(vty); -	int ret; -	int action;  	peer = peer_and_group_lookup_vty(vty, peer_str);  	if (!peer)  		return CMD_WARNING_CONFIG_FAILED; -	action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0); - -	ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi, -				     PEER_FLAG_DISABLE_ADDPATH_RX); - -	bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action); - -	return ret; +	return peer_af_flag_unset_vty(vty, peer_str, afi, safi, PEER_FLAG_DISABLE_ADDPATH_RX);  }  DEFUN (neighbor_addpath_tx_all_paths, @@ -9261,15 +9243,12 @@ DEFUN (neighbor_addpath_tx_all_paths,  {  	int idx_peer = 1;  	struct peer *peer; -	afi_t afi = bgp_node_afi(vty); -	safi_t safi = bgp_node_safi(vty);  	peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);  	if (!peer)  		return CMD_WARNING_CONFIG_FAILED; -	bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_ALL, 0); - +	bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), BGP_ADDPATH_ALL, 0);  	return CMD_SUCCESS;  } @@ -9289,20 +9268,18 @@ DEFUN (no_neighbor_addpath_tx_all_paths,  {  	int idx_peer = 2;  	struct peer *peer; -	afi_t afi = bgp_node_afi(vty); -	safi_t safi = bgp_node_safi(vty);  	peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);  	if (!peer)  		return CMD_WARNING_CONFIG_FAILED; -	if (peer->addpath_type[afi][safi] != BGP_ADDPATH_ALL) { +	if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)] != BGP_ADDPATH_ALL) {  		vty_out(vty,  			"%% Peer not currently configured to transmit all paths.");  		return CMD_WARNING_CONFIG_FAILED;  	} -	bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE, 0); +	bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), BGP_ADDPATH_NONE, 0);  	return CMD_SUCCESS;  } @@ -17598,12 +17575,6 @@ DEFUN (bgp_redistribute_ipv4_ospf,  	if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)  		protocol = ZEBRA_ROUTE_OSPF;  	else { -		if (bgp->vrf_id != VRF_DEFAULT) { -			vty_out(vty, -				"%% Only default BGP instance can use '%s'\n", -				argv[idx_ospf_table]->arg); -			return CMD_WARNING_CONFIG_FAILED; -		}  		if (strncmp(argv[idx_ospf_table]->arg, "table-direct",  			    strlen("table-direct")) == 0) {  			protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17657,12 +17628,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap,  	if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)  		protocol = ZEBRA_ROUTE_OSPF;  	else { -		if (bgp->vrf_id != VRF_DEFAULT) { -			vty_out(vty, -				"%% Only default BGP instance can use '%s'\n", -				argv[idx_ospf_table]->arg); -			return CMD_WARNING_CONFIG_FAILED; -		}  		if (strncmp(argv[idx_ospf_table]->arg, "table-direct",  			    strlen("table-direct")) == 0) {  			protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17720,12 +17685,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric,  	if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)  		protocol = ZEBRA_ROUTE_OSPF;  	else { -		if (bgp->vrf_id != VRF_DEFAULT) { -			vty_out(vty, -				"%% Only default BGP instance can use '%s'\n", -				argv[idx_ospf_table]->arg); -			return CMD_WARNING_CONFIG_FAILED; -		}  		if (strncmp(argv[idx_ospf_table]->arg, "table-direct",  			    strlen("table-direct")) == 0) {  			protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17790,12 +17749,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric,  	if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)  		protocol = ZEBRA_ROUTE_OSPF;  	else { -		if (bgp->vrf_id != VRF_DEFAULT) { -			vty_out(vty, -				"%% Only default BGP instance can use '%s'\n", -				argv[idx_ospf_table]->arg); -			return CMD_WARNING_CONFIG_FAILED; -		}  		if (strncmp(argv[idx_ospf_table]->arg, "table-direct",  			    strlen("table-direct")) == 0) {  			protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17865,13 +17818,7 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap,  	if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)  		protocol = ZEBRA_ROUTE_OSPF;  	else { -		if (bgp->vrf_id != VRF_DEFAULT) { -			vty_out(vty, -				"%% Only default BGP instance can use '%s'\n", -				argv[idx_ospf_table]->arg); -			return CMD_WARNING_CONFIG_FAILED; -		} else if (strncmp(argv[idx_ospf_table]->arg, "table-direct", -				   strlen("table-direct")) == 0) { +		if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) {  			protocol = ZEBRA_ROUTE_TABLE_DIRECT;  			if (instance == RT_TABLE_MAIN ||  			    instance == RT_TABLE_LOCAL) { @@ -17934,12 +17881,6 @@ DEFUN (no_bgp_redistribute_ipv4_ospf,  	if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)  		protocol = ZEBRA_ROUTE_OSPF;  	else { -		if (bgp->vrf_id != VRF_DEFAULT) { -			vty_out(vty, -				"%% Only default BGP instance can use '%s'\n", -				argv[idx_ospf_table]->arg); -			return CMD_WARNING_CONFIG_FAILED; -		}  		if (strncmp(argv[idx_ospf_table]->arg, "table-direct",  			    strlen("table-direct")) == 0) {  			protocol = ZEBRA_ROUTE_TABLE_DIRECT; diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 8e8616c155..1669aabc60 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1674,11 +1674,23 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi)  	for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest))  		for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)  			if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && -			    (pi->type == ZEBRA_ROUTE_BGP -			     && (pi->sub_type == BGP_ROUTE_NORMAL -				 || pi->sub_type == BGP_ROUTE_IMPORTED))) -				bgp_zebra_route_install(dest, pi, bgp, true, -							NULL, false); +			    (pi->type == ZEBRA_ROUTE_BGP && (pi->sub_type == BGP_ROUTE_NORMAL || +							     pi->sub_type == BGP_ROUTE_IMPORTED))) { +				bool is_add = true; + +				if (bgp->table_map[afi][safi].name) { +					struct attr local_attr = *pi->attr; +					struct bgp_path_info local_info = *pi; + +					local_info.attr = &local_attr; + +					is_add = bgp_table_map_apply(bgp->table_map[afi][safi].map, +								     bgp_dest_get_prefix(dest), +								     &local_info); +				} + +				bgp_zebra_route_install(dest, pi, bgp, is_add, NULL, false); +			}  }  /* Announce routes of any bgp subtype of a table to zebra */ @@ -2042,11 +2054,34 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type,  	/* Return if already redistribute flag is set. */  	if (instance) { -		if (redist_check_instance(&zclient->mi_redist[afi][type], -					  instance)) -			return CMD_WARNING; +		if (type == ZEBRA_ROUTE_TABLE_DIRECT) { +			/* +			 * When redistribution type is `table-direct` the +			 * instance means `table identification`. +			 * +			 * `table_id` support 32bit integers, however since +			 * `instance` is being overloaded to `table_id` it +			 * will only be possible to use the first 65535 +			 * entries. +			 * +			 * Also the ZAPI must also support `int` +			 * (see `zebra_redistribute_add`). +			 */ +			struct redist_table_direct table = { +				.table_id = instance, +				.vrf_id = bgp->vrf_id, +			}; +			if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) != +			    NULL) +				return CMD_WARNING; + +			redist_add_table_direct(&zclient->mi_redist[afi][type], &table); +		} else { +			if (redist_check_instance(&zclient->mi_redist[afi][type], instance)) +				return CMD_WARNING; -		redist_add_instance(&zclient->mi_redist[afi][type], instance); +			redist_add_instance(&zclient->mi_redist[afi][type], instance); +		}  	} else {  		if (vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id))  			return CMD_WARNING; @@ -2174,10 +2209,22 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type,  	/* Return if zebra connection is disabled. */  	if (instance) { -		if (!redist_check_instance(&zclient->mi_redist[afi][type], -					   instance)) -			return CMD_WARNING; -		redist_del_instance(&zclient->mi_redist[afi][type], instance); +		if (type == ZEBRA_ROUTE_TABLE_DIRECT) { +			struct redist_table_direct table = { +				.table_id = instance, +				.vrf_id = bgp->vrf_id, +			}; +			if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) == +			    NULL) +				return CMD_WARNING; + +			redist_del_table_direct(&zclient->mi_redist[afi][type], &table); +		} else { +			if (!redist_check_instance(&zclient->mi_redist[afi][type], instance)) +				return CMD_WARNING; + +			redist_del_instance(&zclient->mi_redist[afi][type], instance); +		}  	} else {  		if (!vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id))  			return CMD_WARNING; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index d5463f3d0c..edf90d3dd8 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2026,8 +2026,11 @@ 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 && peer_active(peer->connection)) { +		if (peer->last_reset == PEER_DOWN_NOAFI_ACTIVATED) +			peer->last_reset = 0;  		bgp_timer_set(peer->connection); +	}  	bgp_peer_gr_flags_update(peer);  	BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); @@ -4820,39 +4823,40 @@ static const struct peer_flag_action peer_flag_action_list[] = {  	{0, 0, 0}};  static const struct peer_flag_action peer_af_flag_action_list[] = { -	{PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out}, -	{PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out}, -	{PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out}, -	{PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out}, -	{PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset}, -	{PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset}, -	{PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in}, -	{PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out}, -	{PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out}, -	{PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out}, -	{PEER_FLAG_DEFAULT_ORIGINATE, 0, peer_change_none}, -	{PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out}, -	{PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in}, -	{PEER_FLAG_ALLOWAS_IN_ORIGIN, 0, peer_change_reset_in}, -	{PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset}, -	{PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset}, -	{PEER_FLAG_MAX_PREFIX, 0, peer_change_none}, -	{PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none}, -	{PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none}, -	{PEER_FLAG_MAX_PREFIX_OUT, 0, peer_change_none}, -	{PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out}, -	{PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out}, -	{PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out}, -	{PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, 1, peer_change_reset_out}, -	{PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out}, -	{PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out}, -	{PEER_FLAG_WEIGHT, 0, peer_change_reset_in}, -	{PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_none}, -	{PEER_FLAG_SOO, 0, peer_change_reset}, -	{PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset}, -	{PEER_FLAG_SEND_EXT_COMMUNITY_RPKI, 1, peer_change_reset_out}, -	{PEER_FLAG_ADDPATH_RX_PATHS_LIMIT, 0, peer_change_none}, -	{0, 0, 0}}; +	{ PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out }, +	{ PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out }, +	{ PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out }, +	{ PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out }, +	{ PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset }, +	{ PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset }, +	{ PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in }, +	{ PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out }, +	{ PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out }, +	{ PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out }, +	{ PEER_FLAG_DEFAULT_ORIGINATE, 0, peer_change_none }, +	{ PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out }, +	{ PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in }, +	{ PEER_FLAG_ALLOWAS_IN_ORIGIN, 0, peer_change_reset_in }, +	{ PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset }, +	{ PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset }, +	{ PEER_FLAG_MAX_PREFIX, 0, peer_change_none }, +	{ PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none }, +	{ PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none }, +	{ PEER_FLAG_MAX_PREFIX_OUT, 0, peer_change_none }, +	{ PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out }, +	{ PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out }, +	{ PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out }, +	{ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, 1, peer_change_reset_out }, +	{ PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out }, +	{ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out }, +	{ PEER_FLAG_WEIGHT, 0, peer_change_reset_in }, +	{ PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset }, +	{ PEER_FLAG_SOO, 0, peer_change_reset }, +	{ PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset }, +	{ PEER_FLAG_SEND_EXT_COMMUNITY_RPKI, 1, peer_change_reset_out }, +	{ PEER_FLAG_ADDPATH_RX_PATHS_LIMIT, 0, peer_change_none }, +	{ 0, 0, 0 } +};  /* Proper action set. */  static int peer_flag_action_set(const struct peer_flag_action *action_list, diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index c64bbf7f69..5798d318f2 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -777,12 +777,6 @@ struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size);  #define ISIS_MT_AT_MASK        0x4000  #endif -/* RFC 8919 */ -#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ -#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ -#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ -#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ -  void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd);  void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,  				  struct list *addresses); diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 3247a0372c..7203c8ac8e 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -309,13 +309,3 @@ static ssize_t printfrr_rn(struct fbuf *buf, struct printfrr_eargs *ea,  		    cbuf, sizeof(cbuf));  	return bputs(buf, cbuf);  } - -struct route_table *srcdest_srcnode_table(struct route_node *rn) -{ -	if (rnode_is_dstnode(rn)) { -		struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn); - -		return srn->src_table; -	} -	return NULL; -} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index ff97f9b735..a699d4a11b 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -87,8 +87,6 @@ static inline void *srcdest_rnode_table_info(struct route_node *rn)  	return route_table_get_info(srcdest_rnode_table(rn));  } -extern struct route_table *srcdest_srcnode_table(struct route_node *rn); -  #ifdef __cplusplus  }  #endif diff --git a/lib/zclient.c b/lib/zclient.c index 063944fd3b..d8c75c9029 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -31,6 +31,7 @@  DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient");  DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs"); +DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct");  /* Zebra client events. */  enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; @@ -104,6 +105,11 @@ void zclient_free(struct zclient *zclient)  	XFREE(MTYPE_ZCLIENT, zclient);  } +static void redist_free_instance(void *data) +{ +	XFREE(MTYPE_REDIST_INST, data); +} +  unsigned short *redist_check_instance(struct redist_proto *red,  				      unsigned short instance)  { @@ -126,8 +132,10 @@ void redist_add_instance(struct redist_proto *red, unsigned short instance)  	red->enabled = 1; -	if (!red->instances) +	if (!red->instances) {  		red->instances = list_new(); +		red->instances->del = redist_free_instance; +	}  	in = XMALLOC(MTYPE_REDIST_INST, sizeof(unsigned short));  	*in = instance; @@ -143,23 +151,100 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance)  		return;  	listnode_delete(red->instances, id); -	XFREE(MTYPE_REDIST_INST, id); +	red->instances->del(id);  	if (!red->instances->count) {  		red->enabled = 0;  		list_delete(&red->instances);  	}  } -void redist_del_all_instances(struct redist_proto *red) +static void redist_free_table_direct(void *data)  { -	struct listnode *ln, *nn; -	unsigned short *id; +	XFREE(MTYPE_REDIST_TABLE_DIRECT, data); +} + +struct redist_table_direct *redist_lookup_table_direct(const struct redist_proto *red, +						       const struct redist_table_direct *table) +{ +	struct redist_table_direct *ntable; +	struct listnode *node; + +	if (red->instances == NULL) +		return NULL; + +	for (ALL_LIST_ELEMENTS_RO(red->instances, node, ntable)) { +		if (table->vrf_id != ntable->vrf_id) +			continue; +		if (table->table_id != ntable->table_id) +			continue; + +		return ntable; +	} + +	return NULL; +} + +bool redist_table_direct_has_id(const struct redist_proto *red, int table_id) +{ +	struct redist_table_direct *table; +	struct listnode *node; + +	if (red->instances == NULL) +		return false; + +	for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) { +		if (table->table_id != table_id) +			continue; + +		return true; +	} + +	return false; +} + +void redist_add_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ +	struct redist_table_direct *ntable; + +	ntable = redist_lookup_table_direct(red, table); +	if (ntable != NULL) +		return; + +	if (red->instances == NULL) { +		red->instances = list_new(); +		red->instances->del = redist_free_table_direct; +	} + +	red->enabled = 1; + +	ntable = XCALLOC(MTYPE_REDIST_TABLE_DIRECT, sizeof(*ntable)); +	ntable->vrf_id = table->vrf_id; +	ntable->table_id = table->table_id; +	listnode_add(red->instances, ntable); +} + +void redist_del_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ +	struct redist_table_direct *ntable; +	ntable = redist_lookup_table_direct(red, table); +	if (ntable == NULL) +		return; + +	listnode_delete(red->instances, ntable); +	red->instances->del(ntable); +	if (red->instances->count == 0) { +		red->enabled = 0; +		list_delete(&red->instances); +	} +} + +void redist_del_all_instances(struct redist_proto *red) +{  	if (!red->instances)  		return; -	for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id)) -		redist_del_instance(red, *id); +	list_delete(&red->instances);  }  /* Stop zebra client services. */ @@ -480,6 +565,17 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient,  	return zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api);  } +static void zclient_send_table_direct(struct zclient *zclient, afi_t afi, int type) +{ +	struct redist_table_direct *table; +	struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; +	struct listnode *node; + +	for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) +		zebra_redistribute_send(type, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, +					table->table_id, table->vrf_id); +} +  /* Send register requests to zebra daemon for the information in a VRF. */  void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)  { @@ -513,6 +609,12 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)  				if (!zclient->mi_redist[afi][i].enabled)  					continue; +				if (i == ZEBRA_ROUTE_TABLE_DIRECT) { +					zclient_send_table_direct(zclient, afi, +								  ZEBRA_REDISTRIBUTE_ADD); +					continue; +				} +  				struct listnode *node;  				unsigned short *id; @@ -580,6 +682,12 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)  				if (!zclient->mi_redist[afi][i].enabled)  					continue; +				if (i == ZEBRA_ROUTE_TABLE_DIRECT) { +					zclient_send_table_direct(zclient, afi, +								  ZEBRA_REDISTRIBUTE_DELETE); +					continue; +				} +  				struct listnode *node;  				unsigned short *id; @@ -2016,6 +2124,15 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p,  			      enum zapi_route_notify_owner *note,  			      afi_t *afi, safi_t *safi)  { +	struct prefix dummy; + +	return zapi_route_notify_decode_srcdest(s, p, &dummy, tableid, note, afi, safi); +} + +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, +				      uint32_t *tableid, enum zapi_route_notify_owner *note, +				      afi_t *afi, safi_t *safi) +{  	uint32_t t;  	afi_t afi_val;  	safi_t safi_val; @@ -2025,6 +2142,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p,  	STREAM_GETC(s, p->family);  	STREAM_GETC(s, p->prefixlen);  	STREAM_GET(&p->u.prefix, s, prefix_blen(p)); +	src_p->family = p->family; +	STREAM_GETC(s, src_p->prefixlen); +	STREAM_GET(&src_p->u.prefix, s, prefix_blen(src_p));  	STREAM_GETL(s, t);  	STREAM_GETC(s, afi_val);  	STREAM_GETC(s, safi_val); @@ -4634,9 +4754,52 @@ static void zclient_read(struct event *thread)  	zclient_event(ZCLIENT_READ, zclient);  } +static void zclient_redistribute_table_direct(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, +					      int instance, int command) +{ +	struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; +	bool has_table; +	struct redist_table_direct table = { +		.vrf_id = vrf_id, +		.table_id = instance, +	}; + +	has_table = redist_lookup_table_direct(red, &table); + +	if (command == ZEBRA_REDISTRIBUTE_ADD) { +		if (has_table) +			return; + +		redist_add_table_direct(red, &table); +	} else { +		if (!has_table) +			return; + +		redist_del_table_direct(red, &table); +	} + +	if (zclient->sock > 0) +		zebra_redistribute_send(command, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, instance, +					vrf_id); +} +  void zclient_redistribute(int command, struct zclient *zclient, afi_t afi,  			  int type, unsigned short instance, vrf_id_t vrf_id)  { +	/* +	 * When asking for table-direct redistribution the parameter +	 * `instance` has a different meaning: it means table +	 * identification. +	 * +	 * The table identification information is stored in +	 * `zclient->mi_redist` along with the VRF identification +	 * information in a pair (different from the usual single protocol +	 * instance value). +	 */ +	if (type == ZEBRA_ROUTE_TABLE_DIRECT) { +		zclient_redistribute_table_direct(zclient, vrf_id, afi, instance, command); +		return; +	}  	if (instance) {  		if (command == ZEBRA_REDISTRIBUTE_ADD) { diff --git a/lib/zclient.h b/lib/zclient.h index 2385a8a219..afd84acce2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -268,6 +268,21 @@ struct redist_proto {  	struct list *instances;  }; +/** + * Redistribute table direct instance data structure: keeps the VRF + * that subscribed to the table ID. + * + * **NOTE** + * `table_id` is an integer because that is what the netlink interface + * uses for route attribute RTA_TABLE (32bit int), however the whole + * zclient API uses `unsigned short` (and CLI commands) so it will be + * limited to the range 1 to 65535. + */ +struct redist_table_direct { +	vrf_id_t vrf_id; +	int table_id; +}; +  struct zclient_capabilities {  	uint32_t ecmp;  	bool mpls_enabled; @@ -924,6 +939,15 @@ extern void redist_add_instance(struct redist_proto *, unsigned short);  extern void redist_del_instance(struct redist_proto *, unsigned short);  extern void redist_del_all_instances(struct redist_proto *red); +extern struct redist_table_direct * +redist_lookup_table_direct(const struct redist_proto *red, const struct redist_table_direct *table); +extern bool redist_table_direct_has_id(const struct redist_proto *red, int table_id); +extern void redist_add_table_direct(struct redist_proto *red, +				    const struct redist_table_direct *table); +extern void redist_del_table_direct(struct redist_proto *red, +				    const struct redist_table_direct *table); + +  /*   * Send to zebra that the specified vrf is using label to resolve   * itself for L3VPN's.  Repeated calls of this function with @@ -1144,6 +1168,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p,  			      uint32_t *tableid,  			      enum zapi_route_notify_owner *note,  			      afi_t *afi, safi_t *safi); +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, +				      uint32_t *tableid, enum zapi_route_notify_owner *note, +				      afi_t *afi, safi_t *safi);  bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno,  			     uint32_t *priority, uint32_t *unique, char *ifname,  			     enum zapi_rule_notify_owner *note); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index b718d498ae..f45135f44f 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -304,6 +304,27 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,  		if (api.nexthop_num >= ospf->max_multipath)  			break; +		/* +		 * Prune duplicate next-hops from the route that is +		 * installed in the zebra IP route table. OSPF Intra-Area +		 * routes never have duplicates. +		 */ +		if (or->path_type != OSPF_PATH_INTRA_AREA) { +			struct zapi_nexthop *api_nh = &api.nexthops[0]; +			unsigned int nh_index; +			bool duplicate_next_hop = false; + +			for (nh_index = 0; nh_index < api.nexthop_num; api_nh++, nh_index++) { +				if (IPV4_ADDR_SAME(&api_nh->gate.ipv4, &path->nexthop) && +				    (api_nh->ifindex == path->ifindex)) { +					duplicate_next_hop = true; +					break; +				} +			} +			if (duplicate_next_hop) +				continue; +		} +  		ospf_zebra_add_nexthop(ospf, path, &api);  		if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 4c0a654c8e..4e0196d24a 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -191,6 +191,9 @@ BuildRequires:  ncurses-devel  BuildRequires:  readline-devel  BuildRequires:  texinfo  BuildRequires:  libyang-devel >= 2.1.128 +# Version requirement don't get reflected down from a BuildRequire +# to Require, so need to require libyang version as both ways +Requires: libyang >= 2.1.128  BuildRequires:  pcre2-devel  %if 0%{?rhel} && 0%{?rhel} < 7  #python27-devel is available from ius community repo for RedHat/CentOS 6 diff --git a/staticd/static_nb.c b/staticd/static_nb.c index 356324126a..ef363bfe7e 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -135,96 +135,6 @@ const struct frr_yang_module_info frr_staticd_info = {  			}  		},  		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", -			.cbs = { -				.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", -			.cbs = { -				.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", -			.cbs = { -				.apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish, -				.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy, -				.pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry", -			.cbs = { -				.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", -			.cbs = { -				.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class", -			.cbs = { -				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify, -				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy, -			} -		}, -		{  			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid",  			.cbs = {  				.apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish, diff --git a/staticd/static_nb.h b/staticd/static_nb.h index d11bf5363b..aa11f34021 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -72,52 +72,6 @@ int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args);  int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args);  int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args);  int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( -	struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( -	struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( -	struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( -	struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( -	struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( -	struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( -	struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( -	struct nb_cb_destroy_args *args);  int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create(  	struct nb_cb_create_args *args);  int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_destroy( @@ -151,8 +105,6 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routi  void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish(  	struct nb_cb_apply_finish_args *args); -void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( -	struct nb_cb_apply_finish_args *args);  void routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish(  	struct nb_cb_apply_finish_args *args); @@ -169,16 +121,16 @@ int routing_control_plane_protocols_name_validate(  /* xpath macros */  /* route-list */ -#define FRR_STATIC_ROUTE_INFO_KEY_XPATH                                        \ -	"/frr-routing:routing/control-plane-protocols/"                        \ -	"control-plane-protocol[type='%s'][name='%s'][vrf='%s']/"              \ -	"frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/"          \ +#define FRR_STATIC_ROUTE_INFO_KEY_XPATH                                                            \ +	"/frr-routing:routing/control-plane-protocols/"                                            \ +	"control-plane-protocol[type='%s'][name='%s'][vrf='%s']/"                                  \ +	"frr-staticd:staticd/route-list[prefix='%s'][src-prefix='%s'][afi-safi='%s']/"             \  	"path-list[table-id='%u'][distance='%u']" -#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH                            \ -	"/frr-routing:routing/control-plane-protocols/"                        \ -	"control-plane-protocol[type='%s'][name='%s'][vrf='%s']/"              \ -	"frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/"          \ +#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH                                                \ +	"/frr-routing:routing/control-plane-protocols/"                                            \ +	"control-plane-protocol[type='%s'][name='%s'][vrf='%s']/"                                  \ +	"frr-staticd:staticd/route-list[prefix='%s'][src-prefix='%s'][afi-safi='%s']/"             \  	"path-list[table-id='%u']" @@ -203,19 +155,6 @@ int routing_control_plane_protocols_name_validate(  #define FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH "/entry[id='%u']/seg" -/* route-list/srclist */ -#define FRR_S_ROUTE_SRC_INFO_KEY_XPATH                                         \ -	"/frr-routing:routing/control-plane-protocols/"                        \ -	"control-plane-protocol[type='%s'][name='%s'][vrf='%s']/"              \ -	"frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/"          \ -	"src-list[src-prefix='%s']/path-list[table-id='%u'][distance='%u']" - -#define FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH                             \ -	"/frr-routing:routing/control-plane-protocols/"                        \ -	"control-plane-protocol[type='%s'][name='%s'][vrf='%s']/"              \ -	"frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/"          \ -	"src-list[src-prefix='%s']/path-list[table-id='%u']" -  /* route-list/frr-nexthops */  #define FRR_DEL_S_ROUTE_NH_KEY_XPATH                                           \  	FRR_STATIC_ROUTE_INFO_KEY_XPATH                                        \ @@ -226,16 +165,6 @@ int routing_control_plane_protocols_name_validate(  	FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH                            \  	FRR_STATIC_ROUTE_NH_KEY_XPATH -/* route-list/src/src-list/frr-nexthops*/ -#define FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH                                       \ -	FRR_S_ROUTE_SRC_INFO_KEY_XPATH                                         \ -	FRR_STATIC_ROUTE_NH_KEY_XPATH - -/* route-list/src/src-list/frr-nexthops*/ -#define FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH                           \ -	FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH                             \ -	FRR_STATIC_ROUTE_NH_KEY_XPATH -  /* srv6 */  #define FRR_STATIC_SRV6_INFO_KEY_XPATH                                                             \  	"/frr-routing:routing/control-plane-protocols/"                                            \ diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index 51de05c2ea..e2ab1f2ffe 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -502,16 +502,6 @@ void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_p  	static_install_nexthop(nh);  } -void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( -	struct nb_cb_apply_finish_args *args) -{ -	struct static_nexthop *nh; - -	nh = nb_running_get_entry(args->dnode, NULL, true); - -	static_install_nexthop(nh); -} -  int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate(  	struct nb_cb_pre_validate_args *args)  { @@ -576,7 +566,7 @@ int routing_control_plane_protocols_staticd_destroy(  		if (!stable)  			continue; -		for (rn = route_top(stable); rn; rn = route_next(rn)) +		for (rn = route_top(stable); rn; rn = srcdest_route_next(rn))  			static_del_route(rn);  	} @@ -595,7 +585,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr  	struct static_vrf *svrf;  	struct route_node *rn;  	const struct lyd_node *vrf_dnode; -	struct prefix prefix; +	struct prefix prefix, src_prefix, *src_p;  	const char *afi_safi;  	afi_t prefix_afi;  	afi_t afi; @@ -604,6 +594,8 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr  	switch (args->event) {  	case NB_EV_VALIDATE:  		yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); +		yang_dnode_get_prefix(&src_prefix, args->dnode, "src-prefix"); +		src_p = src_prefix.prefixlen ? &src_prefix : NULL;  		afi_safi = yang_dnode_get_string(args->dnode, "afi-safi");  		yang_afi_safi_identity2value(afi_safi, &afi, &safi);  		prefix_afi = family2afi(prefix.family); @@ -614,6 +606,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr  				yang_dnode_get_string(args->dnode, "prefix"));  			return NB_ERR_VALIDATION;  		} + +		if (src_p && afi != AFI_IP6) { +			flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, +				  "invalid use of IPv6 dst-src prefix %s on %s", +				  yang_dnode_get_string(args->dnode, "src-prefix"), +				  yang_dnode_get_string(args->dnode, "prefix")); +			return NB_ERR_VALIDATION; +		}  		break;  	case NB_EV_PREPARE:  	case NB_EV_ABORT: @@ -624,10 +624,12 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr  		svrf = nb_running_get_entry(vrf_dnode, NULL, true);  		yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); +		yang_dnode_get_prefix(&src_prefix, args->dnode, "src-prefix"); +		src_p = src_prefix.prefixlen ? &src_prefix : NULL;  		afi_safi = yang_dnode_get_string(args->dnode, "afi-safi");  		yang_afi_safi_identity2value(afi_safi, &afi, &safi); -		rn = static_add_route(afi, safi, &prefix, NULL, svrf); +		rn = static_add_route(afi, safi, &prefix, (struct prefix_ipv6 *)src_p, svrf);  		if (!svrf->vrf || svrf->vrf->vrf_id == VRF_UNKNOWN)  			snprintf(  				args->errmsg, args->errmsg_len, @@ -1048,331 +1050,6 @@ int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args)  /*   * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( -	struct nb_cb_create_args *args) -{ -	struct static_vrf *s_vrf; -	struct route_node *rn; -	struct route_node *src_rn; -	struct prefix_ipv6 src_prefix = {}; -	struct stable_info *info; -	afi_t afi; -	safi_t safi = SAFI_UNICAST; - -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -		break; -	case NB_EV_APPLY: -		rn = nb_running_get_entry(args->dnode, NULL, true); -		info = route_table_get_info(rn->table); -		s_vrf = info->svrf; -		yang_dnode_get_ipv6p(&src_prefix, args->dnode, "src-prefix"); -		afi = family2afi(src_prefix.family); -		src_rn = -			static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf); -		nb_running_set_entry(args->dnode, src_rn); -		break; -	} -	return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( -	struct nb_cb_destroy_args *args) -{ -	struct route_node *src_rn; - -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -		break; -	case NB_EV_APPLY: -		src_rn = nb_running_unset_entry(args->dnode); -		static_del_route(src_rn); -		break; -	} - -	return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( -	struct nb_cb_create_args *args) -{ -	return static_path_list_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( -	struct nb_cb_destroy_args *args) -{ -	return static_path_list_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( -	struct nb_cb_modify_args *args) -{ -	return static_path_list_tag_modify(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( -	struct nb_cb_create_args *args) -{ -	return static_nexthop_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( -	struct nb_cb_destroy_args *args) -{ -	return static_nexthop_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( -	struct nb_cb_modify_args *args) -{ -	return static_nexthop_bh_type_modify(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( -	struct nb_cb_modify_args *args) -{ -	return static_nexthop_onlink_modify(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( -	struct nb_cb_modify_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -		break; -	case NB_EV_APPLY: -		if (static_nexthop_color_modify(args) != NB_OK) -			return NB_ERR; - -		break; -	} -	return NB_OK; -} - - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( -	struct nb_cb_destroy_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -		break; -	case NB_EV_APPLY: -		if (static_nexthop_color_destroy(args) != NB_OK) -			return NB_ERR; -		break; -	} -	return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( -	struct nb_cb_create_args *args) -{ -	return nexthop_srv6_segs_stack_entry_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( -	struct nb_cb_destroy_args *args) -{ -	return nexthop_srv6_segs_stack_entry_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( -	struct nb_cb_modify_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -		break; -	case NB_EV_APPLY: -		if (static_nexthop_srv6_segs_modify(args) != NB_OK) -			return NB_ERR; -		break; -	} -	return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( -	struct nb_cb_destroy_args *args) -{ -	/* -	 * No operation is required in this call back. -	 * nexthop_mpls_seg_stack_entry_destroy() will take care -	 * to reset the seg vaue. -	 */ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -	case NB_EV_APPLY: -		break; -	} -	return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( -	struct nb_cb_create_args *args) -{ -	return nexthop_mpls_label_stack_entry_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( -	struct nb_cb_destroy_args *args) -{ -	return nexthop_mpls_label_stack_entry_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( -	struct nb_cb_modify_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -		break; -	case NB_EV_APPLY: -		if (static_nexthop_mpls_label_modify(args) != NB_OK) -			return NB_ERR; -		break; -	} -	return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( -	struct nb_cb_destroy_args *args) -{ -	/* -	 * No operation is required in this call back. -	 * nexthop_mpls_label_stack_entry_destroy() will take care -	 * to reset the label vaue. -	 */ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -	case NB_EV_APPLY: -		break; -	} -	return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( -	struct nb_cb_modify_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -	case NB_EV_APPLY: -		break; -	} - -	return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( -	struct nb_cb_destroy_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -	case NB_EV_APPLY: -		break; -	} - -	return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( -	struct nb_cb_modify_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -	case NB_EV_APPLY: -		break; -	} - -	return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( -	struct nb_cb_destroy_args *args) -{ -	switch (args->event) { -	case NB_EV_VALIDATE: -	case NB_EV_PREPARE: -	case NB_EV_ABORT: -	case NB_EV_APPLY: -		break; -	} - -	return NB_OK; -} - -/* - * XPath:   * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing   */  int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create( diff --git a/staticd/static_nht.c b/staticd/static_nht.c index 06d27c6f59..367ee85040 100644 --- a/staticd/static_nht.c +++ b/staticd/static_nht.c @@ -49,8 +49,8 @@ static void static_nht_update_path(struct static_path *pn, struct prefix *nhp,  		static_zebra_route_add(pn, true);  } -static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, -				   uint32_t nh_num, afi_t afi, safi_t safi, +static void static_nht_update_safi(const struct prefix *sp, const struct prefix *ssrc_p, +				   struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi,  				   struct static_vrf *svrf, vrf_id_t nh_vrf_id)  {  	struct route_table *stable; @@ -63,7 +63,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp,  		return;  	if (sp) { -		rn = srcdest_rnode_lookup(stable, sp, NULL); +		rn = srcdest_rnode_lookup(stable, sp, (const struct prefix_ipv6 *)ssrc_p);  		if (rn && rn->info) {  			si = static_route_info_from_rnode(rn);  			frr_each(static_path_list, &si->path_list, pn) { @@ -75,7 +75,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp,  		return;  	} -	for (rn = route_top(stable); rn; rn = route_next(rn)) { +	for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {  		si = static_route_info_from_rnode(rn);  		if (!si)  			continue; @@ -85,14 +85,13 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp,  	}  } -void static_nht_update(struct prefix *sp, struct prefix *nhp, uint32_t nh_num, -		       afi_t afi, safi_t safi, vrf_id_t nh_vrf_id) +void static_nht_update(const struct prefix *sp, const struct prefix *ssrc_p, struct prefix *nhp, +		       uint32_t nh_num, afi_t afi, safi_t safi, vrf_id_t nh_vrf_id)  {  	struct static_vrf *svrf;  	RB_FOREACH (svrf, svrf_name_head, &svrfs) -		static_nht_update_safi(sp, nhp, nh_num, afi, safi, svrf, -				       nh_vrf_id); +		static_nht_update_safi(sp, ssrc_p, nhp, nh_num, afi, safi, svrf, nh_vrf_id);  }  static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, @@ -109,7 +108,7 @@ static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi,  	if (!stable)  		return; -	for (rn = route_top(stable); rn; rn = route_next(rn)) { +	for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {  		si = static_route_info_from_rnode(rn);  		if (!si)  			continue; @@ -150,8 +149,8 @@ void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi,  		static_nht_reset_start_safi(nhp, afi, safi, svrf, nh_vrf_id);  } -static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, -				       safi_t safi, struct vrf *vrf, +static void static_nht_mark_state_safi(const struct prefix *sp, const struct prefix *ssrc_p, +				       afi_t afi, safi_t safi, struct vrf *vrf,  				       enum static_install_states state)  {  	struct static_vrf *svrf; @@ -169,7 +168,7 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi,  	if (!stable)  		return; -	rn = srcdest_rnode_lookup(stable, sp, NULL); +	rn = srcdest_rnode_lookup(stable, sp, (const struct prefix_ipv6 *)ssrc_p);  	if (!rn)  		return;  	si = rn->info; @@ -184,8 +183,8 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi,  	route_unlock_node(rn);  } -void static_nht_mark_state(struct prefix *sp, safi_t safi, vrf_id_t vrf_id, -			   enum static_install_states state) +void static_nht_mark_state(const struct prefix *sp, const struct prefix *ssrc_p, safi_t safi, +			   vrf_id_t vrf_id, enum static_install_states state)  {  	struct vrf *vrf; @@ -198,5 +197,5 @@ void static_nht_mark_state(struct prefix *sp, safi_t safi, vrf_id_t vrf_id,  	if (!vrf || !vrf->info)  		return; -	static_nht_mark_state_safi(sp, afi, safi, vrf, state); +	static_nht_mark_state_safi(sp, ssrc_p, afi, safi, vrf, state);  } diff --git a/staticd/static_nht.h b/staticd/static_nht.h index 74f4401e49..41ff30cd52 100644 --- a/staticd/static_nht.h +++ b/staticd/static_nht.h @@ -16,15 +16,14 @@ extern "C" {   * us call this function to find the nexthop we are tracking so it   * can be installed or removed.   * - * sp -> The route we are looking at.  If NULL then look at all - *       routes. + * sp + ssrc_p -> The route we are looking at.  If NULL then look at all routes.   * nhp -> The nexthop that is being tracked.   * nh_num -> number of valid nexthops.   * afi -> The afi we are working in.   * vrf_id -> The vrf the nexthop is in.   */ -extern void static_nht_update(struct prefix *sp, struct prefix *nhp, -			      uint32_t nh_num, afi_t afi, safi_t safi, +extern void static_nht_update(const struct prefix *sp, const struct prefix *ssrc_p, +			      struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi,  			      vrf_id_t vrf_id);  /* @@ -35,11 +34,10 @@ extern void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi,  				   vrf_id_t nh_vrf_id);  /* - * For the given prefix, sp, mark it as in a particular state + * For the given prefix, sp + ssrc_p, mark it as in a particular state   */ -extern void static_nht_mark_state(struct prefix *sp, safi_t safi, -				  vrf_id_t vrf_id, -				  enum static_install_states state); +extern void static_nht_mark_state(const struct prefix *sp, const struct prefix *ssrc_p, safi_t safi, +				  vrf_id_t vrf_id, enum static_install_states state);  /*   * For the given nexthop, returns the string diff --git a/staticd/static_routes.c b/staticd/static_routes.c index cba38183bb..cbe1c3c8c0 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -33,10 +33,6 @@ void zebra_stable_node_cleanup(struct route_table *table,  	struct static_nexthop *nh;  	struct static_path *pn;  	struct static_route_info *si; -	struct route_table *src_table; -	struct route_node *src_node; -	struct static_path *src_pn; -	struct static_route_info *src_si;  	si = node->info; @@ -50,36 +46,6 @@ void zebra_stable_node_cleanup(struct route_table *table,  			static_path_list_del(&si->path_list, pn);  			XFREE(MTYPE_STATIC_PATH, pn);  		} - -		/* clean up for dst table */ -		src_table = srcdest_srcnode_table(node); -		if (src_table) { -			/* This means the route_node is part of the top -			 * hierarchy and refers to a destination prefix. -			 */ -			for (src_node = route_top(src_table); src_node; -			     src_node = route_next(src_node)) { -				src_si = src_node->info; - -				frr_each_safe(static_path_list, -					      &src_si->path_list, src_pn) { -					frr_each_safe(static_nexthop_list, -						      &src_pn->nexthop_list, -						      nh) { -						static_nexthop_list_del( -							&src_pn->nexthop_list, -							nh); -						XFREE(MTYPE_STATIC_NEXTHOP, nh); -					} -					static_path_list_del(&src_si->path_list, -							     src_pn); -					XFREE(MTYPE_STATIC_PATH, src_pn); -				} - -				XFREE(MTYPE_STATIC_ROUTE, src_node->info); -			} -		} -  		XFREE(MTYPE_STATIC_ROUTE, node->info);  	}  } @@ -124,28 +90,10 @@ struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p,  	return rn;  } -/* To delete the srcnodes */ -static void static_del_src_route(struct route_node *rn) -{ -	struct static_path *pn; -	struct static_route_info *si; - -	si = rn->info; - -	frr_each_safe(static_path_list, &si->path_list, pn) { -		static_del_path(pn); -	} - -	XFREE(MTYPE_STATIC_ROUTE, rn->info); -	route_unlock_node(rn); -} -  void static_del_route(struct route_node *rn)  {  	struct static_path *pn;  	struct static_route_info *si; -	struct route_table *src_table; -	struct route_node *src_node;  	si = rn->info; @@ -153,17 +101,6 @@ void static_del_route(struct route_node *rn)  		static_del_path(pn);  	} -	/* clean up for dst table */ -	src_table = srcdest_srcnode_table(rn); -	if (src_table) { -		/* This means the route_node is part of the top hierarchy -		 * and refers to a destination prefix. -		 */ -		for (src_node = route_top(src_table); src_node; -		     src_node = route_next(src_node)) { -			static_del_src_route(src_node); -		} -	}  	XFREE(MTYPE_STATIC_ROUTE, rn->info);  	route_unlock_node(rn);  } @@ -477,7 +414,7 @@ static void static_fixup_vrf(struct vrf *vrf, struct route_table *stable,  	struct static_path *pn;  	struct static_route_info *si; -	for (rn = route_top(stable); rn; rn = route_next(rn)) { +	for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {  		si = static_route_info_from_rnode(rn);  		if (!si)  			continue; @@ -517,7 +454,7 @@ static void static_enable_vrf(struct route_table *stable, afi_t afi, safi_t safi  	struct static_path *pn;  	struct static_route_info *si; -	for (rn = route_top(stable); rn; rn = route_next(rn)) { +	for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {  		si = static_route_info_from_rnode(rn);  		if (!si)  			continue; @@ -575,7 +512,7 @@ static void static_cleanup_vrf(struct vrf *vrf, struct route_table *stable,  	struct static_path *pn;  	struct static_route_info *si; -	for (rn = route_top(stable); rn; rn = route_next(rn)) { +	for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {  		si = static_route_info_from_rnode(rn);  		if (!si)  			continue; @@ -608,7 +545,7 @@ static void static_disable_vrf(struct route_table *stable,  	struct static_path *pn;  	struct static_route_info *si; -	for (rn = route_top(stable); rn; rn = route_next(rn)) { +	for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {  		si = static_route_info_from_rnode(rn);  		if (!si)  			continue; diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 710827a9ff..78bc30500b 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -51,10 +51,8 @@ struct static_vrf *static_vrf_alloc(const char *name)  	for (afi = AFI_IP; afi <= AFI_IP6; afi++) {  		for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { -			if (afi == AFI_IP6) -				table = srcdest_table_init(); -			else -				table = route_table_init(); +			table = srcdest_table_init(); +			table->cleanup = zebra_stable_node_cleanup;  			info = XCALLOC(MTYPE_STATIC_RTABLE_INFO,  				       sizeof(struct stable_info)); @@ -63,7 +61,6 @@ struct static_vrf *static_vrf_alloc(const char *name)  			info->safi = safi;  			route_table_set_info(table, info); -			table->cleanup = zebra_stable_node_cleanup;  			svrf->stable[afi][safi] = table;  		}  	} diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 2fadc1f0d4..ed2805d3ea 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -79,7 +79,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)  	char xpath_seg[XPATH_MAXLEN];  	char ab_xpath[XPATH_MAXLEN];  	char buf_prefix[PREFIX_STRLEN]; -	char buf_src_prefix[PREFIX_STRLEN] = {}; +	char buf_src_prefix[PREFIX_STRLEN] = "::/0";  	char buf_nh_type[PREFIX_STRLEN] = {};  	char buf_tag[PREFIX_STRLEN];  	uint8_t label_stack_id = 0; @@ -116,6 +116,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)  	}  	assert(!!str2prefix(args->prefix, &p)); +	src = (struct prefix){ .family = p.family, .prefixlen = 0 };  	switch (args->afi) {  	case AFI_IP: @@ -146,7 +147,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)  		return CMD_WARNING_CONFIG_FAILED;  	} -	if (args->source) +	if (src.prefixlen)  		prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix));  	if (args->gateway)  		buf_gate_str = args->gateway; @@ -183,25 +184,10 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)  	static_get_nh_type(type, buf_nh_type, sizeof(buf_nh_type));  	if (!args->delete) { -		if (args->source) -			snprintf(ab_xpath, sizeof(ab_xpath), -				 FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, -				 "frr-staticd:staticd", "staticd", args->vrf, -				 buf_prefix, -				 yang_afi_safi_value2identity(args->afi, -							      args->safi), -				 buf_src_prefix, table_id, buf_nh_type, -				 args->nexthop_vrf, buf_gate_str, -				 args->interface_name); -		else -			snprintf(ab_xpath, sizeof(ab_xpath), -				 FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, -				 "frr-staticd:staticd", "staticd", args->vrf, -				 buf_prefix, -				 yang_afi_safi_value2identity(args->afi, -							      args->safi), -				 table_id, buf_nh_type, args->nexthop_vrf, -				 buf_gate_str, args->interface_name); +		snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, +			 "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, buf_src_prefix, +			 yang_afi_safi_value2identity(args->afi, args->safi), table_id, buf_nh_type, +			 args->nexthop_vrf, buf_gate_str, args->interface_name);  		/*  		 * If there's already the same nexthop but with a different @@ -218,22 +204,9 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)  		}  		/* route + path procesing */ -		if (args->source) -			snprintf(xpath_prefix, sizeof(xpath_prefix), -				 FRR_S_ROUTE_SRC_INFO_KEY_XPATH, -				 "frr-staticd:staticd", "staticd", args->vrf, -				 buf_prefix, -				 yang_afi_safi_value2identity(args->afi, -							      args->safi), -				 buf_src_prefix, table_id, distance); -		else -			snprintf(xpath_prefix, sizeof(xpath_prefix), -				 FRR_STATIC_ROUTE_INFO_KEY_XPATH, -				 "frr-staticd:staticd", "staticd", args->vrf, -				 buf_prefix, -				 yang_afi_safi_value2identity(args->afi, -							      args->safi), -				 table_id, distance); +		snprintf(xpath_prefix, sizeof(xpath_prefix), FRR_STATIC_ROUTE_INFO_KEY_XPATH, +			 "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, buf_src_prefix, +			 yang_afi_safi_value2identity(args->afi, args->safi), table_id, distance);  		nb_cli_enqueue_change(vty, xpath_prefix, NB_OP_CREATE, NULL); @@ -412,51 +385,18 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)  		if (orig_seg)  			XFREE(MTYPE_TMP, orig_seg);  	} else { -		if (args->source) { -			if (args->distance) -				snprintf(ab_xpath, sizeof(ab_xpath), -					 FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH, -					 "frr-staticd:staticd", "staticd", -					 args->vrf, buf_prefix, -					 yang_afi_safi_value2identity( -						 args->afi, args->safi), -					 buf_src_prefix, table_id, distance, -					 buf_nh_type, args->nexthop_vrf, -					 buf_gate_str, args->interface_name); -			else -				snprintf( -					ab_xpath, sizeof(ab_xpath), -					FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, -					"frr-staticd:staticd", "staticd", -					args->vrf, buf_prefix, -					yang_afi_safi_value2identity( -						args->afi, args->safi), -					buf_src_prefix, table_id, buf_nh_type, -					args->nexthop_vrf, buf_gate_str, -					args->interface_name); -		} else { -			if (args->distance) -				snprintf(ab_xpath, sizeof(ab_xpath), -					 FRR_DEL_S_ROUTE_NH_KEY_XPATH, -					 "frr-staticd:staticd", "staticd", -					 args->vrf, buf_prefix, -					 yang_afi_safi_value2identity( -						 args->afi, args->safi), -					 table_id, distance, buf_nh_type, -					 args->nexthop_vrf, buf_gate_str, -					 args->interface_name); -			else -				snprintf( -					ab_xpath, sizeof(ab_xpath), -					FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, -					"frr-staticd:staticd", "staticd", -					args->vrf, buf_prefix, -					yang_afi_safi_value2identity( -						args->afi, args->safi), -					table_id, buf_nh_type, -					args->nexthop_vrf, buf_gate_str, -					args->interface_name); -		} +		if (args->distance) +			snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_XPATH, +				 "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, +				 buf_src_prefix, yang_afi_safi_value2identity(args->afi, args->safi), +				 table_id, distance, buf_nh_type, args->nexthop_vrf, buf_gate_str, +				 args->interface_name); +		else +			snprintf(ab_xpath, sizeof(ab_xpath), +				 FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, "frr-staticd:staticd", +				 "staticd", args->vrf, buf_prefix, buf_src_prefix, +				 yang_afi_safi_value2identity(args->afi, args->safi), table_id, +				 buf_nh_type, args->nexthop_vrf, buf_gate_str, args->interface_name);  		dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath);  		if (!dnode) { @@ -1439,9 +1379,8 @@ static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg)  }  static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, -			     const struct lyd_node *src, -			     const struct lyd_node *path, -			     const struct lyd_node *nexthop, bool show_defaults) +			     const struct lyd_node *path, const struct lyd_node *nexthop, +			     bool show_defaults)  {  	const char *vrf;  	const char *afi_safi; @@ -1455,6 +1394,7 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,  	struct srv6_seg_iter seg_iter;  	const char *nexthop_vrf;  	uint32_t table_id; +	struct prefix src_prefix;  	bool onlink;  	vrf = yang_dnode_get_string(route, "../../vrf"); @@ -1476,9 +1416,9 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,  	vty_out(vty, " %s", yang_dnode_get_string(route, "prefix")); -	if (src) -		vty_out(vty, " from %s", -			yang_dnode_get_string(src, "src-prefix")); +	yang_dnode_get_prefix(&src_prefix, route, "src-prefix"); +	if (src_prefix.prefixlen) +		vty_out(vty, " from %pFX", &src_prefix);  	nh_type = yang_dnode_get_enum(nexthop, "nh-type");  	switch (nh_type) { @@ -1582,18 +1522,7 @@ static void static_nexthop_cli_show(struct vty *vty,  	const struct lyd_node *route =  		yang_dnode_get_parent(path, "route-list"); -	nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults); -} - -static void static_src_nexthop_cli_show(struct vty *vty, -					const struct lyd_node *dnode, -					bool show_defaults) -{ -	const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); -	const struct lyd_node *src = yang_dnode_get_parent(path, "src-list"); -	const struct lyd_node *route = yang_dnode_get_parent(src, "route-list"); - -	nexthop_cli_show(vty, route, src, path, dnode, show_defaults); +	nexthop_cli_show(vty, route, path, dnode, show_defaults);  }  static int static_nexthop_cli_cmp(const struct lyd_node *dnode1, @@ -1658,6 +1587,8 @@ static int static_route_list_cli_cmp(const struct lyd_node *dnode1,  	afi_t afi1, afi2;  	safi_t safi1, safi2;  	struct prefix prefix1, prefix2; +	struct prefix src_prefix1, src_prefix2; +	int rv;  	afi_safi1 = yang_dnode_get_string(dnode1, "afi-safi");  	yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1); @@ -1673,19 +1604,13 @@ static int static_route_list_cli_cmp(const struct lyd_node *dnode1,  	yang_dnode_get_prefix(&prefix1, dnode1, "prefix");  	yang_dnode_get_prefix(&prefix2, dnode2, "prefix"); +	rv = prefix_cmp(&prefix1, &prefix2); +	if (rv) +		return rv; -	return prefix_cmp(&prefix1, &prefix2); -} - -static int static_src_list_cli_cmp(const struct lyd_node *dnode1, -				   const struct lyd_node *dnode2) -{ -	struct prefix prefix1, prefix2; - -	yang_dnode_get_prefix(&prefix1, dnode1, "src-prefix"); -	yang_dnode_get_prefix(&prefix2, dnode2, "src-prefix"); - -	return prefix_cmp(&prefix1, &prefix2); +	yang_dnode_get_prefix(&src_prefix1, dnode1, "src-prefix"); +	yang_dnode_get_prefix(&src_prefix2, dnode2, "src-prefix"); +	return prefix_cmp(&src_prefix1, &src_prefix2);  }  static int static_path_list_cli_cmp(const struct lyd_node *dnode1, @@ -1831,25 +1756,6 @@ const struct frr_yang_module_info frr_staticd_cli_info = {  			}  		},  		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", -			.cbs = { -				.cli_cmp = static_src_list_cli_cmp, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", -			.cbs = { -				.cli_cmp = static_path_list_cli_cmp, -			} -		}, -		{ -			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", -			.cbs = { -				.cli_show = static_src_nexthop_cli_show, -				.cli_cmp = static_nexthop_cli_cmp, -			} -		}, -		{  			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing",  			.cbs = {  				.cli_show = static_segment_routing_cli_show, diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index e87eaed008..057193aa08 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -132,35 +132,37 @@ static int static_ifp_down(struct interface *ifp)  static int route_notify_owner(ZAPI_CALLBACK_ARGS)  { -	struct prefix p; +	struct prefix p, src_p, *src_pp;  	enum zapi_route_notify_owner note;  	uint32_t table_id;  	safi_t safi; -	if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, ¬e, NULL, -				      &safi)) +	if (!zapi_route_notify_decode_srcdest(zclient->ibuf, &p, &src_p, &table_id, ¬e, NULL, +					      &safi))  		return -1; +	src_pp = src_p.prefixlen ? &src_p : NULL; +  	switch (note) {  	case ZAPI_ROUTE_FAIL_INSTALL: -		static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); +		static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED);  		zlog_warn("%s: Route %pFX failed to install for table: %u",  			  __func__, &p, table_id);  		break;  	case ZAPI_ROUTE_BETTER_ADMIN_WON: -		static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); +		static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED);  		zlog_warn(  			"%s: Route %pFX over-ridden by better route for table: %u",  			__func__, &p, table_id);  		break;  	case ZAPI_ROUTE_INSTALLED: -		static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED); +		static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_INSTALLED);  		break;  	case ZAPI_ROUTE_REMOVED: -		static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); +		static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED);  		break;  	case ZAPI_ROUTE_REMOVE_FAIL: -		static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED); +		static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_INSTALLED);  		zlog_warn("%s: Route %pFX failure to remove for table: %u",  			  __func__, &p, table_id);  		break; @@ -226,8 +228,8 @@ static void static_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched,  		nhtd->nh_num = nhr->nexthop_num;  		static_nht_reset_start(matched, afi, nhr->safi, nhtd->nh_vrf_id); -		static_nht_update(NULL, matched, nhr->nexthop_num, afi, -				  nhr->safi, nhtd->nh_vrf_id); +		static_nht_update(NULL, NULL, matched, nhr->nexthop_num, afi, nhr->safi, +				  nhtd->nh_vrf_id);  	} else  		zlog_err("No nhtd?");  } @@ -312,10 +314,13 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg)  {  	struct static_path *pn = nh->pn;  	struct route_node *rn = pn->rn; +	const struct prefix *p, *src_p;  	struct static_route_info *si = static_route_info_from_rnode(rn);  	struct static_nht_data *nhtd, lookup = {};  	uint32_t cmd; +	srcdest_rnode_prefixes(rn, &p, &src_p); +  	if (!static_zebra_nht_get_prefix(nh, &lookup.nh))  		return;  	lookup.nh_vrf_id = nh->nh_vrf_id; @@ -351,8 +356,8 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg)  			if (nh->state == STATIC_NOT_INSTALLED ||  			    nh->state == STATIC_SENT_TO_ZEBRA)  				nh->state = STATIC_START; -			static_nht_update(&rn->p, &nhtd->nh, nhtd->nh_num, afi, -					  si->safi, nh->nh_vrf_id); +			static_nht_update(p, src_p, &nhtd->nh, nhtd->nh_num, afi, si->safi, +					  nh->nh_vrf_id);  			return;  		} diff --git a/tests/topotests/bgp_aggregate_address_topo1/r1/bgp_192_168_0_1.json b/tests/topotests/bgp_aggregate_address_topo1/r1/bgp_192_168_0_1.json new file mode 100644 index 0000000000..8c0da8dc92 --- /dev/null +++ b/tests/topotests/bgp_aggregate_address_topo1/r1/bgp_192_168_0_1.json @@ -0,0 +1,41 @@ +{ +  "prefix":"192.168.0.1/32", +  "paths":[ +    { +      "aspath":{ +        "string":"65001", +        "segments":[ +          { +            "type":"as-sequence", +            "list":[ +              65001 +            ] +          } +        ], +        "length":1 +      }, +      "suppressed":true, +      "origin":"IGP", +      "metric":10, +      "valid":true, +      "bestpath":{ +        "overall":true, +        "selectionReason":"First path received" +      }, +      "nexthops":[ +        { +          "ip":"10.0.0.2", +          "afi":"ipv4", +          "metric":0, +          "accessible":true, +          "used":true +        } +      ], +      "peer":{ +        "peerId":"10.0.0.2", +        "routerId":"10.254.254.3", +        "type":"external" +      } +    } +  ] +} diff --git a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py index 370d01e525..a0a1027c98 100644 --- a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py +++ b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py @@ -13,6 +13,7 @@  Test BGP aggregate address features.  """ +import json  import os  import sys  import pytest @@ -265,6 +266,24 @@ match ip address acl-sup-three      ) +def test_check_bgp_attribute(): +    "Dump the suppressed attribute of the 192.168.0.1/32 prefix in r1." +    tgen = get_topogen() + +    logger.info("Test that the BGP path to 192.168.0.1 is as expected.") +    expected = json.loads(open("{}/r1/bgp_192_168_0_1.json".format(CWD)).read()) + +    test_func = functools.partial( +        topotest.router_json_cmp, +        tgen.gears["r1"], +        "show bgp ipv4 192.168.0.1/32 json", +        expected, +    ) +    _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) +    assertmsg = '"r1" BGP 192.168.0.1 route output failed' +    assert result is None, assertmsg + +  def test_memory_leak():      "Run the memory leak test and report results."      tgen = get_topogen() diff --git a/tests/topotests/bgp_dynamic_capability/r2/frr.conf b/tests/topotests/bgp_dynamic_capability/r2/frr.conf index 621e9381e3..cca07078ea 100644 --- a/tests/topotests/bgp_dynamic_capability/r2/frr.conf +++ b/tests/topotests/bgp_dynamic_capability/r2/frr.conf @@ -18,7 +18,6 @@ router bgp 65002   neighbor 192.168.1.1 timers connect 1   neighbor 192.168.1.1 capability dynamic   neighbor 192.168.1.1 capability extended-nexthop - neighbor 192.168.1.1 addpath-rx-paths-limit 20   neighbor 2001:db8::1 remote-as external   neighbor 2001:db8::1 timers 1 3   neighbor 2001:db8::1 timers connect 1 @@ -27,6 +26,9 @@ router bgp 65002   !   address-family ipv4 unicast    redistribute connected +  neighbor 192.168.1.1 addpath-tx-all-paths +  neighbor 192.168.1.1 disable-addpath-rx +  neighbor 192.168.1.1 addpath-rx-paths-limit 20   exit-address-family   !   address-family ipv6 unicast diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_path_limit.py index 91df89b1b5..22e4fe687b 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_path_limit.py @@ -9,8 +9,6 @@  Test if Addpath/Paths-Limit capabilities are adjusted dynamically.  T1: Enable Addpath/Paths-Limit capabilities and check if they are exchanged dynamically  T2: Disable paths limit and check if it's exchanged dynamically -T3: Disable Addpath capability RX and check if it's exchanged dynamically -T4: Disable Addpath capability and check if it's exchanged dynamically  """  import os @@ -65,12 +63,12 @@ def test_bgp_addpath_paths_limit():                      "dynamic": "advertisedAndReceived",                      "addPath": {                          "ipv4Unicast": { -                            "txAdvertisedAndReceived": False, +                            "txAdvertisedAndReceived": True,                              "txAdvertised": True, -                            "txReceived": False, -                            "rxAdvertisedAndReceived": True, +                            "txReceived": True, +                            "rxAdvertisedAndReceived": False,                              "rxAdvertised": True, -                            "rxReceived": True, +                            "rxReceived": False,                          }                      },                      "pathsLimit": { @@ -105,7 +103,6 @@ def test_bgp_addpath_paths_limit():      configure terminal       router bgp        address-family ipv4 unicast -       neighbor 192.168.1.1 addpath-tx-all-paths         neighbor 192.168.1.1 addpath-rx-paths-limit 21      """      ) @@ -122,9 +119,9 @@ def test_bgp_addpath_paths_limit():                              "txAdvertisedAndReceived": True,                              "txAdvertised": True,                              "txReceived": True, -                            "rxAdvertisedAndReceived": True, +                            "rxAdvertisedAndReceived": False,                              "rxAdvertised": True, -                            "rxReceived": True, +                            "rxReceived": False,                          }                      },                      "pathsLimit": { @@ -143,7 +140,7 @@ def test_bgp_addpath_paths_limit():                  "messageStats": {                      "notificationsRecv": 0,                      "notificationsSent": 0, -                    "capabilityRecv": 2, +                    "capabilityRecv": 1,                  },              }          } @@ -181,58 +178,6 @@ def test_bgp_addpath_paths_limit():                              "txAdvertisedAndReceived": True,                              "txAdvertised": True,                              "txReceived": True, -                            "rxAdvertisedAndReceived": True, -                            "rxAdvertised": True, -                            "rxReceived": True, -                        } -                    }, -                    "pathsLimit": { -                        "ipv4Unicast": { -                            "advertisedAndReceived": True, -                            "advertisedPathsLimit": 10, -                            "receivedPathsLimit": 0, -                        } -                    }, -                }, -                "messageStats": { -                    "notificationsRecv": 0, -                    "notificationsSent": 0, -                    "capabilityRecv": 3, -                }, -            } -        } -        return topotest.json_cmp(output, expected) - -    test_func = functools.partial( -        _disable_paths_limit, -    ) -    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) -    assert result is None, "Something went wrong after disabling paths limit" - -    ### -    # T3: Disable Addpath capability RX and check if it's exchanged dynamically -    ### -    r2.vtysh_cmd( -        """ -    configure terminal -    router bgp -     address-family ipv4 unicast -      neighbor 192.168.1.1 disable-addpath-rx -    """ -    ) - -    def _disable_addpath_rx(): -        output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) -        expected = { -            "192.168.1.2": { -                "bgpState": "Established", -                "neighborCapabilities": { -                    "dynamic": "advertisedAndReceived", -                    "addPath": { -                        "ipv4Unicast": { -                            "txAdvertisedAndReceived": True, -                            "txAdvertised": True, -                            "txReceived": True,                              "rxAdvertisedAndReceived": False,                              "rxAdvertised": True,                              "rxReceived": False, @@ -249,63 +194,17 @@ def test_bgp_addpath_paths_limit():                  "messageStats": {                      "notificationsRecv": 0,                      "notificationsSent": 0, -                    "capabilityRecv": 4, -                }, -            } -        } -        return topotest.json_cmp(output, expected) - -    test_func = functools.partial( -        _disable_addpath_rx, -    ) -    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) -    assert result is None, "Something went wrong after disabling Addpath RX flags" - -    ### -    # T4: Disable Addpath capability and check if it's exchanged dynamically -    ### -    r1.vtysh_cmd( -        """ -    configure terminal -    router bgp -     address-family ipv4 unicast -      no neighbor 192.168.1.2 addpath-tx-all-paths -    """ -    ) - -    def _disable_addpath(): -        output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) -        expected = { -            "192.168.1.2": { -                "bgpState": "Established", -                "neighborCapabilities": { -                    "dynamic": "advertisedAndReceived", -                    "addPath": { -                        "ipv4Unicast": { -                            "txAdvertisedAndReceived": False, -                            "txAdvertised": False, -                            "txReceived": True, -                            "rxAdvertisedAndReceived": False, -                            "rxAdvertised": True, -                            "rxReceived": False, -                        } -                    }, -                }, -                "messageStats": { -                    "notificationsRecv": 0, -                    "notificationsSent": 0, -                    "capabilitySent": 1, -                    "capabilityRecv": 4, +                    "capabilityRecv": 2,                  },              }          }          return topotest.json_cmp(output, expected)      test_func = functools.partial( -        _disable_addpath, +        _disable_paths_limit,      )      _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) -    assert result is None, "Something went wrong when disabling Addpath capability" +    assert result is None, "Something went wrong after disabling paths limit"  if __name__ == "__main__": diff --git a/tests/topotests/bgp_table_direct_topo1/__init__.py b/tests/topotests/bgp_table_direct_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/__init__.py diff --git a/tests/topotests/bgp_table_direct_topo1/r1/frr.conf b/tests/topotests/bgp_table_direct_topo1/r1/frr.conf new file mode 100644 index 0000000000..c45e3456a4 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r1/frr.conf @@ -0,0 +1,31 @@ +log commands +! +debug bgp zebra +debug zebra events +! +ip route 10.254.254.1/32 lo table 2000 +ip route 10.254.254.2/32 lo table 2000 +ip route 10.254.254.3/32 lo table 2000 +! +interface r1-eth0 + ip address 192.168.10.1/24 +! +interface r1-eth1 vrf blue + ip address 192.168.20.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.10.2 remote-as external + address-family ipv4 unicast +  redistribute table-direct 2000 + exit-address-family +! +router bgp 65001 vrf blue + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.20.2 remote-as external + address-family ipv4 unicast +  redistribute table-direct 2000 + exit-address-family +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/r2/frr.conf b/tests/topotests/bgp_table_direct_topo1/r2/frr.conf new file mode 100644 index 0000000000..04787be0b3 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r2/frr.conf @@ -0,0 +1,10 @@ +log commands +! +interface r2-eth0 + ip address 192.168.10.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.10.1 remote-as external +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/r3/frr.conf b/tests/topotests/bgp_table_direct_topo1/r3/frr.conf new file mode 100644 index 0000000000..2530b28bfd --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r3/frr.conf @@ -0,0 +1,10 @@ +log commands +! +interface r3-eth0 + ip address 192.168.20.2/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.20.1 remote-as external +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py b/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py new file mode 100644 index 0000000000..70257be3e7 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_table_direct_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2025 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_table_direct_topo1.py: Test the FRR PIM MSDP peer. +""" + +import os +import sys +import json +from functools import partial +import re +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +from lib.pim import McastTesterHelper + +pytestmark = [pytest.mark.bgpd, pytest.mark.pimd] + +app_helper = McastTesterHelper() + + +def build_topo(tgen): +    """ +    +----+     +----+ +    | r1 | <-> | r2 | +    +----+     +----+ +       | +       |       +----+ +       --------| r3 | +               +----+ +    """ + +    # Create 3 routers +    for routern in range(1, 4): +        tgen.add_router(f"r{routern}") + +    switch = tgen.add_switch("s1") +    switch.add_link(tgen.gears["r1"]) +    switch.add_link(tgen.gears["r2"]) + +    switch = tgen.add_switch("s2") +    switch.add_link(tgen.gears["r1"]) +    switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): +    "Sets up the pytest environment" +    tgen = Topogen(build_topo, mod.__name__) +    tgen.start_topology() + +    router_list = tgen.routers() +    for _, router in router_list.items(): +        router.load_frr_config(os.path.join(CWD, f"{router.name}/frr.conf")) + +    tgen.gears["r1"].run("ip link add blue type vrf table 10") +    tgen.gears["r1"].run("ip link set blue up") +    tgen.gears["r1"].run("ip link set r1-eth1 master blue") + +    # Initialize all routers. +    tgen.start_router() + +    app_helper.init(tgen) + + +def teardown_module(): +    "Teardown the pytest environment" +    tgen = get_topogen() +    app_helper.cleanup() +    tgen.stop_topology() + + +def expect_bgp_route(router, iptype, route, missing=False): +    "Wait until route is present on RIB for protocol." +    if missing: +        logger.info("waiting route {} go missing in {}".format(route, router)) +    else: +        logger.info("waiting route {} in {}".format(route, router)) + +    tgen = get_topogen() +    expected_output = {route: [{"protocol": "bgp"}]} +    wait_time = 130 +    if missing: +        expected_output = {route: None} +        wait_time = 5 + +    test_func = partial( +        topotest.router_json_cmp, +        tgen.gears[router], +        "show {} route json".format(iptype), +        expected_output +    ) + +    _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) +    assertmsg = f'"{router}" convergence failure' +    assert result is None, assertmsg + + +def test_bgp_convergence(): +    "Wait for BGP protocol convergence" +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    logger.info("waiting for protocols to converge") + +    # Wait for R2 +    expect_bgp_route("r2", "ip", "10.254.254.1/32") +    expect_bgp_route("r2", "ip", "10.254.254.2/32") +    expect_bgp_route("r2", "ip", "10.254.254.3/32") + +    # Wait for R3 +    expect_bgp_route("r3", "ip", "10.254.254.1/32") +    expect_bgp_route("r3", "ip", "10.254.254.2/32") +    expect_bgp_route("r3", "ip", "10.254.254.3/32") + + +def test_route_change_convergence(): +    "Change routes in table 2000 to test zebra redistribution." +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    tgen.gears["r1"].vtysh_cmd(""" +        configure terminal +        no ip route 10.254.254.2/32 lo table 2000 +        ip route 10.254.254.10/32 lo table 2000 +    """) + +    # Check R2 +    expect_bgp_route("r2", "ip", "10.254.254.2/32", missing=True) +    expect_bgp_route("r2", "ip", "10.254.254.10/32") + +    # Check R3 +    expect_bgp_route("r3", "ip", "10.254.254.2/32", missing=True) +    expect_bgp_route("r3", "ip", "10.254.254.10/32") + + +def test_configuration_removal_convergence(): +    "Remove table direct configuration and check if routes went missing." +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    tgen.gears["r1"].vtysh_cmd(""" +        configure terminal +        router bgp 65001 +         address-family ipv4 unicast +          no redistribute table-direct 2000 +         exit-address-family +        exit + +        router bgp 65001 vrf blue +         address-family ipv4 unicast +          no redistribute table-direct 2000 +         exit-address-family +        exit +    """) + +    # Check R2 +    expect_bgp_route("r2", "ip", "10.254.254.1/32", missing=True) +    expect_bgp_route("r2", "ip", "10.254.254.3/32", missing=True) +    expect_bgp_route("r2", "ip", "10.254.254.10/32", missing=True) + +    # Check R3 +    expect_bgp_route("r3", "ip", "10.254.254.1/32", missing=True) +    expect_bgp_route("r3", "ip", "10.254.254.3/32", missing=True) +    expect_bgp_route("r3", "ip", "10.254.254.10/32", missing=True) + + +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_table_map/r1/frr.conf b/tests/topotests/bgp_table_map/r1/frr.conf new file mode 100644 index 0000000000..f74440c384 --- /dev/null +++ b/tests/topotests/bgp_table_map/r1/frr.conf @@ -0,0 +1,22 @@ +! +int r1-eth0 + ip address 10.255.0.1/24 +! +access-list AccList seq 5 permit 10.0.0.1/32 +! +route-map TableMap permit 10 + match ip address AccList +exit +! +router bgp 65001 + bgp router-id 10.255.0.1 + no bgp ebgp-requires-policy + neighbor 10.255.0.2 remote-as external + neighbor 10.255.0.2 timers 1 3 + neighbor 10.255.0.2 timers connect 1 + ! + address-family ipv4 unicast +  table-map TableMap + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/r2/frr.conf b/tests/topotests/bgp_table_map/r2/frr.conf new file mode 100644 index 0000000000..4523fe49ea --- /dev/null +++ b/tests/topotests/bgp_table_map/r2/frr.conf @@ -0,0 +1,18 @@ +! +int r2-eth0 + ip address 10.255.0.2/24 +! +router bgp 65002 + bgp router-id 10.255.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.255.0.1 remote-as external + neighbor 10.255.0.1 timers 1 3 + neighbor 10.255.0.1 timers connect 1 + ! + address-family ipv4 unicast +  network 10.0.0.1/32 +  network 10.0.0.2/32 + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/test_bgp_table_map.py b/tests/topotests/bgp_table_map/test_bgp_table_map.py new file mode 100644 index 0000000000..b10680f741 --- /dev/null +++ b/tests/topotests/bgp_table_map/test_bgp_table_map.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +import functools, json, os, pytest, re, sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +from lib import topotest +from lib.topogen import Topogen, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): +    topodef = {"s1": ("r1", "r2")} +    tgen = Topogen(topodef, mod.__name__) +    tgen.start_topology() + +    router_list = tgen.routers() + +    for _, (rname, router) in enumerate(router_list.items(), 1): +        router.load_frr_config( +            os.path.join(CWD, "{}/frr.conf".format(rname)) +        ) + +    tgen.start_router() + + +def teardown_module(mod): +    tgen = get_topogen() +    tgen.stop_topology() + + +def test_bgp_table_map(): +    tgen = get_topogen() + +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    r1 = tgen.gears["r1"] + +    def _bgp_converge(): +        output = json.loads( +            r1.vtysh_cmd( "show bgp ipv4 unicast summary json") +        ) +        expected = { +            "peers": { +                "10.255.0.2": { +                    "remoteAs": 65002, +                    "state": "Established", +                    "peerState": "OK", +                }, +            }, +            "totalPeers": 1, +        } + +        return topotest.json_cmp(output, expected) + +    test_func = functools.partial( +        _bgp_converge, +    ) +    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) +    assert result is None, "Can't converge initial state" + +    def _bgp_with_table_map(): +        output = json.loads(r1.vtysh_cmd("show ip fib json")) +        expected = { +            "10.0.0.1/32": [], +            "10.0.0.2/32": None, +        } + +        return topotest.json_cmp(output, expected) + +    test_func = functools.partial( +        _bgp_with_table_map, +    ) +    _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) +    assert result is None, "Should contain only one of two shared networks" + +    # +    # Unset table-map +    # +    r1.vtysh_cmd( +        """ +        configure terminal +          router bgp 65001 +            address-family ipv4 unicast +              no table-map TableMap +    """ +    ) + +    def _bgp_without_table_map(): +        output = json.loads(r1.vtysh_cmd("show ip fib json")) +        expected = { +            "10.0.0.1/32": [], +            "10.0.0.2/32": [], +        } + +        return topotest.json_cmp(output, expected) + +    test_func = functools.partial( +        _bgp_without_table_map, +    ) +    _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) +    assert result is None, "Shouldn't contain both shared routes" + +    # +    # Reset table-map +    # +    r1.vtysh_cmd( +        """ +        configure terminal +          router bgp 65001 +            address-family ipv4 unicast +              table-map TableMap +        """ +    ) + +    test_func = functools.partial( +        _bgp_with_table_map, +    ) +    _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) +    assert result is None, "Should contain only one of two shared networks" + + +if __name__ == "__main__": +    args = ["-s"] + sys.argv[1:] +    sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_advertised_10_125_0_2.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_advertised_10_125_0_2.json new file mode 100644 index 0000000000..7891982653 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_advertised_10_125_0_2.json @@ -0,0 +1,105 @@ +{ +    "bgpLocalRouterId":"192.0.2.1", +    "defaultLocPrf":100, +    "localAS":65500, +    "advertisedRoutes":{ +        "192.0.2.1:1":{ +            "rd":"192.0.2.1:1", +            "10.101.0.0/24":{ +                "prefix":"10.101.0.0/24", +                "advertisedTo":{ +                    "10.125.0.2":{ +                        "hostname":"r2" +                    } +                }, +                "paths":[{ +                    "aspath":{ +                        "string":"Local", +                        "segments":[], +                        "length":0 +                    }, +                    "nhVrfName":"vrf1", +                    "announceNexthopSelf":true, +                    "origin":"incomplete", +                    "metric":0, +                    "locPrf":100, +                    "weight":32768, +                    "valid":true, +                    "sourced":true, +                    "local":true, +                    "bestpath":{ +                        "overall":true, +                        "selectionReason":"First path received" +                    }, +                    "extendedCommunity":{ +                        "string":"RT:192.0.2.1:100" +                    }, +                    "originatorId":"192.0.2.1", +                    "remoteLabel":101, +                    "nexthops":[{ +                        "ip":"0.0.0.0", +                        "hostname":"r1", +                        "afi":"ipv4", +                        "metric":0, +                        "accessible":true, +                        "used":true +                    }], +                    "peer":{ +                        "peerId":"0.0.0.0", +                        "routerId":"192.0.2.1" +                    } +                }] +            } +        }, +        "192.0.2.1:3":{ +            "rd":"192.0.2.1:3", +            "10.103.0.0/24":{ +                "prefix":"10.103.0.0/24", +                "advertisedTo":{ +                    "10.125.0.2":{ +                        "hostname":"r2" +                    } +                }, +                "paths":[{ +                    "aspath":{ +                        "string":"Local", +                        "segments":[], +                        "length":0 +                    }, +                    "nhVrfName":"vrf3", +                    "announceNexthopSelf":true, +                    "origin":"incomplete", +                    "metric":0, +                    "locPrf":100, +                    "weight":32768, +                    "valid":true, +                    "sourced":true, +                    "local":true, +                    "bestpath":{ +                        "overall":true, +                        "selectionReason":"First path received" +                    }, +                    "extendedCommunity":{ +                        "string":"RT:192.0.2.1:300" +                    }, +                    "originatorId":"192.0.2.1", +                    "remoteLabel":103, +                    "nexthops":[{ +                        "ip":"0.0.0.0", +                        "hostname":"r1", +                        "afi":"ipv4", +                        "metric":0, +                        "accessible":true, +                        "used":true +                    }], +                    "peer":{ +                        "peerId":"0.0.0.0", +                        "routerId":"192.0.2.1" +                    } +                }] +            } +        } +    }, +    "totalPrefixCounter":2, +    "filteredPrefixCounter":0 +} diff --git a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py index ee84e375fb..ada37c28c1 100644 --- a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py +++ b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py @@ -218,6 +218,29 @@ def check_show_bgp_ipv4_vpn(rname, json_file):      assert result is None, assertmsg +def check_show_bgp_ipv4_vpn_peer_advertised_routes(rname, peer, json_file): +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) +    router = tgen.gears[rname] + +    logger.info( +        "Checking VPNv4 advertised routes for on {} for peer {}".format(rname, peer) +    ) + +    json_file = "{}/{}/{}".format(CWD, router.name, json_file) +    expected = json.loads(open(json_file).read()) +    test_func = partial( +        topotest.router_json_cmp, +        router, +        "show bgp ipv4 vpn neighbors {} advertised-routes detail json".format(peer), +        expected, +    ) +    _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) +    assertmsg = '"{}" JSON output mismatches'.format(router.name) +    assert result is None, assertmsg + +  def check_show_bgp_vrf_ipv4(rname, json_file):      tgen = get_topogen()      if tgen.routers_have_failure(): @@ -563,6 +586,21 @@ router bgp 65500      check_show_bgp_vrf_ipv4(rname, "ipv4_vrf_all_routes_init.json") +def test_bgp_advertised_routes_step13(): +    """ +    Dump advertised routes from r1 to r2 +    Check that the localpref attribute is set on the show command +    """ + +    tgen = get_topogen() +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    check_show_bgp_ipv4_vpn_peer_advertised_routes( +        "r1", "10.125.0.2", "ipv4_vpn_routes_advertised_10_125_0_2.json" +    ) + +  def test_memory_leak():      "Run the memory leak test and report results."      tgen = get_topogen() diff --git a/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py index 9c1a23f54f..d17b4702f7 100644 --- a/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py +++ b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py @@ -245,7 +245,7 @@ def check_ping6(name, dest_addr, expect_connected):          if match not in output:              return "ping fail" -    match = "{} packet loss".format("0%" if expect_connected else "100%") +    match = "{} packet loss".format(", 0%" if expect_connected else ", 100%")      logger.info("[+] check {} {} {}".format(name, dest_addr, match))      tgen = get_topogen()      func = functools.partial(_check, name, dest_addr, match) @@ -333,7 +333,7 @@ def test_ping_step1():      # Setup encap route on rt1, decap route on rt2      tgen.gears["rt1"].vtysh_cmd( -        "sharp install seg6-routes fc00:0:9::1 nexthop-seg6 2001:db8:1::2 encap fc00:0:1:2:6:f00d:: 1" +        "sharp install seg6-routes fc00:0:9::1 nexthop-seg6 2001:db8:1::2 encap fc00:0:2:6:f00d:: 1"      )      tgen.gears["rt6"].vtysh_cmd(          "sharp install seg6local-routes fc00:0:f00d:: nexthop-seg6local eth-dst End_DT6 254 1" @@ -443,7 +443,8 @@ def test_ping_step2():      if tgen.routers_have_failure():          pytest.skip(tgen.errors) -    check_ping6("rt1", "fc00:0:9::1", False) +    # ping should pass because route to fc00:0:2:6:f00d:: is still valid +    check_ping6("rt1", "fc00:0:9::1", True)  # @@ -643,7 +644,8 @@ def test_ping_step4():      if tgen.routers_have_failure():          pytest.skip(tgen.errors) -    check_ping6("rt1", "fc00:0:9::1", False) +    # ping should pass because route to fc00:0:2:6:f00d:: is still valid +    check_ping6("rt1", "fc00:0:9::1", True)  # @@ -838,7 +840,8 @@ def test_ping_step6():      if tgen.routers_have_failure():          pytest.skip(tgen.errors) -    check_ping6("rt1", "fc00:0:9::1", False) +    # ping should pass because route to fc00:0:2:6:f00d:: is still valid +    check_ping6("rt1", "fc00:0:9::1", True)  # @@ -1033,7 +1036,8 @@ def test_ping_step8():      if tgen.routers_have_failure():          pytest.skip(tgen.errors) -    check_ping6("rt1", "fc00:0:9::1", False) +    # ping should pass because route to fc00:0:2:6:f00d:: is still valid +    check_ping6("rt1", "fc00:0:9::1", True)  # diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py index 52f6ba4db7..7b74eab6b7 100644 --- a/tests/topotests/mgmt_tests/test_yang_mgmt.py +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -181,7 +181,7 @@ def test_mgmt_commit_check(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",                  "mgmt commit check",              ]          } @@ -194,7 +194,7 @@ def test_mgmt_commit_check(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",                  "mgmt commit check",              ]          } @@ -245,7 +245,7 @@ def test_mgmt_commit_apply(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",                  "mgmt commit apply",              ]          } @@ -258,7 +258,7 @@ def test_mgmt_commit_apply(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",                  "mgmt commit apply",              ]          } @@ -298,7 +298,7 @@ def test_mgmt_commit_abort(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",                  "mgmt commit abort",              ]          } @@ -350,7 +350,7 @@ def test_mgmt_delete_config(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default",                  "mgmt commit apply",              ]          } @@ -381,7 +381,7 @@ def test_mgmt_delete_config(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']", +                "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']",                  "mgmt commit apply",              ]          } @@ -657,7 +657,7 @@ def test_mgmt_chaos_stop_start_frr(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",                  "mgmt commit apply",              ]          } @@ -689,7 +689,7 @@ def test_mgmt_chaos_stop_start_frr(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt  delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']", +                "mgmt  delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']",                  "mgmt commit apply",              ]          } @@ -733,7 +733,7 @@ def test_mgmt_chaos_kill_daemon(request):      raw_config = {          "r1": {              "raw_config": [ -                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", +                "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec",                  "mgmt commit apply",              ]          } diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py index 8d91826022..a32b82c7f4 100755 --- a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py +++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py @@ -638,12 +638,6 @@ def pre_config_for_source_dr_tests(                          "interfaceName": "r5-r4-eth1",                          "weight": 1,                      }, -                    { -                        "ip": "10.0.3.1", -                        "afi": "ipv4", -                        "interfaceName": "r5-r4-eth1", -                        "weight": 1, -                    },                  ],              }          ] diff --git a/tests/topotests/ospf_prune_next_hop/r1/frr.conf b/tests/topotests/ospf_prune_next_hop/r1/frr.conf new file mode 100644 index 0000000000..130872e8d0 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r1/frr.conf @@ -0,0 +1,23 @@ +! +hostname r1 +ip forwarding +! +interface r1-eth0 + ip address 10.1.1.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r1-eth1 + ip address 10.1.2.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf +  ospf router-id 1.1.1.1 +  distance 20 +  network 10.1.1.0/24 area 0 +  network 10.1.2.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r2/frr.conf b/tests/topotests/ospf_prune_next_hop/r2/frr.conf new file mode 100644 index 0000000000..4268aea857 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r2/frr.conf @@ -0,0 +1,23 @@ +! +hostname r2 +ip forwarding +! +interface r2-eth0 + ip address 10.1.1.2/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip address 10.1.2.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf +  ospf router-id 2.2.2.2 +  distance 20 +  network 10.1.1.0/24 area 0 +  network 10.1.2.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r3/frr.conf b/tests/topotests/ospf_prune_next_hop/r3/frr.conf new file mode 100644 index 0000000000..21d6506d7c --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r3/frr.conf @@ -0,0 +1,35 @@ +! +hostname r3 +ip forwarding +! +interface r3-eth0 + ip address 20.1.3.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r3-eth1 + ip address 10.1.3.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r3-eth2 + ip address 10.1.2.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf +  ospf router-id 3.3.3.3 +  distance 20 +  network 10.1.2.0/24 area 0 +  network 10.1.3.0/24 area 0 +  network 20.1.3.0/24 area 1 +  area 1 range 20.1.0.0/16 +  redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r4/frr.conf b/tests/topotests/ospf_prune_next_hop/r4/frr.conf new file mode 100644 index 0000000000..e66e93e20c --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r4/frr.conf @@ -0,0 +1,34 @@ +! +hostname r4 +ip forwarding +! +interface r4-eth0 + ip address 20.1.4.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r4-eth1 + ip address 10.1.3.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r4-eth2 + ip address 10.1.2.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf +  ospf router-id 4.4.4.4 +  distance 20 +  network 10.1.2.0/24 area 0 +  network 10.1.3.0/24 area 0 +  network 20.1.4.0/24 area 1 +  area 1 range 20.1.0.0/16 +  redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r5/frr.conf b/tests/topotests/ospf_prune_next_hop/r5/frr.conf new file mode 100644 index 0000000000..2d1dad9925 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r5/frr.conf @@ -0,0 +1,34 @@ +! +hostname r5 +ip forwarding +! +interface r5-eth0 + ip address 20.1.5.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r5-eth1 + ip address 10.1.3.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r5-eth2 + ip address 10.1.2.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf +  ospf router-id 5.5.5.5 +  distance 20 +  network 10.1.2.0/24 area 0 +  network 10.1.3.0/24 area 0 +  network 20.1.5.0/24 area 1 +  area 1 range 20.1.0.0/16 +  redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r6/frr.conf b/tests/topotests/ospf_prune_next_hop/r6/frr.conf new file mode 100644 index 0000000000..f343ee7c35 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r6/frr.conf @@ -0,0 +1,34 @@ +! +hostname r6 +ip forwarding +! +interface r6-eth0 + ip address 20.1.6.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r6-eth1 + ip address 10.1.3.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r6-eth2 + ip address 10.1.2.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf +  ospf router-id 6.6.6.6 +  distance 20 +  network 10.1.2.0/24 area 0 +  network 10.1.3.0/24 area 0 +  network 20.1.6.0/24 area 1 +  area 1 range 20.1.0.0/16 +  redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r7/frr.conf b/tests/topotests/ospf_prune_next_hop/r7/frr.conf new file mode 100644 index 0000000000..1eeb88c9d0 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r7/frr.conf @@ -0,0 +1,14 @@ +! +hostname r7 +ip forwarding +! +interface r7-eth0 + ip address 10.1.3.7/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +router ospf +  ospf router-id 7.7.7.7 +  distance 20 +  network 10.1.3.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r8/frr.conf b/tests/topotests/ospf_prune_next_hop/r8/frr.conf new file mode 100644 index 0000000000..d8facbc01f --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r8/frr.conf @@ -0,0 +1,14 @@ +! +hostname r8 +ip forwarding +! +interface r8-eth0 + ip address 10.1.3.8/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +router ospf +  ospf router-id 8.8.8.8 +  distance 20 +  network 10.1.3.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py b/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py new file mode 100644 index 0000000000..88aa6b2e36 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_prune_next_hop +# +# Copyright (c) 2025 LabN Consulting +# Acee Lindem +# + +import os +import sys +from functools import partial +import pytest + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +from lib.common_config import ( +    step, +) + + +""" +test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation +""" + +TOPOLOGY = """ +         20.1.3.0    20.1.4.0    20.1.5.0   20.1.6.0 +         eth0 | .3   eth0 | .4   eth0 | .5  eth0 | .6 +          +--+-+      +--+-+      +--+-+     +--+-+ +10.1 3.0  | R3 |      | R4 |      | R5 |     | R6 | +    +-----+    |      |    |eth1  |    |eth1 |    | 10.1.3.0/24 +    |     |    |      |    +----  |    |---  +    -+---+ +    |     +--+-+      +--+-+      +--+-+     +--+-+    | +    |   eth2 | .3   eth2 | .4   eth2 | .5  eth2 |      | +eth0|        |           |           |          |      | eth0 + +--+--+    ++-------+ Switch Network +---------++  +--+---+ + | R7  |    |           10.1.2.0/24              |  |  R8  | + +-----+    +------------------------------------+  +------+ +                          eth1 | .2 +                            +--+--+ +                            | R2  | +                            +--+--+ +                          eth0 | .2 +                  10.1.1.0/24  | +                          eth0 | .1 +                            +--+--+ +                            | R1  | +                            +-----+ + +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd] + + +def build_topo(tgen): +    "Build function" + +    # Create 8 routers +    tgen.add_router("r1") +    tgen.add_router("r2") +    tgen.add_router("r3") +    tgen.add_router("r4") +    tgen.add_router("r5") +    tgen.add_router("r6") +    tgen.add_router("r7") +    tgen.add_router("r8") + +    # Interconect router 1, 2 (0) +    switch = tgen.add_switch("s1-1-2") +    switch.add_link(tgen.gears["r1"]) +    switch.add_link(tgen.gears["r2"]) + +    # Add standalone networks to router 3 +    switch = tgen.add_switch("s2-3") +    switch.add_link(tgen.gears["r3"]) + +    # Add standalone network to router 4 +    switch = tgen.add_switch("s3-4") +    switch.add_link(tgen.gears["r4"]) + +    # Add standalone network to router 5 +    switch = tgen.add_switch("s4-5") +    switch.add_link(tgen.gears["r5"]) + +    # Add standalone network to router 6 +    switch = tgen.add_switch("s5-6") +    switch.add_link(tgen.gears["r6"]) + +    # Interconect routers 3, 4, 5, and 6 +    switch = tgen.add_switch("s6-3") +    switch.add_link(tgen.gears["r3"]) +    switch.add_link(tgen.gears["r7"]) +    switch = tgen.add_switch("s7-4") +    switch.add_link(tgen.gears["r4"]) +    switch = tgen.add_switch("s8-5") +    switch.add_link(tgen.gears["r5"]) +    switch = tgen.add_switch("s9-6") +    switch.add_link(tgen.gears["r6"]) +    switch.add_link(tgen.gears["r8"]) + +    # Interconect routers 2, 3, 4, 5, and 6 +    switch = tgen.add_switch("s10-lan") +    switch.add_link(tgen.gears["r2"]) +    switch.add_link(tgen.gears["r3"]) +    switch.add_link(tgen.gears["r4"]) +    switch.add_link(tgen.gears["r5"]) +    switch.add_link(tgen.gears["r6"]) + + +def setup_module(mod): +    logger.info("OSPF Prune Next Hops:\n {}".format(TOPOLOGY)) + +    tgen = Topogen(build_topo, mod.__name__) +    tgen.start_topology() + +    # Starting Routers +    router_list = tgen.routers() + +    for rname, router in router_list.items(): +        logger.info("Loading router %s" % rname) +        router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + +    # Initialize all routers. +    tgen.start_router() + + +def teardown_module(): +    "Teardown the pytest environment" +    tgen = get_topogen() +    tgen.stop_topology() + + +def test_intra_area_route_prune(): +    tgen = get_topogen() + +    if tgen.routers_have_failure(): +        pytest.skip("Skipped because of router(s) failure") + +    step("Test OSPF intra-area route 10.1.3.0/24 duplicate nexthops already pruned") +    # Verify OSPF route 10.1.3.0/24 nexthops pruned already. +    r1 = tgen.gears["r1"] +    input_dict = { +        "10.1.3.0/24": { +            "routeType": "N", +            "transit": True, +            "cost": 30, +            "area": "0.0.0.0", +            "nexthops": [ +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "8.8.8.8"} +            ], +        } +    } +    test_func = partial( +        topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict +    ) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +    assertmsg = "OSPF Intra-Area route 10.1.3.0/24 mismatch on router r1" +    assert result is None, assertmsg + +    step("Test IP route 10.1.3.0/24 installed") +    input_dict = { +        "10.1.3.0/24": [ +            { +                "prefix": "10.1.3.0/24", +                "prefixLen": 24, +                "protocol": "ospf", +                "vrfName": "default", +                "distance": 20, +                "metric": 30, +                "installed": True, +                "internalNextHopNum": 1, +                "internalNextHopActiveNum": 1, +                "nexthops": [ +                    { +                        "ip": "10.1.1.2", +                        "afi": "ipv4", +                        "interfaceName": "r1-eth0", +                        "active": True, +                        "weight": 1, +                    } +                ], +            } +        ] +    } +    test_func = partial( +        topotest.router_json_cmp, r1, "show ip route 10.1.3.0/24 json", input_dict +    ) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +    assertmsg = "IP route 10.1.3.0/24 mismatch on router r1" +    assert result is None, assertmsg + + +def test_inter_area_route_prune(): +    tgen = get_topogen() + +    if tgen.routers_have_failure(): +        pytest.skip("Skipped because of router(s) failure") + +    step("Test OSPF inter-area route 20.1.0.0/16 duplicate nexthops installed") +    # Verify OSPF route 20.1.0.0/16 duplication nexthops +    r1 = tgen.gears["r1"] +    input_dict = { +        "20.1.0.0/16": { +            "routeType": "N IA", +            "cost": 30, +            "area": "0.0.0.0", +            "nexthops": [ +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "3.3.3.3"}, +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "4.4.4.4"}, +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "5.5.5.5"}, +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "6.6.6.6"}, +            ], +        } +    } +    test_func = partial( +        topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict +    ) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +    assertmsg = "OSPF Inter-Area route 20.1.0.0/16 mismatch on router r1" +    assert result is None, assertmsg + +    step("Test IP route 10.1.3.0/24 installed with pruned next-hops") +    input_dict = { +        "20.1.0.0/16": [ +            { +                "prefix": "20.1.0.0/16", +                "prefixLen": 16, +                "protocol": "ospf", +                "vrfName": "default", +                "distance": 20, +                "metric": 30, +                "installed": True, +                "internalNextHopNum": 1, +                "internalNextHopActiveNum": 1, +                "nexthops": [ +                    { +                        "ip": "10.1.1.2", +                        "afi": "ipv4", +                        "interfaceName": "r1-eth0", +                        "active": True, +                        "weight": 1, +                    } +                ], +            } +        ] +    } +    test_func = partial( +        topotest.router_json_cmp, r1, "show ip route 20.1.0.0/16 json", input_dict +    ) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +    assertmsg = "IP route 20.1.1.0/24 mismatch on router r1" +    assert result is None, assertmsg + + +def test_as_external_route_prune(): +    tgen = get_topogen() + +    if tgen.routers_have_failure(): +        pytest.skip("Skipped because of router(s) failure") + +    step("Test OSPF AS external route 100.100.100.100 duplicate nexthops installed") +    # Verify OSPF route 20.1.0.0/16 duplication nexthops +    r1 = tgen.gears["r1"] +    input_dict = { +        "100.100.100.100/32": { +            "routeType": "N E2", +            "cost": 20, +            "type2cost": 20, +            "tag": 0, +            "nexthops": [ +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "3.3.3.3"}, +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "4.4.4.4"}, +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "5.5.5.5"}, +                {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "6.6.6.6"}, +            ], +        } +    } +    test_func = partial( +        topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict +    ) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +    assertmsg = "OSPF AS external route 100.100.100.100/32 mismatch on router r1" +    assert result is None, assertmsg + +    step("Test IP route 100.100.100.100/32 installed with pruned next-hops") +    input_dict = { +        "100.100.100.100/32": [ +            { +                "prefix": "100.100.100.100/32", +                "prefixLen": 32, +                "protocol": "ospf", +                "vrfName": "default", +                "distance": 20, +                "metric": 20, +                "installed": True, +                "internalNextHopNum": 1, +                "internalNextHopActiveNum": 1, +                "nexthops": [ +                    { +                        "ip": "10.1.1.2", +                        "afi": "ipv4", +                        "interfaceName": "r1-eth0", +                        "active": True, +                        "weight": 1, +                    } +                ], +            } +        ] +    } +    test_func = partial( +        topotest.router_json_cmp, +        r1, +        "show ip route 100.100.100.100/32 json", +        input_dict, +    ) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +    assertmsg = "IP route 100.100.100.100/32 mismatch on router r1" +    assert result is None, assertmsg + + +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/static_simple/test_static_simple.py b/tests/topotests/static_simple/test_static_simple.py index bb3580a1d8..afde58fbf7 100644 --- a/tests/topotests/static_simple/test_static_simple.py +++ b/tests/topotests/static_simple/test_static_simple.py @@ -61,6 +61,15 @@ def get_ip_networks(super_prefix, count):      return tuple(network.subnets(count_log2))[0:count] +def get_src_networks(src_prefix, count, default=""): +    if src_prefix is not None: +        for net in get_ip_networks(src_prefix, count): +            yield " from {}".format(net) +    else: +        for i in range(0, count): +            yield default + +  def enable_debug(router):      router.vtysh_cmd("debug northbound callbacks configuration") @@ -70,7 +79,7 @@ def disable_debug(router):  @retry(retry_timeout=30, initial_wait=0.1) -def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): +def check_kernel(r1, super_prefix, src_prefix, count, add, is_blackhole, vrf, matchvia):      network = ipaddress.ip_network(super_prefix)      vrfstr = f" vrf {vrf}" if vrf else ""      if network.version == 6: @@ -79,26 +88,30 @@ def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia):          kernel = r1.run(f"ip -4 route show{vrfstr}")      logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel) -    for _, net in enumerate(get_ip_networks(super_prefix, count)): +    for net, srcnet in zip( +        get_ip_networks(super_prefix, count), get_src_networks(src_prefix, count) +    ): +        netfull = str(net) + srcnet          if not add: -            assert str(net) not in kernel +            assert netfull + " nhid" not in kernel +            assert netfull + " via" not in kernel              continue          if is_blackhole: -            route = f"blackhole {str(net)} proto (static|196) metric 20" +            route = f"blackhole {netfull}(?: dev lo)? proto (static|196) metric 20"          else:              route = ( -                f"{str(net)}(?: nhid [0-9]+)? {matchvia} " -                "proto (static|196) metric 20" +                f"{netfull}(?: nhid [0-9]+)? {matchvia} proto (static|196) metric 20"              )          assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'" -def do_config( +def do_config_inner(      r1,      count,      add=True,      do_ipv6=False, +    do_sadr=False,      via=None,      vrf=None,      use_cli=False, @@ -109,11 +122,18 @@ def do_config(      #      # Set the route details      # - -    if vrf: -        super_prefix = "2002::/48" if do_ipv6 else "20.0.0.0/8" +    src_prefs = [None, None] +    if do_ipv6 and do_sadr: +        # intentionally using overlapping prefix +        super_prefs = ["2001::/48", "2002::/48"] +        src_prefs = ["2001:db8:1111::/48", "2001:db8:2222::/48"] +    elif do_ipv6: +        super_prefs = ["2001::/48", "2002::/48"]      else: -        super_prefix = "2001::/48" if do_ipv6 else "10.0.0.0/8" +        super_prefs = ["10.0.0.0/8", "20.0.0.0/8"] + +    super_prefix = super_prefs[1 if vrf else 0] +    src_prefix = src_prefs[1 if vrf else 0]      matchvia = ""      if via == "blackhole": @@ -144,11 +164,13 @@ def do_config(          if vrf:              f.write("vrf {}\n".format(vrf)) -        for _, net in enumerate(get_ip_networks(super_prefix, count)): +        for net, srcnet in zip( +            get_ip_networks(super_prefix, count), get_src_networks(src_prefix, count) +        ):              if add: -                f.write("ip route {} {}\n".format(net, via)) +                f.write("ip route {}{} {}\n".format(net, srcnet, via))              else: -                f.write("no ip route {} {}\n".format(net, via)) +                f.write("no ip route {}{} {}\n".format(net, srcnet, via))      #      # Load config file. @@ -165,7 +187,9 @@ def do_config(      #      # Verify the results are in the kernel      # -    check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia) +    check_kernel( +        r1, super_prefix, src_prefix, count, add, via == "blackhole", vrf, matchvia +    )      optyped = "added" if add else "removed"      logger.debug( @@ -175,6 +199,12 @@ def do_config(      ) +def do_config(*args, **kwargs): +    do_config_inner(*args, do_ipv6=False, do_sadr=False, **kwargs) +    do_config_inner(*args, do_ipv6=True, do_sadr=False, **kwargs) +    do_config_inner(*args, do_ipv6=True, do_sadr=True, **kwargs) + +  def guts(tgen, vrf, use_cli):      if tgen.routers_have_failure():          pytest.skip(tgen.errors) @@ -183,20 +213,20 @@ def guts(tgen, vrf, use_cli):      count = 10      step(f"add {count} via gateway", reset=True) -    do_config(r1, count, True, False, vrf=vrf, use_cli=use_cli) +    do_config(r1, count, True, vrf=vrf, use_cli=use_cli)      step(f"remove {count} via gateway") -    do_config(r1, count, False, False, vrf=vrf, use_cli=use_cli) +    do_config(r1, count, False, vrf=vrf, use_cli=use_cli)      via = f"lo-{vrf}" if vrf else "lo"      step("add via loopback") -    do_config(r1, 1, True, False, via=via, vrf=vrf, use_cli=use_cli) +    do_config(r1, 1, True, via=via, vrf=vrf, use_cli=use_cli)      step("remove via loopback") -    do_config(r1, 1, False, False, via=via, vrf=vrf, use_cli=use_cli) +    do_config(r1, 1, False, via=via, vrf=vrf, use_cli=use_cli)      step("add via blackhole") -    do_config(r1, 1, True, False, via="blackhole", vrf=vrf, use_cli=use_cli) +    do_config(r1, 1, True, via="blackhole", vrf=vrf, use_cli=use_cli)      step("remove via blackhole") -    do_config(r1, 1, False, False, via="blackhole", vrf=vrf, use_cli=use_cli) +    do_config(r1, 1, False, via="blackhole", vrf=vrf, use_cli=use_cli)  def test_static_cli(tgen): diff --git a/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf b/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf new file mode 100644 index 0000000000..f4da11af06 --- /dev/null +++ b/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf @@ -0,0 +1,4 @@ +int r1-eth0 +  ipv6 address fc00::1/64 + +ipv6 route 1::1/128 fc00::2 diff --git a/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py b/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py new file mode 100644 index 0000000000..587a951c85 --- /dev/null +++ b/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2024 by Nvidia Corporation +# Donald Sharp +# + +""" +Check that the v6 nexthop recursive resolution works when it changes +""" + +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, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.staticd] + + +def build_topo(tgen): + +    tgen.add_router("r1") + +    switch = tgen.add_switch("s1") +    switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): +    tgen = Topogen(build_topo, mod.__name__) +    tgen.start_topology() + +    router_list = tgen.routers() + +    for rname, router in router_list.items(): +        router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)), +                               [(TopoRouter.RD_MGMTD, None), +                                (TopoRouter.RD_ZEBRA, None), +                                (TopoRouter.RD_STATIC, None), +                                (TopoRouter.RD_SHARP, None)]) + +    tgen.start_router() + + +def teardown_module(mod): +    tgen = get_topogen() +    tgen.stop_topology() + + +def test_recursive_v6_nexthop_generation(): +    tgen = get_topogen() + +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    step("Testing v6 nexthop resolution") + +    #assert False +    router = tgen.gears["r1"] + +    def _v6_converge_1_1_initial(): +        output = json.loads( +            router.vtysh_cmd("show ipv6 route 1::1 json")) + +        expected = { +            "1::1/128":[ +                { +                    "prefix":"1::1/128", +                    "prefixLen":128, +                    "protocol":"static", +                    "vrfName":"default", +                    "selected":True, +                    "destSelected":True, +                    "distance":1, +                    "metric":0, +                    "installed":True, +                    "table":254, +                    "nexthops":[ +                        { +                            "fib":True, +                            "ip":"fc00::2", +                            "afi":"ipv6", +                            "interfaceName":"r1-eth0", +                            "active":True, +                            "weight":1 +                        } +                    ] +                } +            ] +        } + +        return topotest.json_cmp(output, expected) + +    test_func = functools.partial(_v6_converge_1_1_initial) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) +    assert result is None, "Failed to install v6 1::1 route" + +    router.vtysh_cmd("sharp install routes 2::2 nexthop 1::1 1") +    router.vtysh_cmd("conf\nipv6 route 1::1/128 fc00::3\nno ipv6 route 1::1/128 fc00::2") + +    def _v6_converge_1_1_post(): +        output = json.loads( +            router.vtysh_cmd("show ipv6 route 1::1 json")) + +        expected = { +            "1::1/128":[ +                { +                    "prefix":"1::1/128", +                    "prefixLen":128, +                    "protocol":"static", +                    "vrfName":"default", +                    "selected":True, +                    "destSelected":True, +                    "distance":1, +                    "metric":0, +                    "installed":True, +                    "table":254, +                    "nexthops":[ +                        { +                            "fib":True, +                            "ip":"fc00::3", +                            "afi":"ipv6", +                            "interfaceName":"r1-eth0", +                            "active":True, +                            "weight":1 +                        } +                    ] +                } +            ] +        } + +        return topotest.json_cmp(output, expected) + +    test_func = functools.partial(_v6_converge_1_1_post) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) +    assert result is None, "Failed to change v6 1::1 route" + +    router.vtysh_cmd("sharp install routes 2::2 nexthop 1::1 1") + +    def _v6_change_2_2_post(): +        output = json.loads( +            router.vtysh_cmd("show ipv6 route 2::2 json")) + +        expected = { +            "2::2/128":[ +                { +                    "prefix":"2::2/128", +                    "prefixLen":128, +                    "protocol":"sharp", +                    "vrfName":"default", +                    "selected":True, +                    "destSelected":True, +                    "distance":150, +                    "metric":0, +                    "installed":True, +                    "table":254, +                    "nexthops":[ +                        { +                            "fib":True, +                            "ip":"fc00::3", +                            "afi":"ipv6", +                            "interfaceName":"r1-eth0", +                            "active":True, +                            "weight":1 +                        } +                    ] +                } +            ] +        } + +        return topotest.json_cmp(output, expected) + +    test_func = functools.partial(_v6_change_2_2_post) +    _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) +    assert result is None, "Failed to see sharpd route correctly" + + +if __name__ == "__main__": +    args = ["-s"] + sys.argv[1:] +    sys.exit(pytest.main(args)) diff --git a/tools/frr-reload.py b/tools/frr-reload.py index a138e4e239..f124cae713 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -237,6 +237,14 @@ def get_normalized_interface_vrf(line):      return line +def get_normalized_ebgp_multihop_line(line): +    obj = re.search(r"(.*)ebgp-multihop\s+255", line) +    if obj: +        line = obj.group(1) + "ebgp-multihop" + +    return line + +  # This dictionary contains a tree of all commands that we know start a  # new multi-line context. All other commands are treated either as  # commands inside a multi-line context or as single-line contexts. This @@ -382,6 +390,9 @@ class Config(object):              if ":" in line:                  line = get_normalized_mac_ip_line(line) +            if "ebgp-multihop" in line: +                line = get_normalized_ebgp_multihop_line(line) +              # vrf static routes can be added in two ways. The old way is:              #              # "ip route x.x.x.x/x y.y.y.y vrf <vrfname>" diff --git a/yang/frr-staticd.yang b/yang/frr-staticd.yang index 904e2058e9..8d0e58c0a5 100644 --- a/yang/frr-staticd.yang +++ b/yang/frr-staticd.yang @@ -165,7 +165,7 @@ module frr-staticd {          "Support for a 'staticd' pseudo-protocol instance           consists of a list of routes.";        list route-list { -        key "prefix afi-safi"; +        key "prefix src-prefix afi-safi";          description            "List of staticd IP routes.";          leaf prefix { @@ -173,6 +173,11 @@ module frr-staticd {            description              "IP prefix.";          } +        leaf src-prefix { +          type inet:ipv6-prefix; +          description +            "IPv6 source prefix for dst-src routes"; +        }          leaf afi-safi {            type identityref {              base frr-rt:afi-safi-type; @@ -180,6 +185,12 @@ module frr-staticd {            description              "AFI-SAFI type.";          } +        /* note dst-src routes are semantically invalid in MRIB */ +        must "afi-safi = 'frr-rt:ipv6-unicast' +           or afi-safi = 'frr-rt:ipv6-labeled-unicast' +           or afi-safi = 'frr-rt:l3vpn-ipv6-unicast' +           or src-prefix = '::/0' +        ";          uses staticd-prefix-attributes {            augment "path-list/frr-nexthops/nexthop" { @@ -194,17 +205,6 @@ module frr-staticd {              }            }          } - -        list src-list { -          key "src-prefix"; -          leaf src-prefix { -            type inet:ipv6-prefix; -            description -              "IPv6 source prefix"; -          } - -          uses staticd-prefix-attributes; -        }        }        container segment-routing { diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 66dc5b4b5f..9bf7e2cbb5 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -82,8 +82,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)  		RNODE_FOREACH_RE (rn, newre) {  			if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED)) -				zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, -							 client, rn, newre, false); +				zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, +							 newre, NULL);  		}  		route_unlock_node(rn); @@ -91,6 +91,24 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)  }  /* Redistribute routes. */ +static void redistribute_table_direct(struct zserv *client, int type, const struct route_node *rn, +				      const struct route_entry *re) +{ +	struct redist_table_direct *table; +	struct redist_proto *red; +	struct listnode *node; +	afi_t afi = family2afi(rn->p.family); + +	red = &client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + +	for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) { +		if (table->table_id != (int)re->table) +			continue; + +		zsend_redistribute_route(type, client, rn, re, &table->vrf_id); +	} +} +  static void zebra_redistribute(struct zserv *client, int type,  			       unsigned short instance, struct zebra_vrf *zvrf,  			       int afi) @@ -102,13 +120,9 @@ static void zebra_redistribute(struct zserv *client, int type,  	vrf_id_t vrf_id = zvrf_id(zvrf);  	if (type == ZEBRA_ROUTE_TABLE_DIRECT) { -		if (vrf_id == VRF_DEFAULT) { -			table = zebra_router_find_table(zvrf, instance, afi, -							SAFI_UNICAST); -			type = ZEBRA_ROUTE_ALL; -			is_table_direct = true; -		} else -			return; +		table = zebra_router_find_table(zvrf, instance, afi, SAFI_UNICAST); +		type = ZEBRA_ROUTE_ALL; +		is_table_direct = true;  	} else  		table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); @@ -140,15 +154,20 @@ static void zebra_redistribute(struct zserv *client, int type,  			if (!zebra_check_addr(&rn->p))  				continue; -			zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, -						 client, rn, newre, is_table_direct); +			if (is_table_direct) +				redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn, +							  newre); +			else +				zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, +							 newre, NULL);  		}  }  /* - * Function to return a valid table id value if table-direct is used - * return 0 otherwise - * This function can be called only if zebra_redistribute_check returns TRUE + * Checks if the route entry can be used as table-direct or not. + * `table-direct` routes always belong to `VRF_DEFAULT` and has an table + * ID different than the VRF it belongs (example main VRF table is 254, + * so in order to be `table-direct` the route's table ID must be != 254).   */  static bool zebra_redistribute_is_table_direct(const struct route_entry *re)  { @@ -177,15 +196,14 @@ static bool zebra_redistribute_check(const struct route_node *rn,  	afi = family2afi(rn->p.family);  	zvrf = zebra_vrf_lookup_by_id(re->vrf_id); -	if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) { +	if (zvrf->table_id != re->table) { +		/* +		 * Routes with table ID different from VRFs can be used as +		 * `table-direct` if enabled. +		 */  		if (re->table && -		    redist_check_instance(&client->mi_redist -						   [afi][ZEBRA_ROUTE_TABLE_DIRECT], -					  re->table)) { -			/* table-direct redistribution only for route entries which -			 * are on the default vrf, and that have table id different -			 * from the default table. -			 */ +		    redist_table_direct_has_id(&client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT], +					       re->table)) {  			return true;  		}  		return false; @@ -227,7 +245,6 @@ void redistribute_update(const struct route_node *rn,  {  	struct listnode *node, *nnode;  	struct zserv *client; -	bool is_table_direct;  	if (IS_ZEBRA_DEBUG_RIB)  		zlog_debug( @@ -242,7 +259,6 @@ void redistribute_update(const struct route_node *rn,  		return;  	} -  	for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {  		if (zebra_redistribute_check(rn, re, client)) {  			if (IS_ZEBRA_DEBUG_RIB) { @@ -253,15 +269,19 @@ void redistribute_update(const struct route_node *rn,  					re->vrf_id, re->table, re->type,  					re->distance, re->metric);  			} -			is_table_direct = zebra_redistribute_is_table_direct(re); -			zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, -						 client, rn, re, -						 is_table_direct); +			if (zebra_redistribute_is_table_direct(re)) +				redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn, +							  re); +			else +				zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, +							 re, NULL);  		} else if (zebra_redistribute_check(rn, prev_re, client)) { -			is_table_direct = zebra_redistribute_is_table_direct(prev_re); -			zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, -						 client, rn, prev_re, -						 is_table_direct); +			if (zebra_redistribute_is_table_direct(prev_re)) +				redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn, +							  prev_re); +			else +				zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn, +							 prev_re, NULL);  		}  	}  } @@ -281,7 +301,6 @@ void redistribute_delete(const struct route_node *rn,  	struct listnode *node, *nnode;  	struct zserv *client;  	vrf_id_t vrfid; -	bool is_table_direct;  	if (old_re)  		vrfid = old_re->vrf_id; @@ -344,10 +363,12 @@ void redistribute_delete(const struct route_node *rn,  			 * happy.  			 */  			assert(old_re); -			is_table_direct = zebra_redistribute_is_table_direct(old_re); -			zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, -						 client, rn, old_re, -						 is_table_direct); +			if (zebra_redistribute_is_table_direct(old_re)) +				redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn, +							  old_re); +			else +				zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn, +							 old_re, NULL);  		}  	}  } @@ -383,8 +404,16 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS)  	}  	if (instance) { -		if (!redist_check_instance(&client->mi_redist[afi][type], -					   instance)) { +		if (type == ZEBRA_ROUTE_TABLE_DIRECT) { +			struct redist_table_direct table = { +				.vrf_id = zvrf->vrf->vrf_id, +				.table_id = instance, +			}; +			if (!redist_lookup_table_direct(&client->mi_redist[afi][type], &table)) { +				redist_add_table_direct(&client->mi_redist[afi][type], &table); +				zebra_redistribute(client, type, instance, zvrf, afi); +			} +		} else if (!redist_check_instance(&client->mi_redist[afi][type], instance)) {  			redist_add_instance(&client->mi_redist[afi][type],  					    instance);  			zebra_redistribute(client, type, instance, zvrf, afi); @@ -443,7 +472,13 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS)  	 * themselves should keep track of the received routes from zebra and  	 * withdraw them when necessary.  	 */ -	if (instance) +	if (type == ZEBRA_ROUTE_TABLE_DIRECT) { +		struct redist_table_direct table = { +			.vrf_id = zvrf->vrf->vrf_id, +			.table_id = instance, +		}; +		redist_del_table_direct(&client->mi_redist[afi][type], &table); +	} else if (instance)  		redist_del_instance(&client->mi_redist[afi][type], instance);  	else  		vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf)); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b32882e858..d696b19859 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2588,10 +2588,10 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx  		}  	} -	if ((!fpm && kernel_nexthops_supported() -	     && (!proto_nexthops_only() -		 || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0))) -	    || (fpm && force_nhg)) { +	if ((!fpm && kernel_nexthops_supported() && +	     (!proto_nexthops_only() || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0)) && +	     (!src_p || !src_p->prefixlen)) || +	    (fpm && force_nhg)) {  		/* Kernel supports nexthop objects */  		if (IS_ZEBRA_DEBUG_KERNEL)  			zlog_debug("%s: %pFX nhg_id is %u", __func__, p, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ab55998af0..e9d554ba3d 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -509,9 +509,8 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp)  	return zserv_send_message(client, s);  } -int zsend_redistribute_route(int cmd, struct zserv *client, -			     const struct route_node *rn, -			     const struct route_entry *re, bool is_table_direct) +int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn, +			     const struct route_entry *re, vrf_id_t *to_vrf)  {  	struct zapi_route api;  	struct zapi_nexthop *api_nh; @@ -527,9 +526,10 @@ int zsend_redistribute_route(int cmd, struct zserv *client,  	api.vrf_id = re->vrf_id;  	api.type = re->type;  	api.safi = SAFI_UNICAST; -	if (is_table_direct) { +	if (to_vrf != NULL) {  		api.instance = re->table;  		api.type = ZEBRA_ROUTE_TABLE_DIRECT; +		api.vrf_id = *to_vrf;  	} else  		api.instance = re->instance;  	api.flags = re->flags; @@ -598,7 +598,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,  	/* Attributes. */  	SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); -	if (is_table_direct) +	if (to_vrf != NULL)  		api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT;  	else  		api.distance = re->distance; @@ -740,6 +740,10 @@ static int route_notify_internal(const struct route_node *rn, int type,  	struct zserv *client;  	struct stream *s;  	uint8_t blen; +	const struct prefix *p, *src_p; +	struct prefix src_dummy = {}; + +	srcdest_rnode_prefixes(rn, &p, &src_p);  	client = zserv_find_client(type, instance);  	if (!client || !client->notify_owner) { @@ -771,9 +775,17 @@ static int route_notify_internal(const struct route_node *rn, int type,  	stream_putc(s, rn->p.family); -	blen = prefix_blen(&rn->p); -	stream_putc(s, rn->p.prefixlen); -	stream_put(s, &rn->p.u.prefix, blen); +	blen = prefix_blen(p); +	stream_putc(s, p->prefixlen); +	stream_put(s, &p->u.prefix, blen); + +	if (!src_p) { +		src_dummy.family = p->family; +		src_p = &src_dummy; +	} +	blen = prefix_blen(src_p); +	stream_putc(s, src_p->prefixlen); +	stream_put(s, &src_p->u.prefix, blen);  	stream_putl(s, table_id); diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index a59ccc838b..29a5b69f18 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -51,10 +51,8 @@ extern void nbr_connected_delete_ipv6(struct interface *ifp,  				      struct in6_addr *address);  extern int zsend_interface_update(int cmd, struct zserv *client,  				  struct interface *ifp); -extern int zsend_redistribute_route(int cmd, struct zserv *zclient, -				    const struct route_node *rn, -				    const struct route_entry *re, -				    bool is_table_direct); +extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn, +				    const struct route_entry *re, vrf_id_t *to_vrf);  extern int zsend_router_id_update(struct zserv *zclient, afi_t afi,  				  struct prefix *p, vrf_id_t vrf_id); diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 4f6bc02c6e..f5141c8f23 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -572,8 +572,7 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)  	/* Nexthops should be in-order, so we simply compare them in-place */  	for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop;  	     nexthop1 && nexthop2; -	     nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { - +	     nexthop1 = nexthop_next(nexthop1), nexthop2 = nexthop_next(nexthop2)) {  		if (!nhg_compare_nexthops(nexthop1, nexthop2))  			return false;  	} @@ -608,8 +607,7 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)  	for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop,  	     nexthop2 = nhe2->backup_info->nhe->nhg.nexthop;  	     nexthop1 && nexthop2; -	     nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { - +	     nexthop1 = nexthop_next(nexthop1), nexthop2 = nexthop_next(nexthop2)) {  		if (!nhg_compare_nexthops(nexthop1, nexthop2))  			return false;  	}  | 
