summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/user/isisd.rst18
-rw-r--r--isisd/isis_circuit.h2
-rw-r--r--isisd/isis_cli.c185
-rw-r--r--isisd/isis_lfa.c596
-rw-r--r--isisd/isis_lfa.h24
-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.c44
-rw-r--r--isisd/isis_nb.h24
-rw-r--r--isisd/isis_nb_config.c215
-rw-r--r--isisd/isis_route.c37
-rw-r--r--isisd/isis_route.h2
-rw-r--r--isisd/isis_spf.c36
-rw-r--r--isisd/isis_spf.h1
-rw-r--r--isisd/isis_spf_private.h17
-rw-r--r--isisd/isis_zebra.c76
-rw-r--r--isisd/isis_zebra.h2
-rw-r--r--isisd/isisd.c30
-rw-r--r--isisd/isisd.h5
-rw-r--r--ldpd/adjacency.c5
-rw-r--r--ldpd/hello.c3
-rw-r--r--ldpd/lde.c86
-rw-r--r--ldpd/lde.h4
-rw-r--r--ldpd/lde_lib.c19
-rw-r--r--ldpd/ldp_zebra.c29
-rw-r--r--ldpd/ldpd.c10
-rw-r--r--ldpd/ldpd.h8
-rw-r--r--ldpd/ldpe.c45
-rw-r--r--ldpd/rlfa.c288
-rw-r--r--ldpd/rlfa.h78
-rw-r--r--ldpd/subdir.am2
-rw-r--r--lib/zclient.h52
-rw-r--r--tests/isisd/test_common.c18
-rw-r--r--tests/isisd/test_common.h3
-rw-r--r--tests/isisd/test_isis_spf.c101
-rw-r--r--tests/isisd/test_isis_spf.in12
-rw-r--r--tests/isisd/test_isis_spf.refout1221
-rw-r--r--tests/topotests/isis-rlfa-topo1/__init__.py0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/isisd.conf39
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref235
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref207
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt1/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt2/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt3/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt4/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt5/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt5/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt6/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt6/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt7/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt7/zebra.conf22
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt8/isisd.conf32
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf30
-rw-r--r--tests/topotests/isis-rlfa-topo1/rt8/zebra.conf22
-rwxr-xr-xtests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py662
-rw-r--r--yang/frr-isisd.yang47
86 files changed, 6402 insertions, 62 deletions
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst
index f991e3f073..7e198564b5 100644
--- a/doc/user/isisd.rst
+++ b/doc/user/isisd.rst
@@ -204,6 +204,12 @@ ISIS Fast-Reroute
Disable load sharing across multiple LFA backups.
+.. index:: fast-reroute remote-lfa prefix-list WORD [level-1 | level-2]
+.. clicmd:: [no] fast-reroute remote-lfa prefix-list [WORD] [level-1 | level-2]
+
+ Configure a prefix-list to select eligible PQ nodes (valid for all protected
+ interfaces).
+
.. _isis-region:
ISIS region
@@ -400,6 +406,18 @@ ISIS interface
Enable per-prefix TI-LFA fast reroute link or node protection.
+.. index:: isis fast-reroute remote-lfa tunnel mpls-ldp [level-1 | level-2]
+.. clicmd:: [no] isis fast-reroute remote-lfa tunnel mpls-ldp [level-1 | level-2]
+
+ Enable per-prefix Remote LFA fast reroute link protection. Note that other
+ routers in the network need to be configured to accept LDP targeted hello
+ messages in order for RLFA to work.
+
+.. index:: isis fast-reroute remote-lfa maximum-metric (1-16777215) [level-1 | level-2]
+.. clicmd:: [no] isis fast-reroute remote-lfa maximum-metric (1-16777215) [level-1 | level-2]
+
+ Limit Remote LFA PQ node selection within the specified metric.
+
.. _showing-isis-information:
Showing ISIS information
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
index e736d8fb1f..9a8982dc06 100644
--- a/isisd/isis_circuit.h
+++ b/isisd/isis_circuit.h
@@ -142,6 +142,8 @@ struct isis_circuit {
struct bfd_info *bfd_info;
struct ldp_sync_info *ldp_sync_info;
bool lfa_protection[ISIS_LEVELS];
+ bool rlfa_protection[ISIS_LEVELS];
+ uint32_t rlfa_max_metric[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 1f0bebaf45..5ca70eab0f 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -1915,22 +1915,22 @@ DEFPY_YANG (isis_frr_lfa_load_sharing,
if (no) {
nb_cli_enqueue_change(
vty, "./fast-reroute/level-1/lfa/load-sharing",
- NB_OP_DESTROY, "true");
+ NB_OP_MODIFY, "true");
} else {
nb_cli_enqueue_change(
vty, "./fast-reroute/level-1/lfa/load-sharing",
- NB_OP_CREATE, "false");
+ NB_OP_MODIFY, "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");
+ NB_OP_MODIFY, "true");
} else {
nb_cli_enqueue_change(
vty, "./fast-reroute/level-2/lfa/load-sharing",
- NB_OP_CREATE, "false");
+ NB_OP_MODIFY, "false");
}
}
@@ -1948,6 +1948,62 @@ void cli_show_isis_frr_lfa_load_sharing(struct vty *vty, struct lyd_node *dnode,
}
/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/remote-lfa/prefix-list
+ */
+DEFPY_YANG (isis_frr_remote_lfa_plist,
+ isis_frr_remote_lfa_plist_cmd,
+ "fast-reroute remote-lfa prefix-list WORD$plist [<level-1|level-2>$level]",
+ "Configure Fast ReRoute\n"
+ "Enable remote LFA related configuration\n"
+ "Filter PQ node router ID based on prefix list\n"
+ "Prefix-list name\n"
+ "Enable router ID filtering for level-1 only\n"
+ "Enable router ID filtering for level-2 only\n")
+{
+ if (!level || strmatch(level, "level-1"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-1/remote-lfa/prefix-list",
+ NB_OP_MODIFY, plist);
+ if (!level || strmatch(level, "level-2"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-2/remote-lfa/prefix-list",
+ NB_OP_MODIFY, plist);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_isis_frr_remote_lfa_plist,
+ no_isis_frr_remote_lfa_plist_cmd,
+ "no fast-reroute remote-lfa prefix-list [WORD] [<level-1|level-2>$level]",
+ NO_STR
+ "Configure Fast ReRoute\n"
+ "Enable remote LFA related configuration\n"
+ "Filter PQ node router ID based on prefix list\n"
+ "Prefix-list name\n"
+ "Enable router ID filtering for level-1 only\n"
+ "Enable router ID filtering for level-2 only\n")
+{
+ if (!level || strmatch(level, "level-1"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-1/remote-lfa/prefix-list",
+ NB_OP_DESTROY, NULL);
+ if (!level || strmatch(level, "level-2"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-2/remote-lfa/prefix-list",
+ NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_frr_remote_lfa_plist(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " fast-reroute remote-lfa prefix-list %s %s\n",
+ yang_dnode_get_string(dnode, NULL),
+ dnode->parent->parent->schema->name);
+}
+
+/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/passive
*/
DEFPY_YANG(isis_passive, isis_passive_cmd, "[no] isis passive",
@@ -2630,6 +2686,25 @@ void cli_show_ip_isis_frr(struct vty *vty, struct lyd_node *dnode,
}
}
+ /* Remote LFA */
+ l1_enabled = yang_dnode_get_bool(dnode, "./level-1/remote-lfa/enable");
+ l2_enabled = yang_dnode_get_bool(dnode, "./level-2/remote-lfa/enable");
+
+ if (l1_enabled || l2_enabled) {
+ if (l1_enabled == l2_enabled) {
+ vty_out(vty,
+ " isis fast-reroute remote-lfa tunnel mpls-ldp\n");
+ vty_out(vty, "\n");
+ } else {
+ if (l1_enabled)
+ vty_out(vty,
+ " isis fast-reroute remote-lfa tunnel mpls-ldp level-1\n");
+ if (l2_enabled)
+ vty_out(vty,
+ " isis fast-reroute remote-lfa tunnel mpls-ldp 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");
@@ -2761,6 +2836,104 @@ void cli_show_frr_lfa_exclude_interface(struct vty *vty, struct lyd_node *dnode,
}
/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/remote-lfa/enable
+ */
+DEFPY(isis_remote_lfa, isis_remote_lfa_cmd,
+ "[no] isis fast-reroute remote-lfa tunnel mpls-ldp [level-1|level-2]$level",
+ NO_STR
+ "IS-IS routing protocol\n"
+ "Interface IP Fast-reroute configuration\n"
+ "Enable remote LFA computation\n"
+ "Enable remote LFA computation using tunnels\n"
+ "Use MPLS LDP tunnel to reach the remote LFA node\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/remote-lfa/enable",
+ NB_OP_MODIFY, "false");
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/remote-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/remote-lfa/enable",
+ NB_OP_MODIFY, "false");
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/remote-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}/remote-lfa/maximum-metric
+ */
+DEFPY(isis_remote_lfa_max_metric, isis_remote_lfa_max_metric_cmd,
+ "[no] isis fast-reroute remote-lfa maximum-metric (1-16777215)$metric [level-1|level-2]$level",
+ NO_STR
+ "IS-IS routing protocol\n"
+ "Interface IP Fast-reroute configuration\n"
+ "Enable remote LFA computation\n"
+ "Limit remote LFA node selection within the metric\n"
+ "Value of the metric\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/remote-lfa/maximum-metric",
+ NB_OP_DESTROY, NULL);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/remote-lfa/maximum-metric",
+ NB_OP_MODIFY, metric_str);
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/remote-lfa/maximum-metric",
+ NB_OP_DESTROY, NULL);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/remote-lfa/maximum-metric",
+ NB_OP_MODIFY, metric_str);
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_frr_remote_lfa_max_metric(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " isis fast-reroute remote-lfa maximum-metric %s %s\n",
+ yang_dnode_get_string(dnode, NULL),
+ dnode->parent->parent->schema->name);
+}
+
+/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/ti-lfa/enable
*/
DEFPY(isis_ti_lfa, isis_ti_lfa_cmd,
@@ -3085,6 +3258,8 @@ void isis_cli_init(void)
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(ISIS_NODE, &isis_frr_remote_lfa_plist_cmd);
+ install_element(ISIS_NODE, &no_isis_frr_remote_lfa_plist_cmd);
install_element(INTERFACE_NODE, &isis_passive_cmd);
@@ -3122,6 +3297,8 @@ void isis_cli_init(void)
install_element(INTERFACE_NODE, &isis_lfa_cmd);
install_element(INTERFACE_NODE, &isis_lfa_exclude_interface_cmd);
+ install_element(INTERFACE_NODE, &isis_remote_lfa_cmd);
+ install_element(INTERFACE_NODE, &isis_remote_lfa_max_metric_cmd);
install_element(INTERFACE_NODE, &isis_ti_lfa_cmd);
install_element(ISIS_NODE, &log_adj_changes_cmd);
diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c
index fc6b435b62..5b3a3827a2 100644
--- a/isisd/isis_lfa.c
+++ b/isisd/isis_lfa.c
@@ -25,6 +25,8 @@
#include "vrf.h"
#include "table.h"
#include "srcdest_table.h"
+#include "plist.h"
+#include "zclient.h"
#include "isis_common.h"
#include "isisd.h"
@@ -37,11 +39,13 @@
#include "isis_mt.h"
#include "isis_tlvs.h"
#include "isis_spf_private.h"
-#include "isisd/isis_errors.h"
+#include "isis_zebra.h"
+#include "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");
+DEFINE_MTYPE_STATIC(ISISD, ISIS_RLFA, "ISIS Remote LFA");
static inline int isis_spf_node_compare(const struct isis_spf_node *a,
const struct isis_spf_node *b)
@@ -316,7 +320,7 @@ bool isis_lfa_excise_adj_check(const struct isis_spftree *spftree,
{
const struct lfa_protected_resource *resource;
- if (spftree->type != SPF_TYPE_TI_LFA)
+ if (spftree->type != SPF_TYPE_RLFA && spftree->type != SPF_TYPE_TI_LFA)
return false;
/*
@@ -832,14 +836,14 @@ spf_vertex_check_is_affected(const struct isis_vertex *vertex,
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)
+/* Check if a given RLFA/TI-LFA post-convergence SPF vertex needs protection. */
+static bool lfa_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)
+ /* Only local adjacencies need TI-LFA Adj-SID protection. */
+ if (spftree_pc->type == SPF_TYPE_TI_LFA && VTYPE_IS(vertex->type)
&& !isis_adj_find(spftree_pc->area, spftree_pc->level,
vertex->N.id))
return false;
@@ -849,6 +853,10 @@ static bool tilfa_check_needs_protection(const struct isis_spftree *spftree_pc,
if (!vertex_old)
return false;
+ /* Skip vertex if it's already protected by local LFA. */
+ if (CHECK_FLAG(vertex_old->flags, F_ISIS_VERTEX_LFA_PROTECTED))
+ return false;
+
return spf_vertex_check_is_affected(
vertex_old, spftree_pc->sysid,
&spftree_pc->lfa.protected_resource);
@@ -874,14 +882,12 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
if (!spftree_pc->area->srdb.enabled)
return -1;
- if (IS_DEBUG_LFA)
- vid2string(vertex, buf, sizeof(buf));
-
- if (!tilfa_check_needs_protection(spftree_pc, vertex)) {
+ if (!lfa_check_needs_protection(spftree_pc, vertex)) {
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: %s %s unaffected by %s",
- vtype2string(vertex->type), buf,
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
@@ -902,7 +908,8 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: %s %s already covered by node protection",
- vtype2string(vertex->type), buf);
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)));
return -1;
}
@@ -915,7 +922,8 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: %s %s already covered by node protection",
- vtype2string(vertex->type), buf);
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)));
return -1;
}
@@ -924,7 +932,8 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
- vtype2string(vertex->type), buf,
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
@@ -939,7 +948,8 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
if (ret != 0)
zlog_warn(
"ISIS-LFA: failed to compute repair path(s) of %s %s w.r.t %s",
- vtype2string(vertex->type), buf,
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
@@ -978,13 +988,6 @@ static bool vertex_is_affected(struct isis_spftree *spftree_root,
struct isis_vertex *vertex_child;
struct isis_vertex_adj *vadj;
bool reverse = false;
- char buf1[VID2STR_BUFFER];
- char buf2[VID2STR_BUFFER];
-
- if (IS_DEBUG_LFA)
- zlog_debug("ISIS-LFA: vertex %s parent %s",
- vid2string(vertex, buf1, sizeof(buf1)),
- vid2string(pvertex, buf2, sizeof(buf2)));
if (p_space && resource->type == LFA_NODE_PROTECTION) {
if (isis_spf_node_find(&resource->nodes, vertex->N.id))
@@ -1059,10 +1062,6 @@ static void lfa_calc_reach_nodes(struct isis_spftree *spftree,
if (isis_spf_node_find(nodes, vertex->N.id))
continue;
- 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_LFA)
@@ -1166,7 +1165,7 @@ struct isis_spftree *isis_tilfa_compute(struct isis_area *area,
struct isis_spf_node *adj_node;
if (IS_DEBUG_LFA)
- zlog_debug("ISIS-LFA: computing the P/Q spaces w.r.t. %s",
+ zlog_debug("ISIS-LFA: computing TI-LFAs for %s",
lfa_protected_resource2str(resource));
/* Populate list of nodes affected by link failure. */
@@ -1238,6 +1237,497 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree)
return 0;
}
+/* Find Router ID of PQ node. */
+static struct in_addr *rlfa_pq_node_rtr_id(struct isis_spftree *spftree,
+ const struct isis_vertex *vertex_pq)
+{
+ struct isis_lsp *lsp;
+
+ lsp = isis_root_system_lsp(spftree->lspdb, vertex_pq->N.id);
+ if (!lsp)
+ return NULL;
+
+ if (lsp->tlvs->router_cap->router_id.s_addr == INADDR_ANY)
+ return NULL;
+
+ return &lsp->tlvs->router_cap->router_id;
+}
+
+/* Find PQ node by intersecting the P/Q spaces. This is a recursive function. */
+static const struct in_addr *
+rlfa_find_pq_node(struct isis_spftree *spftree_pc,
+ struct isis_vertex *vertex_dest,
+ const struct isis_vertex *vertex,
+ const struct isis_vertex *vertex_child)
+{
+ struct isis_area *area = spftree_pc->area;
+ int level = spftree_pc->level;
+ struct isis_vertex *pvertex;
+ struct listnode *node;
+ bool is_pnode, is_qnode;
+
+ if (!vertex_child)
+ goto parents;
+ if (vertex->type != VTYPE_NONPSEUDO_IS
+ && vertex->type != VTYPE_NONPSEUDO_TE_IS)
+ goto parents;
+ if (!VTYPE_IS(vertex_child->type))
+ vertex_child = NULL;
+
+ /* Check if node is part of the extended P-space and/or Q-space. */
+ is_pnode = lfa_ext_p_space_check(spftree_pc, vertex_dest, vertex);
+ is_qnode = lfa_q_space_check(spftree_pc, vertex);
+
+ if (is_pnode && is_qnode) {
+ const struct in_addr *rtr_id_pq;
+ uint32_t max_metric;
+ struct prefix_list *plist = NULL;
+
+ rtr_id_pq = rlfa_pq_node_rtr_id(spftree_pc, vertex);
+ if (!rtr_id_pq) {
+ if (IS_DEBUG_LFA) {
+ char buf[VID2STR_BUFFER];
+
+ vid2string(vertex, buf, sizeof(buf));
+ zlog_debug(
+ "ISIS-LFA: tentative PQ node (%s %s) doesn't have a router-ID",
+ vtype2string(vertex->type), buf);
+ }
+ goto parents;
+ }
+
+ max_metric = spftree_pc->lfa.remote.max_metric;
+ if (max_metric && vertex->d_N > max_metric) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: skipping PQ node %pI4 (maximum metric)",
+ rtr_id_pq);
+ goto parents;
+ }
+
+ plist = area->rlfa_plist[level - 1];
+ if (plist) {
+ struct prefix p;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = *rtr_id_pq;
+ if (prefix_list_apply(plist, &p) == PREFIX_DENY) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: PQ node %pI4 filtered by prefix-list",
+ rtr_id_pq);
+ goto parents;
+ }
+ }
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: found PQ node: %pI4", rtr_id_pq);
+
+ return rtr_id_pq;
+ }
+
+parents:
+ for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, pvertex)) {
+ const struct in_addr *rtr_id_pq;
+
+ rtr_id_pq = rlfa_find_pq_node(spftree_pc, vertex_dest, pvertex,
+ vertex);
+ if (rtr_id_pq)
+ return rtr_id_pq;
+ }
+
+ return NULL;
+}
+
+int rlfa_cmp(const struct rlfa *a, const struct rlfa *b)
+{
+ return prefix_cmp(&a->prefix, &b->prefix);
+}
+
+static struct rlfa *rlfa_add(struct isis_spftree *spftree,
+ struct isis_vertex *vertex,
+ struct in_addr pq_address)
+{
+ struct rlfa *rlfa;
+
+ assert(VTYPE_IP(vertex->type));
+ rlfa = XCALLOC(MTYPE_ISIS_RLFA, sizeof(*rlfa));
+ rlfa->prefix = vertex->N.ip.p.dest;
+ rlfa->vertex = vertex;
+ rlfa->pq_address = pq_address;
+ rlfa_tree_add(&spftree->lfa.remote.rlfas, rlfa);
+
+ return rlfa;
+}
+
+static void rlfa_delete(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+ rlfa_tree_del(&spftree->lfa.remote.rlfas, rlfa);
+ XFREE(MTYPE_ISIS_RLFA, rlfa);
+}
+
+static struct rlfa *rlfa_lookup(struct isis_spftree *spftree,
+ union prefixconstptr pu)
+{
+ struct rlfa s = {};
+
+ s.prefix = *pu.p;
+ return rlfa_tree_find(&spftree->lfa.remote.rlfas, &s);
+}
+
+static int isis_area_verify_routes_cb(struct thread *thread)
+{
+ struct isis_area *area = THREAD_ARG(thread);
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: updating RLFAs in the RIB");
+
+ isis_area_verify_routes(area);
+
+ return 0;
+}
+
+static mpls_label_t rlfa_nexthop_label(struct isis_spftree *spftree,
+ struct isis_vertex_adj *vadj,
+ struct zapi_rlfa_response *response)
+{
+ struct isis_spf_adj *sadj = vadj->sadj;
+ struct isis_adjacency *adj = sadj->adj;
+
+ /*
+ * Special case to make unit tests work (use implicit-null labels
+ * instead of artifical ones).
+ */
+ if (CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
+ return MPLS_LABEL_IMPLICIT_NULL;
+
+ for (unsigned int i = 0; i < response->nexthop_num; i++) {
+ switch (response->nexthops[i].family) {
+ case AF_INET:
+ for (unsigned int j = 0; j < adj->ipv4_address_count;
+ j++) {
+ struct in_addr addr = adj->ipv4_addresses[j];
+
+ if (!IPV4_ADDR_SAME(
+ &addr,
+ &response->nexthops[i].gate.ipv4))
+ continue;
+
+ return response->nexthops[i].label;
+ }
+ break;
+ case AF_INET6:
+ for (unsigned int j = 0; j < adj->ipv6_address_count;
+ j++) {
+ struct in6_addr addr = adj->ipv6_addresses[j];
+
+ if (!IPV6_ADDR_SAME(
+ &addr,
+ &response->nexthops[i].gate.ipv6))
+ continue;
+
+ return response->nexthops[i].label;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return MPLS_INVALID_LABEL;
+}
+
+int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa,
+ struct zapi_rlfa_response *response)
+{
+ struct isis_area *area = spftree->area;
+ struct isis_vertex *vertex = rlfa->vertex;
+ struct isis_vertex_adj *vadj;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, vadj)) {
+ mpls_label_t ldp_label;
+ struct mpls_label_stack *label_stack;
+ size_t num_labels = 0;
+ size_t i = 0;
+
+ ldp_label = rlfa_nexthop_label(spftree, vadj, response);
+ if (ldp_label == MPLS_INVALID_LABEL) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %s",
+ sysid_print(vadj->sadj->id));
+ return -1;
+ }
+
+ if (ldp_label != MPLS_LABEL_IMPLICIT_NULL)
+ num_labels++;
+ if (response->pq_label != MPLS_LABEL_IMPLICIT_NULL)
+ num_labels++;
+ if (vadj->sr.present
+ && vadj->sr.label != MPLS_LABEL_IMPLICIT_NULL)
+ num_labels++;
+
+ /* Allocate label stack. */
+ label_stack =
+ XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS,
+ sizeof(struct mpls_label_stack)
+ + num_labels * sizeof(mpls_label_t));
+ label_stack->num_labels = num_labels;
+
+ /* Push label allocated by the nexthop (outer label). */
+ if (ldp_label != MPLS_LABEL_IMPLICIT_NULL)
+ label_stack->label[i++] = ldp_label;
+ /* Push label allocated by the PQ node (inner label). */
+ if (response->pq_label != MPLS_LABEL_IMPLICIT_NULL)
+ label_stack->label[i++] = response->pq_label;
+ /* Preserve the original Prefix-SID label when it's present. */
+ if (vadj->sr.present
+ && vadj->sr.label != MPLS_LABEL_IMPLICIT_NULL)
+ label_stack->label[i++] = vadj->sr.label;
+
+ vadj->label_stack = label_stack;
+ }
+
+ 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, true, area,
+ spftree->route_table_backup);
+ spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] += 1;
+
+ thread_cancel(&area->t_rlfa_rib_update);
+ thread_add_timer(master, isis_area_verify_routes_cb, area, 2,
+ &area->t_rlfa_rib_update);
+
+ return 0;
+}
+
+void isis_rlfa_deactivate(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+ struct isis_area *area = spftree->area;
+ struct isis_vertex *vertex = rlfa->vertex;
+ struct route_node *rn;
+
+ rn = route_node_lookup(spftree->route_table_backup, &rlfa->prefix);
+ if (!rn)
+ return;
+ isis_route_delete(area, rn, spftree->route_table_backup);
+ spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] -= 1;
+
+ thread_cancel(&area->t_rlfa_rib_update);
+ thread_add_timer(master, isis_area_verify_routes_cb, area, 2,
+ &area->t_rlfa_rib_update);
+}
+
+void isis_rlfa_list_init(struct isis_spftree *spftree)
+{
+ rlfa_tree_init(&spftree->lfa.remote.rlfas);
+}
+
+void isis_rlfa_list_clear(struct isis_spftree *spftree)
+{
+ while (rlfa_tree_count(&spftree->lfa.remote.rlfas) > 0) {
+ struct rlfa *rlfa;
+
+ rlfa = rlfa_tree_first(&spftree->lfa.remote.rlfas);
+ isis_rlfa_deactivate(spftree, rlfa);
+ rlfa_delete(spftree, rlfa);
+ }
+}
+
+void isis_rlfa_process_ldp_response(struct zapi_rlfa_response *response)
+{
+ struct isis *isis;
+ struct isis_area *area;
+ struct isis_spftree *spftree;
+ struct rlfa *rlfa;
+ enum spf_tree_id tree_id;
+ uint32_t spf_run_id;
+ int level;
+
+ if (response->igp.protocol != ZEBRA_ROUTE_ISIS)
+ return;
+
+ isis = isis_lookup_by_vrfid(response->igp.vrf_id);
+ if (!isis)
+ return;
+
+ area = isis_area_lookup(response->igp.isis.area_tag,
+ response->igp.vrf_id);
+ if (!area)
+ return;
+
+ tree_id = response->igp.isis.spf.tree_id;
+ if (tree_id != SPFTREE_IPV4 && tree_id != SPFTREE_IPV6) {
+ zlog_warn("ISIS-LFA: invalid SPF tree ID received from LDP");
+ return;
+ }
+
+ level = response->igp.isis.spf.level;
+ if (level != ISIS_LEVEL1 && level != ISIS_LEVEL2) {
+ zlog_warn("ISIS-LFA: invalid IS-IS level received from LDP");
+ return;
+ }
+
+ spf_run_id = response->igp.isis.spf.run_id;
+ spftree = area->spftree[tree_id][level - 1];
+ if (spftree->runcount != spf_run_id)
+ /* Outdated RLFA, ignore... */
+ return;
+
+ rlfa = rlfa_lookup(spftree, &response->destination);
+ if (!rlfa) {
+ zlog_warn(
+ "ISIS-LFA: couldn't find Remote-LFA %pFX received from LDP",
+ &response->destination);
+ return;
+ }
+
+ if (response->pq_label != MPLS_INVALID_LABEL) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: activating/updating RLFA for %pFX",
+ &rlfa->prefix);
+
+ if (isis_rlfa_activate(spftree, rlfa, response) != 0)
+ isis_rlfa_deactivate(spftree, rlfa);
+ } else {
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: deactivating RLFA for %pFX",
+ &rlfa->prefix);
+
+ isis_rlfa_deactivate(spftree, rlfa);
+ }
+}
+
+void isis_ldp_rlfa_handle_client_close(struct zapi_client_close_info *info)
+{
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+ struct isis_area *area;
+ struct listnode *node;
+
+ if (!isis)
+ return;
+
+ /* Check if the LDP main client session closed */
+ if (info->proto != ZEBRA_ROUTE_LDP || info->session_id == 0)
+ return;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: LDP is down, deactivating all RLFAs");
+
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+ level++) {
+ struct isis_spftree *spftree;
+
+ spftree = area->spftree[tree][level - 1];
+ isis_rlfa_list_clear(spftree);
+ }
+ }
+ }
+}
+
+/**
+ * Check if the given SPF vertex needs protection and, if so, attempt to
+ * compute a Remote LFA for it.
+ *
+ * @param spftree_pc The post-convergence SPF tree
+ * @param vertex IS-IS SPF vertex to check
+ */
+void isis_rlfa_check(struct isis_spftree *spftree_pc,
+ struct isis_vertex *vertex)
+{
+ struct isis_spftree *spftree_old = spftree_pc->lfa.old.spftree;
+ struct rlfa *rlfa;
+ const struct in_addr *rtr_id_pq;
+ char buf[VID2STR_BUFFER];
+
+ if (!lfa_check_needs_protection(spftree_pc, vertex)) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: %s %s unaffected by %s",
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
+ lfa_protected_resource2str(
+ &spftree_pc->lfa.protected_resource));
+
+ return;
+ }
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
+ lfa_protected_resource2str(
+ &spftree_pc->lfa.protected_resource));
+
+ /* Find PQ node. */
+ rtr_id_pq = rlfa_find_pq_node(spftree_pc, vertex, vertex, NULL);
+ if (!rtr_id_pq) {
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: no acceptable PQ node found");
+ return;
+ }
+
+ /* Store valid RLFA and store LDP label for the PQ node. */
+ rlfa = rlfa_add(spftree_old, vertex, *rtr_id_pq);
+
+ /* Register RLFA with LDP. */
+ if (isis_zebra_rlfa_register(spftree_old, rlfa) != 0)
+ rlfa_delete(spftree_old, rlfa);
+}
+
+/**
+ * Compute the Remote LFA backup paths for a given protected interface.
+ *
+ * @param area IS-IS area
+ * @param spftree IS-IS SPF tree
+ * @param spftree_reverse IS-IS Reverse SPF tree
+ * @param max_metric Remote LFA maximum metric
+ * @param resource Protected resource
+ *
+ * @return Pointer to the post-convergence SPF tree
+ */
+struct isis_spftree *isis_rlfa_compute(struct isis_area *area,
+ struct isis_spftree *spftree,
+ struct isis_spftree *spftree_reverse,
+ uint32_t max_metric,
+ struct lfa_protected_resource *resource)
+{
+ struct isis_spftree *spftree_pc;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing remote LFAs for %s",
+ lfa_protected_resource2str(resource));
+
+ /* Create post-convergence SPF tree. */
+ spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid,
+ spftree->level, spftree->tree_id,
+ SPF_TYPE_RLFA, spftree->flags);
+ spftree_pc->lfa.old.spftree = spftree;
+ spftree_pc->lfa.old.spftree_reverse = spftree_reverse;
+ spftree_pc->lfa.remote.max_metric = max_metric;
+ spftree_pc->lfa.protected_resource = *resource;
+
+ /* Compute the extended P-space and Q-space. */
+ lfa_calc_pq_spaces(spftree_pc, resource);
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "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. */
+ isis_run_spf(spftree_pc);
+
+ return spftree_pc;
+}
+
/* 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,
@@ -1451,8 +1941,7 @@ static bool clfa_node_protecting_check(struct isis_spftree *spftree,
}
static struct list *
-isis_lfa_tiebreakers(struct isis_area *area, struct isis_circuit *circuit,
- struct isis_spftree *spftree,
+isis_lfa_tiebreakers(struct isis_area *area, struct isis_spftree *spftree,
struct lfa_protected_resource *resource,
struct isis_vertex *vertex,
struct isis_spf_adj *sadj_primary, struct list *lfa_list)
@@ -1572,6 +2061,10 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
resource->type = LFA_LINK_PROTECTION;
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing local LFAs for %s",
+ lfa_protected_resource2str(resource));
+
for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, vnode, vertex)) {
struct list *lfa_list;
struct list *filtered_lfa_list;
@@ -1591,7 +2084,8 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
resource)) {
if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-LFA: route unaffected by %s",
+ "ISIS-LFA: %s %s unaffected by %s",
+ vtype2string(vertex->type), buf,
lfa_protected_resource2str(resource));
continue;
}
@@ -1697,15 +2191,18 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
if (list_isempty(lfa_list)) {
if (IS_DEBUG_LFA)
- zlog_debug("ISIS-LFA: no valid LFAs found");
+ zlog_debug(
+ "ISIS-LFA: no valid local LFAs found");
list_delete(&lfa_list);
continue;
}
+ SET_FLAG(vertex->flags, F_ISIS_VERTEX_LFA_PROTECTED);
+
/* Check tie-breakers. */
filtered_lfa_list =
- isis_lfa_tiebreakers(area, circuit, spftree, resource,
- vertex, sadj_primary, lfa_list);
+ isis_lfa_tiebreakers(area, spftree, resource, vertex,
+ sadj_primary, lfa_list);
/* Create backup route using the best LFAs. */
allow_ecmp = area->lfa_load_sharing[level - 1];
@@ -1746,7 +2243,7 @@ static void isis_spf_run_tilfa(struct isis_area *area,
}
/**
- * Run the LFA/TI-LFA algorithms for all protected interfaces.
+ * Run the LFA/RLFA/TI-LFA algorithms for all protected interfaces.
*
* @param area IS-IS area
* @param spftree IS-IS SPF tree
@@ -1756,13 +2253,11 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
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. */
- if (tilfa_configured)
+ if (area->rlfa_protected_links[level - 1] > 0
+ || area->tilfa_protected_links[level - 1] > 0)
spftree_reverse = isis_spf_reverse_run(spftree);
/* Run forward SPF on all adjacent routers. */
@@ -1808,15 +2303,32 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
continue;
}
- if (circuit->lfa_protection[level - 1])
+ if (circuit->lfa_protection[level - 1]) {
+ /* Run local LFA. */
isis_lfa_compute(area, circuit, spftree, &resource);
- else if (circuit->tilfa_protection[level - 1]) {
+
+ if (circuit->rlfa_protection[level - 1]) {
+ struct isis_spftree *spftree_pc;
+ uint32_t max_metric;
+
+ /* Run remote LFA. */
+ assert(spftree_reverse);
+ max_metric =
+ circuit->rlfa_max_metric[level - 1];
+ spftree_pc = isis_rlfa_compute(
+ area, spftree, spftree_reverse,
+ max_metric, &resource);
+ listnode_add(spftree->lfa.remote.pc_spftrees,
+ spftree_pc);
+ }
+ } else if (circuit->tilfa_protection[level - 1]) {
+ /* Run TI-LFA. */
assert(spftree_reverse);
isis_spf_run_tilfa(area, circuit, spftree,
spftree_reverse, &resource);
}
}
- if (tilfa_configured)
+ if (spftree_reverse)
isis_spftree_del(spftree_reverse);
}
diff --git a/isisd/isis_lfa.h b/isisd/isis_lfa.h
index f09fc663a4..65891cae44 100644
--- a/isisd/isis_lfa.h
+++ b/isisd/isis_lfa.h
@@ -21,8 +21,10 @@
#define _FRR_ISIS_LFA_H
#include "lib/typesafe.h"
+#include "lib/zclient.h"
PREDECL_RBTREE_UNIQ(lfa_tiebreaker_tree)
+PREDECL_RBTREE_UNIQ(rlfa_tree)
enum lfa_tiebreaker_type {
LFA_TIEBREAKER_DOWNSTREAM = 0,
@@ -41,6 +43,15 @@ int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
DECLARE_RBTREE_UNIQ(lfa_tiebreaker_tree, struct lfa_tiebreaker, entry,
lfa_tiebreaker_cmp)
+struct rlfa {
+ struct rlfa_tree_item entry;
+ struct prefix prefix;
+ struct isis_vertex *vertex;
+ struct in_addr pq_address;
+};
+int rlfa_cmp(const struct rlfa *a, const struct rlfa *b);
+DECLARE_RBTREE_UNIQ(rlfa_tree, struct rlfa, entry, rlfa_cmp)
+
enum isis_tilfa_sid_type {
TILFA_SID_PREFIX = 1,
TILFA_SID_ADJ,
@@ -145,6 +156,19 @@ 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);
+int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa,
+ struct zapi_rlfa_response *response);
+void isis_rlfa_deactivate(struct isis_spftree *spftree, struct rlfa *rlfa);
+void isis_rlfa_list_init(struct isis_spftree *spftree);
+void isis_rlfa_list_clear(struct isis_spftree *spftree);
+void isis_rlfa_process_ldp_response(struct zapi_rlfa_response *response);
+void isis_ldp_rlfa_handle_client_close(struct zapi_client_close_info *info);
+void isis_rlfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
+struct isis_spftree *isis_rlfa_compute(struct isis_area *area,
+ struct isis_spftree *spftree,
+ struct isis_spftree *spftree_reverse,
+ uint32_t max_metric,
+ struct lfa_protected_resource *resource);
void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
struct isis_spftree *spftree,
struct lfa_protected_resource *resource);
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index 4576a4a95c..1b04f4f7a0 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -246,6 +246,8 @@ int main(int argc, char **argv, char **envp)
access_list_delete_hook(isis_filter_update);
isis_vrf_init();
prefix_list_init();
+ prefix_list_add_hook(isis_prefix_list_update);
+ prefix_list_delete_hook(isis_prefix_list_update);
isis_init();
isis_circuit_init();
#ifdef FABRICD
diff --git a/isisd/isis_memory.c b/isisd/isis_memory.c
index b63a82f404..f716e060cd 100644
--- a/isisd/isis_memory.c
+++ b/isisd/isis_memory.c
@@ -46,3 +46,4 @@ 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")
+DEFINE_MTYPE(ISISD, ISIS_PLIST_NAME, "ISIS prefix-list name")
diff --git a/isisd/isis_memory.h b/isisd/isis_memory.h
index 3ef1c5bf0b..5bcd2a3983 100644
--- a/isisd/isis_memory.h
+++ b/isisd/isis_memory.h
@@ -45,5 +45,6 @@ DECLARE_MTYPE(ISIS_EXT_ROUTE)
DECLARE_MTYPE(ISIS_EXT_INFO)
DECLARE_MTYPE(ISIS_MPLS_TE)
DECLARE_MTYPE(ISIS_ACL_NAME)
+DECLARE_MTYPE(ISIS_PLIST_NAME)
#endif /* _QUAGGA_ISIS_MEMORY_H */
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index c3d2f238dd..a02e6a45b1 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -485,6 +485,14 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_remote_lfa_plist,
+ .modify = isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify,
+ .destroy = isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_destroy,
+ }
+ },
+ {
.xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing",
.cbs = {
.cli_show = cli_show_isis_frr_lfa_load_sharing,
@@ -514,6 +522,14 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_remote_lfa_plist,
+ .modify = isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify,
+ .destroy = isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy,
+ }
+ },
+ {
.xpath = "/frr-isisd:isis/instance/log-adjacency-changes",
.cbs = {
.cli_show = cli_show_isis_log_adjacency,
@@ -927,6 +943,20 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/remote-lfa/enable",
+ .cbs = {
+ .modify = lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/remote-lfa/maximum-metric",
+ .cbs = {
+ .cli_show = cli_show_frr_remote_lfa_max_metric,
+ .modify = lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify,
+ .destroy = lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_destroy,
+ }
+ },
+ {
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/ti-lfa/enable",
.cbs = {
.modify = lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify,
@@ -953,6 +983,20 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/remote-lfa/enable",
+ .cbs = {
+ .modify = lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/remote-lfa/maximum-metric",
+ .cbs = {
+ .cli_show = cli_show_frr_remote_lfa_max_metric,
+ .modify = lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify,
+ .destroy = lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_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 f529f20861..679bc6345d 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -183,6 +183,10 @@ 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_1_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_destroy(
+ struct nb_cb_destroy_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(
@@ -195,6 +199,10 @@ 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_fast_reroute_level_2_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy(
+ struct nb_cb_destroy_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);
@@ -300,6 +308,12 @@ 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_remote_lfa_enable_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_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(
@@ -310,6 +324,12 @@ 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_remote_lfa_enable_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_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(
@@ -467,6 +487,8 @@ 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_isis_frr_remote_lfa_plist(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,
@@ -503,6 +525,8 @@ 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_frr_remote_lfa_max_metric(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 2f7469b1b1..ed0fea8824 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -28,6 +28,7 @@
#include "log.h"
#include "bfd.h"
#include "filter.h"
+#include "plist.h"
#include "spf_backoff.h"
#include "lib_errors.h"
#include "vrf.h"
@@ -1552,6 +1553,45 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
}
/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list
+ */
+int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ const char *plist_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ plist_name = yang_dnode_get_string(args->dnode, NULL);
+
+ area->rlfa_plist_name[0] = XSTRDUP(MTYPE_ISIS_PLIST_NAME, plist_name);
+ area->rlfa_plist[0] = prefix_list_lookup(AFI_IP, plist_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_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);
+
+ XFREE(MTYPE_ISIS_PLIST_NAME, area->rlfa_plist_name[0]);
+ area->rlfa_plist[0] = 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(
@@ -1662,6 +1702,45 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
}
/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list
+ */
+int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ const char *plist_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ plist_name = yang_dnode_get_string(args->dnode, NULL);
+
+ area->rlfa_plist_name[1] = XSTRDUP(MTYPE_ISIS_PLIST_NAME, plist_name);
+ area->rlfa_plist[1] = prefix_list_lookup(AFI_IP, plist_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_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);
+
+ XFREE(MTYPE_ISIS_PLIST_NAME, area->rlfa_plist_name[1]);
+ area->rlfa_plist[1] = 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)
@@ -3448,6 +3527,74 @@ int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy(
/*
* XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/remote-lfa/enable
+ */
+int lib_interface_isis_fast_reroute_level_1_remote_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->rlfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
+ if (circuit->rlfa_protection[0])
+ circuit->area->rlfa_protected_links[0]++;
+ else {
+ assert(circuit->area->rlfa_protected_links[0] > 0);
+ circuit->area->rlfa_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/remote-lfa/maximum-metric
+ */
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_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->rlfa_max_metric[0] = yang_dnode_get_uint32(args->dnode, NULL);
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_destroy(
+ struct nb_cb_destroy_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->rlfa_max_metric[0] = 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/ti-lfa/enable
*/
int lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify(
@@ -3571,6 +3718,74 @@ int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy(
/*
* XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/remote-lfa/enable
+ */
+int lib_interface_isis_fast_reroute_level_2_remote_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->rlfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
+ if (circuit->rlfa_protection[1])
+ circuit->area->rlfa_protected_links[1]++;
+ else {
+ assert(circuit->area->rlfa_protected_links[1] > 0);
+ circuit->area->rlfa_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/remote-lfa/maximum-metric
+ */
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_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->rlfa_max_metric[1] = yang_dnode_get_uint32(args->dnode, NULL);
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_destroy(
+ struct nb_cb_destroy_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->rlfa_max_metric[1] = 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-2/ti-lfa/enable
*/
int lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify(
diff --git a/isisd/isis_route.c b/isisd/isis_route.c
index d32f219e98..e1baf351f4 100644
--- a/isisd/isis_route.c
+++ b/isisd/isis_route.c
@@ -279,6 +279,22 @@ static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new,
return true;
}
+static bool isis_label_stack_same(struct mpls_label_stack *new,
+ struct mpls_label_stack *old)
+{
+ if (!new && !old)
+ return true;
+ if (!new || !old)
+ return false;
+ if (new->num_labels != old->num_labels)
+ return false;
+ if (memcmp(&new->label, &old->label,
+ sizeof(mpls_label_t) * new->num_labels))
+ return false;
+
+ return true;
+}
+
static int isis_route_info_same(struct isis_route_info *new,
struct isis_route_info *old, char *buf,
size_t buf_size)
@@ -327,6 +343,12 @@ static int isis_route_info_same(struct isis_route_info *new,
snprintf(buf, buf_size, "nhop SR label");
return 0;
}
+ if (!isis_label_stack_same(new_nh->label_stack,
+ old_nh->label_stack)) {
+ if (buf)
+ snprintf(buf, buf_size, "nhop label stack");
+ return 0;
+ }
}
/* only the resync flag needs to be checked */
@@ -400,8 +422,8 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
return route_info;
}
-static void isis_route_delete(struct isis_area *area, struct route_node *rode,
- struct route_table *table)
+void isis_route_delete(struct isis_area *area, struct route_node *rode,
+ struct route_table *table)
{
struct isis_route_info *rinfo;
char buff[SRCDEST2STR_BUFFER];
@@ -466,9 +488,6 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix,
SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
} else {
- if (!CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
- return;
-
/* Uninstall Prefix-SID label. */
if (route_info->sr.present)
isis_zebra_prefix_sid_uninstall(
@@ -516,6 +535,10 @@ static void _isis_route_verify_table(struct isis_area *area,
rinfo->backup = rnode_bck->info;
UNSET_FLAG(rinfo->flag,
ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+ } else if (rinfo->backup) {
+ rinfo->backup = NULL;
+ UNSET_FLAG(rinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
}
}
@@ -629,6 +652,10 @@ void isis_route_verify_merge(struct isis_area *area,
rinfo->backup = rnode_bck->info;
UNSET_FLAG(rinfo->flag,
ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+ } else if (rinfo->backup) {
+ rinfo->backup = NULL;
+ UNSET_FLAG(rinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
}
mrnode = srcdest_rnode_get(merge, prefix, src_p);
diff --git a/isisd/isis_route.h b/isisd/isis_route.h
index 0d4f884959..d6763ec76c 100644
--- a/isisd/isis_route.h
+++ b/isisd/isis_route.h
@@ -63,6 +63,8 @@ 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, bool allow_ecmp,
struct isis_area *area, struct route_table *table);
+void isis_route_delete(struct isis_area *area, struct route_node *rode,
+ 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 7e046167f6..dee082fce1 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -56,6 +56,7 @@
#include "isis_csm.h"
#include "isis_mt.h"
#include "isis_tlvs.h"
+#include "isis_zebra.h"
#include "fabricd.h"
#include "isis_spf_private.h"
@@ -354,7 +355,10 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area,
tree->tree_id = tree_id;
tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6;
tree->flags = flags;
- if (tree->type == SPF_TYPE_TI_LFA) {
+ isis_rlfa_list_init(tree);
+ tree->lfa.remote.pc_spftrees = list_new();
+ tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del;
+ if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) {
isis_spf_node_list_init(&tree->lfa.p_space);
isis_spf_node_list_init(&tree->lfa.q_space);
}
@@ -366,7 +370,11 @@ void isis_spftree_del(struct isis_spftree *spftree)
{
hash_clean(spftree->prefix_sids, NULL);
hash_free(spftree->prefix_sids);
- if (spftree->type == SPF_TYPE_TI_LFA) {
+ isis_zebra_rlfa_unregister_all(spftree);
+ isis_rlfa_list_clear(spftree);
+ list_delete(&spftree->lfa.remote.pc_spftrees);
+ if (spftree->type == SPF_TYPE_RLFA
+ || spftree->type == SPF_TYPE_TI_LFA) {
isis_spf_node_list_clear(&spftree->lfa.q_space);
isis_spf_node_list_clear(&spftree->lfa.p_space);
}
@@ -1440,6 +1448,9 @@ 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);
+ isis_zebra_rlfa_unregister_all(spftree);
+ isis_rlfa_list_clear(spftree);
+ list_delete_all_node(spftree->lfa.remote.pc_spftrees);
memset(&spftree->lfa.protection_counters, 0,
sizeof(spftree->lfa.protection_counters));
@@ -1513,12 +1524,13 @@ static void spf_path_process(struct isis_spftree *spftree,
priority = spf_prefix_priority(spftree, vertex);
vertex->N.ip.priority = priority;
if (vertex->depth == 1 || listcount(vertex->Adj_N) > 0) {
+ struct isis_spftree *pre_spftree;
struct route_table *route_table;
bool allow_ecmp;
- if (spftree->type == SPF_TYPE_TI_LFA) {
- struct isis_spftree *pre_spftree;
-
+ switch (spftree->type) {
+ case SPF_TYPE_RLFA:
+ case SPF_TYPE_TI_LFA:
if (priority
> area->lfa_priority_limit[level - 1]) {
if (IS_DEBUG_LFA)
@@ -1531,7 +1543,16 @@ static void spf_path_process(struct isis_spftree *spftree,
sizeof(buff)));
return;
}
+ break;
+ default:
+ break;
+ }
+ switch (spftree->type) {
+ case SPF_TYPE_RLFA:
+ isis_rlfa_check(spftree, vertex);
+ return;
+ case SPF_TYPE_TI_LFA:
if (isis_tilfa_check(spftree, vertex) != 0)
return;
@@ -1540,7 +1561,8 @@ static void spf_path_process(struct isis_spftree *spftree,
allow_ecmp = area->lfa_load_sharing[level - 1];
pre_spftree->lfa.protection_counters
.tilfa[vertex->N.ip.priority] += 1;
- } else {
+ break;
+ default:
route_table = spftree->route_table;
allow_ecmp = true;
@@ -1555,6 +1577,7 @@ static void spf_path_process(struct isis_spftree *spftree,
spftree->lfa.protection_counters
.ecmp[priority] += 1;
}
+ break;
}
isis_route_create(
@@ -1845,6 +1868,7 @@ int _isis_spf_schedule(struct isis_area *area, int level,
area->area_tag, level, diff, func, file, line);
}
+ thread_cancel(&area->t_rlfa_rib_update);
if (area->spf_delay_ietf[level - 1]) {
/* Need to call schedule function also if spf delay is running
* to
diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h
index 5b6fcdaf68..5b3aa59379 100644
--- a/isisd/isis_spf.h
+++ b/isisd/isis_spf.h
@@ -31,6 +31,7 @@ struct isis_spftree;
enum spf_type {
SPF_TYPE_FORWARD = 1,
SPF_TYPE_REVERSE,
+ SPF_TYPE_RLFA,
SPF_TYPE_TI_LFA,
};
diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h
index b7f326ca86..79dfa3e164 100644
--- a/isisd/isis_spf_private.h
+++ b/isisd/isis_spf_private.h
@@ -76,7 +76,9 @@ struct isis_vertex {
struct list *parents; /* list of parents for ECMP */
struct hash *firsthops; /* first two hops to neighbor */
uint64_t insert_counter;
+ uint8_t flags;
};
+#define F_ISIS_VERTEX_LFA_PROTECTED 0x01
/* Vertex Queue and associated functions */
@@ -349,6 +351,21 @@ struct isis_spftree {
struct isis_spf_nodes p_space;
struct isis_spf_nodes q_space;
+ /* Remote LFA related information. */
+ struct {
+ /* List of RLFAs eligible to be installed. */
+ struct rlfa_tree_head rlfas;
+
+ /*
+ * RLFA post-convergence SPTs (needed to activate RLFAs
+ * once label information is received from LDP).
+ */
+ struct list *pc_spftrees;
+
+ /* RLFA maximum metric (or zero if absent). */
+ uint32_t max_metric;
+ } remote;
+
/* Protection counters. */
struct {
uint32_t lfa[SPF_PREFIX_PRIO_MAX];
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
index f08737c2c1..703532234a 100644
--- a/isisd/isis_zebra.c
+++ b/isisd/isis_zebra.c
@@ -47,6 +47,8 @@
#include "isisd/isis_circuit.h"
#include "isisd/isis_csm.h"
#include "isisd/isis_lsp.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_spf_private.h"
#include "isisd/isis_route.h"
#include "isisd/isis_zebra.h"
#include "isisd/isis_adjacency.h"
@@ -540,6 +542,72 @@ void isis_zebra_redistribute_unset(afi_t afi, int type)
type, 0, VRF_DEFAULT);
}
+/**
+ * Register RLFA with LDP.
+ */
+int isis_zebra_rlfa_register(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+ struct isis_area *area = spftree->area;
+ struct zapi_rlfa_request zr = {};
+ int ret;
+
+ if (!zclient)
+ return 0;
+
+ zr.igp.vrf_id = area->isis->vrf_id;
+ zr.igp.protocol = ZEBRA_ROUTE_ISIS;
+ strlcpy(zr.igp.isis.area_tag, area->area_tag,
+ sizeof(zr.igp.isis.area_tag));
+ zr.igp.isis.spf.tree_id = spftree->tree_id;
+ zr.igp.isis.spf.level = spftree->level;
+ zr.igp.isis.spf.run_id = spftree->runcount;
+ zr.destination = rlfa->prefix;
+ zr.pq_address = rlfa->pq_address;
+
+ zlog_debug("ISIS-LFA: registering RLFA %pFX@%pI4 with LDP",
+ &rlfa->prefix, &rlfa->pq_address);
+
+ ret = zclient_send_opaque_unicast(zclient, LDP_RLFA_REGISTER,
+ ZEBRA_ROUTE_LDP, 0, 0,
+ (const uint8_t *)&zr, sizeof(zr));
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ zlog_warn("ISIS-LFA: failed to register RLFA with LDP");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Unregister all RLFAs from the given SPF tree with LDP.
+ */
+void isis_zebra_rlfa_unregister_all(struct isis_spftree *spftree)
+{
+ struct isis_area *area = spftree->area;
+ struct zapi_rlfa_igp igp = {};
+ int ret;
+
+ if (!zclient || spftree->type != SPF_TYPE_FORWARD
+ || CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
+ return;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: unregistering all RLFAs with LDP");
+
+ igp.vrf_id = area->isis->vrf_id;
+ igp.protocol = ZEBRA_ROUTE_ISIS;
+ strlcpy(igp.isis.area_tag, area->area_tag, sizeof(igp.isis.area_tag));
+ igp.isis.spf.tree_id = spftree->tree_id;
+ igp.isis.spf.level = spftree->level;
+ igp.isis.spf.run_id = spftree->runcount;
+
+ ret = zclient_send_opaque_unicast(zclient, LDP_RLFA_UNREGISTER_ALL,
+ ZEBRA_ROUTE_LDP, 0, 0,
+ (const uint8_t *)&igp, sizeof(igp));
+ if (ret == ZCLIENT_SEND_FAILURE)
+ zlog_warn("ISIS-LFA: failed to unregister RLFA with LDP");
+}
+
/* Label Manager Functions */
/**
@@ -659,6 +727,7 @@ void isis_zebra_vrf_register(struct isis *isis)
static void isis_zebra_connected(struct zclient *zclient)
{
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ zclient_register_opaque(zclient, LDP_RLFA_LABELS);
}
/*
@@ -670,6 +739,7 @@ static int isis_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
struct zapi_opaque_msg info;
struct ldp_igp_sync_if_state state;
struct ldp_igp_sync_announce announce;
+ struct zapi_rlfa_response rlfa;
int ret = 0;
s = zclient->ibuf;
@@ -685,6 +755,10 @@ static int isis_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
STREAM_GET(&announce, s, sizeof(announce));
ret = isis_ldp_sync_announce_update(announce);
break;
+ case LDP_RLFA_LABELS:
+ STREAM_GET(&rlfa, s, sizeof(rlfa));
+ isis_rlfa_process_ldp_response(&rlfa);
+ break;
default:
break;
}
@@ -704,6 +778,7 @@ static int isis_zebra_client_close_notify(ZAPI_CALLBACK_ARGS)
return -1;
isis_ldp_sync_handle_client_close(&info);
+ isis_ldp_rlfa_handle_client_close(&info);
return ret;
}
@@ -742,6 +817,7 @@ void isis_zebra_init(struct thread_master *master, int instance)
void isis_zebra_stop(void)
{
+ zclient_unregister_opaque(zclient, LDP_RLFA_LABELS);
zclient_stop(zclient_sync);
zclient_free(zclient_sync);
zclient_stop(zclient);
diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h
index c5c52a6bc6..b44ec4f085 100644
--- a/isisd/isis_zebra.h
+++ b/isisd/isis_zebra.h
@@ -59,6 +59,8 @@ void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra);
int isis_distribute_list_update(int routetype);
void isis_zebra_redistribute_set(afi_t afi, int type);
void isis_zebra_redistribute_unset(afi_t afi, int type);
+int isis_zebra_rlfa_register(struct isis_spftree *spftree, struct rlfa *rlfa);
+void isis_zebra_rlfa_unregister_all(struct isis_spftree *spftree);
bool isis_zebra_label_manager_ready(void);
int isis_zebra_label_manager_connect(void);
int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size);
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 827e022baf..eabebab4e0 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -32,6 +32,7 @@
#include "if.h"
#include "hash.h"
#include "filter.h"
+#include "plist.h"
#include "stream.h"
#include "prefix.h"
#include "table.h"
@@ -485,6 +486,7 @@ void isis_area_destroy(struct isis_area *area)
thread_cancel(&area->t_tick);
thread_cancel(&area->t_lsp_refresh[0]);
thread_cancel(&area->t_lsp_refresh[1]);
+ thread_cancel(&area->t_rlfa_rib_update);
thread_cancel_event(master, area);
@@ -649,6 +651,34 @@ void isis_filter_update(struct access_list *access)
}
}
+void isis_prefix_list_update(struct prefix_list *plist)
+{
+ 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 level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+ level++) {
+ const char *plist_name =
+ prefix_list_name(plist);
+
+ if (!area->rlfa_plist_name[level - 1])
+ continue;
+
+ if (!strmatch(area->rlfa_plist_name[level - 1],
+ plist_name))
+ continue;
+
+ area->rlfa_plist[level - 1] =
+ prefix_list_lookup(AFI_IP, plist_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)
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 4618d14af3..9b903eed48 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -131,6 +131,7 @@ struct isis_area {
struct thread *t_tick; /* LSP walker */
struct thread *t_lsp_refresh[ISIS_LEVELS];
struct timeval last_lsp_refresh_event[ISIS_LEVELS];
+ struct thread *t_rlfa_rib_update;
/* t_lsp_refresh is used in two ways:
* a) regular refresh of LSPs
* b) (possibly throttled) updates to LSPs
@@ -197,6 +198,9 @@ struct isis_area {
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];
+ char *rlfa_plist_name[ISIS_LEVELS];
+ struct prefix_list *rlfa_plist[ISIS_LEVELS];
+ size_t rlfa_protected_links[ISIS_LEVELS];
size_t tilfa_protected_links[ISIS_LEVELS];
/* Counters */
uint32_t circuit_state_changes;
@@ -240,6 +244,7 @@ struct isis_area *isis_area_lookup_by_vrf(const char *area_tag,
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 isis_prefix_list_update(struct prefix_list *plist);
void print_debug(struct vty *, int, int);
struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
struct isis *isis);
diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c
index 795a41491c..d390e70ad0 100644
--- a/ldpd/adjacency.c
+++ b/ldpd/adjacency.c
@@ -183,7 +183,8 @@ adj_itimer(struct thread *thread)
if (adj->source.type == HELLO_TARGETED) {
if (!(adj->source.target->flags & F_TNBR_CONFIGURED) &&
- adj->source.target->pw_count == 0) {
+ adj->source.target->pw_count == 0 &&
+ adj->source.target->rlfa_count == 0) {
/* remove dynamic targeted neighbor */
tnbr_del(leconf, adj->source.target);
return (0);
@@ -259,7 +260,7 @@ struct tnbr *
tnbr_check(struct ldpd_conf *xconf, struct tnbr *tnbr)
{
if (!(tnbr->flags & (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) &&
- tnbr->pw_count == 0) {
+ tnbr->pw_count == 0 && tnbr->rlfa_count == 0) {
tnbr_del(xconf, tnbr);
return (NULL);
}
diff --git a/ldpd/hello.c b/ldpd/hello.c
index 327cb32434..5aa14ed067 100644
--- a/ldpd/hello.c
+++ b/ldpd/hello.c
@@ -67,7 +67,8 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr)
af = tnbr->af;
holdtime = tnbr_get_hello_holdtime(tnbr);
flags = F_HELLO_TARGETED;
- if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count)
+ if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count
+ || tnbr->rlfa_count)
flags |= F_HELLO_REQ_TARG;
fd = (ldp_af_global_get(&global, af))->ldp_edisc_socket;
diff --git a/ldpd/lde.c b/ldpd/lde.c
index 5ed0ed4520..69338b8bad 100644
--- a/ldpd/lde.c
+++ b/ldpd/lde.c
@@ -27,6 +27,7 @@
#include "log.h"
#include "lde.h"
#include "ldp_debug.h"
+#include "rlfa.h"
#include <lib/log.h>
#include "memory.h"
@@ -444,6 +445,10 @@ lde_dispatch_parent(struct thread *thread)
int shut = 0;
struct fec fec;
struct ldp_access *laccess;
+ struct ldp_rlfa_node *rnode, *rntmp;
+ struct ldp_rlfa_client *rclient;
+ struct zapi_rlfa_request *rlfa_req;
+ struct zapi_rlfa_igp *rlfa_igp;
iev->ev_read = NULL;
@@ -650,6 +655,42 @@ lde_dispatch_parent(struct thread *thread)
lde_check_filter_af(AF_INET6, &ldeconf->ipv6,
laccess->name);
break;
+ case IMSG_RLFA_REG:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_request)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_req = imsg.data;
+ rnode = rlfa_node_find(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ if (!rnode)
+ rnode = rlfa_node_new(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+ if (rclient)
+ /* RLFA already registered - do nothing */
+ break;
+ rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+ lde_rlfa_check(rclient);
+ break;
+ case IMSG_RLFA_UNREG_ALL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_igp)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_igp = imsg.data;
+
+ RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+ &rlfa_node_tree, rntmp) {
+ rclient = rlfa_client_find(rnode, rlfa_igp);
+ if (!rclient)
+ continue;
+
+ rlfa_client_del(rclient);
+ }
+ break;
default:
log_debug("%s: unexpected imsg %d", __func__,
imsg.hdr.type);
@@ -876,6 +917,48 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh)
}
void
+lde_fec2prefix(const struct fec *fec, struct prefix *prefix)
+{
+ memset(prefix, 0, sizeof(*prefix));
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ prefix->family = AF_INET;
+ prefix->u.prefix4 = fec->u.ipv4.prefix;
+ prefix->prefixlen = fec->u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ prefix->family = AF_INET6;
+ prefix->u.prefix6 = fec->u.ipv6.prefix;
+ prefix->prefixlen = fec->u.ipv6.prefixlen;
+ break;
+ default:
+ prefix->family = AF_UNSPEC;
+ break;
+ }
+}
+
+void
+lde_prefix2fec(const struct prefix *prefix, struct fec *fec)
+{
+ memset(fec, 0, sizeof(*fec));
+ switch (prefix->family) {
+ case AF_INET:
+ fec->type = FEC_TYPE_IPV4;
+ fec->u.ipv4.prefix = prefix->u.prefix4;
+ fec->u.ipv4.prefixlen = prefix->prefixlen;
+ break;
+ case AF_INET6:
+ fec->type = FEC_TYPE_IPV6;
+ fec->u.ipv6.prefix = prefix->u.prefix6;
+ fec->u.ipv6.prefixlen = prefix->prefixlen;
+ break;
+ default:
+ fatalx("lde_prefix2fec: unknown af");
+ break;
+ }
+}
+
+void
lde_fec2map(struct fec *fec, struct map *map)
{
memset(map, 0, sizeof(*map));
@@ -1388,6 +1471,9 @@ lde_nbr_del(struct lde_nbr *ln)
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
LIST_FOREACH(fnh, &fn->nexthops, entry) {
switch (f->type) {
case FEC_TYPE_IPV4:
diff --git a/ldpd/lde.h b/ldpd/lde.h
index 660aeafb34..28468931ec 100644
--- a/ldpd/lde.h
+++ b/ldpd/lde.h
@@ -129,7 +129,9 @@ struct fec_node {
uint32_t pw_remote_status;
void *data; /* fec specific data */
+ uint8_t flags;
};
+#define F_FEC_NHS_CHANGED 0x01
#define CHUNK_SIZE 64
struct label_chunk {
@@ -156,6 +158,8 @@ uint32_t lde_update_label(struct fec_node *);
void lde_free_label(uint32_t label);
void lde_send_change_klabel(struct fec_node *, struct fec_nh *);
void lde_send_delete_klabel(struct fec_node *, struct fec_nh *);
+void lde_fec2prefix(const struct fec *fec, struct prefix *prefix);
+void lde_prefix2fec(const struct prefix *prefix, struct fec *fec);
void lde_fec2map(struct fec *, struct map *);
void lde_map2fec(struct map *, struct in_addr, struct fec *);
void lde_send_labelmapping(struct lde_nbr *, struct fec_node *,
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
index 9db931677d..68b721e213 100644
--- a/ldpd/lde_lib.c
+++ b/ldpd/lde_lib.c
@@ -23,6 +23,7 @@
#include "ldpe.h"
#include "lde.h"
#include "log.h"
+#include "rlfa.h"
#include "mpls.h"
@@ -339,6 +340,8 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop,
fnh = fec_nh_find(fn, af, nexthop, ifindex, route_type, route_instance);
if (fnh == NULL) {
+ fn->flags |= F_FEC_NHS_CHANGED;
+
fnh = fec_nh_add(fn, af, nexthop, ifindex, route_type,
route_instance);
/*
@@ -415,11 +418,17 @@ lde_kernel_update(struct fec *fec)
} else
fnh->flags |= F_FEC_NH_NO_LDP;
} else {
+ fn->flags |= F_FEC_NHS_CHANGED;
lde_send_delete_klabel(fn, fnh);
fec_nh_del(fnh);
}
}
+ if (!(fn->flags & F_FEC_NHS_CHANGED))
+ /* return earlier if nothing has changed */
+ return;
+ fn->flags &= ~F_FEC_NHS_CHANGED;
+
if (LIST_EMPTY(&fn->nexthops)) {
RB_FOREACH(ln, nbr_tree, &lde_nbrs)
lde_send_labelwithdraw(ln, fn, NULL, NULL);
@@ -601,6 +610,10 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
break;
}
}
+
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(&fec, ln, map->label);
+
/* LMp.13 & LMp.16: Record the mapping from this peer */
if (me == NULL)
me = lde_map_add(ln, fn, 0);
@@ -858,6 +871,9 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
fnh->remote_label = NO_LABEL;
}
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(&fec, ln, MPLS_INVALID_LABEL);
+
/* LWd.2: send label release */
lde_send_labelrelease(ln, fn, NULL, map->label);
@@ -940,6 +956,9 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
fnh->remote_label = NO_LABEL;
}
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
/* LWd.3: check previously received label mapping */
if (me && (map->label == NO_LABEL ||
map->label == me->map.label))
diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c
index a53854fa56..ea86c2dc03 100644
--- a/ldpd/ldp_zebra.c
+++ b/ldpd/ldp_zebra.c
@@ -114,12 +114,16 @@ static void
ldp_zebra_opaque_register(void)
{
zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+ zclient_register_opaque(zclient, LDP_RLFA_REGISTER);
+ zclient_register_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
}
static void
ldp_zebra_opaque_unregister(void)
{
zclient_unregister_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+ zclient_unregister_opaque(zclient, LDP_RLFA_REGISTER);
+ zclient_unregister_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
}
int
@@ -147,12 +151,29 @@ ldp_sync_zebra_send_announce(void)
return 0;
}
+int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *rlfa_labels)
+{
+ int ret;
+
+ ret = zclient_send_opaque(zclient, LDP_RLFA_LABELS,
+ (const uint8_t *)rlfa_labels,
+ sizeof(*rlfa_labels));
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ log_warn("failed to send RLFA labels to IGP");
+ return -1;
+ }
+
+ return 0;
+}
+
static int
ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct zapi_opaque_msg info;
struct ldp_igp_sync_if_state_req state_req;
+ struct zapi_rlfa_igp igp;
+ struct zapi_rlfa_request rlfa;
s = zclient->ibuf;
@@ -165,6 +186,14 @@ ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
main_imsg_compose_ldpe(IMSG_LDP_SYNC_IF_STATE_REQUEST, 0, &state_req,
sizeof(state_req));
break;
+ case LDP_RLFA_REGISTER:
+ STREAM_GET(&rlfa, s, sizeof(rlfa));
+ main_imsg_compose_both(IMSG_RLFA_REG, &rlfa, sizeof(rlfa));
+ break;
+ case LDP_RLFA_UNREGISTER_ALL:
+ STREAM_GET(&igp, s, sizeof(igp));
+ main_imsg_compose_both(IMSG_RLFA_UNREG_ALL, &igp, sizeof(igp));
+ break;
default:
break;
}
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
index d6da45c862..83e93ebbbc 100644
--- a/ldpd/ldpd.c
+++ b/ldpd/ldpd.c
@@ -625,6 +625,7 @@ main_dispatch_lde(struct thread *thread)
struct imsg imsg;
ssize_t n;
int shut = 0;
+ struct zapi_rlfa_response *rlfa_labels;
iev->ev_read = NULL;
@@ -691,6 +692,15 @@ main_dispatch_lde(struct thread *thread)
fatalx("IMSG_ACL_CHECK imsg with wrong len");
ldp_acl_reply(iev, (struct acl_check *)imsg.data);
break;
+ case IMSG_RLFA_LABELS:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_response)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_labels = imsg.data;
+ ldp_zebra_send_rlfa_labels(rlfa_labels);
+ break;
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h
index f8a94b4e2a..beb625d8a2 100644
--- a/ldpd/ldpd.h
+++ b/ldpd/ldpd.h
@@ -157,7 +157,10 @@ enum imsg_type {
IMSG_FILTER_UPDATE,
IMSG_NBR_SHUTDOWN,
IMSG_LDP_SYNC_IF_STATE_REQUEST,
- IMSG_LDP_SYNC_IF_STATE_UPDATE
+ IMSG_LDP_SYNC_IF_STATE_UPDATE,
+ IMSG_RLFA_REG,
+ IMSG_RLFA_UNREG_ALL,
+ IMSG_RLFA_LABELS,
};
struct ldpd_init {
@@ -373,6 +376,7 @@ struct tnbr {
union ldpd_addr addr;
int state;
uint16_t pw_count;
+ uint32_t rlfa_count;
uint8_t flags;
QOBJ_FIELDS
};
@@ -875,6 +879,8 @@ extern char ctl_sock_path[MAXPATHLEN];
void ldp_zebra_init(struct thread_master *);
void ldp_zebra_destroy(void);
int ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *);
+int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *
+ rlfa_labels);
/* compatibility */
#ifndef __OpenBSD__
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
index f3f8b85102..6a5a0750bd 100644
--- a/ldpd/ldpe.c
+++ b/ldpd/ldpe.c
@@ -27,6 +27,7 @@
#include "control.h"
#include "log.h"
#include "ldp_debug.h"
+#include "rlfa.h"
#include <lib/log.h>
#include "memory.h"
@@ -298,7 +299,11 @@ ldpe_dispatch_main(struct thread *thread)
int n, shut = 0;
struct ldp_access *laccess;
struct ldp_igp_sync_if_state_req *ldp_sync_if_state_req;
-
+ struct ldp_rlfa_node *rnode, *rntmp;
+ struct ldp_rlfa_client *rclient;
+ struct zapi_rlfa_request *rlfa_req;
+ struct zapi_rlfa_igp *rlfa_igp;
+
iev->ev_read = NULL;
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
@@ -569,6 +574,44 @@ ldpe_dispatch_main(struct thread *thread)
ldp_sync_if_state_req = imsg.data;
ldp_sync_fsm_state_req(ldp_sync_if_state_req);
break;
+ case IMSG_RLFA_REG:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_request)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_req = imsg.data;
+
+ rnode = rlfa_node_find(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ if (!rnode)
+ rnode = rlfa_node_new(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+ if (rclient)
+ /* RLFA already registered - do nothing */
+ break;
+ rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+ ldpe_rlfa_init(rclient);
+ break;
+ case IMSG_RLFA_UNREG_ALL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_igp)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_igp = imsg.data;
+
+ RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+ &rlfa_node_tree, rntmp) {
+ rclient = rlfa_client_find(rnode, rlfa_igp);
+ if (!rclient)
+ continue;
+
+ ldpe_rlfa_exit(rclient);
+ rlfa_client_del(rclient);
+ }
+ break;
default:
log_debug("ldpe_dispatch_main: error handling imsg %d",
imsg.hdr.type);
diff --git a/ldpd/rlfa.c b/ldpd/rlfa.c
new file mode 100644
index 0000000000..697ec08af8
--- /dev/null
+++ b/ldpd/rlfa.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "lde.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+
+struct ldp_rlfa_node_head rlfa_node_tree;
+
+static int ldp_rlfa_client_compare(const struct ldp_rlfa_client *a,
+ const struct ldp_rlfa_client *b)
+{
+ if (a->igp.vrf_id < b->igp.vrf_id)
+ return -1;
+ if (a->igp.vrf_id > b->igp.vrf_id)
+ return 1;
+
+ if (a->igp.protocol < b->igp.protocol)
+ return -1;
+ if (a->igp.protocol > b->igp.protocol)
+ return 1;
+
+ if (a->igp.isis.spf.tree_id < b->igp.isis.spf.tree_id)
+ return -1;
+ if (a->igp.isis.spf.tree_id > b->igp.isis.spf.tree_id)
+ return 1;
+
+ if (a->igp.isis.spf.level < b->igp.isis.spf.level)
+ return -1;
+ if (a->igp.isis.spf.level > b->igp.isis.spf.level)
+ return 1;
+
+ return 0;
+}
+RB_GENERATE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+ ldp_rlfa_client_compare)
+
+static int ldp_rlfa_node_compare(const struct ldp_rlfa_node *a,
+ const struct ldp_rlfa_node *b)
+{
+ if (ntohl(a->pq_address.s_addr) < ntohl(b->pq_address.s_addr))
+ return -1;
+ if (ntohl(a->pq_address.s_addr) > ntohl(b->pq_address.s_addr))
+ return 1;
+
+ return prefix_cmp(&a->destination, &b->destination);
+}
+RB_GENERATE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare)
+
+struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp)
+{
+ struct ldp_rlfa_client *rclient;
+
+ if ((rclient = calloc(1, sizeof(*rclient))) == NULL)
+ fatal(__func__);
+
+ rclient->igp = *igp;
+ rclient->node = rnode;
+ RB_INSERT(ldp_rlfa_client_head, &rnode->clients, rclient);
+
+ return rclient;
+}
+
+void rlfa_client_del(struct ldp_rlfa_client *rclient)
+{
+ struct ldp_rlfa_node *rnode = rclient->node;
+
+ RB_REMOVE(ldp_rlfa_client_head, &rnode->clients, rclient);
+ free(rclient);
+
+ /* Delete RLFA node if it's empty. */
+ if (RB_EMPTY(ldp_rlfa_client_head, &rnode->clients))
+ rlfa_node_del(rnode);
+}
+
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp)
+{
+ struct ldp_rlfa_client rclient;
+
+ rclient.igp = *igp;
+ return RB_FIND(ldp_rlfa_client_head, &rnode->clients, &rclient);
+}
+
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+ struct in_addr pq_address)
+{
+ struct ldp_rlfa_node *rnode;
+
+ if ((rnode = calloc(1, sizeof(*rnode))) == NULL)
+ fatal(__func__);
+
+ rnode->destination = *destination;
+ rnode->pq_address = pq_address;
+ rnode->pq_label = MPLS_INVALID_LABEL;
+ RB_INIT(ldp_rlfa_client_head, &rnode->clients);
+ RB_INSERT(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+
+ return rnode;
+}
+
+void rlfa_node_del(struct ldp_rlfa_node *rnode)
+{
+ /* Delete RLFA clients. */
+ while (!RB_EMPTY(ldp_rlfa_client_head, &rnode->clients)) {
+ struct ldp_rlfa_client *rclient;
+
+ rclient = RB_ROOT(ldp_rlfa_client_head, &rnode->clients);
+ rlfa_client_del(rclient);
+ }
+
+ RB_REMOVE(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+ free(rnode);
+}
+
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+ struct in_addr pq_address)
+{
+ struct ldp_rlfa_node rnode = {};
+
+ rnode.destination = *destination;
+ rnode.pq_address = pq_address;
+ return RB_FIND(ldp_rlfa_node_head, &rlfa_node_tree, &rnode);
+}
+
+void lde_rlfa_client_send(struct ldp_rlfa_client *rclient)
+{
+ struct ldp_rlfa_node *rnode = rclient->node;
+ struct zapi_rlfa_response rlfa_labels = {};
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ int i = 0;
+
+ /* Fill in inner label (allocated by PQ node). */
+ rlfa_labels.igp = rclient->igp;
+ rlfa_labels.destination = rnode->destination;
+ rlfa_labels.pq_label = rnode->pq_label;
+
+ /* Fill in outer label(s) (allocated by the nexthop routers). */
+ fec.type = FEC_TYPE_IPV4;
+ fec.u.ipv4.prefix = rnode->pq_address;
+ fec.u.ipv4.prefixlen = IPV4_MAX_BITLEN;
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (!fn)
+ return;
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (fnh->remote_label == NO_LABEL)
+ continue;
+
+ rlfa_labels.nexthops[i].family = fnh->af;
+ switch (fnh->af) {
+ case AF_INET:
+ rlfa_labels.nexthops[i].gate.ipv4 = fnh->nexthop.v4;
+ break;
+ case AF_INET6:
+ rlfa_labels.nexthops[i].gate.ipv6 = fnh->nexthop.v6;
+ break;
+ default:
+ continue;
+ }
+ rlfa_labels.nexthops[i].label = fnh->remote_label;
+ i++;
+ }
+ rlfa_labels.nexthop_num = i;
+
+ lde_imsg_compose_parent(IMSG_RLFA_LABELS, 0, &rlfa_labels,
+ sizeof(rlfa_labels));
+}
+
+void lde_rlfa_label_update(const struct fec *fec)
+{
+ struct ldp_rlfa_node *rnode;
+
+ if (fec->type != FEC_TYPE_IPV4
+ || fec->u.ipv4.prefixlen != IPV4_MAX_BITLEN)
+ return;
+
+ /*
+ * TODO: use an rb-tree lookup to restrict the iteration to the RLFAs
+ * that were effectivelly affected by the label update.
+ */
+ RB_FOREACH (rnode, ldp_rlfa_node_head, &rlfa_node_tree) {
+ struct ldp_rlfa_client *rclient;
+
+ if (!IPV4_ADDR_SAME(&rnode->pq_address, &fec->u.ipv4.prefix))
+ continue;
+
+ RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+ lde_rlfa_client_send(rclient);
+ }
+}
+
+void lde_rlfa_check(struct ldp_rlfa_client *rclient)
+{
+ struct lde_nbr *ln;
+ struct lde_map *me;
+ struct fec fec;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ ln = lde_nbr_find_by_addr(AF_INET, &pq_address);
+ if (!ln)
+ return;
+
+ lde_prefix2fec(&rclient->node->destination, &fec);
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fec);
+ if (!me)
+ return;
+
+ rclient->node->pq_label = me->map.label;
+ lde_rlfa_client_send(rclient);
+}
+
+/*
+ * Check if there's any registered RLFA client for this prefix/neighbor (PQ
+ * node) and notify about the updated label.
+ */
+void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+ uint32_t label)
+{
+ struct prefix rlfa_dest;
+ struct ldp_rlfa_node *rnode;
+
+ lde_fec2prefix(fec, &rlfa_dest);
+ rnode = rlfa_node_find(&rlfa_dest, ln->id);
+ if (rnode) {
+ struct ldp_rlfa_client *rclient;
+
+ rnode->pq_label = label;
+ RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+ lde_rlfa_client_send(rclient);
+ } else
+ lde_rlfa_label_update(fec);
+}
+
+void ldpe_rlfa_init(struct ldp_rlfa_client *rclient)
+{
+ struct tnbr *tnbr;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+ if (tnbr == NULL) {
+ tnbr = tnbr_new(AF_INET, &pq_address);
+ tnbr_update(tnbr);
+ RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
+ }
+
+ tnbr->rlfa_count++;
+}
+
+void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient)
+{
+ struct tnbr *tnbr;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+ if (tnbr) {
+ tnbr->rlfa_count--;
+ tnbr_check(leconf, tnbr);
+ }
+}
diff --git a/ldpd/rlfa.h b/ldpd/rlfa.h
new file mode 100644
index 0000000000..fe67917e8a
--- /dev/null
+++ b/ldpd/rlfa.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _LDPD_RLFA_H_
+#define _LDPD_RLFA_H_
+
+#include "openbsd-tree.h"
+#include "zclient.h"
+
+struct ldp_rlfa_client {
+ RB_ENTRY(ldp_rlfa_client) entry;
+
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Backpointer to RLFA node. */
+ struct ldp_rlfa_node *node;
+};
+RB_HEAD(ldp_rlfa_client_head, ldp_rlfa_client);
+RB_PROTOTYPE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+ ldp_rlfa_client_compare);
+
+struct ldp_rlfa_node {
+ RB_ENTRY(ldp_rlfa_node) entry;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* PQ node address. */
+ struct in_addr pq_address;
+
+ /* RLFA clients. */
+ struct ldp_rlfa_client_head clients;
+
+ /* Label allocated by the PQ node to the RLFA destination. */
+ mpls_label_t pq_label;
+};
+RB_HEAD(ldp_rlfa_node_head, ldp_rlfa_node);
+RB_PROTOTYPE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare);
+
+extern struct ldp_rlfa_node_head rlfa_node_tree;
+
+/* prototypes */
+struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp);
+void rlfa_client_del(struct ldp_rlfa_client *rclient);
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp);
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+ struct in_addr pq_address);
+void rlfa_node_del(struct ldp_rlfa_node *rnode);
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+ struct in_addr pq_address);
+void lde_rlfa_check(struct ldp_rlfa_client *rclient);
+void lde_rlfa_client_send(struct ldp_rlfa_client *rclient);
+void lde_rlfa_label_update(const struct fec *fec);
+void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+ uint32_t label);
+void ldpe_rlfa_init(struct ldp_rlfa_client *rclient);
+void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient);
+
+#endif /* _LDPD_RLFA_H_ */
diff --git a/ldpd/subdir.am b/ldpd/subdir.am
index 2058d2596a..d89d18341d 100644
--- a/ldpd/subdir.am
+++ b/ldpd/subdir.am
@@ -36,6 +36,7 @@ ldpd_libldp_a_SOURCES = \
ldpd/notification.c \
ldpd/packet.c \
ldpd/pfkey.c \
+ ldpd/rlfa.c \
ldpd/socket.c \
ldpd/util.c \
# end
@@ -53,6 +54,7 @@ noinst_HEADERS += \
ldpd/ldpd.h \
ldpd/ldpe.h \
ldpd/log.h \
+ ldpd/rlfa.h \
# end
ldpd_ldpd_SOURCES = ldpd/ldpd.c
diff --git a/lib/zclient.h b/lib/zclient.h
index 2af448a20c..910a4dbae5 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -634,6 +634,52 @@ struct zapi_pw_status {
uint32_t status;
};
+/* IGP instance data associated to a RLFA. */
+struct zapi_rlfa_igp {
+ vrf_id_t vrf_id;
+ int protocol;
+ union {
+ struct {
+ char area_tag[32];
+ struct {
+ int tree_id;
+ int level;
+ unsigned int run_id;
+ } spf;
+ } isis;
+ };
+};
+
+/* IGP -> LDP RLFA (un)registration message. */
+struct zapi_rlfa_request {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* PQ node address. */
+ struct in_addr pq_address;
+};
+
+/* LDP -> IGP RLFA label update. */
+struct zapi_rlfa_response {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* Resolved LDP labels. */
+ mpls_label_t pq_label;
+ uint16_t nexthop_num;
+ struct {
+ int family;
+ union g_addr gate;
+ mpls_label_t label;
+ } nexthops[MULTIPATH_NUM];
+};
+
enum zapi_route_notify_owner {
ZAPI_ROUTE_FAIL_INSTALL,
ZAPI_ROUTE_BETTER_ADMIN_WON,
@@ -1091,6 +1137,12 @@ enum zapi_opaque_registry {
LDP_IGP_SYNC_IF_STATE_UPDATE = 4,
/* Announce that LDP is up */
LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5,
+ /* Register RLFA with LDP */
+ LDP_RLFA_REGISTER = 7,
+ /* Unregister all RLFAs with LDP */
+ LDP_RLFA_UNREGISTER_ALL = 8,
+ /* Announce LDP labels associated to a previously registered RLFA */
+ LDP_RLFA_LABELS = 9,
};
/* Send the hello message.
diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c
index 5fa604c749..5b2028ffd4 100644
--- a/tests/isisd/test_common.c
+++ b/tests/isisd/test_common.c
@@ -69,6 +69,24 @@ test_find_adjacency(const struct isis_test_node *tnode, const char *hostname)
return NULL;
}
+mpls_label_t test_topology_node_ldp_label(const struct isis_topology *topology,
+ struct in_addr router_id)
+{
+ for (size_t i = 0; topology->nodes[i].hostname[0]; i++) {
+ const struct isis_test_node *tnode = &topology->nodes[i];
+ struct in_addr node_router_id;
+
+ if (!tnode->router_id)
+ continue;
+
+ (void)inet_pton(AF_INET, tnode->router_id, &node_router_id);
+ if (IPV4_ADDR_SAME(&router_id, &node_router_id))
+ return (50000 + (i + 1) * 100);
+ }
+
+ return MPLS_INVALID_LABEL;
+}
+
static struct isis_lsp *lsp_add(struct lspdb_head *lspdb,
struct isis_area *area, int level,
const uint8_t *sysid, uint8_t pseudonode_id)
diff --git a/tests/isisd/test_common.h b/tests/isisd/test_common.h
index 6fd0d3813e..3359a893ac 100644
--- a/tests/isisd/test_common.h
+++ b/tests/isisd/test_common.h
@@ -70,6 +70,9 @@ test_topology_find_node(const struct isis_topology *topology,
const char *hostname, uint8_t pseudonode_id);
extern const struct isis_topology *
test_topology_find(struct isis_topology *test_topologies, uint16_t number);
+extern mpls_label_t
+test_topology_node_ldp_label(const struct isis_topology *topology,
+ struct in_addr router_id);
extern int test_topology_load(const struct isis_topology *topology,
struct isis_area *area,
struct lspdb_head lspdb[]);
diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c
index 36ef93669b..e06944a037 100644
--- a/tests/isisd/test_isis_spf.c
+++ b/tests/isisd/test_isis_spf.c
@@ -31,6 +31,7 @@
#include "isisd/isisd.h"
#include "isisd/isis_dynhn.h"
#include "isisd/isis_misc.h"
+#include "isisd/isis_route.h"
#include "isisd/isis_spf.h"
#include "isisd/isis_spf_private.h"
@@ -40,6 +41,7 @@ enum test_type {
TEST_SPF = 1,
TEST_REVERSE_SPF,
TEST_LFA,
+ TEST_RLFA,
TEST_TI_LFA,
};
@@ -105,6 +107,86 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
isis_spftree_del(spftree_self);
}
+static void test_run_rlfa(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;
+ struct isis_spftree *spftree_reverse;
+ struct isis_spftree *spftree_pc;
+ struct isis_spf_node *spf_node, *node;
+ struct rlfa *rlfa;
+ 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 reverse SPF in the root node. */
+ spftree_reverse = isis_spf_reverse_run(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the local LFA repair paths. */
+ isis_lfa_compute(area, NULL, spftree_self, protected_resource);
+
+ /* Compute the remote LFA repair paths. */
+ spftree_pc = isis_rlfa_compute(area, spftree_self, spftree_reverse, 0,
+ protected_resource);
+
+ /* Print the extended P-space and Q-space. */
+ vty_out(vty, "P-space (self):\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
+ if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
+ continue;
+ vty_out(vty, "P-space (%s):\n",
+ print_sys_hostname(spf_node->sysid));
+ RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "Q-space:\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+
+ /* Print the post-convergence SPT. */
+ isis_print_spftree(vty, spftree_pc);
+
+ /*
+ * Activate the computed RLFAs (if any) using artificial LDP labels for
+ * the PQ nodes.
+ */
+ frr_each_safe (rlfa_tree, &spftree_self->lfa.remote.rlfas, rlfa) {
+ struct zapi_rlfa_response response = {};
+
+ response.pq_label = test_topology_node_ldp_label(
+ topology, rlfa->pq_address);
+ assert(response.pq_label != MPLS_INVALID_LABEL);
+ isis_rlfa_activate(spftree_self, rlfa, &response);
+ }
+
+ /* 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);
+ isis_spftree_del(spftree_reverse);
+ isis_spftree_del(spftree_pc);
+}
+
static void test_run_ti_lfa(struct vty *vty,
const struct isis_topology *topology,
const struct isis_test_node *root,
@@ -242,6 +324,11 @@ static int test_run(struct vty *vty, const struct isis_topology *topology,
&area->lspdb[level - 1], level,
tree, &protected_resource);
break;
+ case TEST_RLFA:
+ test_run_rlfa(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,
@@ -266,6 +353,7 @@ DEFUN(test_isis, test_isis_cmd,
spf\
|reverse-spf\
|lfa system-id WORD [pseudonode-id <1-255>]\
+ |remote-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>]",
@@ -282,6 +370,11 @@ DEFUN(test_isis, test_isis_cmd,
"System ID\n"
"Pseudonode-ID\n"
"Pseudonode-ID\n"
+ "Remote LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
"Topology Independent LFA\n"
"System ID\n"
"System ID\n"
@@ -335,6 +428,14 @@ DEFUN(test_isis, test_isis_cmd,
fail_pseudonode_id =
strtoul(argv[idx + 1]->arg, NULL, 10);
protection_type = LFA_LINK_PROTECTION;
+ } else if (argv_find(argv, argc, "remote-lfa", &idx)) {
+ test_type = TEST_RLFA;
+
+ 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;
diff --git a/tests/isisd/test_isis_spf.in b/tests/isisd/test_isis_spf.in
index 93e18124e6..f8f65ffdf7 100644
--- a/tests/isisd/test_isis_spf.in
+++ b/tests/isisd/test_isis_spf.in
@@ -31,6 +31,18 @@ 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 remote-lfa system-id rt2
+test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1
+test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only
+test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only
+test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only
+test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only
+test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only
+test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only
+test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only
+test isis topology 11 root rt2 remote-lfa system-id rt4
+test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only
+
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 dced6fb103..024f7256e0 100644
--- a/tests/isisd/test_isis_spf.refout
+++ b/tests/isisd/test_isis_spf.refout
@@ -1807,6 +1807,1227 @@ IS-IS L1 IPv6 routing table:
2001:db8::4/128 60 - rt3 -
test#
+test# test isis topology 1 root rt1 remote-lfa system-id rt2
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+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)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+10.0.255.2/32 IP TE 60 rt3 - rt2(4)
+
+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:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 60 - rt3 50600/16020
+ 10.0.255.4/32 50 - rt3 50600/16040
+
+P-space (self):
+ rt3
+ rt5
+
+P-space (rt3):
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt2
+ rt4
+ rt6
+
+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)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+rt4 TE-IS 40 rt3 - rt6(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+rt2 TE-IS 50 rt3 - rt4(4)
+2001:db8::4/128 IP6 internal 50 rt3 - rt4(4)
+2001:db8::2/128 IP6 internal 60 rt3 - rt2(4)
+
+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:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::2/128 60 - rt3 50600/16021
+ 2001:db8::4/128 50 - rt3 50600/16041
+
+test# test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+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)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+10.0.255.1/32 IP TE 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)
+10.0.255.2/32 IP TE 55 rt6 - rt2(4)
+
+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)
+rt1 TE-IS 10 rt1 - rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt4 - rt4(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(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 rt3 - rt5(4)
+ rt1 - rt1(4)
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)
+ rt1 -
+
+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 - rt3 implicit-null
+ - rt1 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 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.1/32 40 - rt6 50400/16010
+ 10.0.255.2/32 55 - rt6 50400/16020
+ 10.0.255.4/32 30 - rt6 50400/16040
+
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+
+P-space (rt6):
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+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)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)
+rt1 TE-IS 30 rt6 - rt1(2)
+2001:db8::4/128 IP6 internal 30 rt6 - rt4(4)
+rt3 TE-IS 40 rt3 - rt5(4)
+2001:db8::1/128 IP6 internal 40 rt6 - rt1(4)
+rt2 TE-IS 45 rt6 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 55 rt6 - rt2(4)
+
+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)
+rt1 TE-IS 10 rt1 - rt5(4)
+rt4 TE-IS 10 rt4 - rt5(4)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)
+ rt4 - rt4(4)
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(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 rt3 - rt5(4)
+ rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)
+ rt1 -
+
+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 - rt3 implicit-null
+ - rt1 implicit-null
+ 2001:db8::4/128 20 - rt4 implicit-null
+ 2001:db8::5/128 0 - - -
+ 2001:db8::6/128 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 2001:db8::1/128 40 - rt6 50400/16011
+ 2001:db8::2/128 55 - rt6 50400/16021
+ 2001:db8::4/128 30 - rt6 50400/16041
+
+test# test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only
+P-space (self):
+ rt6
+
+P-space (rt3):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+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)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt4 TE-IS 20 rt6 - rt6(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+rt2 TE-IS 30 rt6 - rt4(4)
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)
+rt1 TE-IS 40 rt3 - rt3(4)
+ rt6 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+10.0.255.2/32 IP TE 40 rt6 - rt2(4)
+10.0.255.1/32 IP TE 50 rt3 - rt1(4)
+ rt6 -
+
+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)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+ rt4 - rt2(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+ rt4 -
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt4 16010
+ 10.0.255.2/32 30 - rt4 16020
+ 10.0.255.3/32 40 - rt3 implicit-null
+ - rt4 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 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.1/32 40 - rt3 16010
+ - rt6 16010
+ 10.0.255.2/32 30 - rt3 16020
+ - rt6 16020
+ 10.0.255.4/32 20 - rt3 16040
+ - rt6 16040
+
+test# test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt1
+ rt2
+ rt4
+ rt6
+
+P-space (rt4):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+P-space (rt6):
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt6
+
+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)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+rt3 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)
+
+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)
+rt6 TE-IS 10 rt6 - rt5(4)
+rt2 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt3 - rt5(4)
+ rt4 - rt2(4)
+rt1 TE-IS 30 rt4 - rt2(4)
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+ rt4 -
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 40 - rt4 16010
+ 10.0.255.2/32 30 - rt4 16020
+ 10.0.255.3/32 40 - rt3 implicit-null
+ - rt4 implicit-null
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 0 - - -
+ 10.0.255.6/32 20 - rt6 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+test# test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only
+P-space (self):
+ rt3
+ rt5
+ rt7
+
+P-space (rt3):
+ rt3
+ rt5
+ rt7
+ rt8
+
+Q-space:
+ rt2
+ rt4
+ rt6
+ rt8
+
+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)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt3 - rt7(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+rt6 TE-IS 50 rt3 - rt8(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt4 TE-IS 60 rt3 - rt6(4)
+10.0.255.6/32 IP TE 60 rt3 - rt6(4)
+rt2 TE-IS 70 rt3 - rt4(4)
+10.0.255.4/32 IP TE 70 rt3 - rt4(4)
+10.0.255.2/32 IP TE 80 rt3 - rt2(4)
+
+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)
+rt7 TE-IS 30 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)
+rt8 TE-IS 40 rt2 - rt6(4)
+ rt3 - rt7(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(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
+ 10.0.255.7/32 40 - rt3 16070
+ 10.0.255.8/32 50 - rt2 16080
+ - rt3 16080
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.2/32 80 - rt3 50800/16020
+ 10.0.255.4/32 70 - rt3 50800/16040
+ 10.0.255.6/32 60 - rt3 50800/16060
+
+test# test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+ rt5
+ rt6
+ rt7
+ rt8
+
+P-space (rt2):
+ rt1
+ rt2
+
+P-space (rt6):
+ rt5
+ rt6
+ rt7
+ rt8
+
+Q-space:
+ rt1
+ 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)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt6 - rt6(4)
+rt8 TE-IS 20 rt6 - rt6(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt3 TE-IS 30 rt2 - rt1(4)
+rt7 TE-IS 30 rt6 - rt5(4)
+ rt8(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)
+
+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)
+rt6 TE-IS 10 rt6 - rt4(4)
+rt1 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt6 - rt6(4)
+rt8 TE-IS 20 rt6 - rt6(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)
+rt7 TE-IS 30 rt6 - rt5(4)
+ rt8(4)
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)
+10.0.255.7/32 IP TE 40 rt6 - 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 - rt6 16050
+ 10.0.255.6/32 20 - rt6 implicit-null
+ 10.0.255.7/32 40 - rt6 16070
+ 10.0.255.8/32 30 - rt6 16080
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.3/32 40 - rt2 50100/16030
+
+test# test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only
+P-space (self):
+ rt10
+ rt12
+
+P-space (rt10):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt12):
+ rt9
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+ rt8
+ rt9
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt11
+10.0.255.11/32 IP internal 0 rt11(4)
+rt10 TE-IS 10 rt10 - rt11(4)
+rt12 TE-IS 10 rt12 - rt11(4)
+rt9 TE-IS 20 rt12 - rt12(4)
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)
+rt7 TE-IS 30 rt10 - rt10(4)
+rt8 TE-IS 30 rt12 - rt9(4)
+10.0.255.9/32 IP TE 30 rt12 - rt9(4)
+rt4 TE-IS 40 rt10 - rt7(4)
+rt5 TE-IS 40 rt12 - rt8(4)
+10.0.255.7/32 IP TE 40 rt10 - rt7(4)
+10.0.255.8/32 IP TE 40 rt12 - rt8(4)
+rt6 TE-IS 50 rt12 - rt9(4)
+ rt5(4)
+rt1 TE-IS 50 rt10 - rt4(4)
+rt2 TE-IS 50 rt12 - rt5(4)
+10.0.255.4/32 IP TE 50 rt10 - rt4(4)
+10.0.255.5/32 IP TE 50 rt12 - rt5(4)
+rt3 TE-IS 60 rt12 - rt6(4)
+ rt2(4)
+10.0.255.6/32 IP TE 60 rt12 - rt6(4)
+10.0.255.1/32 IP TE 60 rt10 - rt1(4)
+10.0.255.2/32 IP TE 60 rt12 - rt2(4)
+10.0.255.3/32 IP TE 70 rt12 - rt3(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt11
+10.0.255.11/32 IP internal 0 rt11(4)
+rt8 TE-IS 10 rt8 - rt11(4)
+rt10 TE-IS 10 rt10 - rt11(4)
+rt12 TE-IS 10 rt12 - rt11(4)
+rt5 TE-IS 20 rt8 - rt8(4)
+rt7 TE-IS 20 rt8 - rt8(4)
+rt9 TE-IS 20 rt8 - rt8(4)
+ rt12 - rt12(4)
+10.0.255.8/32 IP TE 20 rt8 - rt8(4)
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)
+rt2 TE-IS 30 rt8 - rt5(4)
+rt4 TE-IS 30 rt8 - rt5(4)
+ rt7(4)
+rt6 TE-IS 30 rt8 - rt5(4)
+10.0.255.5/32 IP TE 30 rt8 - rt5(4)
+10.0.255.7/32 IP TE 30 rt8 - rt7(4)
+10.0.255.9/32 IP TE 30 rt8 - rt9(4)
+ rt12 -
+rt3 TE-IS 40 rt8 - rt2(4)
+ rt6(4)
+rt1 TE-IS 40 rt8 - rt4(4)
+10.0.255.2/32 IP TE 40 rt8 - rt2(4)
+10.0.255.4/32 IP TE 40 rt8 - rt4(4)
+10.0.255.6/32 IP TE 40 rt8 - rt6(4)
+10.0.255.3/32 IP TE 50 rt8 - rt3(4)
+10.0.255.1/32 IP TE 50 rt8 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------------
+ 10.0.255.1/32 50 - rt8 16010
+ 10.0.255.2/32 40 - rt8 16020
+ 10.0.255.3/32 50 - rt8 16030
+ 10.0.255.4/32 40 - rt8 16040
+ 10.0.255.5/32 30 - rt8 16050
+ 10.0.255.6/32 40 - rt8 16060
+ 10.0.255.7/32 30 - rt8 16070
+ 10.0.255.8/32 20 - rt8 implicit-null
+ 10.0.255.9/32 30 - rt8 16090
+ - rt12 16090
+ 10.0.255.10/32 20 - rt10 implicit-null
+ 10.0.255.11/32 0 - - -
+ 10.0.255.12/32 20 - rt12 implicit-null
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ --------------------------------------------------------
+ 10.0.255.1/32 50 - rt10 16010
+ 10.0.255.2/32 60 - rt12 50900/16020
+ 10.0.255.3/32 70 - rt12 50900/16030
+ 10.0.255.4/32 40 - rt10 16040
+ 10.0.255.5/32 50 - rt12 50900/16050
+ 10.0.255.6/32 60 - rt12 50900/16060
+ 10.0.255.7/32 30 - rt10 16070
+ 10.0.255.8/32 40 - rt12 50900/16080
+
+test# test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt3
+
+P-space (rt3):
+ rt2
+ rt3
+
+P-space (rt9):
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+Q-space:
+ rt1
+ rt2
+ rt4
+ rt5
+ rt7
+ rt8
+ rt9
+ rt10
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt6
+10.0.255.6/32 IP internal 0 rt6(4)
+rt3 TE-IS 10 rt3 - rt6(4)
+rt2 TE-IS 20 rt3 - rt3(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt9 TE-IS 30 rt9 - rt6(4)
+rt5 TE-IS 30 rt3 - rt2(4)
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)
+rt8 TE-IS 40 rt9 - rt9(4)
+ rt3 - rt5(4)
+rt12 TE-IS 40 rt9 - rt9(4)
+rt4 TE-IS 40 rt3 - rt5(4)
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt7 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt4(4)
+rt11 TE-IS 50 rt9 - rt8(4)
+ rt3 - rt12(4)
+rt1 TE-IS 50 rt3 - rt4(4)
+10.0.255.8/32 IP TE 50 rt9 - rt8(4)
+ rt3 -
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)
+rt10 TE-IS 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.7/32 IP TE 60 rt9 - rt7(4)
+ rt3 -
+10.0.255.11/32 IP TE 60 rt9 - rt11(4)
+ rt3 -
+10.0.255.1/32 IP TE 60 rt3 - rt1(4)
+10.0.255.10/32 IP TE 70 rt9 - rt10(4)
+ rt3 -
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt6
+10.0.255.6/32 IP internal 0 rt6(4)
+rt3 TE-IS 10 rt3 - rt6(4)
+rt5 TE-IS 10 rt5 - rt6(4)
+rt2 TE-IS 20 rt3 - rt3(4)
+ rt5 - rt5(4)
+rt4 TE-IS 20 rt5 - rt5(4)
+rt8 TE-IS 20 rt5 - rt5(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt9 TE-IS 30 rt9 - rt6(4)
+ rt5 - rt8(4)
+rt1 TE-IS 30 rt5 - rt4(4)
+rt7 TE-IS 30 rt5 - rt4(4)
+ rt8(4)
+rt11 TE-IS 30 rt5 - rt8(4)
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)
+ rt5 -
+10.0.255.4/32 IP TE 30 rt5 - rt4(4)
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)
+rt12 TE-IS 40 rt9 - rt9(4)
+ rt5 - rt11(4)
+rt10 TE-IS 40 rt5 - rt11(4)
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)
+ rt5 -
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)
+10.0.255.7/32 IP TE 40 rt5 - rt7(4)
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)
+ rt5 -
+10.0.255.10/32 IP TE 50 rt5 - rt10(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 - rt3 16020
+ - rt5 16020
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt5 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 0 - - -
+ 10.0.255.7/32 40 - rt5 16070
+ 10.0.255.8/32 30 - rt5 16080
+ 10.0.255.9/32 40 - rt9 implicit-null
+ - rt5 implicit-null
+ 10.0.255.10/32 50 - rt5 16100
+ 10.0.255.11/32 40 - rt5 16110
+ 10.0.255.12/32 50 - rt9 16120
+ - rt5 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.1/32 70 - rt9 16010
+ 10.0.255.4/32 60 - rt9 16040
+ 10.0.255.5/32 50 - rt9 16050
+ 10.0.255.7/32 50 - rt9 16070
+ 10.0.255.8/32 40 - rt9 16080
+ 10.0.255.10/32 60 - rt9 16100
+ 10.0.255.11/32 50 - rt9 16110
+
+test# test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only
+P-space (self):
+ rt1
+ rt3
+ rt4
+ rt7
+ rt10
+
+P-space (rt1):
+ rt1
+ rt4
+ rt7
+ rt10
+
+P-space (rt3):
+ rt3
+ rt6
+
+Q-space:
+ rt5
+ rt6
+ rt8
+ rt9
+ rt11
+ rt12
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 10 rt1 - rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt4 TE-IS 20 rt1 - rt1(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt1 - rt4(4)
+rt5 TE-IS 30 rt3 - rt6(4)
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+rt10 TE-IS 40 rt1 - rt7(4)
+rt8 TE-IS 40 rt3 - rt5(4)
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)
+rt9 TE-IS 50 rt3 - rt8(4)
+rt11 TE-IS 50 rt3 - rt8(4)
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)
+rt12 TE-IS 60 rt3 - rt9(4)
+ rt11(4)
+10.0.255.9/32 IP TE 60 rt3 - rt9(4)
+10.0.255.11/32 IP TE 60 rt3 - rt11(4)
+10.0.255.12/32 IP TE 70 rt3 - rt12(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 10 rt1 - rt2(4)
+rt3 TE-IS 10 rt3 - rt2(4)
+rt5 TE-IS 10 rt5 - rt2(4)
+rt4 TE-IS 20 rt1 - rt1(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+ rt5 - rt5(4)
+rt8 TE-IS 20 rt5 - rt5(4)
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+rt7 TE-IS 30 rt1 - rt4(4)
+rt9 TE-IS 30 rt5 - rt8(4)
+rt11 TE-IS 30 rt5 - rt8(4)
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+ rt5 -
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)
+rt10 TE-IS 40 rt1 - rt7(4)
+rt12 TE-IS 40 rt5 - rt9(4)
+ rt11(4)
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)
+10.0.255.9/32 IP TE 40 rt5 - rt9(4)
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)
+10.0.255.12/32 IP TE 50 rt5 - rt12(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 0 - - -
+ 10.0.255.3/32 20 - rt3 implicit-null
+ 10.0.255.4/32 30 - rt1 16040
+ 10.0.255.5/32 20 - rt5 implicit-null
+ 10.0.255.6/32 30 - rt3 16060
+ - rt5 16060
+ 10.0.255.7/32 40 - rt1 16070
+ 10.0.255.8/32 30 - rt5 16080
+ 10.0.255.9/32 40 - rt5 16090
+ 10.0.255.10/32 50 - rt1 16100
+ 10.0.255.11/32 40 - rt5 16110
+ 10.0.255.12/32 50 - rt5 16120
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ---------------------------------------------------------
+ 10.0.255.5/32 40 - rt3 50600/16050
+ 10.0.255.8/32 50 - rt3 50600/16080
+ 10.0.255.9/32 60 - rt3 50600/16090
+ 10.0.255.11/32 60 - rt3 50600/16110
+ 10.0.255.12/32 70 - rt3 50600/16120
+
+test# test isis topology 11 root rt2 remote-lfa system-id rt4
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+10.0.255.1/32 IP TE 60 rt1 - rt1(4)
+10.0.255.3/32 IP TE 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+10.0.255.5/32 IP TE 70 rt3 - rt5(4)
+10.0.255.4/32 IP TE 80 rt3 - rt4(4)
+10.0.255.6/32 IP TE 80 rt3 - rt6(4)
+
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+10.0.255.2/32 IP internal 0 rt2(4)
+rt4 TE-IS 10 rt4 - rt2(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt6 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt3 TE-IS 30 rt4 - rt5(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+10.0.255.6/32 IP TE 30 rt4 - rt6(4)
+rt2
+rt1 TE-IS 40 rt4 - rt2(2)
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)
+10.0.255.1/32 IP TE 50 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 50 - rt4 16010
+ 10.0.255.2/32 0 - - -
+ 10.0.255.3/32 40 - rt4 16030
+ 10.0.255.4/32 20 - rt4 implicit-null
+ 10.0.255.5/32 30 - rt4 16050
+ 10.0.255.6/32 30 - rt4 16060
+
+Backup:
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ----------------------------------------------------------
+ 10.0.255.1/32 50 - rt1 implicit-null
+ - rt3 16010
+ 10.0.255.3/32 50 - rt1 16030
+ - rt3 implicit-null
+ 10.0.255.4/32 80 - rt3 50500/16040
+ 10.0.255.5/32 60 - rt1 16050
+ - rt3 16050
+ 10.0.255.6/32 70 - rt3 16060
+
+P-space (self):
+
+P-space (rt1):
+ rt1
+ rt3
+ rt5
+
+P-space (rt3):
+ rt1
+ rt3
+ rt5
+ rt6
+
+Q-space:
+ rt1
+ rt3
+ rt4
+ rt5
+ rt6
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+2001:db8::2/128 IP6 internal 0 rt2(4)
+rt1 TE-IS 50 rt1 - rt2(4)
+rt3 TE-IS 50 rt3 - rt2(4)
+rt2
+rt5 TE-IS 60 rt3 - rt3(4)
+2001:db8::1/128 IP6 internal 60 rt1 - rt1(4)
+2001:db8::3/128 IP6 internal 60 rt3 - rt3(4)
+rt4 TE-IS 70 rt3 - rt5(4)
+rt6 TE-IS 70 rt3 - rt5(4)
+2001:db8::5/128 IP6 internal 70 rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 80 rt3 - rt4(4)
+2001:db8::6/128 IP6 internal 80 rt3 - rt6(4)
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt2
+2001:db8::2/128 IP6 internal 0 rt2(4)
+rt4 TE-IS 10 rt4 - rt2(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt6 TE-IS 20 rt4 - rt4(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+rt3 TE-IS 30 rt4 - rt5(4)
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)
+2001:db8::6/128 IP6 internal 30 rt4 - rt6(4)
+rt2
+rt1 TE-IS 40 rt4 - rt2(2)
+2001:db8::3/128 IP6 internal 40 rt4 - rt3(4)
+2001:db8::1/128 IP6 internal 50 rt4 - rt1(4)
+
+Main:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 50 - rt4 16011
+ 2001:db8::2/128 0 - - -
+ 2001:db8::3/128 40 - rt4 16031
+ 2001:db8::4/128 20 - rt4 implicit-null
+ 2001:db8::5/128 30 - rt4 16051
+ 2001:db8::6/128 30 - rt4 16061
+
+Backup:
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------------
+ 2001:db8::1/128 50 - rt1 implicit-null
+ - rt3 16011
+ 2001:db8::3/128 50 - rt1 16031
+ - rt3 implicit-null
+ 2001:db8::4/128 80 - rt3 50500/16041
+ 2001:db8::5/128 60 - rt1 16051
+ - rt3 16051
+ 2001:db8::6/128 70 - rt3 16061
+
+test# test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only
+P-space (self):
+ rt2
+
+P-space (rt2):
+ rt2
+ rt4
+
+Q-space:
+ rt3
+ rt4
+ rt5
+ rt6
+ rt7
+
+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)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt3 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt5 TE-IS 40 rt2 - rt3(4)
+rt6 TE-IS 40 rt2 - rt3(4)
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)
+rt7 TE-IS 50 rt2 - rt5(4)
+ rt6(4)
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)
+10.0.255.6/32 IP TE 50 rt2 - rt6(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+
+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)
+ 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.4/32 IP TE 30 rt2 - rt4(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 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
+ - rt3 16040
+ 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 40 - rt2 50400/16030
+ 10.0.255.5/32 50 - rt2 50400/16050
+ 10.0.255.6/32 50 - rt2 50400/16060
+ 10.0.255.7/32 60 - rt2 50400/16070
+
+test#
test# test isis topology 1 root rt1 ti-lfa system-id rt2
P-space (self):
rt3
diff --git a/tests/topotests/isis-rlfa-topo1/__init__.py b/tests/topotests/isis-rlfa-topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/__init__.py
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt1/isisd.conf
new file mode 100644
index 0000000000..a80f30dc7b
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/isisd.conf
@@ -0,0 +1,39 @@
+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
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+ip prefix-list PLIST seq 5 permit 10.0.255.8/32
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+ fast-reroute remote-lfa prefix-list PLIST
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf
new file mode 100644
index 0000000000..f60fdb9742
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt1
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.1
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.1
+ !
+ interface eth-rt2
+ interface eth-rt3
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::1
+ !
+ interface eth-rt2
+ interface eth-rt3
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000000..680b31eb8d
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,235 @@
+{
+ "10.0.255.2\/32":[
+ {
+ "prefix":"10.0.255.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.3\/32":[
+ {
+ "prefix":"10.0.255.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.4\/32":[
+ {
+ "prefix":"10.0.255.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.5\/32":[
+ {
+ "prefix":"10.0.255.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.6\/32":[
+ {
+ "prefix":"10.0.255.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.7\/32":[
+ {
+ "prefix":"10.0.255.7\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.8\/32":[
+ {
+ "prefix":"10.0.255.8\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000000..c487d2740d
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,207 @@
+{
+ "2001:db8::2\/128":[
+ {
+ "prefix":"2001:db8::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
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::3\/128":[
+ {
+ "prefix":"2001:db8::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
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::4\/128":[
+ {
+ "prefix":"2001:db8::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::5\/128":[
+ {
+ "prefix":"2001:db8::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::6\/128":[
+ {
+ "prefix":"2001:db8::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::7\/128":[
+ {
+ "prefix":"2001:db8::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::8\/128":[
+ {
+ "prefix":"2001:db8::8\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..3fe2b798a0
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "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-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000000..ef5707f14a
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step9/show_ip_route.ref
++++ b/rt1/step10/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -125,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..acd2ce003a
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step10/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step9/show_ipv6_route.ref
++++ b/rt1/step10/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -62,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -111,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000000..f7f31ac021
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ip_route.ref.diff
@@ -0,0 +1,134 @@
+--- a/rt1/step1/show_ip_route.ref
++++ b/rt1/step2/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -49,20 +36,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +57,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -117,20 +78,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +99,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -185,20 +120,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..e980031ad7
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step2/show_ipv6_route.ref.diff
@@ -0,0 +1,122 @@
+--- a/rt1/step1/show_ipv6_route.ref
++++ b/rt1/step2/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -43,18 +32,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +51,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -103,18 +70,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +89,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -163,18 +108,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000000..f3ed764f0b
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ip_route.ref.diff
@@ -0,0 +1,134 @@
+--- a/rt1/step2/show_ip_route.ref
++++ b/rt1/step3/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -36,7 +49,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -57,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -78,7 +117,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -99,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -120,7 +185,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..57b0b1de1a
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step3/show_ipv6_route.ref.diff
@@ -0,0 +1,122 @@
+--- a/rt1/step2/show_ipv6_route.ref
++++ b/rt1/step3/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -32,7 +43,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -51,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +103,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -89,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -108,7 +163,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000000..107a0ba2f7
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step3/show_ip_route.ref
++++ b/rt1/step4/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +70,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +125,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..9cf24082e1
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step3/show_ipv6_route.ref
++++ b/rt1/step4/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +62,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +111,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000000..09469501f5
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step4/show_ip_route.ref
++++ b/rt1/step5/show_ip_route.ref
+@@ -36,20 +36,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -91,20 +78,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -146,20 +120,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..70fb1a65c7
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step4/show_ipv6_route.ref
++++ b/rt1/step5/show_ipv6_route.ref
+@@ -32,18 +32,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -81,18 +70,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -130,18 +108,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000000..4e4a5692a4
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ip_route.ref.diff
@@ -0,0 +1,134 @@
+--- a/rt1/step5/show_ip_route.ref
++++ b/rt1/step6/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -36,7 +49,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -57,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -78,7 +117,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -99,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -120,7 +185,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..c9ebb1e3f5
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step6/show_ipv6_route.ref.diff
@@ -0,0 +1,122 @@
+--- a/rt1/step5/show_ipv6_route.ref
++++ b/rt1/step6/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -32,7 +43,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -51,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +103,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -89,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -108,7 +163,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ip_route.ref.diff
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step7/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ip_route.ref.diff
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step8/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000000..33eb6577bd
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step8/show_ip_route.ref
++++ b/rt1/step9/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +70,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +125,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000000..7aaca3354e
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step8/show_ipv6_route.ref
++++ b/rt1/step9/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +62,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +111,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis-rlfa-topo1/rt1/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt1/zebra.conf
new file mode 100644
index 0000000000..741fc2d02b
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt1/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt1
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.1/32
+ ipv6 address 2001:db8::1/128
+!
+interface eth-rt2
+ ip address 10.0.255.1/32
+!
+interface eth-rt3
+ ip address 10.0.255.1/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt2/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt2/isisd.conf
new file mode 100644
index 0000000000..7b4c6c50b9
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt2/isisd.conf
@@ -0,0 +1,32 @@
+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
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf
new file mode 100644
index 0000000000..0a815ef004
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt2/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt2
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.2
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.2
+ !
+ interface eth-rt1
+ interface eth-rt4
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::2
+ !
+ interface eth-rt1
+ interface eth-rt4
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt2/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..657c69bf28
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt2
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.2/32
+ ipv6 address 2001:db8::2/128
+!
+interface eth-rt1
+ ip address 10.0.255.2/32
+!
+interface eth-rt4
+ ip address 10.0.255.2/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt3/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt3/isisd.conf
new file mode 100644
index 0000000000..17d58a9d15
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt3/isisd.conf
@@ -0,0 +1,32 @@
+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
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf
new file mode 100644
index 0000000000..40f1f5587a
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt3/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt3
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.3
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.3
+ !
+ interface eth-rt1
+ interface eth-rt5
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::3
+ !
+ interface eth-rt1
+ interface eth-rt5
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt3/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..86f5d2871a
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt3
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.3/32
+ ipv6 address 2001:db8::3/128
+!
+interface eth-rt1
+ ip address 10.0.255.3/32
+!
+interface eth-rt5
+ ip address 10.0.255.3/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt4/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt4/isisd.conf
new file mode 100644
index 0000000000..1519fd4c16
--- /dev/null
+++ b/tests/topotests/isis-rlfa-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
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf
new file mode 100644
index 0000000000..569ecf733e
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt4/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt4
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.4
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.4
+ !
+ interface eth-rt2
+ interface eth-rt6
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::4
+ !
+ interface eth-rt2
+ interface eth-rt6
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt4/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..1dd09bf83b
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt4/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt4
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.4/32
+ ipv6 address 2001:db8::4/128
+!
+interface eth-rt2
+ ip address 10.0.255.4/32
+!
+interface eth-rt6
+ ip address 10.0.255.4/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt5/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt5/isisd.conf
new file mode 100644
index 0000000000..caf7477073
--- /dev/null
+++ b/tests/topotests/isis-rlfa-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
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf
new file mode 100644
index 0000000000..519c3d3628
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt5/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt5
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.5
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.5
+ !
+ interface eth-rt3
+ interface eth-rt7
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::5
+ !
+ interface eth-rt3
+ interface eth-rt7
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt5/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..7117a2a2e3
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt5/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt5
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.5/32
+ ipv6 address 2001:db8::5/128
+!
+interface eth-rt3
+ ip address 10.0.255.5/32
+!
+interface eth-rt7
+ ip address 10.0.255.5/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt6/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt6/isisd.conf
new file mode 100644
index 0000000000..cdf6267236
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt6/isisd.conf
@@ -0,0 +1,32 @@
+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
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf
new file mode 100644
index 0000000000..a5b7062bec
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt6/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt6
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.6
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.6
+ !
+ interface eth-rt4
+ interface eth-rt8
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::6
+ !
+ interface eth-rt4
+ interface eth-rt8
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt6/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt6/zebra.conf
new file mode 100644
index 0000000000..c6344870b7
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt6/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt6
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.6/32
+ ipv6 address 2001:db8::6/128
+!
+interface eth-rt4
+ ip address 10.0.255.6/32
+!
+interface eth-rt8
+ ip address 10.0.255.6/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt7/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt7/isisd.conf
new file mode 100644
index 0000000000..8ab8fcb232
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt7/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt7
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0007.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf
new file mode 100644
index 0000000000..26d428c4c6
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt7/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt7
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.7
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.7
+ !
+ interface eth-rt5
+ interface eth-rt8
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::7
+ !
+ interface eth-rt5
+ interface eth-rt8
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt7/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt7/zebra.conf
new file mode 100644
index 0000000000..4c5e0f1126
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt7/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt7
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.7/32
+ ipv6 address 2001:db8::7/128
+!
+interface eth-rt5
+ ip address 10.0.255.7/32
+!
+interface eth-rt8
+ ip address 10.0.255.7/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt8/isisd.conf b/tests/topotests/isis-rlfa-topo1/rt8/isisd.conf
new file mode 100644
index 0000000000..abdc6a53a5
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt8/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt8
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0008.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf b/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf
new file mode 100644
index 0000000000..1629f82de1
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt8/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt8
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.8
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.8
+ !
+ interface eth-rt6
+ interface eth-rt7
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::8
+ !
+ interface eth-rt6
+ interface eth-rt7
+ !
+ !
+!
diff --git a/tests/topotests/isis-rlfa-topo1/rt8/zebra.conf b/tests/topotests/isis-rlfa-topo1/rt8/zebra.conf
new file mode 100644
index 0000000000..f3f10f649a
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/rt8/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt8
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.8/32
+ ipv6 address 2001:db8::8/128
+!
+interface eth-rt6
+ ip address 10.0.255.8/32
+!
+interface eth-rt7
+ ip address 10.0.255.8/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py b/tests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py
new file mode 100755
index 0000000000..872fef8fdb
--- /dev/null
+++ b/tests/topotests/isis-rlfa-topo1/test_isis_rlfa_topo1.py
@@ -0,0 +1,662 @@
+#!/usr/bin/env python
+
+#
+# test_isis_rlfa_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_rlfa_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | RT1 | | RT2 |
+ | +---------------------+ |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT3 | | RT4 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT5 | | RT6 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT7 | | RT8 |
+ | +---------------------+ |
+ | | | |
+ +---------+ +---------+
+"""
+
+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", "rt8"]:
+ 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["rt1"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3")
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5")
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt8")
+ switch.add_link(tgen.gears["rt8"], nodeif="eth-rt6")
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt8")
+ switch.add_link(tgen.gears["rt8"], nodeif="eth-rt7")
+
+ #
+ # Populate multi-dimensional dictionary containing all expected outputs
+ #
+ files = [
+ "show_ip_route.ref",
+ "show_ipv6_route.ref",
+ "show_yang_interface_isis_adjacencies.ref",
+ ]
+ for rname in ["rt1"]:
+ outputs[rname] = {}
+ for step in range(1, 10 + 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 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))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.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"]:
+ 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_ipv4_step1():
+ logger.info("Test (step 1): verify IPv4 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"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][1]["show_ip_route.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"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Configure rt8 (rt1's PQ router) to not accept targeted hello messages
+#
+# Expected changes:
+# -All rt1 backup routes should be uninstalled
+#
+def test_rib_ipv4_step2():
+ logger.info("Test (step 2): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring rt8 to not accept targeted hello messages")
+ tgen.net["rt8"].cmd(
+ 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "no discovery targeted-hello accept"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][2]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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):
+# -Configure rt8 (rt1's PQ router) to accept targeted hello messages
+#
+# Expected changes:
+# -All rt1 previously uninstalled backup routes should be reinstalled
+#
+def test_rib_ipv4_step3():
+ logger.info("Test (step 3): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring rt8 to accept targeted hello messages")
+ tgen.net["rt8"].cmd(
+ 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "discovery targeted-hello accept"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][3]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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 RLFA on rt1's eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
+#
+def test_rib_ipv4_step4():
+ logger.info("Test (step 4): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling RLFA on rt1's eth-rt2 interface")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][4]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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):
+# -Disable RLFA on rt1's eth-rt3 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt3 should lose their backup nexthops
+#
+def test_rib_ipv4_step5():
+ logger.info("Test (step 5): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling RLFA on rt1's eth-rt3 interface")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][5]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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):
+# -Re-enable RLFA on rt1's eth-rt2 and eth-rt3 interfaces
+#
+# Expected changes:
+# -Revert changes from the previous two steps (reinstall all backup routes)
+#
+def test_rib_ipv4_step6():
+ logger.info("Test (step 6): verify IPv4 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 RLFA on rt1's eth-rt2 and eth-rt3 interfaces")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][6]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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 PQ node prefix-list filter
+#
+# Expected changes:
+# -All backup routes should be uninstalled
+#
+def test_rib_ipv4_step7():
+ logger.info("Test (step 7): verify IPv4 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 PQ node prefix-list filter")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute remote-lfa prefix-list PLIST"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][7]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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):
+# -Configure a prefix-list allowing rt8 as a PQ node
+#
+# Expected changes:
+# -All backup routes should be installed again
+#
+def test_rib_ipv4_step8():
+ logger.info("Test (step 8): verify IPv4 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-list allowing rt8 as a PQ node")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "ip prefix-list PLIST seq 5 permit 10.0.255.8/32"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][8]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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):
+# -Change the maximum metric up to the PQ node to 30 on the eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
+#
+def test_rib_ipv4_step9():
+ logger.info("Test (step 9): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Changing the maximum metric up to the PQ node to 30 on the eth-rt2 interface"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 30"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][9]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ 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):
+# -Change the maximum metric up to the PQ node to 40 on the eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should recover their backup nexthops
+#
+def test_rib_ipv4_step10():
+ logger.info("Test (step 10): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Changing the maximum metric up to the PQ node to 40 on the eth-rt2 interface"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 40"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][10]["show_ip_route.ref"]
+ )
+
+
+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)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][10]["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 d751a19f07..812dd4159d 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -248,6 +248,10 @@ module frr-isisd {
type string;
}
+ typedef prefix-list-ref {
+ type string;
+ }
+
grouping redistribute-attributes {
description
"Common optional attributes of any redistribute entry.";
@@ -410,6 +414,19 @@ module frr-isisd {
}
}
+ grouping global-config-remote-lfa {
+ container remote-lfa {
+ description
+ "Remote LFA configuration.";
+
+ leaf prefix-list {
+ type prefix-list-ref;
+ description
+ "Filter PQ node router ID based on prefix list.";
+ }
+ }
+ }
+
grouping interface-config-lfa {
container lfa {
description
@@ -428,6 +445,32 @@ module frr-isisd {
}
}
+ grouping interface-config-remote-lfa {
+ container remote-lfa {
+ description
+ "Remote LFA configuration.";
+
+ leaf enable {
+ type boolean;
+ default false;
+ description
+ "Enables remote LFA computation using LDP tunnels.";
+ must ". = 'false' or ../../lfa/enable = 'true'" {
+ error-message
+ "Remote LFA depends on classic LFA being configured in the interface.";
+ }
+
+ }
+ leaf maximum-metric {
+ type uint32 {
+ range "1..16777215";
+ }
+ description
+ "Limit remote LFA node selection within the metric.";
+ }
+ }
+ }
+
grouping interface-config-ti-lfa {
container ti-lfa {
description
@@ -761,6 +804,7 @@ module frr-isisd {
"Can't enable both classic LFA and TI-LFA in the same interface.";
}
uses interface-config-lfa;
+ uses interface-config-remote-lfa;
uses interface-config-ti-lfa;
}
container level-2 {
@@ -771,6 +815,7 @@ module frr-isisd {
"Can't enable both classic LFA and TI-LFA in the same interface.";
}
uses interface-config-lfa;
+ uses interface-config-remote-lfa;
uses interface-config-ti-lfa;
}
}
@@ -1394,11 +1439,13 @@ module frr-isisd {
description
"Level-1 IP Fast-reroute configuration.";
uses global-config-lfa;
+ uses global-config-remote-lfa;
}
container level-2 {
description
"Level-2 IP Fast-reroute configuration.";
uses global-config-lfa;
+ uses global-config-remote-lfa;
}
}