From 71252973234e70ffa3cb5eec43d4efc1be3d3331 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Mon, 21 Mar 2022 17:59:27 +0100 Subject: [PATCH] isisd: apply fast-reroute on an adjacency failure When a adjacency falls down, the primary routes are not deleted on the dataplane until the SPF is recomputed. Even the backup routes are pre-installed on the dataplane, there is no fast-route optimization. Reasons for an adjacency to come down are: - BFD down - Hello timer timeout - User adjacency clear Apply the backup route switchover for fast-reroute as soon an IS-IS adjacency falls down before the first SPF re-computation. Pre-computed backup routes are applied sooner. Signed-off-by: Louis Scalbert --- isisd/isis_adjacency.c | 40 +++++++++++++++++++++++++++++++++ isisd/isis_adjacency.h | 1 - isisd/isis_circuit.c | 21 ++++++++++++++++++ isisd/isis_circuit.h | 4 ++++ isisd/isis_route.c | 50 ++++++++++++++++++++++++++++++++++++++++++ isisd/isis_route.h | 5 +++++ isisd/isis_spf.c | 9 ++++++++ isisd/isis_spf.h | 4 ++++ isisd/isisd.c | 19 ++++++++++++++++ isisd/isisd.h | 3 +++ 10 files changed, 155 insertions(+), 1 deletion(-) diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 11f17ec7bf..5b32a9e388 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) { + 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 fedceed3bb..3bbdaac649 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -604,6 +604,27 @@ 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 = 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); diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 5ff0390c26..db8e2f752a 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" @@ -209,6 +210,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_route.c b/isisd/isis_route.c index 9f8f639e5d..b04e8e4553 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -724,3 +724,53 @@ 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. */ + 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 49c62d2cf5..d82925536e 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1860,6 +1860,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); 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 996a62f4d5..3a1eff0335 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -3077,6 +3077,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 c313fd9ef7..a3dbfde6bc 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -287,6 +287,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_attached_bit_send_set(struct isis_area *area, bool attached_bit); -- 2.39.5