Merge pull request #7707 from opensourcerouting/isisd-rlfa

isisd, ldpd: add Remote LFA support
This commit is contained in:
Olivier Dugeon 2021-01-12 19:25:15 +01:00 committed by GitHub
commit 4683138cda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 6402 additions and 62 deletions

View File

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

View File

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

View File

@ -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");
}
}
@ -1947,6 +1947,62 @@ void cli_show_isis_frr_lfa_load_sharing(struct vty *vty, struct lyd_node *dnode,
dnode->parent->parent->schema->name);
}
/*
* 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
*/
@ -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");
@ -2760,6 +2835,104 @@ void cli_show_frr_lfa_exclude_interface(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(dnode, NULL));
}
/*
* 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
*/
@ -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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -484,6 +484,14 @@ const struct frr_yang_module_info frr_isisd_info = {
.modify = isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify,
}
},
{
.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 = {
@ -513,6 +521,14 @@ const struct frr_yang_module_info frr_isisd_info = {
.modify = isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify,
}
},
{
.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 = {
@ -926,6 +942,20 @@ const struct frr_yang_module_info frr_isisd_info = {
.destroy = 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",
.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 = {
@ -952,6 +982,20 @@ const struct frr_yang_module_info frr_isisd_info = {
.destroy = 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",
.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 = {

View File

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

View File

@ -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"
@ -1551,6 +1552,45 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
return NB_OK;
}
/*
* 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
*/
@ -1661,6 +1701,45 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
return NB_OK;
}
/*
* 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
*/
@ -3446,6 +3525,74 @@ int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy(
return NB_OK;
}
/*
* 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
@ -3569,6 +3716,74 @@ int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy(
return NB_OK;
}
/*
* 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

View File

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

View File

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

View File

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

View File

@ -31,6 +31,7 @@ struct isis_spftree;
enum spf_type {
SPF_TYPE_FORWARD = 1,
SPF_TYPE_REVERSE,
SPF_TYPE_RLFA,
SPF_TYPE_TI_LFA,
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
@ -875,6 +916,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)
{
@ -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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

288
ldpd/rlfa.c Normal file
View File

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

78
ldpd/rlfa.h Normal file
View File

@ -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_ */

View File

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

View File

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

View File

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

View File

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

View File

@ -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"
@ -330,6 +423,14 @@ DEFUN(test_isis, test_isis_cmd,
else if (argv_find(argv, argc, "lfa", &idx)) {
test_type = TEST_LFA;
fail_sysid_str = argv[idx + 2]->arg;
if (argv_find(argv, argc, "pseudonode-id", &idx))
fail_pseudonode_id =
strtoul(argv[idx + 1]->arg, NULL, 10);
protection_type = LFA_LINK_PROTECTION;
} else if (argv_find(argv, argc, "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 =

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -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
}
]
}
]
}

View File

@ -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
}
]
}
]
}

View File

@ -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"
}
]
}
}
}
}
]
}
}

View File

@ -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":"*"
}
]
}

View File

@ -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":"*"
}
]
}

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

@ -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":"*"
}
]
}

View File

@ -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":"*"
}
]
}

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

@ -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":"*"
}
]
}

View File

@ -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":"*"
}
]
}

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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