isisd: implement Remote LFA

Remote LFA (RFC 7490) is an extension to the base LFA mechanism
that uses dynamically determined tunnels to extend the IP-FRR
protection coverage.

RLFA is similar to TI-LFA in that it computes a post-convergence
SPT (with the protected interface pruned from the network topology)
and the P/Q spaces based on that SPT. There are a few differences
however:
* RLFAs can push at most one label, so the P/Q spaces need to
  intersect otherwise the destination can't be protected (the
  protection coverage is topology dependent).
* isisd needs to interface with ldpd to obtain the labels it needs to
  create a tunnel to the PQ node. That interaction needs to be done
  asynchronously to prevent blocking the daemon for too long. With
  TI-LFA all required labels are already available in the LSPDB.

RLFA and TI-LFA have more similarities than differences though,
and thanks to that both features share a lot of code.

Limitations:
* Only RLFA link protection is implemented. The algorithm used
  to find node-protecting RLFAs (RFC 8102) is too CPU intensive and
  doesn't always work. Most vendors implement RLFA link protection
  only.
* RFC 7490 says it should be a local matter whether the repair path
  selection policy favors LFA repairs over RLFA repairs. It might be
  desirable, for instance, to prefer RLFAs that satisfy the downstream
  condition over LFAs that don't. In this implementation, however,
  RLFAs are only computed for destinations that can't be protected
  by local LFAs.

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
Renato Westphal 2020-11-25 23:39:09 -03:00
parent 381200be9d
commit 16fe8cffa1
16 changed files with 890 additions and 113 deletions

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

@ -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);
@ -877,7 +885,7 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
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",
@ -1166,7 +1174,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 +1246,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 +1950,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 +2070,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 +2093,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 +2200,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 +2252,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 +2262,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 +2312,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

@ -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"
@ -1548,14 +1549,18 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
struct nb_cb_modify_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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;
}
@ -1563,14 +1568,16 @@ int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_destroy(
struct nb_cb_destroy_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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;
}
@ -1691,14 +1698,18 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
struct nb_cb_modify_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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;
}
@ -1706,14 +1717,16 @@ int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy(
struct nb_cb_destroy_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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;
}
@ -3510,15 +3523,24 @@ int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy(
int lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify(
struct nb_cb_modify_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
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;
}
@ -3529,14 +3551,17 @@ int lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify(
int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify(
struct nb_cb_modify_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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;
}
@ -3544,19 +3569,21 @@ int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify(
int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_destroy(
struct nb_cb_destroy_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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
@ -3687,15 +3714,24 @@ int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy(
int lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify(
struct nb_cb_modify_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
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;
}
@ -3706,14 +3742,17 @@ int lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify(
int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify(
struct nb_cb_modify_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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;
}
@ -3721,14 +3760,17 @@ int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify(
int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_destroy(
struct nb_cb_destroy_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
/* TODO: implement me. */
break;
}
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;
}

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);
}
@ -1429,6 +1437,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));
@ -1502,12 +1513,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)
@ -1520,7 +1532,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;
@ -1529,7 +1550,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;
@ -1544,6 +1566,7 @@ static void spf_path_process(struct isis_spftree *spftree,
spftree->lfa.protection_counters
.ecmp[priority] += 1;
}
break;
}
isis_route_create(
@ -1834,6 +1857,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);