summaryrefslogtreecommitdiffstats
path: root/isisd
diff options
context:
space:
mode:
authorRuss White <russ@riw.us>2022-07-08 17:14:07 +0200
committerGitHub <noreply@github.com>2022-07-08 17:14:07 +0200
commit36153aa328c33cd7daff795948577455571a7a18 (patch)
tree517d26af2b85ad98e18bf134af601a029d8c555b /isisd
parentMerge pull request #10592 from patrasar/master_pimv6_bsm (diff)
parenttopotests: isis-lfa add a switchover test after BFD down (diff)
downloadfrr-36153aa328c33cd7daff795948577455571a7a18.tar.xz
frr-36153aa328c33cd7daff795948577455571a7a18.zip
Merge pull request #10962 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.c41
-rw-r--r--isisd/isis_adjacency.h1
-rw-r--r--isisd/isis_circuit.c34
-rw-r--r--isisd/isis_circuit.h5
-rw-r--r--isisd/isis_lfa.c18
-rw-r--r--isisd/isis_route.c88
-rw-r--r--isisd/isis_route.h5
-rw-r--r--isisd/isis_spf.c27
-rw-r--r--isisd/isis_spf.h4
-rw-r--r--isisd/isisd.c19
-rw-r--r--isisd/isisd.h3
11 files changed, 218 insertions, 27 deletions
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index 11f17ec7b..1b85299ad 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,17 @@ 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 7467a619c..49adc89ae 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 28d4b530f..26e1de20f 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -593,6 +593,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);
@@ -1597,17 +1618,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 5ff0390c2..b3ad3f7ff 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 800cac852..b348c876d 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 9f8f639e5..0e7947dd4 100644
--- a/isisd/isis_route.c
+++ b/isisd/isis_route.c
@@ -91,11 +91,17 @@ 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 +465,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,17 +488,7 @@ 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,
@@ -724,3 +735,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 0e206d08f..a0e0500ae 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 3aef8ada2..d7335e06d 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 815db7b22..3fa5182ba 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 996a62f4d..3a1eff033 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 c313fd9ef..a3dbfde6b 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);