diff options
| author | Pushpasis Sarkar <pushpasis.frr@gmail.com> | 2022-11-08 23:19:39 +0530 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-08 23:19:39 +0530 | 
| commit | 5b86dc9f0fdb95f9a3c2ac83e26740159c268ecd (patch) | |
| tree | 2d5bea420d4fedb0c7449b01a2f425ad33ec3a8d /isisd | |
| parent | 195ad0e793dd966550e0feb9b22b33f5ece22d94 (diff) | |
| parent | acc00297790899586c1d431c0e574fdcf9ce30e8 (diff) | |
Merge pull request #11594 from louis-6wind/lfa-netlink
isisd: apply fast-reroute as soon an interface or an adjacency falls down
Diffstat (limited to 'isisd')
| -rw-r--r-- | isisd/isis_adjacency.c | 40 | ||||
| -rw-r--r-- | isisd/isis_adjacency.h | 1 | ||||
| -rw-r--r-- | isisd/isis_circuit.c | 39 | ||||
| -rw-r--r-- | isisd/isis_circuit.h | 5 | ||||
| -rw-r--r-- | isisd/isis_lfa.c | 18 | ||||
| -rw-r--r-- | isisd/isis_route.c | 99 | ||||
| -rw-r--r-- | isisd/isis_route.h | 5 | ||||
| -rw-r--r-- | isisd/isis_spf.c | 27 | ||||
| -rw-r--r-- | isisd/isis_spf.h | 4 | ||||
| -rw-r--r-- | isisd/isisd.c | 19 | ||||
| -rw-r--r-- | isisd/isisd.h | 3 | 
11 files changed, 229 insertions, 31 deletions
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 00763135e6..0957c897e6 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -212,6 +212,36 @@ static const char *adj_level2string(int level)  	return NULL; /* not reached */  } +static void isis_adj_route_switchover(struct isis_adjacency *adj) +{ +	union g_addr ip = {}; +	ifindex_t ifindex; +	unsigned int i; + +	if (!adj->circuit || !adj->circuit->interface) +		return; + +	ifindex = adj->circuit->interface->ifindex; + +	for (i = 0; i < adj->ipv4_address_count; i++) { +		ip.ipv4 = adj->ipv4_addresses[i]; +		isis_circuit_switchover_routes(adj->circuit, AF_INET, &ip, +					       ifindex); +	} + +	for (i = 0; i < adj->ll_ipv6_count; i++) { +		ip.ipv6 = adj->ll_ipv6_addrs[i]; +		isis_circuit_switchover_routes(adj->circuit, AF_INET6, &ip, +					       ifindex); +	} + +	for (i = 0; i < adj->global_ipv6_count; i++) { +		ip.ipv6 = adj->global_ipv6_addrs[i]; +		isis_circuit_switchover_routes(adj->circuit, AF_INET6, &ip, +					       ifindex); +	} +} +  void isis_adj_process_threeway(struct isis_adjacency *adj,  			       struct isis_threeway_adj *tw_adj,  			       enum isis_adj_usage adj_usage) @@ -298,6 +328,16 @@ void isis_adj_state_change(struct isis_adjacency **padj,  	if (new_state == old_state)  		return; +	if (old_state == ISIS_ADJ_UP && +	    !CHECK_FLAG(adj->circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z)) { +		if (IS_DEBUG_EVENTS) +			zlog_debug( +				"ISIS-Adj (%s): Starting fast-reroute on state change %d->%d: %s", +				circuit->area->area_tag, old_state, new_state, +				reason ? reason : "unspecified"); +		isis_adj_route_switchover(adj); +	} +  	adj->adj_state = new_state;  	send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY); diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 7467a619cb..49adc89ae5 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -151,5 +151,4 @@ void isis_adj_build_up_list(struct list *adjdb, struct list *list);  int isis_adj_usage2levels(enum isis_adj_usage usage);  void isis_bfd_startup_timer(struct thread *thread);  const char *isis_adj_name(const struct isis_adjacency *adj); -  #endif /* ISIS_ADJACENCY_H */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index dcc4ed6e42..fa0f2c998f 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -598,6 +598,32 @@ size_t isis_circuit_pdu_size(struct isis_circuit *circuit)  	return ISO_MTU(circuit);  } +static bool isis_circuit_lfa_enabled(struct isis_circuit *circuit, int level) +{ +	return (circuit->lfa_protection[level - 1] || +		circuit->rlfa_protection[level - 1] || +		circuit->tilfa_protection[level - 1]); +} + +void isis_circuit_switchover_routes(struct isis_circuit *circuit, int family, +				    union g_addr *nexthop_ip, ifindex_t ifindex) +{ +	char is_type; + +	if (!circuit->area) +		return; + +	is_type = circuit->area->is_type; +	if ((is_type == IS_LEVEL_1 || is_type == IS_LEVEL_1_AND_2) && +	    isis_circuit_lfa_enabled(circuit, IS_LEVEL_1)) +		isis_area_switchover_routes(circuit->area, family, nexthop_ip, +					    ifindex, IS_LEVEL_1); +	if ((is_type == IS_LEVEL_2 || is_type == IS_LEVEL_1_AND_2) && +	    isis_circuit_lfa_enabled(circuit, IS_LEVEL_2)) +		isis_area_switchover_routes(circuit->area, family, nexthop_ip, +					    ifindex, IS_LEVEL_2); +} +  void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream)  {  	size_t stream_size = isis_circuit_pdu_size(circuit); @@ -1602,17 +1628,26 @@ static int isis_ifp_up(struct interface *ifp)  {  	struct isis_circuit *circuit = ifp->info; -	if (circuit) +	if (circuit) { +		UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z);  		isis_csm_state_change(IF_UP_FROM_Z, circuit, ifp); +	}  	return 0;  }  static int isis_ifp_down(struct interface *ifp)  { +	afi_t afi;  	struct isis_circuit *circuit = ifp->info; -	if (circuit) { +	if (circuit && +	    !CHECK_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z)) { +		SET_FLAG(circuit->flags, ISIS_CIRCUIT_IF_DOWN_FROM_Z); +		for (afi = AFI_IP; afi <= AFI_IP6; afi++) +			isis_circuit_switchover_routes( +				circuit, afi == AFI_IP ? AF_INET : AF_INET6, +				NULL, ifp->ifindex);  		isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp);  		SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 5ff0390c26..b3ad3f7ffe 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -28,6 +28,7 @@  #include "qobj.h"  #include "prefix.h"  #include "ferr.h" +#include "nexthop.h"  #include "isis_constants.h"  #include "isis_common.h" @@ -141,6 +142,7 @@ struct isis_circuit {  	struct list *ipv6_non_link; /* our non-link local IPv6 addresses */  	uint16_t upadjcount[ISIS_LEVELS];  #define ISIS_CIRCUIT_FLAPPED_AFTER_SPF 0x01 +#define ISIS_CIRCUIT_IF_DOWN_FROM_Z 0x02  	uint8_t flags;  	bool disable_threeway_adj;  	struct { @@ -209,6 +211,9 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty,  void isis_circuit_print_json(struct isis_circuit *circuit,  			     struct json_object *json, char detail);  size_t isis_circuit_pdu_size(struct isis_circuit *circuit); +void isis_circuit_switchover_routes(struct isis_circuit *circuit, int family, +				    union g_addr *nexthop_ip, +				    ifindex_t ifindex);  void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream);  void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c index c4fadcba03..2ec6dafd3f 100644 --- a/isisd/isis_lfa.c +++ b/isisd/isis_lfa.c @@ -1836,7 +1836,7 @@ static bool clfa_loop_free_check(struct isis_spftree *spftree,  				 struct isis_vertex *vertex_S_D,  				 struct isis_spf_adj *sadj_primary,  				 struct isis_spf_adj *sadj_N, -				 uint32_t *lfa_metric) +				 uint32_t *path_metric)  {  	struct isis_spf_node *node_N;  	uint32_t dist_N_D; @@ -1882,7 +1882,7 @@ static bool clfa_loop_free_check(struct isis_spftree *spftree,  			   dist_N_S, dist_S_D);  	if (dist_N_D < (dist_N_S + dist_S_D)) { -		*lfa_metric = sadj_N->metric + dist_N_D; +		*path_metric = sadj_N->metric + dist_N_D;  		return true;  	} @@ -2082,7 +2082,7 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,  		      struct isis_spftree *spftree,  		      struct lfa_protected_resource *resource)  { -	struct isis_vertex *vertex; +	struct isis_vertex *vertex, *parent_vertex;  	struct listnode *vnode, *snode;  	int level = spftree->level; @@ -2099,7 +2099,7 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,  		struct isis_vertex_adj *vadj_primary;  		struct isis_spf_adj *sadj_primary;  		bool allow_ecmp; -		uint32_t best_metric = UINT32_MAX; +		uint32_t prefix_metric, best_metric = UINT32_MAX;  		char buf[VID2STR_BUFFER];  		if (!VTYPE_IP(vertex->type)) @@ -2133,6 +2133,9 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,  		vadj_primary = listnode_head(vertex->Adj_N);  		sadj_primary = vadj_primary->sadj; +		parent_vertex = listnode_head(vertex->parents); +		prefix_metric = vertex->d_N - parent_vertex->d_N; +  		/*  		 * Loop over list of SPF adjacencies and compute a list of  		 * preliminary LFAs. @@ -2140,7 +2143,7 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,  		lfa_list = list_new();  		lfa_list->del = isis_vertex_adj_free;  		for (ALL_LIST_ELEMENTS_RO(spftree->sadj_list, snode, sadj_N)) { -			uint32_t lfa_metric; +			uint32_t lfa_metric, path_metric;  			struct isis_vertex_adj *lfa;  			struct isis_prefix_sid *psid = NULL;  			bool last_hop = false; @@ -2190,7 +2193,7 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,  			/* Check loop-free criterion. */  			if (!clfa_loop_free_check(spftree, vertex, sadj_primary, -						  sadj_N, &lfa_metric)) { +						  sadj_N, &path_metric)) {  				if (IS_DEBUG_LFA)  					zlog_debug(  						"ISIS-LFA: LFA condition not met for %s", @@ -2198,6 +2201,7 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,  				continue;  			} +			lfa_metric = path_metric + prefix_metric;  			if (lfa_metric < best_metric)  				best_metric = lfa_metric; @@ -2208,7 +2212,7 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,  			if (vertex->N.ip.sr.present) {  				psid = &vertex->N.ip.sr.sid; -				if (lfa_metric == sadj_N->metric) +				if (path_metric == sadj_N->metric)  					last_hop = true;  			}  			lfa = isis_vertex_adj_add(spftree, vertex, lfa_list, diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 9f8f639e5d..4fdb11b211 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -91,11 +91,18 @@ static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family,  	struct isis_nexthop *nh;  	for (ALL_LIST_ELEMENTS_RO(nexthops, node, nh)) { -		if (nh->family != family) -			continue;  		if (nh->ifindex != ifindex)  			continue; +		/* if the IP is unspecified, return the first nexthop found on +		 * the interface +		 */ +		if (!ip) +			return nh; + +		if (nh->family != family) +			continue; +  		switch (family) {  		case AF_INET:  			if (IPV4_ADDR_CMP(&nh->ip.ipv4, &ip->ipv4)) @@ -459,6 +466,21 @@ void isis_route_delete(struct isis_area *area, struct route_node *rode,  	route_unlock_node(rode);  } +static void isis_route_remove_previous_sid(struct isis_area *area, +					   struct prefix *prefix, +					   struct isis_route_info *route_info) +{ +	/* +	 * Explicitly uninstall previous Prefix-SID label if it has +	 * changed or was removed. +	 */ +	if (route_info->sr_previous.present && +	    (!route_info->sr.present || +	     route_info->sr_previous.label != route_info->sr.label)) +		isis_zebra_prefix_sid_uninstall(area, prefix, route_info, +						&route_info->sr_previous); +} +  static void isis_route_update(struct isis_area *area, struct prefix *prefix,  			      struct prefix_ipv6 *src_p,  			      struct isis_route_info *route_info) @@ -467,21 +489,12 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix,  		if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))  			return; -		/* -		 * Explicitly uninstall previous Prefix-SID label if it has -		 * changed or was removed. -		 */ -		if (route_info->sr_previous.present -		    && (!route_info->sr.present -			|| route_info->sr_previous.label -				   != route_info->sr.label)) -			isis_zebra_prefix_sid_uninstall( -				area, prefix, route_info, -				&route_info->sr_previous); +		isis_route_remove_previous_sid(area, prefix, route_info);  		/* Install route. */ -		isis_zebra_route_add_route(area->isis, prefix, src_p, -					   route_info); +		if (area) +			isis_zebra_route_add_route(area->isis, prefix, src_p, +						   route_info);  		/* Install/reinstall Prefix-SID label. */  		if (route_info->sr.present)  			isis_zebra_prefix_sid_install(area, prefix, route_info, @@ -496,8 +509,9 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix,  			isis_zebra_prefix_sid_uninstall(  				area, prefix, route_info, &route_info->sr);  		/* Uninstall route. */ -		isis_zebra_route_del_route(area->isis, prefix, src_p, -					   route_info); +		if (area) +			isis_zebra_route_del_route(area->isis, prefix, src_p, +						   route_info);  		hook_call(isis_route_update_hook, area, prefix, route_info);  		UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); @@ -724,3 +738,54 @@ void isis_route_invalidate_table(struct isis_area *area,  		UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);  	}  } + +void isis_route_switchover_nexthop(struct isis_area *area, +				   struct route_table *table, int family, +				   union g_addr *nexthop_addr, +				   ifindex_t ifindex) +{ +	const char *ifname = NULL, *vrfname = NULL; +	struct isis_route_info *rinfo; +	struct prefix_ipv6 *src_p; +	struct route_node *rnode; +	vrf_id_t vrf_id; +	struct prefix *prefix; + +	if (IS_DEBUG_EVENTS) { +		if (area && area->isis) { +			vrf_id = area->isis->vrf_id; +			vrfname = vrf_id_to_name(vrf_id); +			ifname = ifindex2ifname(ifindex, vrf_id); +		} +		zlog_debug("%s: initiating fast-reroute %s on VRF %s iface %s", +			   __func__, family2str(family), vrfname ? vrfname : "", +			   ifname ? ifname : ""); +	} + +	for (rnode = route_top(table); rnode; +	     rnode = srcdest_route_next(rnode)) { +		if (!rnode->info) +			continue; +		rinfo = rnode->info; + +		if (!rinfo->backup) +			continue; + +		if (!nexthoplookup(rinfo->nexthops, family, nexthop_addr, +				   ifindex)) +			continue; + +		srcdest_rnode_prefixes(rnode, (const struct prefix **)&prefix, +				       (const struct prefix **)&src_p); + +		/* Switchover route. */ +		isis_route_remove_previous_sid(area, prefix, rinfo); +		UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); +		isis_route_update(area, prefix, src_p, rinfo->backup); + +		isis_route_info_delete(rinfo); + +		rnode->info = NULL; +		route_unlock_node(rnode); +	} +} diff --git a/isisd/isis_route.h b/isisd/isis_route.h index 0e206d08f4..a0e0500aec 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -86,4 +86,9 @@ void isis_route_invalidate_table(struct isis_area *area,  void isis_route_node_cleanup(struct route_table *table,  			     struct route_node *node); +void isis_route_switchover_nexthop(struct isis_area *area, +				   struct route_table *table, int family, +				   union g_addr *nexthop_addr, +				   ifindex_t ifindex); +  #endif /* _ZEBRA_ISIS_ROUTE_H */ diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index bdd323e1a7..0d1a5db0d6 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1851,6 +1851,15 @@ void isis_spf_invalidate_routes(struct isis_spftree *tree)  	tree->route_table_backup->cleanup = isis_route_node_cleanup;  } +void isis_spf_switchover_routes(struct isis_area *area, +				struct isis_spftree **trees, int family, +				union g_addr *nexthop_ip, ifindex_t ifindex, +				int level) +{ +	isis_route_switchover_nexthop(area, trees[level - 1]->route_table, +				      family, nexthop_ip, ifindex); +} +  static void isis_run_spf_cb(struct thread *thread)  {  	struct isis_spf_run *run = THREAD_ARG(thread); @@ -1922,9 +1931,19 @@ void isis_spf_timer_free(void *run)  int _isis_spf_schedule(struct isis_area *area, int level,  		       const char *func, const char *file, int line)  { -	struct isis_spftree *spftree = area->spftree[SPFTREE_IPV4][level - 1]; -	time_t now = monotime(NULL); -	int diff = now - spftree->last_run_monotime; +	struct isis_spftree *spftree; +	time_t now; +	long tree_diff, diff; +	int tree; + +	now = monotime(NULL); +	diff = 0; +	for (tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { +		spftree = area->spftree[tree][level - 1]; +		tree_diff = difftime(now - spftree->last_run_monotime, 0); +		if (tree_diff != now && (diff == 0 || tree_diff < diff)) +			diff = tree_diff; +	}  	if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST))  		return 0; @@ -1934,7 +1953,7 @@ int _isis_spf_schedule(struct isis_area *area, int level,  	if (IS_DEBUG_SPF_EVENTS) {  		zlog_debug( -			"ISIS-SPF (%s) L%d SPF schedule called, lastrun %d sec ago Caller: %s %s:%d", +			"ISIS-SPF (%s) L%d SPF schedule called, lastrun %ld sec ago Caller: %s %s:%d",  			area->area_tag, level, diff, func, file, line);  	} diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 815db7b226..3fa5182baf 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -60,6 +60,10 @@ struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,  void isis_spf_invalidate_routes(struct isis_spftree *tree);  void isis_spf_verify_routes(struct isis_area *area,  			    struct isis_spftree **trees); +void isis_spf_switchover_routes(struct isis_area *area, +				struct isis_spftree **trees, int family, +				union g_addr *nexthop_ip, ifindex_t ifindex, +				int level);  void isis_spftree_del(struct isis_spftree *spftree);  void spftree_area_init(struct isis_area *area);  void spftree_area_del(struct isis_area *area); diff --git a/isisd/isisd.c b/isisd/isisd.c index 17f4b20737..cd08525471 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -3090,6 +3090,25 @@ void isis_area_verify_routes(struct isis_area *area)  		isis_spf_verify_routes(area, area->spftree[tree]);  } +void isis_area_switchover_routes(struct isis_area *area, int family, +				 union g_addr *nexthop_ip, ifindex_t ifindex, +				 int level) +{ +	int tree; + +	/* TODO SPFTREE_DSTSRC */ +	if (family == AF_INET) +		tree = SPFTREE_IPV4; +	else if (family == AF_INET6) +		tree = SPFTREE_IPV6; +	else +		return; + +	isis_spf_switchover_routes(area, area->spftree[tree], family, +				   nexthop_ip, ifindex, level); +} + +  static void area_resign_level(struct isis_area *area, int level)  {  	isis_area_invalidate_routes(area, level); diff --git a/isisd/isisd.h b/isisd/isisd.h index a9c1d60439..38f20b2113 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -291,6 +291,9 @@ struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str,  void isis_area_invalidate_routes(struct isis_area *area, int levels);  void isis_area_verify_routes(struct isis_area *area); +void isis_area_switchover_routes(struct isis_area *area, int family, +				 union g_addr *nexthop_ip, ifindex_t ifindex, +				 int level);  void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit);  void isis_area_overload_on_startup_set(struct isis_area *area,  | 
