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];
#include "vrf.h"
#include "table.h"
#include "srcdest_table.h"
+#include "plist.h"
+#include "zclient.h"
#include "isis_common.h"
#include "isisd.h"
#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)
{
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;
/*
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;
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);
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",
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. */
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,
}
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)
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;
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;
}
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];
}
/**
- * 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
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. */
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);
}
#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,
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,
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);
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
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")
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 */
#include "log.h"
#include "bfd.h"
#include "filter.h"
+#include "plist.h"
#include "spf_backoff.h"
#include "lib_errors.h"
#include "vrf.h"
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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
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;
}
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;
}
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;
}
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)
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 */
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];
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(
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);
}
}
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);
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 */
#include "isis_csm.h"
#include "isis_mt.h"
#include "isis_tlvs.h"
+#include "isis_zebra.h"
#include "fabricd.h"
#include "isis_spf_private.h"
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);
}
{
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);
}
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));
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)
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;
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;
spftree->lfa.protection_counters
.ecmp[priority] += 1;
}
+ break;
}
isis_route_create(
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
enum spf_type {
SPF_TYPE_FORWARD = 1,
SPF_TYPE_REVERSE,
+ SPF_TYPE_RLFA,
SPF_TYPE_TI_LFA,
};
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 */
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];
#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"
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 */
/**
static void isis_zebra_connected(struct zclient *zclient)
{
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ zclient_register_opaque(zclient, LDP_RLFA_LABELS);
}
/*
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;
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;
}
return -1;
isis_ldp_sync_handle_client_close(&info);
+ isis_ldp_rlfa_handle_client_close(&info);
return ret;
}
void isis_zebra_stop(void)
{
+ zclient_unregister_opaque(zclient, LDP_RLFA_LABELS);
zclient_stop(zclient_sync);
zclient_free(zclient_sync);
zclient_stop(zclient);
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);
#include "if.h"
#include "hash.h"
#include "filter.h"
+#include "plist.h"
#include "stream.h"
#include "prefix.h"
#include "table.h"
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);
}
}
+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)
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
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;
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);