summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonald Sharp <sharpd@cumulusnetworks.com>2020-12-03 02:43:51 +0100
committerGitHub <noreply@github.com>2020-12-03 02:43:51 +0100
commitcb5a294642575f8c0789631226c7afa70040be78 (patch)
treec09c95dffd8b6c6a74f9dc07ad6909aa4775c6ae
parentMerge pull request #7657 from mjstapp/fix_topo_asan_noise (diff)
parenttests: add IS-IS classic LFA topotest (diff)
downloadfrr-cb5a294642575f8c0789631226c7afa70040be78.tar.xz
frr-cb5a294642575f8c0789631226c7afa70040be78.zip
Merge pull request #7590 from opensourcerouting/isisd-lfa
isisd: add support for classic LFA
-rw-r--r--doc/user/isisd.rst55
-rw-r--r--isisd/isis_circuit.c4
-rw-r--r--isisd/isis_circuit.h2
-rw-r--r--isisd/isis_cli.c419
-rw-r--r--isisd/isis_ldp_sync.c12
-rw-r--r--isisd/isis_ldp_sync.h3
-rw-r--r--isisd/isis_lfa.c894
-rw-r--r--isisd/isis_lfa.h55
-rw-r--r--isisd/isis_main.c2
-rw-r--r--isisd/isis_memory.c1
-rw-r--r--isisd/isis_memory.h1
-rw-r--r--isisd/isis_nb.c112
-rw-r--r--isisd/isis_nb.h63
-rw-r--r--isisd/isis_nb_config.c519
-rw-r--r--isisd/isis_route.c12
-rw-r--r--isisd/isis_route.h4
-rw-r--r--isisd/isis_spf.c420
-rw-r--r--isisd/isis_spf.h3
-rw-r--r--isisd/isis_spf_private.h16
-rw-r--r--isisd/isis_tlvs.h3
-rw-r--r--isisd/isisd.c86
-rw-r--r--isisd/isisd.h15
-rw-r--r--tests/isisd/test_isis_spf.c62
-rw-r--r--tests/isisd/test_isis_spf.in16
-rw-r--r--tests/isisd/test_isis_spf.refout1086
-rw-r--r--tests/isisd/test_topologies.c180
-rw-r--r--tests/topotests/isis-lfa-topo1/__init__.py0
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/isisd.conf52
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step1/show_ipv6_route.ref236
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref101
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step10/show_ipv6_route.ref.diff46
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step11/show_ipv6_route.ref.diff23
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step12/show_ipv6_route.ref.diff107
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step13/show_ipv6_route.ref.diff45
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step2/show_ipv6_route.ref.diff164
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step3/show_ipv6_route.ref.diff164
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step4/show_ipv6_route.ref.diff142
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step5/show_ipv6_route.ref.diff142
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step6/show_ipv6_route.ref.diff164
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step7/show_ipv6_route.ref.diff37
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step8/show_ipv6_route.ref.diff129
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/step9/show_ipv6_route.ref.diff46
-rw-r--r--tests/topotests/isis-lfa-topo1/rt1/zebra.conf16
-rw-r--r--tests/topotests/isis-lfa-topo1/rt2/isisd.conf39
-rw-r--r--tests/topotests/isis-lfa-topo1/rt2/step1/show_ipv6_route.ref162
-rw-r--r--tests/topotests/isis-lfa-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis-lfa-topo1/rt2/zebra.conf16
-rw-r--r--tests/topotests/isis-lfa-topo1/rt3/isisd.conf38
-rw-r--r--tests/topotests/isis-lfa-topo1/rt3/step1/show_ipv6_route.ref188
-rw-r--r--tests/topotests/isis-lfa-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis-lfa-topo1/rt3/zebra.conf16
-rw-r--r--tests/topotests/isis-lfa-topo1/rt4/isisd.conf32
-rw-r--r--tests/topotests/isis-lfa-topo1/rt4/step1/show_ipv6_route.ref172
-rw-r--r--tests/topotests/isis-lfa-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis-lfa-topo1/rt4/zebra.conf16
-rw-r--r--tests/topotests/isis-lfa-topo1/rt5/isisd.conf32
-rw-r--r--tests/topotests/isis-lfa-topo1/rt5/step1/show_ipv6_route.ref176
-rw-r--r--tests/topotests/isis-lfa-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis-lfa-topo1/rt5/zebra.conf16
-rw-r--r--tests/topotests/isis-lfa-topo1/rt6/isisd.conf31
-rw-r--r--tests/topotests/isis-lfa-topo1/rt6/step1/show_ipv6_route.ref172
-rw-r--r--tests/topotests/isis-lfa-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis-lfa-topo1/rt6/zebra.conf16
-rw-r--r--tests/topotests/isis-lfa-topo1/rt7/isisd.conf51
-rw-r--r--tests/topotests/isis-lfa-topo1/rt7/step1/show_ipv6_route.ref186
-rw-r--r--tests/topotests/isis-lfa-topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref101
-rw-r--r--tests/topotests/isis-lfa-topo1/rt7/zebra.conf16
-rwxr-xr-xtests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py545
-rw-r--r--yang/frr-isisd.yang153
69 files changed, 7844 insertions, 247 deletions
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst
index 81333a216..f991e3f07 100644
--- a/doc/user/isisd.rst
+++ b/doc/user/isisd.rst
@@ -175,6 +175,35 @@ ISIS Timer
Set minimum interval between consecutive SPF calculations in seconds.
+.. _isis-fast-reroute:
+
+ISIS Fast-Reroute
+=================
+
+.. index:: spf prefix-priority [critical | high | medium] WORD
+.. clicmd:: spf prefix-priority [critical | high | medium] WORD
+
+.. index:: spf prefix-priority [critical | high | medium] WORD
+.. clicmd:: no spf prefix-priority [critical | high | medium] [WORD]
+
+ Assign a priority to the prefixes that match the specified access-list.
+
+.. index:: fast-reroute priority-limit [critical | high | medium] [level-1 | level-2]
+.. clicmd:: [no] fast-reroute priority-limit [critical | high | medium] [level-1 | level-2]
+
+ Limit LFA backup computation up to the specified prefix priority.
+
+.. index:: fast-reroute lfa tiebreaker [downstream | lowest-backup-metric | node-protecting] index (1-255) [level-1 | level-2]
+.. clicmd:: [no] fast-reroute lfa tiebreaker [downstream | lowest-backup-metric | node-protecting] index (1-255) [level-1 | level-2]
+
+ Configure a tie-breaker for multiple LFA backups. Lower indexes are processed
+ first.
+
+.. index:: fast-reroute load-sharing disable [level-1 | level-2]
+.. clicmd:: [no] fast-reroute load-sharing disable [level-1 | level-2]
+
+ Disable load sharing across multiple LFA backups.
+
.. _isis-region:
ISIS region
@@ -356,6 +385,16 @@ ISIS interface
Enable or disable :rfc:`5303` Three-Way Handshake for P2P adjacencies.
Three-Way Handshake is enabled by default.
+.. index:: isis fast-reroute lfa [level-1 | level-2]
+.. clicmd:: [no] isis fast-reroute lfa [level-1 | level-2]
+
+ Enable per-prefix LFA fast reroute link protection.
+
+.. index:: isis fast-reroute lfa [level-1 | level-2] exclude interface IFNAME
+.. clicmd:: [no] isis fast-reroute lfa [level-1 | level-2] exclude interface IFNAME
+
+ Exclude an interface from the LFA backup nexthop computation.
+
.. index:: isis fast-reroute ti-lfa [level-1|level-2] [node-protection]
.. clicmd:: [no] isis fast-reroute ti-lfa [level-1|level-2] [node-protection]
@@ -430,6 +469,12 @@ Showing ISIS information
Show the ISIS routing table, as determined by the most recent SPF
calculation.
+.. index:: show isis fast-reroute summary [level-1|level-2]
+.. clicmd:: show isis fast-reroute summary [level-1|level-2]
+
+ Show information about the number of prefixes having LFA protection,
+ and network-wide LFA coverage.
+
.. _isis-traffic-engineering:
Traffic Engineering
@@ -641,13 +686,13 @@ Debugging ISIS
IS-IS Segment Routing events.
-.. index:: debug isis ti-lfa
-.. clicmd:: debug isis ti-lfa
+.. index:: debug isis lfa
+.. clicmd:: debug isis lfa
-.. index:: debug isis ti-lfa
-.. clicmd:: no debug isis ti-lfa
+.. index:: debug isis lfa
+.. clicmd:: no debug isis lfa
- IS-IS TI-LFA events.
+ IS-IS LFA events.
.. index:: show debugging isis
.. clicmd:: show debugging isis
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index e3c70264f..2580a7c43 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -140,6 +140,8 @@ struct isis_circuit *isis_circuit_new(struct isis *isis)
#endif /* ifndef FABRICD */
circuit_mt_init(circuit);
+ isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL1);
+ isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL2);
QOBJ_REG(circuit, isis_circuit);
@@ -156,6 +158,8 @@ void isis_circuit_del(struct isis_circuit *circuit)
isis_circuit_if_unbind(circuit, circuit->interface);
circuit_mt_finish(circuit);
+ isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL1);
+ isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL2);
/* and lastly the circuit itself */
XFREE(MTYPE_ISIS_CIRCUIT, circuit);
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
index b4b03bf6b..e736d8fb1 100644
--- a/isisd/isis_circuit.h
+++ b/isisd/isis_circuit.h
@@ -141,6 +141,8 @@ struct isis_circuit {
bool disable_threeway_adj;
struct bfd_info *bfd_info;
struct ldp_sync_info *ldp_sync_info;
+ bool lfa_protection[ISIS_LEVELS];
+ struct hash *lfa_excluded_ifaces[ISIS_LEVELS];
bool tilfa_protection[ISIS_LEVELS];
bool tilfa_node_protection[ISIS_LEVELS];
/*
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 6c6d88ad5..1f0bebaf4 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -1137,6 +1137,54 @@ void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode,
}
/*
+ * XPath: /frr-isisd:isis/instance/spf/prefix-priorities/medium/access-list-name
+ */
+DEFPY_YANG(spf_prefix_priority, spf_prefix_priority_cmd,
+ "spf prefix-priority <critical|high|medium>$priority WORD$acl_name",
+ "SPF configuration\n"
+ "Configure a prefix priority list\n"
+ "Specify critical priority prefixes\n"
+ "Specify high priority prefixes\n"
+ "Specify medium priority prefixes\n"
+ "Access-list name\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, XPATH_MAXLEN,
+ "./spf/prefix-priorities/%s/access-list-name", priority);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, acl_name);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_spf_prefix_priority, no_spf_prefix_priority_cmd,
+ "no spf prefix-priority <critical|high|medium>$priority [WORD]",
+ NO_STR
+ "SPF configuration\n"
+ "Configure a prefix priority list\n"
+ "Specify critical priority prefixes\n"
+ "Specify high priority prefixes\n"
+ "Specify medium priority prefixes\n"
+ "Access-list name\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, XPATH_MAXLEN,
+ "./spf/prefix-priorities/%s/access-list-name", priority);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_spf_prefix_priority(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " spf prefix-priority %s %s\n",
+ dnode->parent->schema->name,
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
* XPath: /frr-isisd:isis/instance/purge-originator
*/
DEFPY_YANG(area_purge_originator, area_purge_originator_cmd, "[no] purge-originator",
@@ -1729,6 +1777,176 @@ void cli_show_isis_prefix_sid(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, "\n");
}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/lfa/priority-limit
+ */
+DEFPY_YANG (isis_frr_lfa_priority_limit,
+ isis_frr_lfa_priority_limit_cmd,
+ "[no] fast-reroute priority-limit <critical|high|medium>$priority [<level-1|level-2>$level]",
+ NO_STR
+ "Configure Fast ReRoute\n"
+ "Limit backup computation up to the prefix priority\n"
+ "Compute for critical priority prefixes only\n"
+ "Compute for critical & high priority prefixes\n"
+ "Compute for critical, high & medium priority prefixes\n"
+ "Set priority-limit for level-1 only\n"
+ "Set priority-limit for level-2 only\n")
+{
+ if (!level || strmatch(level, "level-1")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./fast-reroute/level-1/lfa/priority-limit",
+ NB_OP_DESTROY, NULL);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./fast-reroute/level-1/lfa/priority-limit",
+ NB_OP_CREATE, priority);
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./fast-reroute/level-2/lfa/priority-limit",
+ NB_OP_DESTROY, NULL);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./fast-reroute/level-2/lfa/priority-limit",
+ NB_OP_CREATE, priority);
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_frr_lfa_priority_limit(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " fast-reroute priority-limit %s %s\n",
+ yang_dnode_get_string(dnode, NULL),
+ dnode->parent->parent->schema->name);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/lfa/tiebreaker
+ */
+DEFPY_YANG (isis_frr_lfa_tiebreaker,
+ isis_frr_lfa_tiebreaker_cmd,
+ "[no] fast-reroute lfa\
+ tiebreaker <downstream|lowest-backup-metric|node-protecting>$type\
+ index (1-255)$index\
+ [<level-1|level-2>$level]",
+ NO_STR
+ "Configure Fast ReRoute\n"
+ "LFA configuration\n"
+ "Configure tiebreaker for multiple backups\n"
+ "Prefer backup path via downstream node\n"
+ "Prefer backup path with lowest total metric\n"
+ "Prefer node protecting backup path\n"
+ "Set preference order among tiebreakers\n"
+ "Index\n"
+ "Configure tiebreaker for level-1 only\n"
+ "Configure tiebreaker for level-2 only\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ if (!level || strmatch(level, "level-1")) {
+ if (no) {
+ snprintf(
+ xpath, XPATH_MAXLEN,
+ "./fast-reroute/level-1/lfa/tiebreaker[index='%s']",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ } else {
+ snprintf(
+ xpath, XPATH_MAXLEN,
+ "./fast-reroute/level-1/lfa/tiebreaker[index='%s']/type",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, type);
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ snprintf(
+ xpath, XPATH_MAXLEN,
+ "./fast-reroute/level-2/lfa/tiebreaker[index='%s']",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ } else {
+ snprintf(
+ xpath, XPATH_MAXLEN,
+ "./fast-reroute/level-2/lfa/tiebreaker[index='%s']/type",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, type);
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_frr_lfa_tiebreaker(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " fast-reroute lfa tiebreaker %s index %s %s\n",
+ yang_dnode_get_string(dnode, "./type"),
+ yang_dnode_get_string(dnode, "./index"),
+ dnode->parent->parent->schema->name);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/lfa/load-sharing
+ */
+DEFPY_YANG (isis_frr_lfa_load_sharing,
+ isis_frr_lfa_load_sharing_cmd,
+ "[no] fast-reroute load-sharing disable [<level-1|level-2>$level]",
+ NO_STR
+ "Configure Fast ReRoute\n"
+ "Load share prefixes across multiple backups\n"
+ "Disable load sharing\n"
+ "Disable load sharing for level-1 only\n"
+ "Disable load sharing for level-2 only\n")
+{
+ if (!level || strmatch(level, "level-1")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-1/lfa/load-sharing",
+ NB_OP_DESTROY, "true");
+ } else {
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-1/lfa/load-sharing",
+ NB_OP_CREATE, "false");
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-2/lfa/load-sharing",
+ NB_OP_DESTROY, "true");
+ } else {
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-2/lfa/load-sharing",
+ NB_OP_CREATE, "false");
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_frr_lfa_load_sharing(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (!yang_dnode_get_bool(dnode, NULL))
+ vty_out(vty, " no");
+
+ vty_out(vty, " fast-reroute load-sharing disable %s\n",
+ dnode->parent->parent->schema->name);
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/passive
*/
@@ -2386,7 +2604,164 @@ void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode,
}
/*
- * XPath: /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/ti-lfa/enable
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/fast-reroute
+ */
+void cli_show_ip_isis_frr(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ bool l1_enabled, l2_enabled;
+ bool l1_node_protection, l2_node_protection;
+
+ /* Classic LFA */
+ l1_enabled = yang_dnode_get_bool(dnode, "./level-1/lfa/enable");
+ l2_enabled = yang_dnode_get_bool(dnode, "./level-2/lfa/enable");
+
+ if (l1_enabled || l2_enabled) {
+ if (l1_enabled == l2_enabled) {
+ vty_out(vty, " isis fast-reroute lfa\n");
+ vty_out(vty, "\n");
+ } else {
+ if (l1_enabled)
+ vty_out(vty,
+ " isis fast-reroute lfa level-1\n");
+ if (l2_enabled)
+ vty_out(vty,
+ " isis fast-reroute lfa level-2\n");
+ }
+ }
+
+ /* TI-LFA */
+ l1_enabled = yang_dnode_get_bool(dnode, "./level-1/ti-lfa/enable");
+ l2_enabled = yang_dnode_get_bool(dnode, "./level-2/ti-lfa/enable");
+ l1_node_protection =
+ yang_dnode_get_bool(dnode, "./level-1/ti-lfa/node-protection");
+ l2_node_protection =
+ yang_dnode_get_bool(dnode, "./level-2/ti-lfa/node-protection");
+
+ if (l1_enabled || l2_enabled) {
+ if (l1_enabled == l2_enabled
+ && l1_node_protection == l2_node_protection) {
+ vty_out(vty, " isis fast-reroute ti-lfa");
+ if (l1_node_protection)
+ vty_out(vty, " node-protection");
+ vty_out(vty, "\n");
+ } else {
+ if (l1_enabled) {
+ vty_out(vty,
+ " isis fast-reroute ti-lfa level-1");
+ if (l1_node_protection)
+ vty_out(vty, " node-protection");
+ vty_out(vty, "\n");
+ }
+ if (l2_enabled) {
+ vty_out(vty,
+ " isis fast-reroute ti-lfa level-2");
+ if (l2_node_protection)
+ vty_out(vty, " node-protection");
+ vty_out(vty, "\n");
+ }
+ }
+ }
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/lfa/enable
+ */
+DEFPY(isis_lfa, isis_lfa_cmd,
+ "[no] isis fast-reroute lfa [level-1|level-2]$level",
+ NO_STR
+ "IS-IS routing protocol\n"
+ "Interface IP Fast-reroute configuration\n"
+ "Enable LFA computation\n"
+ "Enable LFA computation for Level 1 only\n"
+ "Enable LFA computation for Level 2 only\n")
+{
+ if (!level || strmatch(level, "level-1")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/lfa/enable",
+ NB_OP_MODIFY, "false");
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/lfa/enable",
+ NB_OP_MODIFY, "true");
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/lfa/enable",
+ NB_OP_MODIFY, "false");
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/lfa/enable",
+ NB_OP_MODIFY, "true");
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/lfa/exclude-interface
+ */
+DEFPY(isis_lfa_exclude_interface, isis_lfa_exclude_interface_cmd,
+ "[no] isis fast-reroute lfa [level-1|level-2]$level exclude interface IFNAME$ifname",
+ NO_STR
+ "IS-IS routing protocol\n"
+ "Interface IP Fast-reroute configuration\n"
+ "Enable LFA computation\n"
+ "Enable LFA computation for Level 1 only\n"
+ "Enable LFA computation for Level 2 only\n"
+ "FRR exclusion information\n"
+ "Exclude an interface from computation\n"
+ "Interface name\n")
+{
+ if (!level || strmatch(level, "level-1")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/lfa/exclude-interface",
+ NB_OP_DESTROY, ifname);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/lfa/exclude-interface",
+ NB_OP_CREATE, ifname);
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/lfa/exclude-interface",
+ NB_OP_DESTROY, ifname);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/lfa/exclude-interface",
+ NB_OP_CREATE, ifname);
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_frr_lfa_exclude_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " isis fast-reroute lfa %s exclude interface %s\n",
+ dnode->parent->parent->schema->name,
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/ti-lfa/enable
*/
DEFPY(isis_ti_lfa, isis_ti_lfa_cmd,
"[no] isis fast-reroute ti-lfa [level-1|level-2]$level [node-protection$node_protection]",
@@ -2446,41 +2821,6 @@ DEFPY(isis_ti_lfa, isis_ti_lfa_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-void cli_show_ip_isis_ti_lfa(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults)
-{
- bool l1_enabled, l2_enabled;
- bool l1_node_protection, l2_node_protection;
-
- l1_enabled = yang_dnode_get_bool(dnode, "./level-1/ti-lfa/enable");
- l2_enabled = yang_dnode_get_bool(dnode, "./level-2/ti-lfa/enable");
- l1_node_protection =
- yang_dnode_get_bool(dnode, "./level-1/ti-lfa/node-protection");
- l2_node_protection =
- yang_dnode_get_bool(dnode, "./level-2/ti-lfa/node-protection");
-
- if (l1_enabled == l2_enabled
- && l1_node_protection == l2_node_protection) {
- vty_out(vty, " isis fast-reroute ti-lfa");
- if (l1_node_protection)
- vty_out(vty, " node-protection");
- vty_out(vty, "\n");
- } else {
- if (l1_enabled) {
- vty_out(vty, " isis fast-reroute ti-lfa level-1");
- if (l1_node_protection)
- vty_out(vty, " node-protection");
- vty_out(vty, "\n");
- }
- if (l2_enabled) {
- vty_out(vty, " isis fast-reroute ti-lfa level-2");
- if (l2_node_protection)
- vty_out(vty, " node-protection");
- vty_out(vty, "\n");
- }
- }
-}
-
/*
* XPath: /frr-isisd:isis/instance/log-adjacency-changes
*/
@@ -2714,6 +3054,8 @@ void isis_cli_init(void)
install_element(ISIS_NODE, &spf_interval_cmd);
install_element(ISIS_NODE, &no_spf_interval_cmd);
+ install_element(ISIS_NODE, &spf_prefix_priority_cmd);
+ install_element(ISIS_NODE, &no_spf_prefix_priority_cmd);
install_element(ISIS_NODE, &spf_delay_ietf_cmd);
install_element(ISIS_NODE, &no_spf_delay_ietf_cmd);
@@ -2740,6 +3082,9 @@ void isis_cli_init(void)
install_element(ISIS_NODE, &no_isis_sr_node_msd_cmd);
install_element(ISIS_NODE, &isis_sr_prefix_sid_cmd);
install_element(ISIS_NODE, &no_isis_sr_prefix_sid_cmd);
+ install_element(ISIS_NODE, &isis_frr_lfa_priority_limit_cmd);
+ install_element(ISIS_NODE, &isis_frr_lfa_tiebreaker_cmd);
+ install_element(ISIS_NODE, &isis_frr_lfa_load_sharing_cmd);
install_element(INTERFACE_NODE, &isis_passive_cmd);
@@ -2775,6 +3120,8 @@ void isis_cli_init(void)
install_element(INTERFACE_NODE, &isis_priority_cmd);
install_element(INTERFACE_NODE, &no_isis_priority_cmd);
+ install_element(INTERFACE_NODE, &isis_lfa_cmd);
+ install_element(INTERFACE_NODE, &isis_lfa_exclude_interface_cmd);
install_element(INTERFACE_NODE, &isis_ti_lfa_cmd);
install_element(ISIS_NODE, &log_adj_changes_cmd);
diff --git a/isisd/isis_ldp_sync.c b/isisd/isis_ldp_sync.c
index 988af64c4..3b1faffe5 100644
--- a/isisd/isis_ldp_sync.c
+++ b/isisd/isis_ldp_sync.c
@@ -429,20 +429,24 @@ void isis_ldp_sync_set_if_metric(struct isis_circuit *circuit, bool run_regen)
if (circuit->area->newmetric) {
ldp_sync_info->metric[0] =
circuit->te_metric[0];
- circuit->te_metric[0] = LDP_ISIS_LSINFINITY;
+ circuit->te_metric[0] =
+ ISIS_WIDE_METRIC_INFINITY;
} else {
ldp_sync_info->metric[0] = circuit->metric[0];
- circuit->metric[0] = LDP_ISIS_LSINFINITY_NL;
+ circuit->metric[0] =
+ ISIS_NARROW_METRIC_INFINITY;
}
}
if (circuit->is_type & IS_LEVEL_2) {
if (circuit->area->newmetric) {
ldp_sync_info->metric[1] =
circuit->te_metric[1];
- circuit->te_metric[1] = LDP_ISIS_LSINFINITY;
+ circuit->te_metric[1] =
+ ISIS_WIDE_METRIC_INFINITY;
} else {
ldp_sync_info->metric[1] = circuit->metric[1];
- circuit->metric[1] = LDP_ISIS_LSINFINITY_NL;
+ circuit->metric[1] =
+ ISIS_NARROW_METRIC_INFINITY;
}
}
} else {
diff --git a/isisd/isis_ldp_sync.h b/isisd/isis_ldp_sync.h
index 6017cdf00..61ac94607 100644
--- a/isisd/isis_ldp_sync.h
+++ b/isisd/isis_ldp_sync.h
@@ -20,9 +20,6 @@
#ifndef _ZEBRA_ISIS_LDP_SYNC_H
#define _ZEBRA_ISIS_LDP_SYNC_H
-#define LDP_ISIS_LSINFINITY 0xFFFFFE /* wide link metric */
-#define LDP_ISIS_LSINFINITY_NL 62 /* narrow link metric */
-
/* Macro to log debug message */
#define ils_debug(...) \
do { \
diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c
index f22e4a708..fc6b435b6 100644
--- a/isisd/isis_lfa.c
+++ b/isisd/isis_lfa.c
@@ -40,6 +40,8 @@
#include "isisd/isis_errors.h"
DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_NODE, "ISIS SPF Node");
+DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_TIEBREAKER, "ISIS LFA Tiebreaker");
+DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_EXCL_IFACE, "ISIS LFA Excluded Interface");
static inline int isis_spf_node_compare(const struct isis_spf_node *a,
const struct isis_spf_node *b)
@@ -120,6 +122,185 @@ struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes,
}
/**
+ * LFA tiebreaker RB-tree comparison function.
+ *
+ * @param a First LFA tiebreaker
+ * @param b Second LFA tiebreaker
+ *
+ * @return -1 (a < b), 0 (a == b) or +1 (a > b)
+ */
+int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
+ const struct lfa_tiebreaker *b)
+{
+ if (a->index < b->index)
+ return -1;
+ if (a->index > b->index)
+ return 1;
+
+ return a->type - b->type;
+}
+
+/**
+ * Initialize list of LFA tie-breakers.
+ *
+ * @param area IS-IS area
+ * @param level IS-IS level
+ */
+void isis_lfa_tiebreakers_init(struct isis_area *area, int level)
+{
+ lfa_tiebreaker_tree_init(&area->lfa_tiebreakers[level - 1]);
+}
+
+/**
+ * Clear list of LFA tie-breakers, releasing all allocated memory.
+ *
+ * @param area IS-IS area
+ * @param level IS-IS level
+ */
+void isis_lfa_tiebreakers_clear(struct isis_area *area, int level)
+{
+ while (lfa_tiebreaker_tree_count(&area->lfa_tiebreakers[level - 1])
+ > 0) {
+ struct lfa_tiebreaker *tie_b;
+
+ tie_b = lfa_tiebreaker_tree_first(
+ &area->lfa_tiebreakers[level - 1]);
+ isis_lfa_tiebreaker_delete(area, level, tie_b);
+ }
+}
+
+/**
+ * Add new LFA tie-breaker to list of LFA tie-breakers.
+ *
+ * @param area IS-IS area
+ * @param level IS-IS level
+ * @param index LFA tie-breaker index
+ * @param type LFA tie-breaker type
+ *
+ * @return Pointer to new LFA tie-breaker structure.
+ */
+struct lfa_tiebreaker *isis_lfa_tiebreaker_add(struct isis_area *area,
+ int level, uint8_t index,
+ enum lfa_tiebreaker_type type)
+{
+ struct lfa_tiebreaker *tie_b;
+
+ tie_b = XCALLOC(MTYPE_ISIS_LFA_TIEBREAKER, sizeof(*tie_b));
+ tie_b->index = index;
+ tie_b->type = type;
+ tie_b->area = area;
+ lfa_tiebreaker_tree_add(&area->lfa_tiebreakers[level - 1], tie_b);
+
+ return tie_b;
+}
+
+/**
+ * Remove LFA tie-breaker from list of LFA tie-breakers.
+ *
+ * @param area IS-IS area
+ * @param level IS-IS level
+ * @param tie_b Pointer to LFA tie-breaker structure
+ */
+void isis_lfa_tiebreaker_delete(struct isis_area *area, int level,
+ struct lfa_tiebreaker *tie_b)
+{
+ lfa_tiebreaker_tree_del(&area->lfa_tiebreakers[level - 1], tie_b);
+ XFREE(MTYPE_ISIS_LFA_TIEBREAKER, tie_b);
+}
+
+static bool lfa_excl_interface_hash_cmp(const void *value1, const void *value2)
+{
+ return strmatch(value1, value2);
+}
+
+static unsigned int lfa_excl_interface_hash_make(const void *value)
+{
+ return string_hash_make(value);
+}
+
+static void *lfa_excl_interface_hash_alloc(void *p)
+{
+ return XSTRDUP(MTYPE_ISIS_LFA_EXCL_IFACE, p);
+}
+
+static void lfa_excl_interface_hash_free(void *arg)
+{
+ XFREE(MTYPE_ISIS_LFA_EXCL_IFACE, arg);
+}
+
+/**
+ * Initialize hash table of LFA excluded interfaces.
+ *
+ * @param circuit IS-IS interface
+ * @param level IS-IS level
+ */
+void isis_lfa_excluded_ifaces_init(struct isis_circuit *circuit, int level)
+{
+ circuit->lfa_excluded_ifaces[level - 1] = hash_create(
+ lfa_excl_interface_hash_make, lfa_excl_interface_hash_cmp,
+ "LFA Excluded Interfaces");
+}
+
+/**
+ * Clear hash table of LFA excluded interfaces, releasing all allocated memory.
+ *
+ * @param nodes List of SPF nodes
+ */
+void isis_lfa_excluded_ifaces_clear(struct isis_circuit *circuit, int level)
+{
+ hash_clean(circuit->lfa_excluded_ifaces[level - 1],
+ lfa_excl_interface_hash_free);
+}
+
+/**
+ * Add new interface to hash table of excluded interfaces.
+ *
+ * @param circuit IS-IS interface
+ * @param level IS-IS level
+ * @param ifname Excluded interface name
+ */
+void isis_lfa_excluded_iface_add(struct isis_circuit *circuit, int level,
+ const char *ifname)
+{
+ hash_get(circuit->lfa_excluded_ifaces[level - 1], (char *)ifname,
+ lfa_excl_interface_hash_alloc);
+}
+
+/**
+ * Remove interface from hash table of excluded interfaces.
+ *
+ * @param circuit IS-IS interface
+ * @param level IS-IS level
+ * @param ifname Excluded interface name
+ */
+void isis_lfa_excluded_iface_delete(struct isis_circuit *circuit, int level,
+ const char *ifname)
+{
+ char *found;
+
+ found = hash_lookup(circuit->lfa_excluded_ifaces[level - 1],
+ (char *)ifname);
+ if (found) {
+ hash_release(circuit->lfa_excluded_ifaces[level - 1], found);
+ lfa_excl_interface_hash_free(found);
+ }
+}
+
+/**
+ * Lookup excluded interface.
+ *
+ * @param circuit IS-IS interface
+ * @param level IS-IS level
+ * @param ifname Excluded interface name
+ */
+bool isis_lfa_excluded_iface_check(struct isis_circuit *circuit, int level,
+ const char *ifname)
+{
+ return hash_lookup(circuit->lfa_excluded_ifaces[level - 1],
+ (char *)ifname);
+}
+
+/**
* Check if a given IS-IS adjacency needs to be excised when computing the SPF
* post-convergence tree.
*
@@ -438,10 +619,10 @@ static int tilfa_build_repair_list(struct isis_spftree *spftree_pc,
uint32_t sid_index;
mpls_label_t label_qnode;
- if (IS_DEBUG_TILFA) {
+ if (IS_DEBUG_LFA) {
vid2string(vertex, buf, sizeof(buf));
- zlog_debug("ISIS-TI-LFA: vertex %s %s",
- vtype2string(vertex->type), buf);
+ zlog_debug("ISIS-LFA: vertex %s %s", vtype2string(vertex->type),
+ buf);
}
/* Push original Prefix-SID label when necessary. */
@@ -450,9 +631,9 @@ static int tilfa_build_repair_list(struct isis_spftree *spftree_pc,
assert(pvertex);
sid_index = vertex->N.ip.sr.sid.value;
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: pushing Prefix-SID to %pFX (index %u)",
+ "ISIS-LFA: pushing Prefix-SID to %pFX (index %u)",
&vertex->N.ip.p.dest, sid_index);
sid_dest.type = TILFA_SID_PREFIX;
sid_dest.value.index.value = sid_index;
@@ -481,14 +662,14 @@ static int tilfa_build_repair_list(struct isis_spftree *spftree_pc,
label_qnode = tilfa_find_qnode_adj_sid(spftree_pc, vertex->N.id,
vertex_child->N.id);
if (label_qnode == MPLS_INVALID_LABEL) {
- zlog_warn("ISIS-TI-LFA: failed to find %s->%s Adj-SID",
+ zlog_warn("ISIS-LFA: failed to find %s->%s Adj-SID",
print_sys_hostname(vertex->N.id),
print_sys_hostname(vertex_child->N.id));
return -1;
}
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: pushing %s->%s Adj-SID (label %u)",
+ "ISIS-LFA: pushing %s->%s Adj-SID (label %u)",
print_sys_hostname(vertex->N.id),
print_sys_hostname(vertex_child->N.id),
label_qnode);
@@ -501,17 +682,17 @@ static int tilfa_build_repair_list(struct isis_spftree *spftree_pc,
if (is_pnode) {
/* The same P-node can't be used more than once. */
if (isis_spf_node_find(used_pnodes, vertex->N.id)) {
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: skipping already used P-node");
+ "ISIS-LFA: skipping already used P-node");
return 0;
}
isis_spf_node_new(used_pnodes, vertex->N.id);
if (!vertex_child) {
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: destination is within Ext-P-Space");
+ "ISIS-LFA: destination is within Ext-P-Space");
return 0;
}
@@ -519,20 +700,29 @@ static int tilfa_build_repair_list(struct isis_spftree *spftree_pc,
tilfa_find_pnode_prefix_sid(spftree_pc, vertex->N.id);
if (sid_index == UINT32_MAX) {
zlog_warn(
- "ISIS-TI-LFA: failed to find Prefix-SID corresponding to PQ-node %s",
+ "ISIS-LFA: failed to find Prefix-SID corresponding to PQ-node %s",
print_sys_hostname(vertex->N.id));
return -1;
}
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: pushing Node-SID to %s (index %u)",
+ "ISIS-LFA: pushing Node-SID to %s (index %u)",
print_sys_hostname(vertex->N.id), sid_index);
sid_pnode.type = TILFA_SID_PREFIX;
sid_pnode.value.index.value = sid_index;
listnode_add_head(repair_list, &sid_pnode);
/* Apply repair list. */
+ if (spftree_pc->area->srdb.config.msd
+ && listcount(repair_list)
+ > spftree_pc->area->srdb.config.msd) {
+ zlog_warn(
+ "ISIS-LFA: list of repair segments exceeds locally configured MSD (%u > %u)",
+ listcount(repair_list),
+ spftree_pc->area->srdb.config.msd);
+ return -1;
+ }
if (tilfa_repair_list_apply(spftree_pc, vertex_dest, vertex,
repair_list)
!= 0)
@@ -610,36 +800,25 @@ spf_adj_check_is_affected(const struct isis_spf_adj *sadj,
return false;
}
-/* Check if the given SPF vertex needs LFA protection. */
-static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
- const struct isis_vertex *vertex)
+/* Check if the given vertex is affected by a given local failure. */
+static bool
+spf_vertex_check_is_affected(const struct isis_vertex *vertex,
+ const uint8_t *root_sysid,
+ const struct lfa_protected_resource *resource)
{
- struct isis_vertex *vertex_old;
+ struct isis_vertex_adj *vadj;
struct listnode *node;
size_t affected_nhs = 0;
- struct isis_vertex_adj *vadj;
/* Local routes don't need protection. */
if (VTYPE_IP(vertex->type) && vertex->depth == 1)
return false;
- /* Only local adjacencies need Adj-SID protection. */
- if (VTYPE_IS(vertex->type)
- && !isis_adj_find(spftree_pc->area, spftree_pc->level,
- vertex->N.id))
- return false;
-
- vertex_old = isis_find_vertex(&spftree_pc->lfa.old.spftree->paths,
- &vertex->N, vertex->type);
- if (!vertex_old)
- return false;
-
- for (ALL_LIST_ELEMENTS_RO(vertex_old->Adj_N, node, vadj)) {
+ for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, vadj)) {
struct isis_spf_adj *sadj = vadj->sadj;
- if (spf_adj_check_is_affected(
- sadj, &spftree_pc->lfa.protected_resource,
- spftree_pc->sysid, false))
+ if (spf_adj_check_is_affected(sadj, resource, root_sysid,
+ false))
affected_nhs++;
}
@@ -647,12 +826,34 @@ static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
* No need to compute backup paths for ECMP routes, except if all
* primary nexthops share the same broadcast interface.
*/
- if (listcount(vertex_old->Adj_N) == affected_nhs)
+ if (listcount(vertex->Adj_N) == affected_nhs)
return true;
return false;
}
+/* Check if a given TI-LFA post-convergence SPF vertex needs protection. */
+static bool tilfa_check_needs_protection(const struct isis_spftree *spftree_pc,
+ const struct isis_vertex *vertex)
+{
+ struct isis_vertex *vertex_old;
+
+ /* Only local adjacencies need Adj-SID protection. */
+ if (VTYPE_IS(vertex->type)
+ && !isis_adj_find(spftree_pc->area, spftree_pc->level,
+ vertex->N.id))
+ return false;
+
+ vertex_old = isis_find_vertex(&spftree_pc->lfa.old.spftree->paths,
+ &vertex->N, vertex->type);
+ if (!vertex_old)
+ return false;
+
+ return spf_vertex_check_is_affected(
+ vertex_old, spftree_pc->sysid,
+ &spftree_pc->lfa.protected_resource);
+}
+
/**
* Check if the given SPF vertex needs protection and, if so, compute and
* install the corresponding repair paths.
@@ -662,7 +863,8 @@ static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
*
* @return 0 if the vertex needs to be protected, -1 otherwise
*/
-int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
+int isis_tilfa_check(struct isis_spftree *spftree_pc,
+ struct isis_vertex *vertex)
{
struct isis_spf_nodes used_pnodes;
char buf[VID2STR_BUFFER];
@@ -672,13 +874,13 @@ int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
if (!spftree_pc->area->srdb.enabled)
return -1;
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
vid2string(vertex, buf, sizeof(buf));
- if (!lfa_check_needs_protection(spftree_pc, vertex)) {
- if (IS_DEBUG_TILFA)
+ if (!tilfa_check_needs_protection(spftree_pc, vertex)) {
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: %s %s unaffected by %s",
+ "ISIS-LFA: %s %s unaffected by %s",
vtype2string(vertex->type), buf,
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
@@ -697,9 +899,9 @@ int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
if (adj
&& isis_sr_adj_sid_find(adj, spftree_pc->family,
ISIS_SR_LAN_BACKUP)) {
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: %s %s already covered by node protection",
+ "ISIS-LFA: %s %s already covered by node protection",
vtype2string(vertex->type), buf);
return -1;
@@ -710,18 +912,18 @@ int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
route_table = spftree_pc->lfa.old.spftree->route_table_backup;
if (route_node_lookup(route_table, &vertex->N.ip.p.dest)) {
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: %s %s already covered by node protection",
+ "ISIS-LFA: %s %s already covered by node protection",
vtype2string(vertex->type), buf);
return -1;
}
}
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: computing repair path(s) of %s %s w.r.t %s",
+ "ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
vtype2string(vertex->type), buf,
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
@@ -735,7 +937,11 @@ int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
isis_spf_node_list_clear(&used_pnodes);
list_delete(&repair_list);
if (ret != 0)
- zlog_warn("ISIS-TI-LFA: failed to compute repair path(s)");
+ zlog_warn(
+ "ISIS-LFA: failed to compute repair path(s) of %s %s w.r.t %s",
+ vtype2string(vertex->type), buf,
+ lfa_protected_resource2str(
+ &spftree_pc->lfa.protected_resource));
return ret;
}
@@ -775,8 +981,8 @@ static bool vertex_is_affected(struct isis_spftree *spftree_root,
char buf1[VID2STR_BUFFER];
char buf2[VID2STR_BUFFER];
- if (IS_DEBUG_TILFA)
- zlog_debug("ISIS-TI-LFA: vertex %s parent %s",
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: vertex %s parent %s",
vid2string(vertex, buf1, sizeof(buf1)),
vid2string(pvertex, buf2, sizeof(buf2)));
@@ -853,15 +1059,15 @@ static void lfa_calc_reach_nodes(struct isis_spftree *spftree,
if (isis_spf_node_find(nodes, vertex->N.id))
continue;
- if (IS_DEBUG_TILFA)
- zlog_debug("ISIS-TI-LFA: checking %s",
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: checking %s",
vid2string(vertex, buf, sizeof(buf)));
if (!vertex_is_affected(spftree_root, adj_nodes, p_space,
vertex, resource)) {
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: adding %s",
+ "ISIS-LFA: adding %s",
vid2string(vertex, buf, sizeof(buf)));
isis_spf_node_new(nodes, vertex->N.id);
@@ -875,7 +1081,7 @@ static void lfa_calc_reach_nodes(struct isis_spftree *spftree,
*
* @param spftree IS-IS SPF tree
*
- * @return Pointer to new SPF tree structure.
+ * @return Pointer to new SPF tree structure.
*/
struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree)
{
@@ -907,18 +1113,17 @@ static void lfa_calc_pq_spaces(struct isis_spftree *spftree_pc,
spftree_reverse = spftree_pc->lfa.old.spftree_reverse;
adj_nodes = &spftree->adj_nodes;
- if (IS_DEBUG_TILFA)
- zlog_debug("ISIS-TI-LFA: computing P-space (self)");
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing P-space (self)");
lfa_calc_reach_nodes(spftree, spftree, adj_nodes, true, resource,
&spftree_pc->lfa.p_space);
RB_FOREACH (adj_node, isis_spf_nodes, adj_nodes) {
if (spf_adj_node_is_affected(adj_node, resource,
spftree->sysid)) {
- if (IS_DEBUG_TILFA)
- zlog_debug(
- "ISIS-TI-LFA: computing Q-space (%s)",
- print_sys_hostname(adj_node->sysid));
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing Q-space (%s)",
+ print_sys_hostname(adj_node->sysid));
/*
* Compute the reverse SPF in the behalf of the node
@@ -932,10 +1137,9 @@ static void lfa_calc_pq_spaces(struct isis_spftree *spftree_pc,
resource,
&spftree_pc->lfa.q_space);
} else {
- if (IS_DEBUG_TILFA)
- zlog_debug(
- "ISIS-TI-LFA: computing P-space (%s)",
- print_sys_hostname(adj_node->sysid));
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing P-space (%s)",
+ print_sys_hostname(adj_node->sysid));
lfa_calc_reach_nodes(adj_node->lfa.spftree, spftree,
adj_nodes, true, resource,
&adj_node->lfa.p_space);
@@ -961,8 +1165,8 @@ struct isis_spftree *isis_tilfa_compute(struct isis_area *area,
struct isis_spftree *spftree_pc;
struct isis_spf_node *adj_node;
- if (IS_DEBUG_TILFA)
- zlog_debug("ISIS-TI-LFA: computing the P/Q spaces w.r.t. %s",
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing the P/Q spaces w.r.t. %s",
lfa_protected_resource2str(resource));
/* Populate list of nodes affected by link failure. */
@@ -987,9 +1191,9 @@ struct isis_spftree *isis_tilfa_compute(struct isis_area *area,
/* Compute the extended P-space and Q-space. */
lfa_calc_pq_spaces(spftree_pc, resource);
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-TI-LFA: computing the post convergence SPT w.r.t. %s",
+ "ISIS-LFA: computing the post convergence SPT w.r.t. %s",
lfa_protected_resource2str(resource));
/* Re-run SPF in the local node to find the post-convergence paths. */
@@ -1019,8 +1223,8 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree)
return -1;
RB_FOREACH (adj_node, isis_spf_nodes, &spftree->adj_nodes) {
- if (IS_DEBUG_TILFA)
- zlog_debug("ISIS-TI-LFA: running SPF on neighbor %s",
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: running SPF on neighbor %s",
print_sys_hostname(adj_node->sysid));
/* Compute the SPT on behalf of the neighbor. */
@@ -1034,20 +1238,532 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree)
return 0;
}
+/* Calculate the distance from the root node to the given IP destination. */
+static int lfa_calc_dist_destination(struct isis_spftree *spftree,
+ const struct isis_vertex *vertex_N,
+ uint32_t *distance)
+{
+ struct isis_vertex *vertex, *vertex_best = NULL;
+
+ switch (spftree->family) {
+ case AF_INET:
+ for (int vtype = VTYPE_IPREACH_INTERNAL;
+ vtype <= VTYPE_IPREACH_TE; vtype++) {
+ vertex = isis_find_vertex(
+ &spftree->paths, &vertex_N->N.ip.p.dest, vtype);
+ if (!vertex)
+ continue;
+
+ /* Pick vertex with the best metric. */
+ if (!vertex_best || vertex_best->d_N > vertex->d_N)
+ vertex_best = vertex;
+ }
+ break;
+ case AF_INET6:
+ for (int vtype = VTYPE_IP6REACH_INTERNAL;
+ vtype <= VTYPE_IP6REACH_EXTERNAL; vtype++) {
+ vertex = isis_find_vertex(
+ &spftree->paths, &vertex_N->N.ip.p.dest, vtype);
+ if (!vertex)
+ continue;
+
+ /* Pick vertex with the best metric. */
+ if (!vertex_best || vertex_best->d_N > vertex->d_N)
+ vertex_best = vertex;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!vertex_best)
+ return -1;
+
+ assert(VTYPE_IP(vertex_best->type));
+ vertex_best = listnode_head(vertex_best->parents);
+ *distance = vertex_best->d_N;
+
+ return 0;
+}
+
+/* Calculate the distance from the root node to the given node. */
+static int lfa_calc_dist_node(struct isis_spftree *spftree,
+ const uint8_t *sysid, uint32_t *distance)
+{
+ struct isis_vertex *vertex, *vertex_best = NULL;
+
+ for (int vtype = VTYPE_PSEUDO_IS; vtype <= VTYPE_NONPSEUDO_TE_IS;
+ vtype++) {
+ vertex = isis_find_vertex(&spftree->paths, sysid, vtype);
+ if (!vertex)
+ continue;
+
+ /* Pick vertex with the best metric. */
+ if (!vertex_best || vertex_best->d_N > vertex->d_N)
+ vertex_best = vertex;
+ }
+
+ if (!vertex_best)
+ return -1;
+
+ *distance = vertex_best->d_N;
+
+ return 0;
+}
+
+/*
+ * Check loop-free criterion (RFC 5286's inequality 1):
+ * - Dist_opt(N, D) < Dist_opt(N, S) + Dist_opt(S, D)
+ */
+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)
+{
+ struct isis_spf_node *node_N;
+ uint32_t dist_N_D;
+ uint32_t dist_N_S;
+ uint32_t dist_S_D;
+
+ node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
+ assert(node_N);
+
+ /* Distance from N to D. */
+ if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
+ &dist_N_D)
+ != 0)
+ return false;
+
+ /* Distance from N to S (or PN). */
+ if (CHECK_FLAG(sadj_primary->flags, F_ISIS_SPF_ADJ_BROADCAST)) {
+ static uint8_t pn_sysid[ISIS_SYS_ID_LEN + 1];
+
+ memcpy(pn_sysid, sadj_primary->id, ISIS_SYS_ID_LEN + 1);
+ if (lfa_calc_dist_node(node_N->lfa.spftree, pn_sysid, &dist_N_S)
+ != 0)
+ return false;
+ } else {
+ static uint8_t root_sysid[ISIS_SYS_ID_LEN + 1];
+
+ memcpy(root_sysid, spftree->sysid, ISIS_SYS_ID_LEN);
+ LSP_PSEUDO_ID(root_sysid) = 0;
+ if (lfa_calc_dist_node(node_N->lfa.spftree, root_sysid,
+ &dist_N_S)
+ != 0)
+ return false;
+ }
+
+ /* Distance from S (or PN) to D. */
+ vertex_S_D = listnode_head(vertex_S_D->parents);
+ dist_S_D = vertex_S_D->d_N;
+ if (CHECK_FLAG(sadj_primary->flags, F_ISIS_SPF_ADJ_BROADCAST))
+ dist_S_D -= sadj_primary->metric;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: loop-free check: %u < %u + %u", dist_N_D,
+ dist_N_S, dist_S_D);
+
+ if (dist_N_D < (dist_N_S + dist_S_D)) {
+ *lfa_metric = sadj_N->metric + dist_N_D;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Check loop-free criterion (RFC 5286's inequality 2):
+ * - Distance_opt(N, D) < Distance_opt(S, D)
+ */
+static bool clfa_downstream_check(struct isis_spftree *spftree,
+ struct isis_vertex *vertex_S_D,
+ struct isis_spf_adj *sadj_N)
+{
+ struct isis_spf_node *node_N;
+ uint32_t dist_N_D;
+ uint32_t dist_S_D;
+
+ node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
+ assert(node_N);
+
+ /* Distance from N to D. */
+ if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
+ &dist_N_D)
+ != 0)
+ return false;
+
+ /* Distance from S (or PN) to D. */
+ vertex_S_D = listnode_head(vertex_S_D->parents);
+ dist_S_D = vertex_S_D->d_N;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: downstream check: %u < %u", dist_N_D,
+ dist_S_D);
+
+ if (dist_N_D < dist_S_D)
+ return true;
+
+ return false;
+}
+
+/*
+ * Check loop-free criterion (RFC 5286's inequality 3):
+ * - Dist_opt(N, D) < Dist_opt(N, E) + Dist_opt(E, D)
+ */
+static bool clfa_node_protecting_check(struct isis_spftree *spftree,
+ struct isis_vertex *vertex_S_D,
+ struct isis_spf_adj *sadj_N,
+ struct isis_spf_adj *sadj_E)
+{
+ struct isis_spf_node *node_N, *node_E;
+ uint32_t dist_N_D;
+ uint32_t dist_N_E;
+ uint32_t dist_E_D;
+
+ node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
+ assert(node_N);
+ node_E = isis_spf_node_find(&spftree->adj_nodes, sadj_E->id);
+ assert(node_E);
+
+ /* Distance from N to D. */
+ if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
+ &dist_N_D)
+ != 0)
+ return false;
+
+ /* Distance from N to E. */
+ if (lfa_calc_dist_node(node_N->lfa.spftree, node_E->sysid, &dist_N_E)
+ != 0)
+ return false;
+
+ /* Distance from E to D. */
+ if (lfa_calc_dist_destination(node_E->lfa.spftree, vertex_S_D,
+ &dist_E_D)
+ != 0)
+ return false;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: node protecting check: %u < %u + %u",
+ dist_N_D, dist_N_E, dist_E_D);
+
+ return (dist_N_D < (dist_N_E + dist_E_D));
+}
+
+static struct list *
+isis_lfa_tiebreakers(struct isis_area *area, struct isis_circuit *circuit,
+ struct isis_spftree *spftree,
+ struct lfa_protected_resource *resource,
+ struct isis_vertex *vertex,
+ struct isis_spf_adj *sadj_primary, struct list *lfa_list)
+{
+ struct lfa_tiebreaker *tie_b;
+ int level = spftree->level;
+ struct list *filtered_lfa_list;
+ struct list *tent_lfa_list;
+
+ filtered_lfa_list = list_dup(lfa_list);
+ filtered_lfa_list->del = NULL;
+
+ if (listcount(filtered_lfa_list) == 1)
+ return filtered_lfa_list;
+
+ /* Check tiebreakers in ascending order by index. */
+ frr_each (lfa_tiebreaker_tree, &area->lfa_tiebreakers[level - 1],
+ tie_b) {
+ struct isis_vertex_adj *lfa;
+ struct listnode *node, *nnode;
+ uint32_t best_metric = UINT32_MAX;
+
+ tent_lfa_list = list_dup(filtered_lfa_list);
+
+ switch (tie_b->type) {
+ case LFA_TIEBREAKER_DOWNSTREAM:
+ for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
+ lfa)) {
+ if (clfa_downstream_check(spftree, vertex,
+ lfa->sadj))
+ continue;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: LFA %s doesn't satisfy the downstream condition",
+ print_sys_hostname(
+ lfa->sadj->id));
+ listnode_delete(tent_lfa_list, lfa);
+ }
+ break;
+ case LFA_TIEBREAKER_LOWEST_METRIC:
+ /* Find the best metric first. */
+ for (ALL_LIST_ELEMENTS_RO(tent_lfa_list, node, lfa)) {
+ if (lfa->lfa_metric < best_metric)
+ best_metric = lfa->lfa_metric;
+ }
+
+ /* Remove LFAs that don't have the best metric. */
+ for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
+ lfa)) {
+ if (lfa->lfa_metric == best_metric)
+ continue;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: LFA %s doesn't have the lowest cost metric",
+ print_sys_hostname(
+ lfa->sadj->id));
+ listnode_delete(tent_lfa_list, lfa);
+ }
+ break;
+ case LFA_TIEBREAKER_NODE_PROTECTING:
+ for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
+ lfa)) {
+ if (clfa_node_protecting_check(spftree, vertex,
+ lfa->sadj,
+ sadj_primary))
+ continue;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: LFA %s doesn't provide node protection",
+ print_sys_hostname(
+ lfa->sadj->id));
+ listnode_delete(tent_lfa_list, lfa);
+ }
+ break;
+ }
+
+ /*
+ * Decide what to do next based on the number of remaining LFAs.
+ */
+ switch (listcount(tent_lfa_list)) {
+ case 0:
+ /*
+ * Ignore this tie-breaker since it excluded all LFAs.
+ * Move on to the next one (if any).
+ */
+ list_delete(&tent_lfa_list);
+ break;
+ case 1:
+ /* Finish tie-breaking once we get a single LFA. */
+ list_delete(&filtered_lfa_list);
+ filtered_lfa_list = tent_lfa_list;
+ return filtered_lfa_list;
+ default:
+ /*
+ * We still have two or more LFAs. Move on to the next
+ * tie-breaker (if any).
+ */
+ list_delete(&filtered_lfa_list);
+ filtered_lfa_list = tent_lfa_list;
+ break;
+ }
+ }
+
+ return filtered_lfa_list;
+}
+
+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 listnode *vnode, *snode;
+ int level = spftree->level;
+
+ resource->type = LFA_LINK_PROTECTION;
+
+ for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, vnode, vertex)) {
+ struct list *lfa_list;
+ struct list *filtered_lfa_list;
+ struct isis_spf_adj *sadj_N;
+ struct isis_vertex_adj *vadj_primary;
+ struct isis_spf_adj *sadj_primary;
+ bool allow_ecmp;
+ uint32_t best_metric = UINT32_MAX;
+ char buf[VID2STR_BUFFER];
+
+ if (!VTYPE_IP(vertex->type))
+ continue;
+
+ vid2string(vertex, buf, sizeof(buf));
+
+ if (!spf_vertex_check_is_affected(vertex, spftree->sysid,
+ resource)) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: route unaffected by %s",
+ lfa_protected_resource2str(resource));
+ continue;
+ }
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: checking %s %s w.r.t %s",
+ vtype2string(vertex->type), buf,
+ lfa_protected_resource2str(resource));
+
+ if (vertex->N.ip.priority
+ > area->lfa_priority_limit[level - 1]) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: skipping computing LFAs due to low prefix priority");
+ continue;
+ }
+
+ vadj_primary = listnode_head(vertex->Adj_N);
+ sadj_primary = vadj_primary->sadj;
+
+ /*
+ * Loop over list of SPF adjacencies and compute a list of
+ * preliminary LFAs.
+ */
+ 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;
+ struct isis_vertex_adj *lfa;
+ struct isis_prefix_sid *psid = NULL;
+ bool last_hop = false;
+
+ /* Skip pseudonodes. */
+ if (LSP_PSEUDO_ID(sadj_N->id))
+ continue;
+
+ /*
+ * Skip nexthops that are along a link whose cost is
+ * infinite.
+ */
+ if (CHECK_FLAG(sadj_N->flags,
+ F_ISIS_SPF_ADJ_METRIC_INFINITY))
+ continue;
+
+ /* Skip nexthops that have the overload bit set. */
+ if (spftree->mtid != ISIS_MT_IPV4_UNICAST) {
+ struct isis_mt_router_info *mt_router_info;
+
+ mt_router_info =
+ isis_tlvs_lookup_mt_router_info(
+ sadj_N->lsp->tlvs,
+ spftree->mtid);
+ if (mt_router_info && mt_router_info->overload)
+ continue;
+ } else if (ISIS_MASK_LSP_OL_BIT(
+ sadj_N->lsp->hdr.lsp_bits))
+ continue;
+
+ /* Skip primary nexthop. */
+ if (spf_adj_check_is_affected(sadj_N, resource, NULL,
+ false))
+ continue;
+
+ /* Skip excluded interfaces as per the configuration. */
+ if (circuit
+ && isis_lfa_excluded_iface_check(
+ circuit, level,
+ sadj_N->adj->circuit->interface->name))
+ continue;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: checking candidate LFA %s",
+ print_sys_hostname(sadj_N->id));
+
+ /* Check loop-free criterion. */
+ if (!clfa_loop_free_check(spftree, vertex, sadj_primary,
+ sadj_N, &lfa_metric)) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: LFA condition not met for %s",
+ print_sys_hostname(sadj_N->id));
+ continue;
+ }
+
+ if (lfa_metric < best_metric)
+ best_metric = lfa_metric;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: %s is a valid loop-free alternate",
+ print_sys_hostname(sadj_N->id));
+
+ if (vertex->N.ip.sr.present) {
+ psid = &vertex->N.ip.sr.sid;
+ if (lfa_metric == sadj_N->metric)
+ last_hop = true;
+ }
+ lfa = isis_vertex_adj_add(spftree, vertex, lfa_list,
+ sadj_N, psid, last_hop);
+ lfa->lfa_metric = lfa_metric;
+ }
+
+ if (list_isempty(lfa_list)) {
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: no valid LFAs found");
+ list_delete(&lfa_list);
+ continue;
+ }
+
+ /* Check tie-breakers. */
+ filtered_lfa_list =
+ isis_lfa_tiebreakers(area, circuit, spftree, resource,
+ vertex, sadj_primary, lfa_list);
+
+ /* Create backup route using the best LFAs. */
+ allow_ecmp = area->lfa_load_sharing[level - 1];
+ isis_route_create(&vertex->N.ip.p.dest, &vertex->N.ip.p.src,
+ best_metric, vertex->depth, &vertex->N.ip.sr,
+ filtered_lfa_list, allow_ecmp, area,
+ spftree->route_table_backup);
+ spftree->lfa.protection_counters.lfa[vertex->N.ip.priority] +=
+ 1;
+
+ list_delete(&filtered_lfa_list);
+ list_delete(&lfa_list);
+ }
+}
+
+static void isis_spf_run_tilfa(struct isis_area *area,
+ struct isis_circuit *circuit,
+ struct isis_spftree *spftree,
+ struct isis_spftree *spftree_reverse,
+ struct lfa_protected_resource *resource)
+{
+ struct isis_spftree *spftree_pc_link;
+ struct isis_spftree *spftree_pc_node;
+
+ /* Compute node protecting repair paths first (if necessary). */
+ if (circuit->tilfa_node_protection[spftree->level - 1]) {
+ resource->type = LFA_NODE_PROTECTION;
+ spftree_pc_node = isis_tilfa_compute(area, spftree,
+ spftree_reverse, resource);
+ isis_spftree_del(spftree_pc_node);
+ }
+
+ /* Compute link protecting repair paths. */
+ resource->type = LFA_LINK_PROTECTION;
+ spftree_pc_link =
+ isis_tilfa_compute(area, spftree, spftree_reverse, resource);
+ isis_spftree_del(spftree_pc_link);
+}
+
/**
- * Run the TI-LFA algorithm for all proctected interfaces.
+ * Run the LFA/TI-LFA algorithms for all protected interfaces.
*
* @param area IS-IS area
* @param spftree IS-IS SPF tree
*/
void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
{
- struct isis_spftree *spftree_reverse;
+ struct isis_spftree *spftree_reverse = NULL;
struct isis_circuit *circuit;
struct listnode *node;
+ bool tilfa_configured;
+ int level = spftree->level;
+
+ tilfa_configured = (area->tilfa_protected_links[level - 1] > 0);
/* Run reverse SPF locally. */
- spftree_reverse = isis_spf_reverse_run(spftree);
+ if (tilfa_configured)
+ spftree_reverse = isis_spf_reverse_run(spftree);
/* Run forward SPF on all adjacent routers. */
isis_spf_run_neighbors(spftree);
@@ -1056,20 +1772,19 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
struct lfa_protected_resource resource = {};
struct isis_adjacency *adj;
- struct isis_spftree *spftree_pc_link;
- struct isis_spftree *spftree_pc_node;
static uint8_t null_sysid[ISIS_SYS_ID_LEN + 1];
- if (!(circuit->is_type & spftree->level))
+ if (!(circuit->is_type & level))
continue;
- if (!circuit->tilfa_protection[spftree->level - 1])
+ if (!circuit->lfa_protection[level - 1]
+ && !circuit->tilfa_protection[level - 1])
continue;
/* Fill in the protected resource. */
switch (circuit->circ_type) {
case CIRCUIT_T_BROADCAST:
- if (spftree->level == 1)
+ if (level == ISIS_LEVEL1)
memcpy(resource.adjacency,
circuit->u.bc.l1_desig_is,
ISIS_SYS_ID_LEN + 1);
@@ -1093,20 +1808,15 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
continue;
}
- /* Compute node protecting repair paths first (if necessary). */
- if (circuit->tilfa_node_protection[spftree->level - 1]) {
- resource.type = LFA_NODE_PROTECTION;
- spftree_pc_node = isis_tilfa_compute(
- area, spftree, spftree_reverse, &resource);
- isis_spftree_del(spftree_pc_node);
+ if (circuit->lfa_protection[level - 1])
+ isis_lfa_compute(area, circuit, spftree, &resource);
+ else if (circuit->tilfa_protection[level - 1]) {
+ assert(spftree_reverse);
+ isis_spf_run_tilfa(area, circuit, spftree,
+ spftree_reverse, &resource);
}
-
- /* Compute link protecting repair paths. */
- resource.type = LFA_LINK_PROTECTION;
- spftree_pc_link = isis_tilfa_compute(
- area, spftree, spftree_reverse, &resource);
- isis_spftree_del(spftree_pc_link);
}
- isis_spftree_del(spftree_reverse);
+ if (tilfa_configured)
+ isis_spftree_del(spftree_reverse);
}
diff --git a/isisd/isis_lfa.h b/isisd/isis_lfa.h
index 835618760..f09fc663a 100644
--- a/isisd/isis_lfa.h
+++ b/isisd/isis_lfa.h
@@ -20,6 +20,27 @@
#ifndef _FRR_ISIS_LFA_H
#define _FRR_ISIS_LFA_H
+#include "lib/typesafe.h"
+
+PREDECL_RBTREE_UNIQ(lfa_tiebreaker_tree)
+
+enum lfa_tiebreaker_type {
+ LFA_TIEBREAKER_DOWNSTREAM = 0,
+ LFA_TIEBREAKER_LOWEST_METRIC,
+ LFA_TIEBREAKER_NODE_PROTECTING,
+};
+
+struct lfa_tiebreaker {
+ struct lfa_tiebreaker_tree_item entry;
+ uint8_t index;
+ enum lfa_tiebreaker_type type;
+ struct isis_area *area;
+};
+int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
+ const struct lfa_tiebreaker *b);
+DECLARE_RBTREE_UNIQ(lfa_tiebreaker_tree, struct lfa_tiebreaker, entry,
+ lfa_tiebreaker_cmp)
+
enum isis_tilfa_sid_type {
TILFA_SID_PREFIX = 1,
TILFA_SID_ADJ,
@@ -37,6 +58,20 @@ struct isis_tilfa_sid {
} value;
};
+enum spf_prefix_priority {
+ SPF_PREFIX_PRIO_CRITICAL = 0,
+ SPF_PREFIX_PRIO_HIGH,
+ SPF_PREFIX_PRIO_MEDIUM,
+ SPF_PREFIX_PRIO_LOW,
+ SPF_PREFIX_PRIO_MAX,
+};
+
+struct spf_prefix_priority_acl {
+ char *name;
+ struct access_list *list_v4;
+ struct access_list *list_v6;
+};
+
RB_HEAD(isis_spf_nodes, isis_spf_node);
RB_PROTOTYPE(isis_spf_nodes, isis_spf_node, entry, isis_spf_node_compare)
struct isis_spf_node {
@@ -89,14 +124,32 @@ struct isis_spf_node *isis_spf_node_new(struct isis_spf_nodes *nodes,
const uint8_t *sysid);
struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes,
const uint8_t *sysid);
+void isis_lfa_tiebreakers_init(struct isis_area *area, int level);
+void isis_lfa_tiebreakers_clear(struct isis_area *area, int level);
+struct lfa_tiebreaker *isis_lfa_tiebreaker_add(struct isis_area *area,
+ int level, uint8_t index,
+ enum lfa_tiebreaker_type type);
+void isis_lfa_tiebreaker_delete(struct isis_area *area, int level,
+ struct lfa_tiebreaker *tie_b);
+void isis_lfa_excluded_ifaces_init(struct isis_circuit *circuit, int level);
+void isis_lfa_excluded_ifaces_clear(struct isis_circuit *circuit, int level);
+void isis_lfa_excluded_iface_add(struct isis_circuit *circuit, int level,
+ const char *ifname);
+void isis_lfa_excluded_iface_delete(struct isis_circuit *circuit, int level,
+ const char *ifname);
+bool isis_lfa_excluded_iface_check(struct isis_circuit *circuit, int level,
+ const char *ifname);
bool isis_lfa_excise_adj_check(const struct isis_spftree *spftree,
const uint8_t *id);
bool isis_lfa_excise_node_check(const struct isis_spftree *spftree,
const uint8_t *id);
struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree);
int isis_spf_run_neighbors(struct isis_spftree *spftree);
+void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
+ struct isis_spftree *spftree,
+ struct lfa_protected_resource *resource);
void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree);
-int isis_lfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
+int isis_tilfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
struct isis_spftree *
isis_tilfa_compute(struct isis_area *area, struct isis_spftree *spftree,
struct isis_spftree *spftree_reverse,
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index 22df3ff37..4576a4a95 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -242,6 +242,8 @@ int main(int argc, char **argv, char **envp)
*/
isis_error_init();
access_list_init();
+ access_list_add_hook(isis_filter_update);
+ access_list_delete_hook(isis_filter_update);
isis_vrf_init();
prefix_list_init();
isis_init();
diff --git a/isisd/isis_memory.c b/isisd/isis_memory.c
index a64decc14..b63a82f40 100644
--- a/isisd/isis_memory.c
+++ b/isisd/isis_memory.c
@@ -45,3 +45,4 @@ DEFINE_MTYPE(ISISD, ISIS_DICT_NODE, "ISIS dictionary node")
DEFINE_MTYPE(ISISD, ISIS_EXT_ROUTE, "ISIS redistributed route")
DEFINE_MTYPE(ISISD, ISIS_EXT_INFO, "ISIS redistributed route info")
DEFINE_MTYPE(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters")
+DEFINE_MTYPE(ISISD, ISIS_ACL_NAME, "ISIS access-list name")
diff --git a/isisd/isis_memory.h b/isisd/isis_memory.h
index 6b63b3ccb..3ef1c5bf0 100644
--- a/isisd/isis_memory.h
+++ b/isisd/isis_memory.h
@@ -44,5 +44,6 @@ DECLARE_MTYPE(ISIS_DICT_NODE)
DECLARE_MTYPE(ISIS_EXT_ROUTE)
DECLARE_MTYPE(ISIS_EXT_INFO)
DECLARE_MTYPE(ISIS_MPLS_TE)
+DECLARE_MTYPE(ISIS_ACL_NAME)
#endif /* _QUAGGA_ISIS_MEMORY_H */
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 2d3c7e1e3..c3d2f238d 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -194,6 +194,30 @@ const struct frr_yang_module_info frr_isisd_info = {
},
},
{
+ .xpath = "/frr-isisd:isis/instance/spf/prefix-priorities/critical/access-list-name",
+ .cbs = {
+ .cli_show = cli_show_isis_spf_prefix_priority,
+ .modify = isis_instance_spf_prefix_priorities_critical_access_list_name_modify,
+ .destroy = isis_instance_spf_prefix_priorities_critical_access_list_name_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/prefix-priorities/high/access-list-name",
+ .cbs = {
+ .cli_show = cli_show_isis_spf_prefix_priority,
+ .modify = isis_instance_spf_prefix_priorities_high_access_list_name_modify,
+ .destroy = isis_instance_spf_prefix_priorities_high_access_list_name_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/spf/prefix-priorities/medium/access-list-name",
+ .cbs = {
+ .cli_show = cli_show_isis_spf_prefix_priority,
+ .modify = isis_instance_spf_prefix_priorities_medium_access_list_name_modify,
+ .destroy = isis_instance_spf_prefix_priorities_medium_access_list_name_destroy,
+ }
+ },
+ {
.xpath = "/frr-isisd:isis/instance/area-password",
.cbs = {
.apply_finish = area_password_apply_finish,
@@ -432,6 +456,64 @@ const struct frr_yang_module_info frr_isisd_info = {
},
},
{
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/load-sharing",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_lfa_load_sharing,
+ .modify = isis_instance_fast_reroute_level_1_lfa_load_sharing_modify,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/priority-limit",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_lfa_priority_limit,
+ .modify = isis_instance_fast_reroute_level_1_lfa_priority_limit_modify,
+ .destroy = isis_instance_fast_reroute_level_1_lfa_priority_limit_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_lfa_tiebreaker,
+ .create = isis_instance_fast_reroute_level_1_lfa_tiebreaker_create,
+ .destroy = isis_instance_fast_reroute_level_1_lfa_tiebreaker_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker/type",
+ .cbs = {
+ .modify = isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_lfa_load_sharing,
+ .modify = isis_instance_fast_reroute_level_2_lfa_load_sharing_modify,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/priority-limit",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_lfa_priority_limit,
+ .modify = isis_instance_fast_reroute_level_2_lfa_priority_limit_modify,
+ .destroy = isis_instance_fast_reroute_level_2_lfa_priority_limit_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_lfa_tiebreaker,
+ .create = isis_instance_fast_reroute_level_2_lfa_tiebreaker_create,
+ .destroy = isis_instance_fast_reroute_level_2_lfa_tiebreaker_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker/type",
+ .cbs = {
+ .modify = isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify,
+ }
+ },
+ {
.xpath = "/frr-isisd:isis/instance/log-adjacency-changes",
.cbs = {
.cli_show = cli_show_isis_log_adjacency,
@@ -827,7 +909,21 @@ const struct frr_yang_module_info frr_isisd_info = {
{
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute",
.cbs = {
- .cli_show = cli_show_ip_isis_ti_lfa,
+ .cli_show = cli_show_ip_isis_frr,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/lfa/enable",
+ .cbs = {
+ .modify = lib_interface_isis_fast_reroute_level_1_lfa_enable_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/lfa/exclude-interface",
+ .cbs = {
+ .cli_show = cli_show_frr_lfa_exclude_interface,
+ .create = lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_create,
+ .destroy = lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy,
}
},
{
@@ -843,6 +939,20 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/lfa/enable",
+ .cbs = {
+ .modify = lib_interface_isis_fast_reroute_level_2_lfa_enable_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/lfa/exclude-interface",
+ .cbs = {
+ .cli_show = cli_show_frr_lfa_exclude_interface,
+ .create = lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_create,
+ .destroy = lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy,
+ }
+ },
+ {
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/ti-lfa/enable",
.cbs = {
.modify = lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify,
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index fb843131d..f529f2086 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -68,6 +68,18 @@ int isis_instance_spf_minimum_interval_level_1_modify(
struct nb_cb_modify_args *args);
int isis_instance_spf_minimum_interval_level_2_modify(
struct nb_cb_modify_args *args);
+int isis_instance_spf_prefix_priorities_critical_access_list_name_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_spf_prefix_priorities_critical_access_list_name_destroy(
+ struct nb_cb_destroy_args *args);
+int isis_instance_spf_prefix_priorities_high_access_list_name_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_spf_prefix_priorities_high_access_list_name_destroy(
+ struct nb_cb_destroy_args *args);
+int isis_instance_spf_prefix_priorities_medium_access_list_name_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_spf_prefix_priorities_medium_access_list_name_destroy(
+ struct nb_cb_destroy_args *args);
int isis_instance_area_password_create(struct nb_cb_create_args *args);
int isis_instance_area_password_destroy(struct nb_cb_destroy_args *args);
int isis_instance_area_password_password_modify(struct nb_cb_modify_args *args);
@@ -159,6 +171,30 @@ int isis_instance_multi_topology_ipv6_dstsrc_destroy(
struct nb_cb_destroy_args *args);
int isis_instance_multi_topology_ipv6_dstsrc_overload_modify(
struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_1_lfa_load_sharing_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_1_lfa_priority_limit_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_1_lfa_priority_limit_destroy(
+ struct nb_cb_destroy_args *args);
+int isis_instance_fast_reroute_level_1_lfa_tiebreaker_create(
+ struct nb_cb_create_args *args);
+int isis_instance_fast_reroute_level_1_lfa_tiebreaker_destroy(
+ struct nb_cb_destroy_args *args);
+int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_2_lfa_load_sharing_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_2_lfa_priority_limit_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_2_lfa_priority_limit_destroy(
+ struct nb_cb_destroy_args *args);
+int isis_instance_fast_reroute_level_2_lfa_tiebreaker_create(
+ struct nb_cb_create_args *args);
+int isis_instance_fast_reroute_level_2_lfa_tiebreaker_destroy(
+ struct nb_cb_destroy_args *args);
+int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
+ struct nb_cb_modify_args *args);
int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args);
int isis_instance_mpls_te_create(struct nb_cb_create_args *args);
int isis_instance_mpls_te_destroy(struct nb_cb_destroy_args *args);
@@ -258,10 +294,22 @@ int lib_interface_isis_multi_topology_ipv6_dstsrc_modify(
int lib_interface_isis_mpls_ldp_sync_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_mpls_holddown_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_isis_fast_reroute_level_1_lfa_enable_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_create(
+ struct nb_cb_create_args *args);
+int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy(
+ struct nb_cb_destroy_args *args);
int lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_fast_reroute_level_1_ti_lfa_node_protection_modify(
struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_2_lfa_enable_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_create(
+ struct nb_cb_create_args *args);
+int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy(
+ struct nb_cb_destroy_args *args);
int lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_fast_reroute_level_2_ti_lfa_node_protection_modify(
@@ -374,6 +422,8 @@ void cli_show_isis_spf_min_interval(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
+void cli_show_isis_spf_prefix_priority(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode,
@@ -410,6 +460,13 @@ void cli_show_isis_node_msd(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_prefix_sid(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
+void cli_show_isis_frr_lfa_priority_limit(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_frr_lfa_tiebreaker(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_isis_frr_lfa_load_sharing(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode,
@@ -442,8 +499,10 @@ void cli_show_ip_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
-void cli_show_ip_isis_ti_lfa(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
+void cli_show_ip_isis_frr(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_frr_lfa_exclude_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode,
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index 45089410e..c12ee44a9 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -27,6 +27,7 @@
#include "linklist.h"
#include "log.h"
#include "bfd.h"
+#include "filter.h"
#include "spf_backoff.h"
#include "lib_errors.h"
#include "vrf.h"
@@ -626,6 +627,145 @@ int isis_instance_spf_minimum_interval_level_2_modify(
}
/*
+ * XPath:
+ * /frr-isisd:isis/instance/spf/prefix-priorities/critical/access-list-name
+ */
+int isis_instance_spf_prefix_priorities_critical_access_list_name_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ const char *acl_name;
+ struct spf_prefix_priority_acl *ppa;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ acl_name = yang_dnode_get_string(args->dnode, NULL);
+
+ ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_CRITICAL];
+ XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+ ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
+ ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
+ ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_spf_prefix_priorities_critical_access_list_name_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+ struct spf_prefix_priority_acl *ppa;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+
+ ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_CRITICAL];
+ XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+ ppa->list_v4 = NULL;
+ ppa->list_v6 = NULL;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/prefix-priorities/high/access-list-name
+ */
+int isis_instance_spf_prefix_priorities_high_access_list_name_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ const char *acl_name;
+ struct spf_prefix_priority_acl *ppa;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ acl_name = yang_dnode_get_string(args->dnode, NULL);
+
+ ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_HIGH];
+ XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+ ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
+ ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
+ ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_spf_prefix_priorities_high_access_list_name_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+ struct spf_prefix_priority_acl *ppa;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+
+ ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_HIGH];
+ XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+ ppa->list_v4 = NULL;
+ ppa->list_v6 = NULL;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/spf/prefix-priorities/medium/access-list-name
+ */
+int isis_instance_spf_prefix_priorities_medium_access_list_name_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ const char *acl_name;
+ struct spf_prefix_priority_acl *ppa;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ acl_name = yang_dnode_get_string(args->dnode, NULL);
+
+ ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_MEDIUM];
+ XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+ ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
+ ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
+ ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_spf_prefix_priorities_medium_access_list_name_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+ struct spf_prefix_priority_acl *ppa;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+
+ ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_MEDIUM];
+ XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+ ppa->list_v4 = NULL;
+ ppa->list_v6 = NULL;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-isisd:isis/instance/area-password
*/
void area_password_apply_finish(struct nb_cb_apply_finish_args *args)
@@ -1290,6 +1430,229 @@ int isis_instance_multi_topology_ipv6_dstsrc_overload_modify(
}
/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/lfa/load-sharing
+ */
+int isis_instance_fast_reroute_level_1_lfa_load_sharing_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->lfa_load_sharing[0] = yang_dnode_get_bool(args->dnode, NULL);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/lfa/priority-limit
+ */
+int isis_instance_fast_reroute_level_1_lfa_priority_limit_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->lfa_priority_limit[0] = yang_dnode_get_enum(args->dnode, NULL);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_1_lfa_priority_limit_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->lfa_priority_limit[0] = SPF_PREFIX_PRIO_LOW;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker
+ */
+int isis_instance_fast_reroute_level_1_lfa_tiebreaker_create(
+ struct nb_cb_create_args *args)
+{
+ struct isis_area *area;
+ uint8_t index;
+ enum lfa_tiebreaker_type type;
+ struct lfa_tiebreaker *tie_b;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ index = yang_dnode_get_uint8(args->dnode, "./index");
+ type = yang_dnode_get_enum(args->dnode, "./type");
+
+ tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL1, index, type);
+ nb_running_set_entry(args->dnode, tie_b);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_1_lfa_tiebreaker_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct lfa_tiebreaker *tie_b;
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ tie_b = nb_running_unset_entry(args->dnode);
+ area = tie_b->area;
+ isis_lfa_tiebreaker_delete(area, ISIS_LEVEL1, tie_b);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker/type
+ */
+int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct lfa_tiebreaker *tie_b;
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ tie_b = nb_running_get_entry(args->dnode, NULL, true);
+ area = tie_b->area;
+ tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing
+ */
+int isis_instance_fast_reroute_level_2_lfa_load_sharing_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->lfa_load_sharing[1] = yang_dnode_get_bool(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/lfa/priority-limit
+ */
+int isis_instance_fast_reroute_level_2_lfa_priority_limit_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->lfa_priority_limit[1] = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_2_lfa_priority_limit_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ area->lfa_priority_limit[1] = SPF_PREFIX_PRIO_LOW;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker
+ */
+int isis_instance_fast_reroute_level_2_lfa_tiebreaker_create(
+ struct nb_cb_create_args *args)
+{
+ struct isis_area *area;
+ uint8_t index;
+ enum lfa_tiebreaker_type type;
+ struct lfa_tiebreaker *tie_b;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ index = yang_dnode_get_uint8(args->dnode, "./index");
+ type = yang_dnode_get_enum(args->dnode, "./type");
+
+ tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL2, index, type);
+ nb_running_set_entry(args->dnode, tie_b);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_2_lfa_tiebreaker_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct lfa_tiebreaker *tie_b;
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ tie_b = nb_running_unset_entry(args->dnode);
+ area = tie_b->area;
+ isis_lfa_tiebreaker_delete(area, ISIS_LEVEL2, tie_b);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker/type
+ */
+int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct lfa_tiebreaker *tie_b;
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ tie_b = nb_running_get_entry(args->dnode, NULL, true);
+ area = tie_b->area;
+ tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-isisd:isis/instance/log-adjacency-changes
*/
int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args)
@@ -3005,6 +3368,78 @@ int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args)
/*
* XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/lfa/enable
+ */
+int lib_interface_isis_fast_reroute_level_1_lfa_enable_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->lfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
+ if (circuit->lfa_protection[0])
+ circuit->area->lfa_protected_links[0]++;
+ else {
+ assert(circuit->area->lfa_protected_links[0] > 0);
+ circuit->area->lfa_protected_links[0]--;
+ }
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/lfa/exclude-interface
+ */
+int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_create(
+ struct nb_cb_create_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+ const char *exclude_ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ isis_lfa_excluded_iface_add(circuit, ISIS_LEVEL1, exclude_ifname);
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+ const char *exclude_ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ isis_lfa_excluded_iface_delete(circuit, ISIS_LEVEL1, exclude_ifname);
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
* /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/ti-lfa/enable
*/
int lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify(
@@ -3019,10 +3454,10 @@ int lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify(
circuit = nb_running_get_entry(args->dnode, NULL, true);
circuit->tilfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
if (circuit->tilfa_protection[0])
- circuit->area->lfa_protected_links[0]++;
+ circuit->area->tilfa_protected_links[0]++;
else {
- assert(circuit->area->lfa_protected_links[0] > 0);
- circuit->area->lfa_protected_links[0]--;
+ assert(circuit->area->tilfa_protected_links[0] > 0);
+ circuit->area->tilfa_protected_links[0]--;
}
area = circuit->area;
@@ -3056,6 +3491,78 @@ int lib_interface_isis_fast_reroute_level_1_ti_lfa_node_protection_modify(
/*
* XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/lfa/enable
+ */
+int lib_interface_isis_fast_reroute_level_2_lfa_enable_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->lfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
+ if (circuit->lfa_protection[1])
+ circuit->area->lfa_protected_links[1]++;
+ else {
+ assert(circuit->area->lfa_protected_links[1] > 0);
+ circuit->area->lfa_protected_links[1]--;
+ }
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/lfa/exclude-interface
+ */
+int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_create(
+ struct nb_cb_create_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+ const char *exclude_ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ isis_lfa_excluded_iface_add(circuit, ISIS_LEVEL2, exclude_ifname);
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+ const char *exclude_ifname;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+ isis_lfa_excluded_iface_delete(circuit, ISIS_LEVEL2, exclude_ifname);
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
* /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/ti-lfa/enable
*/
int lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify(
@@ -3070,10 +3577,10 @@ int lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify(
circuit = nb_running_get_entry(args->dnode, NULL, true);
circuit->tilfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
if (circuit->tilfa_protection[1])
- circuit->area->lfa_protected_links[1]++;
+ circuit->area->tilfa_protected_links[1]++;
else {
- assert(circuit->area->lfa_protected_links[1] > 0);
- circuit->area->lfa_protected_links[1]--;
+ assert(circuit->area->tilfa_protected_links[1] > 0);
+ circuit->area->tilfa_protected_links[1]--;
}
area = circuit->area;
diff --git a/isisd/isis_route.c b/isisd/isis_route.c
index d664a6f89..d32f219e9 100644
--- a/isisd/isis_route.c
+++ b/isisd/isis_route.c
@@ -183,7 +183,7 @@ static void isis_route_add_dummy_nexthops(struct isis_route_info *rinfo,
static struct isis_route_info *
isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
- struct list *adjacencies)
+ struct list *adjacencies, bool allow_ecmp)
{
struct isis_route_info *rinfo;
struct isis_vertex_adj *vadj;
@@ -205,6 +205,8 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) {
isis_route_add_dummy_nexthops(rinfo, sadj->id, sr,
label_stack);
+ if (!allow_ecmp)
+ break;
continue;
}
@@ -233,6 +235,8 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
}
adjinfo2nexthop(prefix->family, rinfo->nexthops, adj, sr,
label_stack);
+ if (!allow_ecmp)
+ break;
}
rinfo->cost = cost;
@@ -339,8 +343,8 @@ static int isis_route_info_same(struct isis_route_info *new,
struct isis_route_info *
isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
- struct list *adjacencies, struct isis_area *area,
- struct route_table *table)
+ struct list *adjacencies, bool allow_ecmp,
+ struct isis_area *area, struct route_table *table)
{
struct route_node *route_node;
struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL;
@@ -350,7 +354,7 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
return NULL;
rinfo_new = isis_route_info_new(prefix, src_p, cost, depth, sr,
- adjacencies);
+ adjacencies, allow_ecmp);
route_node = srcdest_rnode_get(table, prefix, src_p);
rinfo_old = route_node->info;
diff --git a/isisd/isis_route.h b/isisd/isis_route.h
index b5e4aed6c..0d4f88495 100644
--- a/isisd/isis_route.h
+++ b/isisd/isis_route.h
@@ -61,8 +61,8 @@ void adjinfo2nexthop(int family, struct list *nexthops,
struct isis_route_info *
isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
- struct list *adjacencies, struct isis_area *area,
- struct route_table *table);
+ struct list *adjacencies, bool allow_ecmp,
+ struct isis_area *area, struct route_table *table);
/* Walk the given table and install new routes to zebra and remove old ones.
* route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 58b0cf268..47918ef47 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -32,6 +32,7 @@
#include "termtable.h"
#include "memory.h"
#include "prefix.h"
+#include "filter.h"
#include "if.h"
#include "hash.h"
#include "table.h"
@@ -216,7 +217,7 @@ struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,
return hash_lookup(spftree->prefix_sids, &lookup);
}
-static void isis_vertex_adj_free(void *arg)
+void isis_vertex_adj_free(void *arg)
{
struct isis_vertex_adj *vadj = arg;
@@ -246,10 +247,10 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree,
return vertex;
}
-static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
- struct isis_vertex *vertex,
- struct isis_spf_adj *sadj,
- struct isis_prefix_sid *psid)
+struct isis_vertex_adj *
+isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
+ struct list *vadj_list, struct isis_spf_adj *sadj,
+ struct isis_prefix_sid *psid, bool last_hop)
{
struct isis_vertex_adj *vadj;
@@ -262,9 +263,6 @@ static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
"ISIS-SPF: ignoring different Prefix-SID for route %pFX",
&vertex->N.ip.p.dest);
else {
- bool last_hop;
-
- last_hop = (vertex->depth == 2);
vadj->sr.sid = *psid;
vadj->sr.label = sr_prefix_out_label(
spftree->lspdb, vertex->N.ip.p.dest.family,
@@ -273,7 +271,7 @@ static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
vadj->sr.present = true;
}
}
- listnode_add(vertex->Adj_N, vadj);
+ listnode_add(vadj_list, vadj);
return vadj;
}
@@ -528,6 +526,7 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
{
struct isis_vertex *vertex;
struct listnode *node;
+ bool last_hop;
char buff[VID2STR_BUFFER];
vertex = isis_find_vertex(&spftree->paths, id, vtype);
@@ -593,14 +592,16 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC))
vertex_update_firsthops(vertex, parent);
+ last_hop = (vertex->depth == 2);
if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
struct isis_vertex_adj *parent_vadj;
for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_vadj))
- isis_vertex_adj_add(spftree, vertex, parent_vadj->sadj,
- psid);
+ isis_vertex_adj_add(spftree, vertex, vertex->Adj_N,
+ parent_vadj->sadj, psid, last_hop);
} else if (sadj) {
- isis_vertex_adj_add(spftree, vertex, sadj, psid);
+ isis_vertex_adj_add(spftree, vertex, vertex->Adj_N, sadj, psid,
+ last_hop);
}
#ifdef EXTREME_DEBUG
@@ -628,9 +629,13 @@ static void isis_spf_add_local(struct isis_spftree *spftree,
if (vertex) {
/* C.2.5 c) */
if (vertex->d_N == cost) {
- if (sadj)
- isis_vertex_adj_add(spftree, vertex, sadj,
- psid);
+ if (sadj) {
+ bool last_hop = (vertex->depth == 2);
+
+ isis_vertex_adj_add(spftree, vertex,
+ vertex->Adj_N, sadj, psid,
+ last_hop);
+ }
/* d) */
if (!CHECK_FLAG(spftree->flags,
F_SPFTREE_NO_ADJACENCIES)
@@ -718,11 +723,16 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
struct isis_vertex_adj *parent_vadj;
for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node,
parent_vadj))
- if (!isis_vertex_adj_exists(spftree, vertex,
- parent_vadj->sadj))
+ if (!isis_vertex_adj_exists(
+ spftree, vertex,
+ parent_vadj->sadj)) {
+ bool last_hop = (vertex->depth == 2);
+
isis_vertex_adj_add(spftree, vertex,
+ vertex->Adj_N,
parent_vadj->sadj,
- psid);
+ psid, last_hop);
+ }
if (CHECK_FLAG(spftree->flags,
F_SPFTREE_HOPCOUNT_METRIC))
vertex_update_firsthops(vertex, parent);
@@ -771,7 +781,7 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree,
bool has_valid_psid;
if (isis_lfa_excise_node_check(spftree, lsp->hdr.lsp_id)) {
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug("ISIS-LFA: excising node %s",
print_sys_hostname(lsp->hdr.lsp_id));
return ISIS_OK;
@@ -1140,7 +1150,7 @@ static void isis_spf_preload_tent(struct isis_spftree *spftree,
adj_id = sadj->id;
if (isis_lfa_excise_adj_check(spftree, adj_id)) {
- if (IS_DEBUG_TILFA)
+ if (IS_DEBUG_LFA)
zlog_debug("ISIS-SPF: excising adjacency %s",
isis_format_id(sadj->id,
ISIS_SYS_ID_LEN + 1));
@@ -1158,9 +1168,9 @@ static void isis_spf_preload_tent(struct isis_spftree *spftree,
: VTYPE_NONPSEUDO_TE_IS,
sadj->id, sadj, metric, NULL,
parent);
- } else if (sadj->lan.lsp_pseudo) {
- isis_spf_process_lsp(spftree, sadj->lan.lsp_pseudo,
- metric, 0, spftree->sysid, parent);
+ } else if (sadj->lsp) {
+ isis_spf_process_lsp(spftree, sadj->lsp, metric, 0,
+ spftree->sysid, parent);
}
}
}
@@ -1243,12 +1253,24 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
struct isis_ext_subtlvs *subtlvs)
{
struct isis_spf_adj *sadj;
+ uint8_t lspid[ISIS_SYS_ID_LEN + 2];
+ struct isis_lsp *lsp;
uint8_t flags = 0;
/* Skip self in the pseudonode. */
if (desig_is_id && !memcmp(id, spftree->sysid, ISIS_SYS_ID_LEN))
return;
+ /* Find LSP from the adjacency. */
+ memcpy(lspid, id, ISIS_SYS_ID_LEN + 1);
+ LSP_FRAGMENT(lspid) = 0;
+ lsp = lsp_search(spftree->lspdb, lspid);
+ if (lsp == NULL || lsp->hdr.rem_lifetime == 0) {
+ zlog_warn("ISIS-SPF: No LSP found from root to L%d %s",
+ spftree->level, rawlspid_print(id));
+ return;
+ }
+
sadj = XCALLOC(MTYPE_ISIS_SPF_ADJ, sizeof(*sadj));
memcpy(sadj->id, id, sizeof(sadj->id));
if (desig_is_id) {
@@ -1260,9 +1282,14 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
sadj->metric = metric;
if (oldmetric)
SET_FLAG(flags, F_ISIS_SPF_ADJ_OLDMETRIC);
+ sadj->lsp = lsp;
sadj->subtlvs = subtlvs;
sadj->flags = flags;
+ if ((oldmetric && metric == ISIS_NARROW_METRIC_INFINITY)
+ || (!oldmetric && metric == ISIS_WIDE_METRIC_INFINITY))
+ SET_FLAG(flags, F_ISIS_SPF_ADJ_METRIC_INFINITY);
+
/* Set real adjacency. */
if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)
&& !LSP_PSEUDO_ID(id)) {
@@ -1294,24 +1321,8 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
}
/* Parse pseudonode LSP too. */
- if (LSP_PSEUDO_ID(id)) {
- uint8_t lspid[ISIS_SYS_ID_LEN + 2];
- struct isis_lsp *lsp_pseudo;
-
- memcpy(lspid, id, ISIS_SYS_ID_LEN + 1);
- LSP_FRAGMENT(lspid) = 0;
- lsp_pseudo = lsp_search(spftree->lspdb, lspid);
- if (lsp_pseudo == NULL || lsp_pseudo->hdr.rem_lifetime == 0) {
- zlog_warn(
- "ISIS-SPF: No LSP found from root to L%d DR %s",
- spftree->level, rawlspid_print(id));
- return;
- }
-
- sadj->lan.lsp_pseudo = lsp_pseudo;
- spf_adj_list_parse_lsp(spftree, adj_list, lsp_pseudo, id,
- metric);
- }
+ if (LSP_PSEUDO_ID(id))
+ spf_adj_list_parse_lsp(spftree, adj_list, lsp, id, metric);
}
static void spf_adj_list_parse_lsp(struct isis_spftree *spftree,
@@ -1418,32 +1429,65 @@ static void init_spt(struct isis_spftree *spftree, int mtid)
list_delete_all_node(spftree->sadj_list);
isis_vertex_queue_clear(&spftree->tents);
isis_vertex_queue_clear(&spftree->paths);
+ memset(&spftree->lfa.protection_counters, 0,
+ sizeof(spftree->lfa.protection_counters));
spftree->mtid = mtid;
}
+static enum spf_prefix_priority
+spf_prefix_priority(struct isis_spftree *spftree, struct isis_vertex *vertex)
+{
+ struct isis_area *area = spftree->area;
+ struct prefix *prefix = &vertex->N.ip.p.dest;
+
+ for (int priority = SPF_PREFIX_PRIO_CRITICAL;
+ priority <= SPF_PREFIX_PRIO_MEDIUM; priority++) {
+ struct spf_prefix_priority_acl *ppa;
+ enum filter_type ret = FILTER_PERMIT;
+
+ ppa = &area->spf_prefix_priorities[priority];
+ switch (spftree->family) {
+ case AF_INET:
+ ret = access_list_apply(ppa->list_v4, prefix);
+ break;
+ case AF_INET6:
+ ret = access_list_apply(ppa->list_v6, prefix);
+ break;
+ default:
+ break;
+ }
+
+ if (ret == FILTER_PERMIT)
+ return priority;
+ }
+
+ /* Assign medium priority to loopback prefixes by default. */
+ if (is_host_route(prefix))
+ return SPF_PREFIX_PRIO_MEDIUM;
+
+ return SPF_PREFIX_PRIO_LOW;
+}
+
static void spf_path_process(struct isis_spftree *spftree,
struct isis_vertex *vertex)
{
struct isis_area *area = spftree->area;
+ int level = spftree->level;
char buff[VID2STR_BUFFER];
- if (VTYPE_IS(vertex->type)
+ if (spftree->type == SPF_TYPE_TI_LFA && VTYPE_IS(vertex->type)
&& !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) {
if (listcount(vertex->Adj_N) > 0) {
- if (spftree->type == SPF_TYPE_TI_LFA) {
- struct isis_adjacency *adj;
+ struct isis_adjacency *adj;
- if (isis_lfa_check(spftree, vertex) != 0)
- return;
+ if (isis_tilfa_check(spftree, vertex) != 0)
+ return;
- adj = isis_adj_find(area, spftree->level,
- vertex->N.id);
- if (adj)
- sr_adj_sid_add_single(
- adj, spftree->family, true,
- vertex->Adj_N);
- }
+ adj = isis_adj_find(area, level, vertex->N.id);
+ if (adj)
+ sr_adj_sid_add_single(adj, spftree->family,
+ true, vertex->Adj_N);
} else if (IS_DEBUG_SPF_EVENTS)
zlog_debug(
"ISIS-SPF: no adjacencies, do not install backup Adj-SID for %s depth %d dist %d",
@@ -1453,21 +1497,59 @@ static void spf_path_process(struct isis_spftree *spftree,
if (VTYPE_IP(vertex->type)
&& !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ROUTES)) {
+ enum spf_prefix_priority priority;
+
+ priority = spf_prefix_priority(spftree, vertex);
+ vertex->N.ip.priority = priority;
if (vertex->depth == 1 || listcount(vertex->Adj_N) > 0) {
struct route_table *route_table;
+ bool allow_ecmp;
if (spftree->type == SPF_TYPE_TI_LFA) {
- if (isis_lfa_check(spftree, vertex) != 0)
+ struct isis_spftree *pre_spftree;
+
+ if (priority
+ > area->lfa_priority_limit[level - 1]) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: skipping %s %s (low prefix priority)",
+ vtype2string(
+ vertex->type),
+ vid2string(
+ vertex, buff,
+ sizeof(buff)));
return;
- route_table = spftree->lfa.old.spftree
- ->route_table_backup;
- } else
+ }
+
+ if (isis_tilfa_check(spftree, vertex) != 0)
+ return;
+
+ pre_spftree = spftree->lfa.old.spftree;
+ route_table = pre_spftree->route_table_backup;
+ allow_ecmp = area->lfa_load_sharing[level - 1];
+ pre_spftree->lfa.protection_counters
+ .tilfa[vertex->N.ip.priority] += 1;
+ } else {
route_table = spftree->route_table;
+ allow_ecmp = true;
+
+ /*
+ * Update LFA protection counters (ignore local
+ * routes).
+ */
+ if (vertex->depth > 1) {
+ spftree->lfa.protection_counters
+ .total[priority] += 1;
+ if (listcount(vertex->Adj_N) > 1)
+ spftree->lfa.protection_counters
+ .ecmp[priority] += 1;
+ }
+ }
- isis_route_create(&vertex->N.ip.p.dest,
- &vertex->N.ip.p.src, vertex->d_N,
- vertex->depth, &vertex->N.ip.sr,
- vertex->Adj_N, area, route_table);
+ isis_route_create(
+ &vertex->N.ip.p.dest, &vertex->N.ip.p.src,
+ vertex->d_N, vertex->depth, &vertex->N.ip.sr,
+ vertex->Adj_N, allow_ecmp, area, route_table);
} else if (IS_DEBUG_SPF_EVENTS)
zlog_debug(
"ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d",
@@ -1641,7 +1723,8 @@ static void isis_run_spf_with_protection(struct isis_area *area,
isis_run_spf(spftree);
/* Run LFA protection if configured. */
- if (area->lfa_protected_links[spftree->level - 1] > 0)
+ if (area->lfa_protected_links[spftree->level - 1] > 0
+ || area->tilfa_protected_links[spftree->level - 1] > 0)
isis_spf_run_lfa(area, spftree);
}
@@ -2281,10 +2364,223 @@ DEFUN(show_isis_route, show_isis_route_cmd,
return CMD_SUCCESS;
}
+static void isis_print_frr_summary_line(struct ttable *tt,
+ const char *protection,
+ uint32_t counters[SPF_PREFIX_PRIO_MAX])
+{
+ uint32_t critical, high, medium, low, total;
+
+ critical = counters[SPF_PREFIX_PRIO_CRITICAL];
+ high = counters[SPF_PREFIX_PRIO_HIGH];
+ medium = counters[SPF_PREFIX_PRIO_MEDIUM];
+ low = counters[SPF_PREFIX_PRIO_LOW];
+ total = critical + high + medium + low;
+
+ ttable_add_row(tt, "%s|%u|%u|%u|%u|%u", protection, critical, high,
+ medium, low, total);
+}
+
+static void
+isis_print_frr_summary_line_coverage(struct ttable *tt, const char *protection,
+ double counters[SPF_PREFIX_PRIO_MAX],
+ double total)
+{
+ double critical, high, medium, low;
+
+ critical = counters[SPF_PREFIX_PRIO_CRITICAL] * 100;
+ high = counters[SPF_PREFIX_PRIO_HIGH] * 100;
+ medium = counters[SPF_PREFIX_PRIO_MEDIUM] * 100;
+ low = counters[SPF_PREFIX_PRIO_LOW] * 100;
+ total *= 100;
+
+ ttable_add_row(tt, "%s|%.2f%%|%.2f%%|%.2f%%|%.2f%%|%.2f%%", protection,
+ critical, high, medium, low, total);
+}
+
+static void isis_print_frr_summary(struct vty *vty,
+ struct isis_spftree *spftree)
+{
+ struct ttable *tt;
+ char *table;
+ const char *tree_id_text = NULL;
+ uint32_t protectd[SPF_PREFIX_PRIO_MAX] = {0};
+ uint32_t unprotected[SPF_PREFIX_PRIO_MAX] = {0};
+ double coverage[SPF_PREFIX_PRIO_MAX] = {0};
+ uint32_t protected_total = 0, grand_total = 0;
+ double coverage_total;
+
+ if (!spftree)
+ return;
+
+ switch (spftree->tree_id) {
+ case SPFTREE_IPV4:
+ tree_id_text = "IPv4";
+ break;
+ case SPFTREE_IPV6:
+ tree_id_text = "IPv6";
+ break;
+ case SPFTREE_DSTSRC:
+ tree_id_text = "IPv6 (dst-src routing)";
+ break;
+ case SPFTREE_COUNT:
+ assert(!"isis_print_frr_summary shouldn't be called with SPFTREE_COUNT as type");
+ return;
+ }
+
+ vty_out(vty, " IS-IS %s %s Fast ReRoute summary:\n\n",
+ circuit_t2string(spftree->level), tree_id_text);
+
+ /* Prepare table. */
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(
+ tt,
+ "Protection \\ Priority|Critical|High |Medium |Low |Total");
+ tt->style.cell.rpad = 2;
+ tt->style.corner = '+';
+ ttable_restyle(tt);
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ /* Compute unprotected and coverage totals. */
+ for (int priority = SPF_PREFIX_PRIO_CRITICAL;
+ priority < SPF_PREFIX_PRIO_MAX; priority++) {
+ uint32_t *lfa = spftree->lfa.protection_counters.lfa;
+ uint32_t *rlfa = spftree->lfa.protection_counters.rlfa;
+ uint32_t *tilfa = spftree->lfa.protection_counters.tilfa;
+ uint32_t *ecmp = spftree->lfa.protection_counters.ecmp;
+ uint32_t *total = spftree->lfa.protection_counters.total;
+
+ protectd[priority] = lfa[priority] + rlfa[priority]
+ + tilfa[priority] + ecmp[priority];
+ /* Safeguard to protect against possible inconsistencies. */
+ if (protectd[priority] > total[priority])
+ protectd[priority] = total[priority];
+ unprotected[priority] = total[priority] - protectd[priority];
+ protected_total += protectd[priority];
+ grand_total += total[priority];
+
+ if (!total[priority])
+ coverage[priority] = 0;
+ else
+ coverage[priority] =
+ protectd[priority] / (double)total[priority];
+ }
+
+ if (!grand_total)
+ coverage_total = 0;
+ else
+ coverage_total = protected_total / (double)grand_total;
+
+ /* Add rows. */
+ isis_print_frr_summary_line(tt, "Classic LFA",
+ spftree->lfa.protection_counters.lfa);
+ isis_print_frr_summary_line(tt, "Remote LFA",
+ spftree->lfa.protection_counters.rlfa);
+ isis_print_frr_summary_line(tt, "Topology Independent LFA",
+ spftree->lfa.protection_counters.tilfa);
+ isis_print_frr_summary_line(tt, "ECMP",
+ spftree->lfa.protection_counters.ecmp);
+ isis_print_frr_summary_line(tt, "Unprotected", unprotected);
+ isis_print_frr_summary_line_coverage(tt, "Protection coverage",
+ coverage, coverage_total);
+
+ /* Dump the generated table. */
+ table = ttable_dump(tt, "\n");
+ vty_out(vty, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+ ttable_del(tt);
+}
+
+static void show_isis_frr_summary_common(struct vty *vty, int levels,
+ struct isis *isis)
+{
+ struct listnode *node;
+ struct isis_area *area;
+
+ if (!isis->area_list || isis->area_list->count == 0)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+ vty_out(vty, "Area %s:\n",
+ area->area_tag ? area->area_tag : "null");
+
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
+ if ((level & levels) == 0)
+ continue;
+
+ if (area->ip_circuits > 0) {
+ isis_print_frr_summary(
+ vty,
+ area->spftree[SPFTREE_IPV4][level - 1]);
+ }
+ if (area->ipv6_circuits > 0) {
+ isis_print_frr_summary(
+ vty,
+ area->spftree[SPFTREE_IPV6][level - 1]);
+ }
+ if (isis_area_ipv6_dstsrc_enabled(area)) {
+ isis_print_frr_summary(
+ vty, area->spftree[SPFTREE_DSTSRC]
+ [level - 1]);
+ }
+ }
+ }
+}
+
+DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd,
+ "show " PROTO_NAME
+ " [vrf <NAME|all>] fast-reroute summary"
+#ifndef FABRICD
+ " [<level-1|level-2>]"
+#endif
+ ,
+ SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR
+ "IS-IS FRR information\n"
+ "FRR summary\n"
+#ifndef FABRICD
+ "level-1 routes\n"
+ "level-2 routes\n"
+#endif
+)
+{
+ int levels;
+ struct isis *isis;
+ struct listnode *node;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "level-1", &idx))
+ levels = ISIS_LEVEL1;
+ else if (argv_find(argv, argc, "level-2", &idx))
+ levels = ISIS_LEVEL2;
+ else
+ levels = ISIS_LEVEL1 | ISIS_LEVEL2;
+
+ if (!im) {
+ vty_out(vty, "IS-IS Routing Process not enabled\n");
+ return CMD_SUCCESS;
+ }
+ ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf);
+
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
+ show_isis_frr_summary_common(vty, levels, isis);
+ return CMD_SUCCESS;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ show_isis_frr_summary_common(vty, levels, isis);
+ }
+
+ return CMD_SUCCESS;
+}
+
void isis_spf_init(void)
{
install_element(VIEW_NODE, &show_isis_topology_cmd);
install_element(VIEW_NODE, &show_isis_route_cmd);
+ install_element(VIEW_NODE, &show_isis_frr_summary_cmd);
/* Register hook(s). */
hook_register(isis_adj_state_change_hook, spf_adj_state_change);
diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h
index ad15d3e3c..5b6fcdaf6 100644
--- a/isisd/isis_spf.h
+++ b/isisd/isis_spf.h
@@ -39,13 +39,14 @@ struct isis_spf_adj {
struct isis_adjacency *adj;
uint32_t metric;
struct isis_ext_subtlvs *subtlvs;
+ struct isis_lsp *lsp;
struct {
uint8_t desig_is_id[ISIS_SYS_ID_LEN + 1];
- struct isis_lsp *lsp_pseudo;
} lan;
uint8_t flags;
#define F_ISIS_SPF_ADJ_BROADCAST 0x01
#define F_ISIS_SPF_ADJ_OLDMETRIC 0x02
+#define F_ISIS_SPF_ADJ_METRIC_INFINITY 0x04
};
struct isis_spftree *isis_spftree_new(struct isis_area *area,
diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h
index 9cb1a39b8..b7f326ca8 100644
--- a/isisd/isis_spf_private.h
+++ b/isisd/isis_spf_private.h
@@ -54,6 +54,7 @@ struct isis_vertex_adj {
struct isis_spf_adj *sadj;
struct isis_sr_psid_info sr;
struct mpls_label_stack *label_stack;
+ uint32_t lfa_metric;
};
/*
@@ -66,6 +67,7 @@ struct isis_vertex {
struct {
struct prefix_pair p;
struct isis_sr_psid_info sr;
+ enum spf_prefix_priority priority;
} ip;
} N;
uint32_t d_N; /* d(N) Distance from this IS */
@@ -193,6 +195,11 @@ static void isis_vertex_del(struct isis_vertex *vertex)
bool isis_vertex_adj_exists(const struct isis_spftree *spftree,
const struct isis_vertex *vertex,
const struct isis_spf_adj *sadj);
+void isis_vertex_adj_free(void *arg);
+struct isis_vertex_adj *
+isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
+ struct list *vadj_list, struct isis_spf_adj *sadj,
+ struct isis_prefix_sid *psid, bool last_hop);
__attribute__((__unused__))
static void isis_vertex_queue_clear(struct isis_vertex_queue *queue)
@@ -341,6 +348,15 @@ struct isis_spftree {
/* P-space and Q-space. */
struct isis_spf_nodes p_space;
struct isis_spf_nodes q_space;
+
+ /* Protection counters. */
+ struct {
+ uint32_t lfa[SPF_PREFIX_PRIO_MAX];
+ uint32_t rlfa[SPF_PREFIX_PRIO_MAX];
+ uint32_t tilfa[SPF_PREFIX_PRIO_MAX];
+ uint32_t ecmp[SPF_PREFIX_PRIO_MAX];
+ uint32_t total[SPF_PREFIX_PRIO_MAX];
+ } protection_counters;
} lfa;
uint8_t flags;
};
diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h
index 54ded8121..037f91f0b 100644
--- a/isisd/isis_tlvs.h
+++ b/isisd/isis_tlvs.h
@@ -42,6 +42,9 @@ struct isis_area_address {
uint8_t len;
};
+#define ISIS_WIDE_METRIC_INFINITY 0xFFFFFE
+#define ISIS_NARROW_METRIC_INFINITY 62
+
struct isis_oldstyle_reach;
struct isis_oldstyle_reach {
struct isis_oldstyle_reach *next;
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 950cdc281..827e022ba 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -31,6 +31,7 @@
#include "linklist.h"
#include "if.h"
#include "hash.h"
+#include "filter.h"
#include "stream.h"
#include "prefix.h"
#include "table.h"
@@ -77,7 +78,7 @@ unsigned long debug_bfd;
unsigned long debug_tx_queue;
unsigned long debug_sr;
unsigned long debug_ldp_sync;
-unsigned long debug_tilfa;
+unsigned long debug_lfa;
DEFINE_QOBJ_TYPE(isis_area)
@@ -310,6 +311,10 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
area->lsp_frag_threshold = 90; /* not currently configurable */
area->lsp_mtu =
yang_get_default_uint16("/frr-isisd:isis/instance/lsp/mtu");
+ area->lfa_load_sharing[0] = yang_get_default_bool(
+ "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/load-sharing");
+ area->lfa_load_sharing[1] = yang_get_default_bool(
+ "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing");
#else
area->max_lsp_lifetime[0] = DEFAULT_LSP_LIFETIME; /* 1200 */
area->max_lsp_lifetime[1] = DEFAULT_LSP_LIFETIME; /* 1200 */
@@ -324,7 +329,13 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
area->newmetric = 1;
area->lsp_frag_threshold = 90;
area->lsp_mtu = DEFAULT_LSP_MTU;
+ area->lfa_load_sharing[0] = true;
+ area->lfa_load_sharing[1] = true;
#endif /* ifndef FABRICD */
+ area->lfa_priority_limit[0] = SPF_PREFIX_PRIO_LOW;
+ area->lfa_priority_limit[1] = SPF_PREFIX_PRIO_LOW;
+ isis_lfa_tiebreakers_init(area, ISIS_LEVEL1);
+ isis_lfa_tiebreakers_init(area, ISIS_LEVEL2);
area_mt_init(area);
@@ -461,6 +472,16 @@ void isis_area_destroy(struct isis_area *area)
}
area->area_addrs = NULL;
+ for (int i = SPF_PREFIX_PRIO_CRITICAL; i <= SPF_PREFIX_PRIO_MEDIUM;
+ i++) {
+ struct spf_prefix_priority_acl *ppa;
+
+ ppa = &area->spf_prefix_priorities[i];
+ XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+ }
+ isis_lfa_tiebreakers_clear(area, ISIS_LEVEL1);
+ isis_lfa_tiebreakers_clear(area, ISIS_LEVEL2);
+
thread_cancel(&area->t_tick);
thread_cancel(&area->t_lsp_refresh[0]);
thread_cancel(&area->t_lsp_refresh[1]);
@@ -605,6 +626,29 @@ void isis_terminate()
isis_finish(isis);
}
+void isis_filter_update(struct access_list *access)
+{
+ struct isis *isis;
+ struct isis_area *area;
+ struct listnode *node, *anode;
+
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+ for (int i = SPF_PREFIX_PRIO_CRITICAL;
+ i <= SPF_PREFIX_PRIO_MEDIUM; i++) {
+ struct spf_prefix_priority_acl *ppa;
+
+ ppa = &area->spf_prefix_priorities[i];
+ ppa->list_v4 =
+ access_list_lookup(AFI_IP, ppa->name);
+ ppa->list_v6 =
+ access_list_lookup(AFI_IP6, ppa->name);
+ }
+ lsp_regenerate_schedule(area, area->is_type, 0);
+ }
+ }
+}
+
#ifdef FABRICD
static void area_set_mt_enabled(struct isis_area *area, uint16_t mtid,
bool enabled)
@@ -1199,8 +1243,8 @@ void print_debug(struct vty *vty, int flags, int onoff)
if (flags & DEBUG_SR)
vty_out(vty, "IS-IS Segment Routing events debugging is %s\n",
onoffs);
- if (flags & DEBUG_TILFA)
- vty_out(vty, "IS-IS TI-LFA events debugging is %s\n", onoffs);
+ if (flags & DEBUG_LFA)
+ vty_out(vty, "IS-IS LFA events debugging is %s\n", onoffs);
if (flags & DEBUG_UPDATE_PACKETS)
vty_out(vty, "IS-IS Update related packet debugging is %s\n",
onoffs);
@@ -1295,8 +1339,8 @@ static int config_write_debug(struct vty *vty)
vty_out(vty, "debug " PROTO_NAME " sr-events\n");
write++;
}
- if (IS_DEBUG_TILFA) {
- vty_out(vty, "debug " PROTO_NAME " ti-lfa\n");
+ if (IS_DEBUG_LFA) {
+ vty_out(vty, "debug " PROTO_NAME " lfa\n");
write++;
}
if (IS_DEBUG_UPDATE_PACKETS) {
@@ -1529,29 +1573,29 @@ DEFUN (no_debug_isis_srevents,
return CMD_SUCCESS;
}
-DEFUN (debug_isis_tilfa,
- debug_isis_tilfa_cmd,
- "debug " PROTO_NAME " ti-lfa",
+DEFUN (debug_isis_lfa,
+ debug_isis_lfa_cmd,
+ "debug " PROTO_NAME " lfa",
DEBUG_STR
PROTO_HELP
- "IS-IS TI-LFA Events\n")
+ "IS-IS LFA Events\n")
{
- debug_tilfa |= DEBUG_TILFA;
- print_debug(vty, DEBUG_TILFA, 1);
+ debug_lfa |= DEBUG_LFA;
+ print_debug(vty, DEBUG_LFA, 1);
return CMD_SUCCESS;
}
-DEFUN (no_debug_isis_tilfa,
- no_debug_isis_tilfa_cmd,
- "no debug " PROTO_NAME " ti-lfa",
+DEFUN (no_debug_isis_lfa,
+ no_debug_isis_lfa_cmd,
+ "no debug " PROTO_NAME " lfa",
NO_STR
UNDEBUG_STR
PROTO_HELP
- "IS-IS TI-LFA Events\n")
+ "IS-IS LFA Events\n")
{
- debug_tilfa &= ~DEBUG_TILFA;
- print_debug(vty, DEBUG_TILFA, 0);
+ debug_lfa &= ~DEBUG_LFA;
+ print_debug(vty, DEBUG_LFA, 0);
return CMD_SUCCESS;
}
@@ -2892,8 +2936,8 @@ void isis_init(void)
install_element(ENABLE_NODE, &no_debug_isis_spfevents_cmd);
install_element(ENABLE_NODE, &debug_isis_srevents_cmd);
install_element(ENABLE_NODE, &no_debug_isis_srevents_cmd);
- install_element(ENABLE_NODE, &debug_isis_tilfa_cmd);
- install_element(ENABLE_NODE, &no_debug_isis_tilfa_cmd);
+ install_element(ENABLE_NODE, &debug_isis_lfa_cmd);
+ install_element(ENABLE_NODE, &no_debug_isis_lfa_cmd);
install_element(ENABLE_NODE, &debug_isis_rtevents_cmd);
install_element(ENABLE_NODE, &no_debug_isis_rtevents_cmd);
install_element(ENABLE_NODE, &debug_isis_events_cmd);
@@ -2923,8 +2967,8 @@ void isis_init(void)
install_element(CONFIG_NODE, &no_debug_isis_spfevents_cmd);
install_element(CONFIG_NODE, &debug_isis_srevents_cmd);
install_element(CONFIG_NODE, &no_debug_isis_srevents_cmd);
- install_element(CONFIG_NODE, &debug_isis_tilfa_cmd);
- install_element(CONFIG_NODE, &no_debug_isis_tilfa_cmd);
+ install_element(CONFIG_NODE, &debug_isis_lfa_cmd);
+ install_element(CONFIG_NODE, &no_debug_isis_lfa_cmd);
install_element(CONFIG_NODE, &debug_isis_rtevents_cmd);
install_element(CONFIG_NODE, &no_debug_isis_rtevents_cmd);
install_element(CONFIG_NODE, &debug_isis_events_cmd);
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 921df4d7e..4618d14af 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -33,6 +33,7 @@
#include "isisd/isis_sr.h"
#include "isis_flags.h"
#include "isis_lsp.h"
+#include "isis_lfa.h"
#include "isis_memory.h"
#include "qobj.h"
#include "ldp_sync.h"
@@ -188,8 +189,15 @@ struct isis_area {
struct isis_sr_db srdb;
int ipv6_circuits;
bool purge_originator;
+ /* SPF prefix priorities. */
+ struct spf_prefix_priority_acl
+ spf_prefix_priorities[SPF_PREFIX_PRIO_MAX];
/* Fast Re-Route information. */
size_t lfa_protected_links[ISIS_LEVELS];
+ size_t lfa_load_sharing[ISIS_LEVELS];
+ enum spf_prefix_priority lfa_priority_limit[ISIS_LEVELS];
+ struct lfa_tiebreaker_tree_head lfa_tiebreakers[ISIS_LEVELS];
+ size_t tilfa_protected_links[ISIS_LEVELS];
/* Counters */
uint32_t circuit_state_changes;
struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT]
@@ -231,6 +239,7 @@ struct isis_area *isis_area_lookup_by_vrf(const char *area_tag,
const char *vrf_name);
int isis_area_get(struct vty *vty, const char *area_tag);
void isis_area_destroy(struct isis_area *area);
+void isis_filter_update(struct access_list *access);
void print_debug(struct vty *, int, int);
struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
struct isis *isis);
@@ -280,7 +289,7 @@ extern unsigned long debug_bfd;
extern unsigned long debug_tx_queue;
extern unsigned long debug_sr;
extern unsigned long debug_ldp_sync;
-extern unsigned long debug_tilfa;
+extern unsigned long debug_lfa;
#define DEBUG_ADJ_PACKETS (1<<0)
#define DEBUG_SNP_PACKETS (1<<1)
@@ -296,7 +305,7 @@ extern unsigned long debug_tilfa;
#define DEBUG_TX_QUEUE (1<<11)
#define DEBUG_SR (1<<12)
#define DEBUG_LDP_SYNC (1<<13)
-#define DEBUG_TILFA (1<<14)
+#define DEBUG_LFA (1<<14)
/* Debug related macro. */
#define IS_DEBUG_ADJ_PACKETS (debug_adj_pkt & DEBUG_ADJ_PACKETS)
@@ -313,7 +322,7 @@ extern unsigned long debug_tilfa;
#define IS_DEBUG_TX_QUEUE (debug_tx_queue & DEBUG_TX_QUEUE)
#define IS_DEBUG_SR (debug_sr & DEBUG_SR)
#define IS_DEBUG_LDP_SYNC (debug_ldp_sync & DEBUG_LDP_SYNC)
-#define IS_DEBUG_TILFA (debug_tilfa & DEBUG_TILFA)
+#define IS_DEBUG_LFA (debug_lfa & DEBUG_LFA)
#define lsp_debug(...) \
do { \
diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c
index 4c89a5be0..36ef93669 100644
--- a/tests/isisd/test_isis_spf.c
+++ b/tests/isisd/test_isis_spf.c
@@ -39,6 +39,7 @@
enum test_type {
TEST_SPF = 1,
TEST_REVERSE_SPF,
+ TEST_LFA,
TEST_TI_LFA,
};
@@ -72,6 +73,38 @@ static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
isis_spftree_del(spftree);
}
+static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags);
+ isis_run_spf(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the LFA repair paths. */
+ isis_lfa_compute(area, NULL, spftree_self, protected_resource);
+
+ /* Print the SPT and the corresponding main/backup routing tables. */
+ isis_print_spftree(vty, spftree_self);
+ vty_out(vty, "Main:\n");
+ isis_print_routes(vty, spftree_self, false, false);
+ vty_out(vty, "Backup:\n");
+ isis_print_routes(vty, spftree_self, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+}
+
static void test_run_ti_lfa(struct vty *vty,
const struct isis_topology *topology,
const struct isis_test_node *root,
@@ -120,7 +153,9 @@ static void test_run_ti_lfa(struct vty *vty,
vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
vty_out(vty, "\n");
- /* Print the post-convergence SPT and the correspoding routing table. */
+ /*
+ * Print the post-convergence SPT and the corresponding routing table.
+ */
isis_print_spftree(vty, spftree_pc);
isis_print_routes(vty, spftree_self, false, true);
@@ -202,6 +237,11 @@ static int test_run(struct vty *vty, const struct isis_topology *topology,
&area->lspdb[level - 1], level,
tree, true);
break;
+ case TEST_LFA:
+ test_run_lfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
case TEST_TI_LFA:
test_run_ti_lfa(vty, topology, root, area,
&area->lspdb[level - 1], level,
@@ -221,10 +261,11 @@ static int test_run(struct vty *vty, const struct isis_topology *topology,
}
DEFUN(test_isis, test_isis_cmd,
- "test isis topology (1-13) root HOSTNAME\
+ "test isis topology (1-14) root HOSTNAME\
<\
spf\
|reverse-spf\
+ |lfa system-id WORD [pseudonode-id <1-255>]\
|ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\
>\
[display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
@@ -236,6 +277,11 @@ DEFUN(test_isis, test_isis_cmd,
"SPF root hostname\n"
"Normal Shortest Path First\n"
"Reverse Shortest Path First\n"
+ "Classic LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
"Topology Independent LFA\n"
"System ID\n"
"System ID\n"
@@ -281,7 +327,15 @@ DEFUN(test_isis, test_isis_cmd,
test_type = TEST_SPF;
else if (argv_find(argv, argc, "reverse-spf", &idx))
test_type = TEST_REVERSE_SPF;
- else if (argv_find(argv, argc, "ti-lfa", &idx)) {
+ else if (argv_find(argv, argc, "lfa", &idx)) {
+ test_type = TEST_LFA;
+
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ protection_type = LFA_LINK_PROTECTION;
+ } else if (argv_find(argv, argc, "ti-lfa", &idx)) {
test_type = TEST_TI_LFA;
fail_sysid_str = argv[idx + 2]->arg;
@@ -404,7 +458,7 @@ int main(int argc, char **argv)
listnode_add(im->isis, isis);
SET_FLAG(im->options, F_ISIS_UNIT_TEST);
debug_spf_events |= DEBUG_SPF_EVENTS;
- debug_tilfa |= DEBUG_TILFA;
+ debug_lfa |= DEBUG_LFA;
debug_events |= DEBUG_EVENTS;
debug_rte_events |= DEBUG_RTE_EVENTS;
diff --git a/tests/isisd/test_isis_spf.in b/tests/isisd/test_isis_spf.in
index 6bc5442f1..93e18124e 100644
--- a/tests/isisd/test_isis_spf.in
+++ b/tests/isisd/test_isis_spf.in
@@ -15,6 +15,22 @@ test isis topology 13 root rt1 spf ipv4-only
test isis topology 4 root rt1 reverse-spf ipv4-only
test isis topology 11 root rt1 reverse-spf
+test isis topology 1 root rt1 lfa system-id rt2
+test isis topology 2 root rt4 lfa system-id rt1 pseudonode-id 1
+test isis topology 2 root rt4 lfa system-id rt6
+test isis topology 3 root rt1 lfa system-id rt2
+test isis topology 3 root rt1 lfa system-id rt3
+test isis topology 7 root rt1 lfa system-id rt4
+test isis topology 7 root rt7 lfa system-id rt8
+test isis topology 7 root rt8 lfa system-id rt11
+test isis topology 9 root rt3 lfa system-id rt1
+test isis topology 10 root rt8 lfa system-id rt5
+test isis topology 11 root rt3 lfa system-id rt5
+test isis topology 13 root rt4 lfa system-id rt3
+test isis topology 14 root rt1 lfa system-id rt1 pseudonode-id 1
+test isis topology 14 root rt1 lfa system-id rt2
+test isis topology 14 root rt5 lfa system-id rt4
+
test isis topology 1 root rt1 ti-lfa system-id rt2
test isis topology 2 root rt1 ti-lfa system-id rt3
test isis topology 2 root rt1 ti-lfa system-id rt1 pseudonode-id 1
diff --git a/tests/isisd/test_isis_spf.refout b/tests/isisd/test_isis_spf.refout
index d24176a09..dced6fb10 100644
--- a/tests/isisd/test_isis_spf.refout
+++ b/tests/isisd/test_isis_spf.refout
@@ -721,6 +721,1092 @@ IS-IS L1 IPv6 routing table:
2001:db8::6/128 40 - rt3 16061
test#
+test# test isis topology 1 root rt1 lfa system-id rt2
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 40 - rt2 16060
+ - rt3 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 20 - rt3 implicit-null
+ 2001:db8::4/128 30 - rt2 16041
+ 2001:db8::5/128 30 - rt3 16051
+ 2001:db8::6/128 40 - rt2 16061
+ - rt3 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 2 root rt4 lfa system-id rt1 pseudonode-id 1
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+10.0.255.2/32 IP TE 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+10.0.255.3/32 IP TE 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 35 - rt1 16020
+ 10.0.255.3/32 50 - rt1 16030
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.2/32 40 - rt2 implicit-null
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+2001:db8::4/128 IP6 internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 35 - rt1 16021
+ 2001:db8::3/128 50 - rt1 16031
+ 2001:db8::4/128 0 - - -
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::2/128 40 - rt2 implicit-null
+
+test# test isis topology 2 root rt4 lfa system-id rt6
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+10.0.255.2/32 IP TE 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+10.0.255.3/32 IP TE 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 35 - rt1 16020
+ 10.0.255.3/32 50 - rt1 16030
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.6/32 20 - rt5 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+2001:db8::4/128 IP6 internal 0 rt4(4)
+rt1 TE-IS 10 rt1 - rt4(4)
+rt5 TE-IS 10 rt5 - rt4(4)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt5 - rt5(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt2 TE-IS 25 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 35 rt1 - rt2(4)
+rt3 TE-IS 40 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt1 - rt3(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 35 - rt1 16021
+ 2001:db8::3/128 50 - rt1 16031
+ 2001:db8::4/128 0 - - -
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::6/128 20 - rt5 16061
+
+test# test isis topology 3 root rt1 lfa system-id rt2
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 40 - rt2 16050
+ 10.0.255.6/32 40 - rt2 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt3 16020
+ 10.0.255.4/32 30 - rt3 16040
+ 10.0.255.5/32 40 - rt3 16050
+ 10.0.255.6/32 40 - rt3 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 3 root rt1 lfa system-id rt3
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt2 16040
+ 10.0.255.5/32 40 - rt2 16050
+ 10.0.255.6/32 40 - rt2 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.3/32 20 - rt2 16030
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 7 root rt1 lfa system-id rt4
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt7 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt5(4)
+rt6 TE-IS 30 rt4 - rt5(4)
+rt8 TE-IS 30 rt4 - rt5(4)
+ rt7(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+10.0.255.7/32 IP TE 30 rt4 - rt7(4)
+rt10 TE-IS 40 rt4 - rt7(4)
+rt3 TE-IS 40 rt4 - rt2(4)
+ rt6(4)
+rt9 TE-IS 40 rt4 - rt8(4)
+rt11 TE-IS 40 rt4 - rt8(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+10.0.255.6/32 IP TE 40 rt4 - rt6(4)
+10.0.255.8/32 IP TE 40 rt4 - rt8(4)
+rt12 TE-IS 50 rt4 - rt9(4)
+ rt11(4)
+10.0.255.10/32 IP TE 50 rt4 - rt10(4)
+10.0.255.3/32 IP TE 50 rt4 - rt3(4)
+10.0.255.9/32 IP TE 50 rt4 - rt9(4)
+10.0.255.11/32 IP TE 50 rt4 - rt11(4)
+10.0.255.12/32 IP TE 60 rt4 - rt12(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 40 - rt4 16020
+ 10.0.255.3/32 50 - rt4 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ 10.0.255.6/32 40 - rt4 16060
+ 10.0.255.7/32 30 - rt4 16070
+ 10.0.255.8/32 40 - rt4 16080
+ 10.0.255.9/32 50 - rt4 16090
+ 10.0.255.10/32 50 - rt4 16100
+ 10.0.255.11/32 50 - rt4 16110
+ 10.0.255.12/32 60 - rt4 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.2/32 40 - rt2 implicit-null
+ 10.0.255.3/32 50 - rt2 16030
+ 10.0.255.4/32 60 - rt2 16040
+ 10.0.255.5/32 50 - rt2 16050
+ 10.0.255.6/32 60 - rt2 16060
+ 10.0.255.7/32 70 - rt2 16070
+ 10.0.255.8/32 60 - rt2 16080
+ 10.0.255.9/32 70 - rt2 16090
+ 10.0.255.10/32 80 - rt2 16100
+ 10.0.255.11/32 70 - rt2 16110
+ 10.0.255.12/32 80 - rt2 16120
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 40 rt2 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 7 root rt7 lfa system-id rt8
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt7
+10.0.255.7/32 IP internal 0 rt7(4)
+rt4 TE-IS 10 rt4 - rt7(4)
+rt8 TE-IS 10 rt8 - rt7(4)
+rt10 TE-IS 20 rt10 - rt7(4)
+rt1 TE-IS 20 rt4 - rt4(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+ rt8 - rt8(4)
+rt9 TE-IS 20 rt8 - rt8(4)
+rt11 TE-IS 20 rt8 - rt8(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.8/32 IP TE 20 rt8 - rt8(4)
+rt2 TE-IS 30 rt4 - rt5(4)
+ rt8 -
+rt6 TE-IS 30 rt4 - rt5(4)
+ rt8 -
+rt12 TE-IS 30 rt8 - rt9(4)
+ rt11(4)
+10.0.255.10/32 IP TE 30 rt10 - rt10(4)
+10.0.255.1/32 IP TE 30 rt4 - rt1(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+ rt8 -
+10.0.255.9/32 IP TE 30 rt8 - rt9(4)
+10.0.255.11/32 IP TE 30 rt8 - rt11(4)
+rt3 TE-IS 40 rt4 - rt2(4)
+ rt8 - rt6(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+ rt8 -
+10.0.255.6/32 IP TE 40 rt4 - rt6(4)
+ rt8 -
+10.0.255.12/32 IP TE 40 rt8 - rt12(4)
+10.0.255.3/32 IP TE 50 rt4 - rt3(4)
+ rt8 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 30 - rt4 16010
+ 10.0.255.2/32 40 - rt4 16020
+ - rt8 16020
+ 10.0.255.3/32 50 - rt4 16030
+ - rt8 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ - rt8 16050
+ 10.0.255.6/32 40 - rt4 16060
+ - rt8 16060
+ 10.0.255.7/32 0 - - -
+ 10.0.255.8/32 20 - rt8 implicit-null
+ 10.0.255.9/32 30 - rt8 16090
+ 10.0.255.10/32 30 - rt10 implicit-null
+ 10.0.255.11/32 30 - rt8 16110
+ 10.0.255.12/32 40 - rt8 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.8/32 40 - rt10 16080
+ 10.0.255.9/32 50 - rt10 16090
+ 10.0.255.11/32 30 - rt10 16110
+ 10.0.255.12/32 40 - rt10 16120
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt7
+rt4 TE-IS 10 rt4 - rt7(4)
+rt8 TE-IS 10 rt8 - rt7(4)
+rt10 TE-IS 20 rt10 - rt7(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 7 root rt8 lfa system-id rt11
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+10.0.255.8/32 IP internal 0 rt8(4)
+rt5 TE-IS 10 rt5 - rt8(4)
+rt7 TE-IS 10 rt7 - rt8(4)
+rt9 TE-IS 10 rt9 - rt8(4)
+rt11 TE-IS 10 rt11 - rt8(4)
+rt2 TE-IS 20 rt5 - rt5(4)
+rt4 TE-IS 20 rt5 - rt5(4)
+ rt7 - rt7(4)
+rt6 TE-IS 20 rt5 - rt5(4)
+rt12 TE-IS 20 rt9 - rt9(4)
+ rt11 - rt11(4)
+rt10 TE-IS 20 rt11 - rt11(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.7/32 IP TE 20 rt7 - rt7(4)
+10.0.255.9/32 IP TE 20 rt9 - rt9(4)
+10.0.255.11/32 IP TE 20 rt11 - rt11(4)
+rt3 TE-IS 30 rt5 - rt2(4)
+ rt6(4)
+rt1 TE-IS 30 rt5 - rt4(4)
+ rt7 -
+10.0.255.2/32 IP TE 30 rt5 - rt2(4)
+10.0.255.4/32 IP TE 30 rt5 - rt4(4)
+ rt7 -
+10.0.255.6/32 IP TE 30 rt5 - rt6(4)
+10.0.255.12/32 IP TE 30 rt9 - rt12(4)
+ rt11 -
+10.0.255.10/32 IP TE 30 rt11 - rt10(4)
+10.0.255.3/32 IP TE 40 rt5 - rt3(4)
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)
+ rt7 -
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 40 - rt5 16010
+ - rt7 16010
+ 10.0.255.2/32 30 - rt5 16020
+ 10.0.255.3/32 40 - rt5 16030
+ 10.0.255.4/32 30 - rt5 16040
+ - rt7 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt5 16060
+ 10.0.255.7/32 20 - rt7 implicit-null
+ 10.0.255.8/32 0 - - -
+ 10.0.255.9/32 20 - rt9 implicit-null
+ 10.0.255.10/32 30 - rt11 16100
+ 10.0.255.11/32 20 - rt11 implicit-null
+ 10.0.255.12/32 30 - rt9 16120
+ - rt11 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.10/32 30 - rt7 16100
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+rt5 TE-IS 10 rt5 - rt8(4)
+rt7 TE-IS 10 rt7 - rt8(4)
+rt9 TE-IS 10 rt9 - rt8(4)
+rt11 TE-IS 10 rt11 - rt8(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 9 root rt3 lfa system-id rt1
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+10.0.255.3/32 IP internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 20 rt1 - rt1(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+rt4 TE-IS 30 rt1 - rt2(4)
+10.0.255.2/32 IP TE 30 rt1 - rt2(4)
+rt5 TE-IS 40 rt1 - rt4(4)
+10.0.255.4/32 IP TE 40 rt1 - rt4(4)
+rt9 TE-IS 50 rt1 - rt5(4)
+10.0.255.5/32 IP TE 50 rt1 - rt5(4)
+rt6 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt7 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt8 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+10.0.255.9/32 IP TE 60 rt1 - rt9(4)
+10.0.255.6/32 IP TE 70 rt1 - rt6(4)
+10.0.255.7/32 IP TE 70 rt1 - rt7(4)
+10.0.255.8/32 IP TE 70 rt1 - rt8(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 30 - rt1 16020
+ 10.0.255.3/32 0 - - -
+ 10.0.255.4/32 40 - rt1 16040
+ 10.0.255.5/32 50 - rt1 16050
+ 10.0.255.6/32 70 - rt1 16060
+ 10.0.255.7/32 70 - rt1 16070
+ 10.0.255.8/32 70 - rt1 16080
+ 10.0.255.9/32 60 - rt1 16090
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 120 - rt4 16010
+ 10.0.255.2/32 110 - rt4 16020
+ 10.0.255.4/32 100 - rt4 implicit-null
+ 10.0.255.5/32 110 - rt4 16050
+ 10.0.255.6/32 130 - rt4 16060
+ 10.0.255.7/32 130 - rt4 16070
+ 10.0.255.8/32 130 - rt4 16080
+ 10.0.255.9/32 120 - rt4 16090
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+2001:db8::3/128 IP6 internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 20 rt1 - rt1(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+rt4 TE-IS 30 rt1 - rt2(4)
+2001:db8::2/128 IP6 internal 30 rt1 - rt2(4)
+rt5 TE-IS 40 rt1 - rt4(4)
+2001:db8::4/128 IP6 internal 40 rt1 - rt4(4)
+rt9 TE-IS 50 rt1 - rt5(4)
+2001:db8::5/128 IP6 internal 50 rt1 - rt5(4)
+rt6 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt7 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+rt8 TE-IS 60 rt1 - rt4(4)
+ rt9(4)
+2001:db8::9/128 IP6 internal 60 rt1 - rt9(4)
+2001:db8::6/128 IP6 internal 70 rt1 - rt6(4)
+2001:db8::7/128 IP6 internal 70 rt1 - rt7(4)
+2001:db8::8/128 IP6 internal 70 rt1 - rt8(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 30 - rt1 16021
+ 2001:db8::3/128 0 - - -
+ 2001:db8::4/128 40 - rt1 16041
+ 2001:db8::5/128 50 - rt1 16051
+ 2001:db8::6/128 70 - rt1 16061
+ 2001:db8::7/128 70 - rt1 16071
+ 2001:db8::8/128 70 - rt1 16081
+ 2001:db8::9/128 60 - rt1 16091
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 120 - rt4 16011
+ 2001:db8::2/128 110 - rt4 16021
+ 2001:db8::4/128 100 - rt4 implicit-null
+ 2001:db8::5/128 110 - rt4 16051
+ 2001:db8::6/128 130 - rt4 16061
+ 2001:db8::7/128 130 - rt4 16071
+ 2001:db8::8/128 130 - rt4 16081
+ 2001:db8::9/128 120 - rt4 16091
+
+test# test isis topology 10 root rt8 lfa system-id rt5
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+10.0.255.8/32 IP internal 0 rt8(4)
+rt5 TE-IS 10 rt5 - rt8(4)
+rt2 TE-IS 20 rt5 - rt5(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt1 TE-IS 30 rt5 - rt2(4)
+10.0.255.2/32 IP TE 30 rt5 - rt2(4)
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)
+rt6 TE-IS 50 rt6 - rt8(4)
+rt7 TE-IS 50 rt7 - rt8(4)
+rt3 TE-IS 50 rt5 - rt1(4)
+rt4 TE-IS 50 rt5 - rt1(4)
+10.0.255.6/32 IP TE 60 rt6 - rt6(4)
+10.0.255.7/32 IP TE 60 rt7 - rt7(4)
+10.0.255.3/32 IP TE 60 rt5 - rt3(4)
+10.0.255.4/32 IP TE 60 rt5 - rt4(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt5 16010
+ 10.0.255.2/32 30 - rt5 16020
+ 10.0.255.3/32 60 - rt5 16030
+ 10.0.255.4/32 60 - rt5 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 60 - rt6 implicit-null
+ 10.0.255.7/32 60 - rt7 implicit-null
+ 10.0.255.8/32 0 - - -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 80 - rt6 16010
+ - rt7 16010
+ 10.0.255.2/32 90 - rt6 16020
+ - rt7 16020
+ 10.0.255.3/32 60 - rt6 16030
+ - rt7 16030
+ 10.0.255.4/32 60 - rt6 16040
+ - rt7 16040
+ 10.0.255.5/32 100 - rt6 16050
+ - rt7 16050
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt8
+2001:db8::8/128 IP6 internal 0 rt8(4)
+rt5 TE-IS 10 rt5 - rt8(4)
+rt2 TE-IS 20 rt5 - rt5(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+rt1 TE-IS 30 rt5 - rt2(4)
+2001:db8::2/128 IP6 internal 30 rt5 - rt2(4)
+2001:db8::1/128 IP6 internal 40 rt5 - rt1(4)
+rt6 TE-IS 50 rt6 - rt8(4)
+rt7 TE-IS 50 rt7 - rt8(4)
+rt3 TE-IS 50 rt5 - rt1(4)
+rt4 TE-IS 50 rt5 - rt1(4)
+2001:db8::6/128 IP6 internal 60 rt6 - rt6(4)
+2001:db8::7/128 IP6 internal 60 rt7 - rt7(4)
+2001:db8::3/128 IP6 internal 60 rt5 - rt3(4)
+2001:db8::4/128 IP6 internal 60 rt5 - rt4(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 40 - rt5 16011
+ 2001:db8::2/128 30 - rt5 16021
+ 2001:db8::3/128 60 - rt5 16031
+ 2001:db8::4/128 60 - rt5 16041
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 60 - rt6 implicit-null
+ 2001:db8::7/128 60 - rt7 implicit-null
+ 2001:db8::8/128 0 - - -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 80 - rt6 16011
+ - rt7 16011
+ 2001:db8::2/128 90 - rt6 16021
+ - rt7 16021
+ 2001:db8::3/128 60 - rt6 16031
+ - rt7 16031
+ 2001:db8::4/128 60 - rt6 16041
+ - rt7 16041
+ 2001:db8::5/128 100 - rt6 16051
+ - rt7 16051
+
+test# test isis topology 11 root rt3 lfa system-id rt5
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+10.0.255.3/32 IP internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 10 rt2 - rt3(4)
+rt5 TE-IS 10 rt5 - rt3(4)
+rt2 pseudo_TE-IS 20 rt1 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt5 - rt5(4)
+rt6 TE-IS 20 rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt5 -
+10.0.255.6/32 IP TE 30 rt5 - rt6(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 20 - rt1 implicit-null
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 0 - - -
+ 10.0.255.4/32 30 - rt2 16040
+ - rt5 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt5 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.5/32 30 - rt2 16050
+ 10.0.255.6/32 30 - rt2 16060
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt3
+2001:db8::3/128 IP6 internal 0 rt3(4)
+rt1 TE-IS 10 rt1 - rt3(4)
+rt2 TE-IS 10 rt2 - rt3(4)
+rt5 TE-IS 10 rt5 - rt3(4)
+rt2 pseudo_TE-IS 20 rt1 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt5 - rt5(4)
+rt6 TE-IS 20 rt5 - rt5(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+ rt5 -
+2001:db8::6/128 IP6 internal 30 rt5 - rt6(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 20 - rt1 implicit-null
+ 2001:db8::2/128 20 - rt2 implicit-null
+ 2001:db8::3/128 0 - - -
+ 2001:db8::4/128 30 - rt2 16041
+ - rt5 16041
+ 2001:db8::5/128 20 - rt5 implicit-null
+ 2001:db8::6/128 30 - rt5 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::5/128 30 - rt2 16051
+ 2001:db8::6/128 30 - rt2 16061
+
+test# test isis topology 13 root rt4 lfa system-id rt3
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+10.0.255.4/32 IP internal 0 rt4(4)
+rt2 TE-IS 10 rt2 - rt4(4)
+rt3 TE-IS 10 rt3 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+ rt6(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 30 - rt2 16010
+ - rt3 16010
+ 10.0.255.2/32 20 - rt2 implicit-null
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 0 - - -
+ 10.0.255.5/32 30 - rt3 16050
+ 10.0.255.6/32 30 - rt3 16060
+ 10.0.255.7/32 40 - rt3 16070
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.3/32 110 - rt5 16030
+ 10.0.255.5/32 100 - rt5 implicit-null
+ 10.0.255.6/32 120 - rt5 16060
+ 10.0.255.7/32 110 - rt5 16070
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt4
+rt2 TE-IS 10 rt2 - rt4(4)
+rt3 TE-IS 10 rt3 - rt4(4)
+rt5 TE-IS 100 rt5 - rt4(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 14 root rt1 lfa system-id rt1 pseudonode-id 1
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::4/128 20 - rt4 -
+ 2001:db8::5/128 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+test# test isis topology 14 root rt1 lfa system-id rt2
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 0 - - -
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt3 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt1
+rt5 TE-IS 20 rt4 - rt4(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 0 - - -
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::4/128 20 - rt4 -
+ 2001:db8::5/128 30 - rt4 -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::2/128 20 - rt3 -
+
+test# test isis topology 14 root rt5 lfa system-id rt4
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+10.0.255.5/32 IP internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt1 pseudo_TE-IS 20 rt4 - rt4(4)
+rt1 TE-IS 20 rt4 - rt1(2)
+rt3 TE-IS 20 rt4 - rt1(2)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt1(4)
+ rt3(4)
+10.0.255.1/32 IP TE 30 rt4 - rt1(4)
+10.0.255.3/32 IP TE 30 rt4 - rt3(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 30 - rt4 -
+ 10.0.255.2/32 40 - rt4 -
+ 10.0.255.3/32 30 - rt4 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 0 - - -
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.1/32 60 - rt3 -
+ 10.0.255.2/32 60 - rt3 -
+ 10.0.255.3/32 50 - rt3 -
+ 10.0.255.4/32 60 - rt3 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt5
+2001:db8::5/128 IP6 internal 0 rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt1 pseudo_TE-IS 20 rt4 - rt4(4)
+rt1 TE-IS 20 rt4 - rt1(2)
+rt3 TE-IS 20 rt4 - rt1(2)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt1(4)
+ rt3(4)
+2001:db8::1/128 IP6 internal 30 rt4 - rt1(4)
+2001:db8::3/128 IP6 internal 30 rt4 - rt3(4)
+2001:db8::2/128 IP6 internal 40 rt4 - rt2(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 30 - rt4 -
+ 2001:db8::2/128 40 - rt4 -
+ 2001:db8::3/128 30 - rt4 -
+ 2001:db8::4/128 20 - rt4 -
+ 2001:db8::5/128 0 - - -
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::1/128 60 - rt3 -
+ 2001:db8::2/128 60 - rt3 -
+ 2001:db8::3/128 50 - rt3 -
+ 2001:db8::4/128 60 - rt3 -
+
+test#
test# test isis topology 1 root rt1 ti-lfa system-id rt2
P-space (self):
rt3
diff --git a/tests/isisd/test_topologies.c b/tests/isisd/test_topologies.c
index ee89407a7..ca103948f 100644
--- a/tests/isisd/test_topologies.c
+++ b/tests/isisd/test_topologies.c
@@ -531,6 +531,34 @@
* | +---------------------+ |
* | | | |
* +---------+ +---------+
+
+ * Test topology 14:
+ * =================
+ *
+ * +---------+ +---------+
+ * | | | |
+ * | RT1 | | RT2 |
+ * | +--------------+ |
+ * | | | |
+ * +----+----+ +----+----+
+ * | |
+ * | |
+ * | |
+ * | +----+----+
+ * | | |
+ * | | RT3 |
+ * +-------------------+ |
+ * | | |
+ * | +----+----+
+ * | |
+ * | |50
+ * | |
+ * +----+----+ +----+----+
+ * | | | |
+ * | RT4 | | RT5 |
+ * | +--------------+ |
+ * | | | |
+ * +---------+ +---------+
*/
struct isis_topology test_topologies[] = {
@@ -3302,6 +3330,158 @@ struct isis_topology test_topologies[] = {
},
},
{
+ .number = 14,
+ .nodes = {
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.1",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.1/32",
+ "2001:db8::1/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ },
+ },
+ {
+ .hostname = "rt2",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.2",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.2/32",
+ "2001:db8::2/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 20,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 10,
+ },
+ },
+ },
+ {
+ .hostname = "rt3",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.3",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.3/32",
+ "2001:db8::3/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt2",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 50,
+ },
+ },
+ },
+ {
+ .hostname = "rt4",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.4",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.4/32",
+ "2001:db8::4/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .pseudonode_id = 1,
+ .metric = 10,
+ },
+ {
+ .hostname = "rt5",
+ .metric = 10,
+ },
+ },
+ },
+ {
+ .hostname = "rt5",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+ .level = IS_LEVEL_1,
+ .router_id = "10.0.255.5",
+ .protocols = {
+ .ipv4 = true,
+ .ipv6 = true,
+ },
+ .networks = {
+ "10.0.255.5/32",
+ "2001:db8::5/128",
+ },
+ .adjacencies = {
+ {
+ .hostname = "rt4",
+ .metric = 10,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 50,
+ },
+ },
+ },
+ {
+ .hostname = "rt1",
+ .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ .pseudonode_id = 1,
+ .level = IS_LEVEL_1,
+ .adjacencies = {
+ {
+ .hostname = "rt1",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt3",
+ .metric = 0,
+ },
+ {
+ .hostname = "rt4",
+ .metric = 0,
+ },
+ },
+ },
+ },
+ },
+ {
/* sentinel */
},
};
diff --git a/tests/topotests/isis-lfa-topo1/__init__.py b/tests/topotests/isis-lfa-topo1/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/__init__.py
diff --git a/tests/topotests/isis-lfa-topo1/rt1/isisd.conf b/tests/topotests/isis-lfa-topo1/rt1/isisd.conf
new file mode 100644
index 000000000..2ad8c1253
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/isisd.conf
@@ -0,0 +1,52 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt4
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt5
+ ipv6 router isis 1
+ isis metric 20
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt6
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+ !fast-reroute lfa tiebreaker node-protecting index 10
+ !fast-reroute lfa tiebreaker downstream index 20
+ !fast-reroute lfa tiebreaker lowest-backup-metric index 30
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis-lfa-topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 000000000..10c61d53e
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,236 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2,
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2,
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 000000000..d8a7c5a9c
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,101 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step10/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step10/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..d626cdca0
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step10/show_ipv6_route.ref.diff
@@ -0,0 +1,46 @@
+--- a/rt1/step9/show_ipv6_route.ref
++++ b/rt1/step10/show_ipv6_route.ref
+@@ -16,7 +16,8 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1
++ 1,
++ 2
+ ]
+ }
+ ],
+@@ -30,6 +31,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
+@@ -198,7 +204,8 @@
+ "backupIndex":[
+ 0,
+ 1,
+- 2
++ 2,
++ 3
+ ]
+ }
+ ],
+@@ -217,6 +224,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step11/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step11/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..f7f99c276
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step11/show_ipv6_route.ref.diff
@@ -0,0 +1,23 @@
+--- a/rt1/step10/show_ipv6_route.ref
++++ b/rt1/step11/show_ipv6_route.ref
+@@ -204,19 +204,13 @@
+ "backupIndex":[
+ 0,
+ 1,
+- 2,
+- 3
++ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step12/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step12/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..3b767f1bf
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step12/show_ipv6_route.ref.diff
@@ -0,0 +1,107 @@
+--- a/rt1/step11/show_ipv6_route.ref
++++ b/rt1/step12/show_ipv6_route.ref
+@@ -15,9 +15,7 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2
++ 0
+ ]
+ }
+ ],
+@@ -26,16 +24,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -56,8 +44,7 @@
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -66,11 +53,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -120,10 +102,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
++ 0
+ ]
+ }
+ ],
+@@ -132,21 +111,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -203,19 +167,13 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step13/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step13/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..504af5ac5
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step13/show_ipv6_route.ref.diff
@@ -0,0 +1,45 @@
+--- a/rt1/step12/show_ipv6_route.ref
++++ b/rt1/step13/show_ipv6_route.ref
+@@ -131,8 +131,7 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -141,11 +140,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -166,19 +160,13 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step2/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step2/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..efc56d983
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step2/show_ipv6_route.ref.diff
@@ -0,0 +1,164 @@
+--- a/rt1/step1/show_ipv6_route.ref
++++ b/rt1/step2/show_ipv6_route.ref
+@@ -13,28 +13,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -54,22 +32,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -89,16 +51,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -118,34 +70,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -165,22 +89,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -200,34 +108,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step3/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step3/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..cafbe490b
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step3/show_ipv6_route.ref.diff
@@ -0,0 +1,164 @@
+--- a/rt1/step2/show_ipv6_route.ref
++++ b/rt1/step3/show_ipv6_route.ref
+@@ -13,6 +13,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -32,6 +54,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -51,6 +89,16 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -70,6 +118,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -89,6 +165,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -108,6 +200,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step4/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step4/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..47d8334a0
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,142 @@
+--- a/rt1/step3/show_ipv6_route.ref
++++ b/rt1/step4/show_ipv6_route.ref
+@@ -15,9 +15,7 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2
++ 0
+ ]
+ }
+ ],
+@@ -26,16 +24,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -56,8 +44,7 @@
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -66,11 +53,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -120,10 +102,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
++ 0
+ ]
+ }
+ ],
+@@ -132,21 +111,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -167,8 +131,7 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -177,11 +140,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -202,10 +160,7 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
++ 0
+ ]
+ }
+ ],
+@@ -214,21 +169,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step5/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step5/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..b6a342df8
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,142 @@
+--- a/rt1/step4/show_ipv6_route.ref
++++ b/rt1/step5/show_ipv6_route.ref
+@@ -15,7 +15,9 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1,
++ 2
+ ]
+ }
+ ],
+@@ -24,6 +26,16 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
+@@ -44,7 +56,8 @@
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1
+ ]
+ }
+ ],
+@@ -53,6 +66,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
+ }
+ ]
+ }
+@@ -102,7 +120,10 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1,
++ 2,
++ 3
+ ]
+ }
+ ],
+@@ -111,6 +132,21 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
+@@ -131,7 +167,8 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1
+ ]
+ }
+ ],
+@@ -140,6 +177,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
+ }
+ ]
+ }
+@@ -160,7 +202,10 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1,
++ 2,
++ 3
+ ]
+ }
+ ],
+@@ -169,6 +214,21 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step6/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step6/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..fafa2999d
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step6/show_ipv6_route.ref.diff
@@ -0,0 +1,164 @@
+--- a/rt1/step5/show_ipv6_route.ref
++++ b/rt1/step6/show_ipv6_route.ref
+@@ -13,28 +13,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -54,22 +32,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -89,16 +51,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -118,34 +70,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -165,22 +89,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -200,34 +108,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step7/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step7/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..1803e2cf5
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step7/show_ipv6_route.ref.diff
@@ -0,0 +1,37 @@
+--- a/rt1/step6/show_ipv6_route.ref
++++ b/rt1/step7/show_ipv6_route.ref
+@@ -108,6 +108,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step8/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step8/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..306f72534
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step8/show_ipv6_route.ref.diff
@@ -0,0 +1,129 @@
+--- a/rt1/step7/show_ipv6_route.ref
++++ b/rt1/step8/show_ipv6_route.ref
+@@ -13,6 +13,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -32,6 +54,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -51,6 +89,16 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -70,6 +118,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -89,6 +165,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis-lfa-topo1/rt1/step9/show_ipv6_route.ref.diff b/tests/topotests/isis-lfa-topo1/rt1/step9/show_ipv6_route.ref.diff
new file mode 100644
index 000000000..3ffab46ee
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,46 @@
+--- a/rt1/step8/show_ipv6_route.ref
++++ b/rt1/step9/show_ipv6_route.ref
+@@ -16,8 +16,7 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+@@ -31,11 +30,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -204,8 +198,7 @@
+ "backupIndex":[
+ 0,
+ 1,
+- 2,
+- 3
++ 2
+ ]
+ }
+ ],
+@@ -224,11 +217,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-lfa-topo1/rt1/zebra.conf b/tests/topotests/isis-lfa-topo1/rt1/zebra.conf
new file mode 100644
index 000000000..317f1031d
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt1/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt1
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt2/isisd.conf b/tests/topotests/isis-lfa-topo1/rt2/isisd.conf
new file mode 100644
index 000000000..39ff2570d
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt2/isisd.conf
@@ -0,0 +1,39 @@
+password 1
+hostname rt2
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis-lfa-topo1/rt2/step1/show_ipv6_route.ref
new file mode 100644
index 000000000..036bfe1f4
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt2/step1/show_ipv6_route.ref
@@ -0,0 +1,162 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lfa-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 000000000..681c5222a
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt2/zebra.conf b/tests/topotests/isis-lfa-topo1/rt2/zebra.conf
new file mode 100644
index 000000000..9feaada79
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt2/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt2
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt3/isisd.conf b/tests/topotests/isis-lfa-topo1/rt3/isisd.conf
new file mode 100644
index 000000000..8b0c7bd0d
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt3/isisd.conf
@@ -0,0 +1,38 @@
+password 1
+hostname rt3
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt2
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis-lfa-topo1/rt3/step1/show_ipv6_route.ref
new file mode 100644
index 000000000..a1aab400e
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt3/step1/show_ipv6_route.ref
@@ -0,0 +1,188 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lfa-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 000000000..1495e3228
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt3/zebra.conf b/tests/topotests/isis-lfa-topo1/rt3/zebra.conf
new file mode 100644
index 000000000..48d732e72
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt3/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt3
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt4/isisd.conf b/tests/topotests/isis-lfa-topo1/rt4/isisd.conf
new file mode 100644
index 000000000..86edee6ab
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt4/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt4
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis metric 15
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis-lfa-topo1/rt4/step1/show_ipv6_route.ref
new file mode 100644
index 000000000..6878e2fac
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt4/step1/show_ipv6_route.ref
@@ -0,0 +1,172 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":35,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lfa-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 000000000..d8cd565b5
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt4/zebra.conf b/tests/topotests/isis-lfa-topo1/rt4/zebra.conf
new file mode 100644
index 000000000..bff10860c
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt4/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt4
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt5/isisd.conf b/tests/topotests/isis-lfa-topo1/rt5/isisd.conf
new file mode 100644
index 000000000..7a7cfe557
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt5/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt5
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis metric 20
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis-lfa-topo1/rt5/step1/show_ipv6_route.ref
new file mode 100644
index 000000000..f8181c776
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt5/step1/show_ipv6_route.ref
@@ -0,0 +1,176 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":35,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lfa-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 000000000..d8cd565b5
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt5/zebra.conf b/tests/topotests/isis-lfa-topo1/rt5/zebra.conf
new file mode 100644
index 000000000..ee1e46c96
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt5/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt5
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt6/isisd.conf b/tests/topotests/isis-lfa-topo1/rt6/isisd.conf
new file mode 100644
index 000000000..20cb7769a
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt6/isisd.conf
@@ -0,0 +1,31 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis-lfa-topo1/rt6/step1/show_ipv6_route.ref
new file mode 100644
index 000000000..e5f3c77ac
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt6/step1/show_ipv6_route.ref
@@ -0,0 +1,172 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lfa-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 000000000..d8cd565b5
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt6/zebra.conf b/tests/topotests/isis-lfa-topo1/rt6/zebra.conf
new file mode 100644
index 000000000..410807889
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt6/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt6
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt7/isisd.conf b/tests/topotests/isis-lfa-topo1/rt7/isisd.conf
new file mode 100644
index 000000000..713e6d39f
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt7/isisd.conf
@@ -0,0 +1,51 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt4
+ ipv6 router isis 1
+ isis metric 15
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt5
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt6
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0007.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-lfa-topo1/rt7/step1/show_ipv6_route.ref b/tests/topotests/isis-lfa-topo1/rt7/step1/show_ipv6_route.ref
new file mode 100644
index 000000000..0dff15e3f
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt7/step1/show_ipv6_route.ref
@@ -0,0 +1,186 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2,
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-lfa-topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 000000000..d8a7c5a9c
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,101 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-lfa-topo1/rt7/zebra.conf b/tests/topotests/isis-lfa-topo1/rt7/zebra.conf
new file mode 100644
index 000000000..353c9efa9
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/rt7/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt7
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 7.7.7.7/32
+ ipv6 address 2001:db8:1000::7/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py b/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py
new file mode 100755
index 000000000..67edcae90
--- /dev/null
+++ b/tests/topotests/isis-lfa-topo1/test_isis_lfa_topo1.py
@@ -0,0 +1,545 @@
+#!/usr/bin/env python
+
+#
+# test_isis_tilfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_isis_lfa_topo1.py:
+
+ +---------+
+ | |
+ +--------------------------------+ RT1 +-------------------------------+
+ | +-------------+ +-------------+ |
+ | | | | | |
+ | | +----+----+ | |
+ | | | |20 |
+ | | | | |
+ | | | | |
+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+
+ | | | | | | | | | |
+ | RT2 | 5 | RT3 | | RT4 | | RT5 | | RT6 |
+ | +--------+ | | | | | | |
+ | | | | | | | | | |
+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+
+ | | | | |
+ | | |15 | |
+ |5 | | | |
+ | | +----+----+ | |
+ | | | | | |
+ | +-------------+ RT7 +-------------+ |
+ +--------------------------------+ +-------------------------------+
+ | |
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+import tempfile
+from time import sleep
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # Define FRR Routers
+ #
+ for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch('s1')
+ switch.add_link(tgen.gears['rt1'], nodeif="eth-rt2")
+ switch.add_link(tgen.gears['rt2'], nodeif="eth-rt1")
+ switch = tgen.add_switch('s2')
+ switch.add_link(tgen.gears['rt2'], nodeif="eth-rt3")
+ switch.add_link(tgen.gears['rt3'], nodeif="eth-rt2")
+ switch = tgen.add_switch('s3')
+ switch.add_link(tgen.gears['rt1'], nodeif="eth-rt3")
+ switch.add_link(tgen.gears['rt3'], nodeif="eth-rt1")
+ switch = tgen.add_switch('s4')
+ switch.add_link(tgen.gears['rt1'], nodeif="eth-rt4")
+ switch.add_link(tgen.gears['rt4'], nodeif="eth-rt1")
+ switch = tgen.add_switch('s5')
+ switch.add_link(tgen.gears['rt1'], nodeif="eth-rt5")
+ switch.add_link(tgen.gears['rt5'], nodeif="eth-rt1")
+ switch = tgen.add_switch('s6')
+ switch.add_link(tgen.gears['rt1'], nodeif="eth-rt6")
+ switch.add_link(tgen.gears['rt6'], nodeif="eth-rt1")
+ switch = tgen.add_switch('s7')
+ switch.add_link(tgen.gears['rt2'], nodeif="eth-rt7")
+ switch.add_link(tgen.gears['rt7'], nodeif="eth-rt2")
+ switch = tgen.add_switch('s8')
+ switch.add_link(tgen.gears['rt3'], nodeif="eth-rt7")
+ switch.add_link(tgen.gears['rt7'], nodeif="eth-rt3")
+ switch = tgen.add_switch('s9')
+ switch.add_link(tgen.gears['rt4'], nodeif="eth-rt7")
+ switch.add_link(tgen.gears['rt7'], nodeif="eth-rt4")
+ switch = tgen.add_switch('s10')
+ switch.add_link(tgen.gears['rt5'], nodeif="eth-rt7")
+ switch.add_link(tgen.gears['rt7'], nodeif="eth-rt5")
+ switch = tgen.add_switch('s11')
+ switch.add_link(tgen.gears['rt6'], nodeif="eth-rt7")
+ switch.add_link(tgen.gears['rt7'], nodeif="eth-rt6")
+
+ #
+ # Populate multi-dimensional dictionary containing all expected outputs
+ #
+ files = ["show_ipv6_route.ref",
+ "show_yang_interface_isis_adjacencies.ref"]
+ for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']:
+ outputs[rname] = {}
+ for step in range(1, 13 + 1):
+ outputs[rname][step] = {}
+ for file in files:
+ if step == 1:
+ # Get snapshots relative to the expected initial network convergence
+ filename = '{}/{}/step{}/{}'.format(CWD, rname, step, file)
+ outputs[rname][step][file] = open(filename).read()
+ else:
+ if rname != "rt1":
+ continue
+ if file == "show_yang_interface_isis_adjacencies.ref":
+ continue
+
+ # Get diff relative to the previous step
+ filename = '{}/{}/step{}/{}.diff'.format(CWD, rname, step, file)
+
+ # Create temporary files in order to apply the diff
+ f_in = tempfile.NamedTemporaryFile()
+ f_in.write(outputs[rname][step - 1][file])
+ f_in.flush()
+ f_out = tempfile.NamedTemporaryFile()
+ os.system("patch -s -o %s %s %s" %(f_out.name, f_in.name, filename))
+
+ # Store the updated snapshot and remove the temporary files
+ outputs[rname][step][file] = open(f_out.name).read()
+ f_in.close()
+ f_out.close()
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS,
+ os.path.join(CWD, '{}/isisd.conf'.format(rname))
+ )
+
+ tgen.start_router()
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+def router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ expected = json.loads(reference)
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp,
+ tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_isis_adjacencies_step1():
+ logger.info("Test (step 1): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']:
+ router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd",
+ outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"])
+
+def test_rib_ipv6_step1():
+ logger.info("Test (step 1): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'rt7']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][1]["show_ipv6_route.ref"])
+
+#
+# Step 2
+#
+# Action(s):
+# -Disable LFA protection on all interfaces
+#
+# Expected changes:
+# -rt1 should uninstall all backup nexthops from all routes
+#
+def test_rib_ipv6_step2():
+ logger.info("Test (step 2): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Disabling LFA protection on all rt1 interfaces')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "no isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "no isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt6" -c "no isis fast-reroute lfa"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][2]["show_ipv6_route.ref"])
+
+#
+# Step 3
+#
+# Action(s):
+# -Re-enable LFA protection on all interfaces
+#
+# Expected changes:
+# -Revert changes from the previous step
+#
+def test_rib_ipv6_step3():
+ logger.info("Test (step 3): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Re-enabling LFA protection on all rt1 interfaces')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "isis fast-reroute lfa"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt6" -c "isis fast-reroute lfa"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][3]["show_ipv6_route.ref"])
+
+#
+# Step 4
+#
+# Action(s):
+# -Disable LFA load-sharing
+#
+# Expected changes:
+# -rt1 should use at most one backup nexthop for each route
+#
+def test_rib_ipv6_step4():
+ logger.info("Test (step 4): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Disabling LFA load-sharing on rt1')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute load-sharing disable"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][4]["show_ipv6_route.ref"])
+
+#
+# Step 5
+#
+# Action(s):
+# -Re-enable LFA load-sharing
+#
+# Expected changes:
+# -Revert changes from the previous step
+#
+def test_rib_ipv6_step5():
+ logger.info("Test (step 5): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Re-enabling LFA load-sharing on rt1')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute load-sharing disable"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][5]["show_ipv6_route.ref"])
+
+#
+# Step 6
+#
+# Action(s):
+# -Limit backup computation to critical priority prefixes only
+#
+# Expected changes:
+# -rt1 should uninstall all backup nexthops from all routes
+#
+def test_rib_ipv6_step6():
+ logger.info("Test (step 6): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Limiting backup computation to critical priority prefixes only')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute priority-limit critical"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][6]["show_ipv6_route.ref"])
+
+#
+# Step 7
+#
+# Action(s):
+# -Configure a prefix priority list to classify rt7's loopback as a
+# critical-priority prefix
+#
+# Expected changes:
+# -rt1 should install backup nexthops for rt7's loopback route.
+#
+def test_rib_ipv6_step7():
+ logger.info("Test (step 7): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Configuring a prefix priority list')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "spf prefix-priority critical CRITICAL_DESTINATIONS"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][7]["show_ipv6_route.ref"])
+
+#
+# Step 8
+#
+# Action(s):
+# -Revert previous changes related to prefix priorities
+#
+# Expected changes:
+# -Revert changes from the previous two steps
+#
+def test_rib_ipv6_step8():
+ logger.info("Test (step 8): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Reverting previous changes related to prefix priorities')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "no ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute priority-limit critical"')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no spf prefix-priority critical CRITICAL_DESTINATIONS"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][8]["show_ipv6_route.ref"])
+
+#
+# Step 9
+#
+# Action(s):
+# -Exclude eth-rt6 from LFA computation for eth-rt2's failure
+#
+# Expected changes:
+# -Uninstall the eth-rt2 protecting backup nexthops that go through eth-rt6
+#
+def test_rib_ipv6_step9():
+ logger.info("Test (step 9): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Excluding eth-rt6 from LFA computation for eth-rt2\'s failure')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa exclude interface eth-rt6"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][9]["show_ipv6_route.ref"])
+
+#
+# Step 10
+#
+# Action(s):
+# -Remove exclusion of eth-rt6 from LFA computation for eth-rt2's failure
+#
+# Expected changes:
+# -Revert changes from the previous step
+#
+def test_rib_ipv6_step10():
+ logger.info("Test (step 10): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Removing exclusion of eth-rt6 from LFA computation for eth-rt2\'s failure')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa exclude interface eth-rt6"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][10]["show_ipv6_route.ref"])
+
+#
+# Step 11
+#
+# Action(s):
+# -Add LFA tiebreaker: prefer node protecting backup path
+#
+# Expected changes:
+# -rt1 should prefer backup nexthops that provide node protection
+#
+def test_rib_ipv6_step11():
+ logger.info("Test (step 11): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Adding LFA tiebreaker: prefer node protecting backup path')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker node-protecting index 10"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][11]["show_ipv6_route.ref"])
+
+#
+# Step 12
+#
+# Action(s):
+# -Add LFA tiebreaker: prefer backup path via downstream node
+#
+# Expected changes:
+# -rt1 should prefer backup nexthops that satisfy the downstream condition
+#
+def test_rib_ipv6_step12():
+ logger.info("Test (step 12): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Adding LFA tiebreaker: prefer backup path via downstream node')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker downstream index 20"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][12]["show_ipv6_route.ref"])
+
+#
+# Step 13
+#
+# Action(s):
+# -Add LFA tiebreaker: prefer backup path with lowest total metric
+#
+# Expected changes:
+# -rt1 should prefer backup nexthops that have the best metric
+#
+def test_rib_ipv6_step13():
+ logger.info("Test (step 13): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info('Adding LFA tiebreaker: prefer backup path with lowest total metric')
+ tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker lowest-backup-metric index 30"')
+
+ for rname in ['rt1']:
+ router_compare_json_output(rname, "show ipv6 route isis json",
+ outputs[rname][13]["show_ipv6_route.ref"])
+
+# Memory leak test template
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip('Memory leak test/report is disabled')
+
+ tgen.report_memory_leaks()
+
+if __name__ == '__main__':
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index c3b39e375..d751a19f0 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -244,6 +244,10 @@ module frr-isisd {
}
}
+ typedef access-list-ref {
+ type string;
+ }
+
grouping redistribute-attributes {
description
"Common optional attributes of any redistribute entry.";
@@ -336,6 +340,94 @@ module frr-isisd {
}
}
+ grouping global-config-lfa {
+ container lfa {
+ description
+ "LFA configuration.";
+
+ leaf load-sharing {
+ type boolean;
+ default "true";
+ description
+ "Load share prefixes across multiple backups.";
+ }
+ leaf priority-limit {
+ type enumeration {
+ enum "critical" {
+ value 0;
+ description
+ "Compute for critical priority prefixes only.";
+ }
+ enum "high" {
+ value 1;
+ description
+ "Compute for critical & high priority prefixes.";
+ }
+ enum "medium" {
+ value 2;
+ description
+ "Compute for critical, high & medium priority prefixes.";
+ }
+ }
+ description
+ "Limit backup computation up to the prefix priority.";
+ }
+ list tiebreaker {
+ key "index";
+ unique "type";
+ description
+ "Configure tiebreaker for multiple backups.";
+ leaf index {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Preference order among tiebreakers.";
+ }
+ leaf type {
+ type enumeration {
+ enum "downstream" {
+ value 0;
+ description
+ "Prefer backup path via downstream node.";
+ }
+ enum "lowest-backup-metric" {
+ value 1;
+ description
+ "Prefer backup path with lowest total metric.";
+ }
+ enum "node-protecting" {
+ value 2;
+ description
+ "Prefer node protecting backup path.";
+ }
+ }
+ mandatory true;
+ description
+ "Tiebreaker type.";
+ }
+ }
+ }
+ }
+
+ grouping interface-config-lfa {
+ container lfa {
+ description
+ "LFA configuration.";
+ leaf enable {
+ type boolean;
+ default false;
+ description
+ "Enables LFA computation.";
+ }
+ leaf-list exclude-interface {
+ type frr-interface:interface-ref;
+ description
+ "Exclude an interface from computation.";
+ }
+ }
+ }
+
grouping interface-config-ti-lfa {
container ti-lfa {
description
@@ -664,11 +756,21 @@ module frr-isisd {
container level-1 {
description
"Level-1 IP Fast-reroute configuration.";
+ must "./lfa/enable = 'false' or ./ti-lfa/enable = 'false'" {
+ error-message
+ "Can't enable both classic LFA and TI-LFA in the same interface.";
+ }
+ uses interface-config-lfa;
uses interface-config-ti-lfa;
}
container level-2 {
description
"Level-2 IP Fast-reroute configuration.";
+ must "./lfa/enable = 'false' or ./ti-lfa/enable = 'false'" {
+ error-message
+ "Can't enable both classic LFA and TI-LFA in the same interface.";
+ }
+ uses interface-config-lfa;
uses interface-config-ti-lfa;
}
}
@@ -1095,6 +1197,42 @@ module frr-isisd {
"Minimum time between consecutive level-2 SPFs.";
}
}
+
+ container prefix-priorities {
+ description
+ "SPF Prefix Priority configuration";
+
+ container critical {
+ description
+ "Critical prefix priority";
+ leaf access-list-name {
+ type access-list-ref;
+ description
+ "Access List to determine prefixes for
+ this priority";
+ }
+ }
+ container high {
+ description
+ "High prefix priority";
+ leaf access-list-name {
+ type access-list-ref;
+ description
+ "Access List to determine prefixes for
+ this priority";
+ }
+ }
+ container medium {
+ description
+ "Medium prefix priority";
+ leaf access-list-name {
+ type access-list-ref;
+ description
+ "Access List to determine prefixes for
+ this priority";
+ }
+ }
+ }
}
container area-password {
@@ -1249,6 +1387,21 @@ module frr-isisd {
}
}
+ container fast-reroute {
+ description
+ "IP Fast-reroute configuration.";
+ container level-1 {
+ description
+ "Level-1 IP Fast-reroute configuration.";
+ uses global-config-lfa;
+ }
+ container level-2 {
+ description
+ "Level-2 IP Fast-reroute configuration.";
+ uses global-config-lfa;
+ }
+ }
+
leaf log-adjacency-changes {
type boolean;
default "false";