]> git.puffer.fish Git - matthieu/frr.git/commitdiff
isisd: apply fast-reroute on an adjacency failure
authorLouis Scalbert <louis.scalbert@6wind.com>
Mon, 21 Mar 2022 16:59:27 +0000 (17:59 +0100)
committerLouis Scalbert <louis.scalbert@6wind.com>
Mon, 23 May 2022 08:44:20 +0000 (10:44 +0200)
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 <louis.scalbert@6wind.com>
isisd/isis_adjacency.c
isisd/isis_adjacency.h
isisd/isis_circuit.c
isisd/isis_circuit.h
isisd/isis_route.c
isisd/isis_route.h
isisd/isis_spf.c
isisd/isis_spf.h
isisd/isisd.c
isisd/isisd.h

index 11f17ec7bfe72c52164439740486419c56a739c1..5b32a9e388a0efcc5e72e07767878aa50533e74e 100644 (file)
@@ -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);
 
index 7467a619cb16c7dc102aac9cd400c69c9221149d..49adc89ae599e6eaff06c6187c9b277799e8559d 100644 (file)
@@ -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 */
index fedceed3bb0be216b7259f9c79eae655964062e7..3bbdaac6498fe065e9bcd1b2e79aa061794b5e3e 100644 (file)
@@ -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);
index 5ff0390c26bf1fed78935430e4813f60211d747d..db8e2f752ac4fd513be57844461aa33cce329195 100644 (file)
@@ -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,
index 9f8f639e5d2e6fbc1d9390491b1fb1c5f04bb6f9..b04e8e455373e14b46390ce5269f10e82c12a2db 100644 (file)
@@ -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);
+       }
+}
index 0e206d08f419bc80fd504eb986dad2cc02805a0d..a0e0500aecbb9bc30c75869e1b6ee8cdff426492 100644 (file)
@@ -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 */
index 49c62d2cf55ffb5d82a5f1b280b858bf5943659d..d82925536ec97d701e75a96a48b53d399f7bdc69 100644 (file)
@@ -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);
index 815db7b226a8e1297d7e9e5a13eec75e547a5736..3fa5182bafdd0dddb74489ac9ae9ec6d3fc9cc50 100644 (file)
@@ -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);
index 996a62f4d55acbee8dfb72cf845a23ea11f90f61..3a1eff03351263d89915a698e8cd155fdbc1823e 100644 (file)
@@ -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);
index c313fd9ef786a7eb18922ba0200dbcec3f158fc2..a3dbfde6bc46620e78f05875aee2a2fc1f3c748e 100644 (file)
@@ -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);