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 <louis.scalbert@6wind.com>
This commit is contained in:
Louis Scalbert 2022-03-21 17:59:27 +01:00
parent 6eb8da37b0
commit 694fa86728
10 changed files with 159 additions and 1 deletions

View 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,15 @@ 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);

View 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 */

View File

@ -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);

View 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,

View 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);
}
}

View 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 */

View File

@ -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);

View 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);

View File

@ -3092,6 +3092,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);

View File

@ -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,