mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-03 02:09:28 +00:00
isisd: add support for classic LFA (RFC 5286)
Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
parent
2d560b3d6d
commit
e886416f81
@ -175,6 +175,35 @@ ISIS Timer
|
||||
|
||||
Set minimum interval between consecutive SPF calculations in seconds.
|
||||
|
||||
.. _isis-fast-reroute:
|
||||
|
||||
ISIS Fast-Reroute
|
||||
=================
|
||||
|
||||
.. index:: spf prefix-priority [critical | high | medium] WORD
|
||||
.. clicmd:: spf prefix-priority [critical | high | medium] WORD
|
||||
|
||||
.. index:: spf prefix-priority [critical | high | medium] WORD
|
||||
.. clicmd:: no spf prefix-priority [critical | high | medium] [WORD]
|
||||
|
||||
Assign a priority to the prefixes that match the specified access-list.
|
||||
|
||||
.. index:: fast-reroute priority-limit [critical | high | medium] [level-1 | level-2]
|
||||
.. clicmd:: [no] fast-reroute priority-limit [critical | high | medium] [level-1 | level-2]
|
||||
|
||||
Limit LFA backup computation up to the specified prefix priority.
|
||||
|
||||
.. index:: fast-reroute lfa tiebreaker [downstream | lowest-backup-metric | node-protecting] index (1-255) [level-1 | level-2]
|
||||
.. clicmd:: [no] fast-reroute lfa tiebreaker [downstream | lowest-backup-metric | node-protecting] index (1-255) [level-1 | level-2]
|
||||
|
||||
Configure a tie-breaker for multiple LFA backups. Lower indexes are processed
|
||||
first.
|
||||
|
||||
.. index:: fast-reroute load-sharing disable [level-1 | level-2]
|
||||
.. clicmd:: [no] fast-reroute load-sharing disable [level-1 | level-2]
|
||||
|
||||
Disable load sharing across multiple LFA backups.
|
||||
|
||||
.. _isis-region:
|
||||
|
||||
ISIS region
|
||||
@ -356,6 +385,16 @@ ISIS interface
|
||||
Enable or disable :rfc:`5303` Three-Way Handshake for P2P adjacencies.
|
||||
Three-Way Handshake is enabled by default.
|
||||
|
||||
.. index:: isis fast-reroute lfa [level-1 | level-2]
|
||||
.. clicmd:: [no] isis fast-reroute lfa [level-1 | level-2]
|
||||
|
||||
Enable per-prefix LFA fast reroute link protection.
|
||||
|
||||
.. index:: isis fast-reroute lfa [level-1 | level-2] exclude interface IFNAME
|
||||
.. clicmd:: [no] isis fast-reroute lfa [level-1 | level-2] exclude interface IFNAME
|
||||
|
||||
Exclude an interface from the LFA backup nexthop computation.
|
||||
|
||||
.. index:: isis fast-reroute ti-lfa [level-1|level-2] [node-protection]
|
||||
.. clicmd:: [no] isis fast-reroute ti-lfa [level-1|level-2] [node-protection]
|
||||
|
||||
|
@ -140,6 +140,8 @@ struct isis_circuit *isis_circuit_new(struct isis *isis)
|
||||
#endif /* ifndef FABRICD */
|
||||
|
||||
circuit_mt_init(circuit);
|
||||
isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL1);
|
||||
isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL2);
|
||||
|
||||
QOBJ_REG(circuit, isis_circuit);
|
||||
|
||||
@ -156,6 +158,8 @@ void isis_circuit_del(struct isis_circuit *circuit)
|
||||
isis_circuit_if_unbind(circuit, circuit->interface);
|
||||
|
||||
circuit_mt_finish(circuit);
|
||||
isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL1);
|
||||
isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL2);
|
||||
|
||||
/* and lastly the circuit itself */
|
||||
XFREE(MTYPE_ISIS_CIRCUIT, circuit);
|
||||
|
@ -141,6 +141,8 @@ struct isis_circuit {
|
||||
bool disable_threeway_adj;
|
||||
struct bfd_info *bfd_info;
|
||||
struct ldp_sync_info *ldp_sync_info;
|
||||
bool lfa_protection[ISIS_LEVELS];
|
||||
struct hash *lfa_excluded_ifaces[ISIS_LEVELS];
|
||||
bool tilfa_protection[ISIS_LEVELS];
|
||||
bool tilfa_node_protection[ISIS_LEVELS];
|
||||
/*
|
||||
|
792
isisd/isis_lfa.c
792
isisd/isis_lfa.c
@ -40,6 +40,8 @@
|
||||
#include "isisd/isis_errors.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_NODE, "ISIS SPF Node");
|
||||
DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_TIEBREAKER, "ISIS LFA Tiebreaker");
|
||||
DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_EXCL_IFACE, "ISIS LFA Excluded Interface");
|
||||
|
||||
static inline int isis_spf_node_compare(const struct isis_spf_node *a,
|
||||
const struct isis_spf_node *b)
|
||||
@ -119,6 +121,185 @@ struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes,
|
||||
return RB_FIND(isis_spf_nodes, nodes, &node);
|
||||
}
|
||||
|
||||
/**
|
||||
* LFA tiebreaker RB-tree comparison function.
|
||||
*
|
||||
* @param a First LFA tiebreaker
|
||||
* @param b Second LFA tiebreaker
|
||||
*
|
||||
* @return -1 (a < b), 0 (a == b) or +1 (a > b)
|
||||
*/
|
||||
int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
|
||||
const struct lfa_tiebreaker *b)
|
||||
{
|
||||
if (a->index < b->index)
|
||||
return -1;
|
||||
if (a->index > b->index)
|
||||
return 1;
|
||||
|
||||
return a->type - b->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize list of LFA tie-breakers.
|
||||
*
|
||||
* @param area IS-IS area
|
||||
* @param level IS-IS level
|
||||
*/
|
||||
void isis_lfa_tiebreakers_init(struct isis_area *area, int level)
|
||||
{
|
||||
lfa_tiebreaker_tree_init(&area->lfa_tiebreakers[level - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear list of LFA tie-breakers, releasing all allocated memory.
|
||||
*
|
||||
* @param area IS-IS area
|
||||
* @param level IS-IS level
|
||||
*/
|
||||
void isis_lfa_tiebreakers_clear(struct isis_area *area, int level)
|
||||
{
|
||||
while (lfa_tiebreaker_tree_count(&area->lfa_tiebreakers[level - 1])
|
||||
> 0) {
|
||||
struct lfa_tiebreaker *tie_b;
|
||||
|
||||
tie_b = lfa_tiebreaker_tree_first(
|
||||
&area->lfa_tiebreakers[level - 1]);
|
||||
isis_lfa_tiebreaker_delete(area, level, tie_b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new LFA tie-breaker to list of LFA tie-breakers.
|
||||
*
|
||||
* @param area IS-IS area
|
||||
* @param level IS-IS level
|
||||
* @param index LFA tie-breaker index
|
||||
* @param type LFA tie-breaker type
|
||||
*
|
||||
* @return Pointer to new LFA tie-breaker structure.
|
||||
*/
|
||||
struct lfa_tiebreaker *isis_lfa_tiebreaker_add(struct isis_area *area,
|
||||
int level, uint8_t index,
|
||||
enum lfa_tiebreaker_type type)
|
||||
{
|
||||
struct lfa_tiebreaker *tie_b;
|
||||
|
||||
tie_b = XCALLOC(MTYPE_ISIS_LFA_TIEBREAKER, sizeof(*tie_b));
|
||||
tie_b->index = index;
|
||||
tie_b->type = type;
|
||||
tie_b->area = area;
|
||||
lfa_tiebreaker_tree_add(&area->lfa_tiebreakers[level - 1], tie_b);
|
||||
|
||||
return tie_b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove LFA tie-breaker from list of LFA tie-breakers.
|
||||
*
|
||||
* @param area IS-IS area
|
||||
* @param level IS-IS level
|
||||
* @param tie_b Pointer to LFA tie-breaker structure
|
||||
*/
|
||||
void isis_lfa_tiebreaker_delete(struct isis_area *area, int level,
|
||||
struct lfa_tiebreaker *tie_b)
|
||||
{
|
||||
lfa_tiebreaker_tree_del(&area->lfa_tiebreakers[level - 1], tie_b);
|
||||
XFREE(MTYPE_ISIS_LFA_TIEBREAKER, tie_b);
|
||||
}
|
||||
|
||||
static bool lfa_excl_interface_hash_cmp(const void *value1, const void *value2)
|
||||
{
|
||||
return strmatch(value1, value2);
|
||||
}
|
||||
|
||||
static unsigned int lfa_excl_interface_hash_make(const void *value)
|
||||
{
|
||||
return string_hash_make(value);
|
||||
}
|
||||
|
||||
static void *lfa_excl_interface_hash_alloc(void *p)
|
||||
{
|
||||
return XSTRDUP(MTYPE_ISIS_LFA_EXCL_IFACE, p);
|
||||
}
|
||||
|
||||
static void lfa_excl_interface_hash_free(void *arg)
|
||||
{
|
||||
XFREE(MTYPE_ISIS_LFA_EXCL_IFACE, arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize hash table of LFA excluded interfaces.
|
||||
*
|
||||
* @param circuit IS-IS interface
|
||||
* @param level IS-IS level
|
||||
*/
|
||||
void isis_lfa_excluded_ifaces_init(struct isis_circuit *circuit, int level)
|
||||
{
|
||||
circuit->lfa_excluded_ifaces[level - 1] = hash_create(
|
||||
lfa_excl_interface_hash_make, lfa_excl_interface_hash_cmp,
|
||||
"LFA Excluded Interfaces");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear hash table of LFA excluded interfaces, releasing all allocated memory.
|
||||
*
|
||||
* @param nodes List of SPF nodes
|
||||
*/
|
||||
void isis_lfa_excluded_ifaces_clear(struct isis_circuit *circuit, int level)
|
||||
{
|
||||
hash_clean(circuit->lfa_excluded_ifaces[level - 1],
|
||||
lfa_excl_interface_hash_free);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new interface to hash table of excluded interfaces.
|
||||
*
|
||||
* @param circuit IS-IS interface
|
||||
* @param level IS-IS level
|
||||
* @param ifname Excluded interface name
|
||||
*/
|
||||
void isis_lfa_excluded_iface_add(struct isis_circuit *circuit, int level,
|
||||
const char *ifname)
|
||||
{
|
||||
hash_get(circuit->lfa_excluded_ifaces[level - 1], (char *)ifname,
|
||||
lfa_excl_interface_hash_alloc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove interface from hash table of excluded interfaces.
|
||||
*
|
||||
* @param circuit IS-IS interface
|
||||
* @param level IS-IS level
|
||||
* @param ifname Excluded interface name
|
||||
*/
|
||||
void isis_lfa_excluded_iface_delete(struct isis_circuit *circuit, int level,
|
||||
const char *ifname)
|
||||
{
|
||||
char *found;
|
||||
|
||||
found = hash_lookup(circuit->lfa_excluded_ifaces[level - 1],
|
||||
(char *)ifname);
|
||||
if (found) {
|
||||
hash_release(circuit->lfa_excluded_ifaces[level - 1], found);
|
||||
lfa_excl_interface_hash_free(found);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup excluded interface.
|
||||
*
|
||||
* @param circuit IS-IS interface
|
||||
* @param level IS-IS level
|
||||
* @param ifname Excluded interface name
|
||||
*/
|
||||
bool isis_lfa_excluded_iface_check(struct isis_circuit *circuit, int level,
|
||||
const char *ifname)
|
||||
{
|
||||
return hash_lookup(circuit->lfa_excluded_ifaces[level - 1],
|
||||
(char *)ifname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given IS-IS adjacency needs to be excised when computing the SPF
|
||||
* post-convergence tree.
|
||||
@ -533,8 +714,9 @@ static int tilfa_build_repair_list(struct isis_spftree *spftree_pc,
|
||||
listnode_add_head(repair_list, &sid_pnode);
|
||||
|
||||
/* Apply repair list. */
|
||||
if (listcount(repair_list)
|
||||
> spftree_pc->area->srdb.config.msd) {
|
||||
if (spftree_pc->area->srdb.config.msd
|
||||
&& listcount(repair_list)
|
||||
> spftree_pc->area->srdb.config.msd) {
|
||||
zlog_warn(
|
||||
"ISIS-LFA: list of repair segments exceeds locally configured MSD (%u > %u)",
|
||||
listcount(repair_list),
|
||||
@ -618,19 +800,44 @@ spf_adj_check_is_affected(const struct isis_spf_adj *sadj,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the given SPF vertex needs LFA protection. */
|
||||
static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
|
||||
const struct isis_vertex *vertex)
|
||||
/* Check if the given vertex is affected by a given local failure. */
|
||||
static bool
|
||||
spf_vertex_check_is_affected(const struct isis_vertex *vertex,
|
||||
const uint8_t *root_sysid,
|
||||
const struct lfa_protected_resource *resource)
|
||||
{
|
||||
struct isis_vertex *vertex_old;
|
||||
struct isis_vertex_adj *vadj;
|
||||
struct listnode *node;
|
||||
size_t affected_nhs = 0;
|
||||
struct isis_vertex_adj *vadj;
|
||||
|
||||
/* Local routes don't need protection. */
|
||||
if (VTYPE_IP(vertex->type) && vertex->depth == 1)
|
||||
return false;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, vadj)) {
|
||||
struct isis_spf_adj *sadj = vadj->sadj;
|
||||
|
||||
if (spf_adj_check_is_affected(sadj, resource, root_sysid,
|
||||
false))
|
||||
affected_nhs++;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to compute backup paths for ECMP routes, except if all
|
||||
* primary nexthops share the same broadcast interface.
|
||||
*/
|
||||
if (listcount(vertex->Adj_N) == affected_nhs)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if a given TI-LFA post-convergence SPF vertex needs protection. */
|
||||
static bool tilfa_check_needs_protection(const struct isis_spftree *spftree_pc,
|
||||
const struct isis_vertex *vertex)
|
||||
{
|
||||
struct isis_vertex *vertex_old;
|
||||
|
||||
/* Only local adjacencies need Adj-SID protection. */
|
||||
if (VTYPE_IS(vertex->type)
|
||||
&& !isis_adj_find(spftree_pc->area, spftree_pc->level,
|
||||
@ -642,23 +849,9 @@ static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
|
||||
if (!vertex_old)
|
||||
return false;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(vertex_old->Adj_N, node, vadj)) {
|
||||
struct isis_spf_adj *sadj = vadj->sadj;
|
||||
|
||||
if (spf_adj_check_is_affected(
|
||||
sadj, &spftree_pc->lfa.protected_resource,
|
||||
spftree_pc->sysid, false))
|
||||
affected_nhs++;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to compute backup paths for ECMP routes, except if all
|
||||
* primary nexthops share the same broadcast interface.
|
||||
*/
|
||||
if (listcount(vertex_old->Adj_N) == affected_nhs)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return spf_vertex_check_is_affected(
|
||||
vertex_old, spftree_pc->sysid,
|
||||
&spftree_pc->lfa.protected_resource);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -670,7 +863,8 @@ static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
|
||||
*
|
||||
* @return 0 if the vertex needs to be protected, -1 otherwise
|
||||
*/
|
||||
int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
|
||||
int isis_tilfa_check(struct isis_spftree *spftree_pc,
|
||||
struct isis_vertex *vertex)
|
||||
{
|
||||
struct isis_spf_nodes used_pnodes;
|
||||
char buf[VID2STR_BUFFER];
|
||||
@ -683,7 +877,7 @@ int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
|
||||
if (IS_DEBUG_LFA)
|
||||
vid2string(vertex, buf, sizeof(buf));
|
||||
|
||||
if (!lfa_check_needs_protection(spftree_pc, vertex)) {
|
||||
if (!tilfa_check_needs_protection(spftree_pc, vertex)) {
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: %s %s unaffected by %s",
|
||||
@ -1044,20 +1238,530 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate the distance from the root node to the given IP destination. */
|
||||
static int lfa_calc_dist_destination(struct isis_spftree *spftree,
|
||||
const struct isis_vertex *vertex_N,
|
||||
uint32_t *distance)
|
||||
{
|
||||
struct isis_vertex *vertex, *vertex_best = NULL;
|
||||
|
||||
switch (spftree->family) {
|
||||
case AF_INET:
|
||||
for (int vtype = VTYPE_IPREACH_INTERNAL;
|
||||
vtype <= VTYPE_IPREACH_TE; vtype++) {
|
||||
vertex = isis_find_vertex(
|
||||
&spftree->paths, &vertex_N->N.ip.p.dest, vtype);
|
||||
if (!vertex)
|
||||
continue;
|
||||
|
||||
/* Pick vertex with the best metric. */
|
||||
if (!vertex_best || vertex_best->d_N > vertex->d_N)
|
||||
vertex_best = vertex;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
for (int vtype = VTYPE_IP6REACH_INTERNAL;
|
||||
vtype <= VTYPE_IP6REACH_EXTERNAL; vtype++) {
|
||||
vertex = isis_find_vertex(
|
||||
&spftree->paths, &vertex_N->N.ip.p.dest, vtype);
|
||||
if (!vertex)
|
||||
continue;
|
||||
|
||||
/* Pick vertex with the best metric. */
|
||||
if (!vertex_best || vertex_best->d_N > vertex->d_N)
|
||||
vertex_best = vertex;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!vertex_best)
|
||||
return -1;
|
||||
|
||||
assert(VTYPE_IP(vertex_best->type));
|
||||
vertex_best = listnode_head(vertex_best->parents);
|
||||
*distance = vertex_best->d_N;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate the distance from the root node to the given node. */
|
||||
static int lfa_calc_dist_node(struct isis_spftree *spftree,
|
||||
const uint8_t *sysid, uint32_t *distance)
|
||||
{
|
||||
struct isis_vertex *vertex, *vertex_best = NULL;
|
||||
|
||||
for (int vtype = VTYPE_PSEUDO_IS; vtype <= VTYPE_NONPSEUDO_TE_IS;
|
||||
vtype++) {
|
||||
vertex = isis_find_vertex(&spftree->paths, sysid, vtype);
|
||||
if (!vertex)
|
||||
continue;
|
||||
|
||||
/* Pick vertex with the best metric. */
|
||||
if (!vertex_best || vertex_best->d_N > vertex->d_N)
|
||||
vertex_best = vertex;
|
||||
}
|
||||
|
||||
if (!vertex_best)
|
||||
return -1;
|
||||
|
||||
*distance = vertex_best->d_N;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check loop-free criterion (RFC 5286's inequality 1):
|
||||
* - Dist_opt(N, D) < Dist_opt(N, S) + Dist_opt(S, D)
|
||||
*/
|
||||
static bool clfa_loop_free_check(struct isis_spftree *spftree,
|
||||
struct isis_vertex *vertex_S_D,
|
||||
struct isis_spf_adj *sadj_primary,
|
||||
struct isis_spf_adj *sadj_N,
|
||||
uint32_t *lfa_metric)
|
||||
{
|
||||
struct isis_spf_node *node_N;
|
||||
uint32_t dist_N_D;
|
||||
uint32_t dist_N_S;
|
||||
uint32_t dist_S_D;
|
||||
|
||||
node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
|
||||
assert(node_N);
|
||||
|
||||
/* Distance from N to D. */
|
||||
if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
|
||||
&dist_N_D)
|
||||
!= 0)
|
||||
return false;
|
||||
|
||||
/* Distance from N to S (or PN). */
|
||||
if (CHECK_FLAG(sadj_primary->flags, F_ISIS_SPF_ADJ_BROADCAST)) {
|
||||
static uint8_t pn_sysid[ISIS_SYS_ID_LEN + 1];
|
||||
|
||||
memcpy(pn_sysid, sadj_primary->id, ISIS_SYS_ID_LEN + 1);
|
||||
if (lfa_calc_dist_node(node_N->lfa.spftree, pn_sysid, &dist_N_S)
|
||||
!= 0)
|
||||
return false;
|
||||
} else {
|
||||
static uint8_t root_sysid[ISIS_SYS_ID_LEN + 1];
|
||||
|
||||
memcpy(root_sysid, spftree->sysid, ISIS_SYS_ID_LEN);
|
||||
LSP_PSEUDO_ID(root_sysid) = 0;
|
||||
if (lfa_calc_dist_node(node_N->lfa.spftree, root_sysid,
|
||||
&dist_N_S)
|
||||
!= 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Distance from S (or PN) to D. */
|
||||
vertex_S_D = listnode_head(vertex_S_D->parents);
|
||||
dist_S_D = vertex_S_D->d_N;
|
||||
if (CHECK_FLAG(sadj_primary->flags, F_ISIS_SPF_ADJ_BROADCAST))
|
||||
dist_S_D -= sadj_primary->metric;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug("ISIS-LFA: loop-free check: %u < %u + %u", dist_N_D,
|
||||
dist_N_S, dist_S_D);
|
||||
|
||||
if (dist_N_D < (dist_N_S + dist_S_D)) {
|
||||
*lfa_metric = sadj_N->metric + dist_N_D;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check loop-free criterion (RFC 5286's inequality 2):
|
||||
* - Distance_opt(N, D) < Distance_opt(S, D)
|
||||
*/
|
||||
static bool clfa_downstream_check(struct isis_spftree *spftree,
|
||||
struct isis_vertex *vertex_S_D,
|
||||
struct isis_spf_adj *sadj_N)
|
||||
{
|
||||
struct isis_spf_node *node_N;
|
||||
uint32_t dist_N_D;
|
||||
uint32_t dist_S_D;
|
||||
|
||||
node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
|
||||
assert(node_N);
|
||||
|
||||
/* Distance from N to D. */
|
||||
if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
|
||||
&dist_N_D)
|
||||
!= 0)
|
||||
return false;
|
||||
|
||||
/* Distance from S (or PN) to D. */
|
||||
vertex_S_D = listnode_head(vertex_S_D->parents);
|
||||
dist_S_D = vertex_S_D->d_N;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug("ISIS-LFA: downstream check: %u < %u", dist_N_D,
|
||||
dist_S_D);
|
||||
|
||||
if (dist_N_D < dist_S_D)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check loop-free criterion (RFC 5286's inequality 3):
|
||||
* - Dist_opt(N, D) < Dist_opt(N, E) + Dist_opt(E, D)
|
||||
*/
|
||||
static bool clfa_node_protecting_check(struct isis_spftree *spftree,
|
||||
struct isis_vertex *vertex_S_D,
|
||||
struct isis_spf_adj *sadj_N,
|
||||
struct isis_spf_adj *sadj_E)
|
||||
{
|
||||
struct isis_spf_node *node_N, *node_E;
|
||||
uint32_t dist_N_D;
|
||||
uint32_t dist_N_E;
|
||||
uint32_t dist_E_D;
|
||||
|
||||
node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
|
||||
assert(node_N);
|
||||
node_E = isis_spf_node_find(&spftree->adj_nodes, sadj_E->id);
|
||||
assert(node_E);
|
||||
|
||||
/* Distance from N to D. */
|
||||
if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
|
||||
&dist_N_D)
|
||||
!= 0)
|
||||
return false;
|
||||
|
||||
/* Distance from N to E. */
|
||||
if (lfa_calc_dist_node(node_N->lfa.spftree, node_E->sysid, &dist_N_E)
|
||||
!= 0)
|
||||
return false;
|
||||
|
||||
/* Distance from E to D. */
|
||||
if (lfa_calc_dist_destination(node_E->lfa.spftree, vertex_S_D,
|
||||
&dist_E_D)
|
||||
!= 0)
|
||||
return false;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug("ISIS-LFA: node protecting check: %u < %u + %u",
|
||||
dist_N_D, dist_N_E, dist_E_D);
|
||||
|
||||
return (dist_N_D < (dist_N_E + dist_E_D));
|
||||
}
|
||||
|
||||
static struct list *
|
||||
isis_lfa_tiebreakers(struct isis_area *area, struct isis_circuit *circuit,
|
||||
struct isis_spftree *spftree,
|
||||
struct lfa_protected_resource *resource,
|
||||
struct isis_vertex *vertex,
|
||||
struct isis_spf_adj *sadj_primary, struct list *lfa_list)
|
||||
{
|
||||
struct lfa_tiebreaker *tie_b;
|
||||
int level = spftree->level;
|
||||
struct list *filtered_lfa_list;
|
||||
struct list *tent_lfa_list;
|
||||
|
||||
filtered_lfa_list = list_dup(lfa_list);
|
||||
filtered_lfa_list->del = NULL;
|
||||
|
||||
if (listcount(filtered_lfa_list) == 1)
|
||||
return filtered_lfa_list;
|
||||
|
||||
/* Check tiebreakers in ascending order by index. */
|
||||
frr_each (lfa_tiebreaker_tree, &area->lfa_tiebreakers[level - 1],
|
||||
tie_b) {
|
||||
struct isis_vertex_adj *lfa;
|
||||
struct listnode *node, *nnode;
|
||||
uint32_t best_metric = UINT32_MAX;
|
||||
|
||||
tent_lfa_list = list_dup(filtered_lfa_list);
|
||||
|
||||
switch (tie_b->type) {
|
||||
case LFA_TIEBREAKER_DOWNSTREAM:
|
||||
for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
|
||||
lfa)) {
|
||||
if (clfa_downstream_check(spftree, vertex,
|
||||
lfa->sadj))
|
||||
continue;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: LFA %s doesn't satisfy the downstream condition",
|
||||
print_sys_hostname(
|
||||
lfa->sadj->id));
|
||||
listnode_delete(tent_lfa_list, lfa);
|
||||
}
|
||||
break;
|
||||
case LFA_TIEBREAKER_LOWEST_METRIC:
|
||||
/* Find the best metric first. */
|
||||
for (ALL_LIST_ELEMENTS_RO(tent_lfa_list, node, lfa)) {
|
||||
if (lfa->lfa_metric < best_metric)
|
||||
best_metric = lfa->lfa_metric;
|
||||
}
|
||||
|
||||
/* Remove LFAs that don't have the best metric. */
|
||||
for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
|
||||
lfa)) {
|
||||
if (lfa->lfa_metric == best_metric)
|
||||
continue;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: LFA %s doesn't have the lowest cost metric",
|
||||
print_sys_hostname(
|
||||
lfa->sadj->id));
|
||||
listnode_delete(tent_lfa_list, lfa);
|
||||
}
|
||||
break;
|
||||
case LFA_TIEBREAKER_NODE_PROTECTING:
|
||||
for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
|
||||
lfa)) {
|
||||
if (clfa_node_protecting_check(spftree, vertex,
|
||||
lfa->sadj,
|
||||
sadj_primary))
|
||||
continue;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: LFA %s doesn't provide node protection",
|
||||
print_sys_hostname(
|
||||
lfa->sadj->id));
|
||||
listnode_delete(tent_lfa_list, lfa);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide what to do next based on the number of remaining LFAs.
|
||||
*/
|
||||
switch (listcount(tent_lfa_list)) {
|
||||
case 0:
|
||||
/*
|
||||
* Ignore this tie-breaker since it excluded all LFAs.
|
||||
* Move on to the next one (if any).
|
||||
*/
|
||||
list_delete(&tent_lfa_list);
|
||||
break;
|
||||
case 1:
|
||||
/* Finish tie-breaking once we get a single LFA. */
|
||||
list_delete(&filtered_lfa_list);
|
||||
filtered_lfa_list = tent_lfa_list;
|
||||
return filtered_lfa_list;
|
||||
default:
|
||||
/*
|
||||
* We still have two or more LFAs. Move on to the next
|
||||
* tie-breaker (if any).
|
||||
*/
|
||||
list_delete(&filtered_lfa_list);
|
||||
filtered_lfa_list = tent_lfa_list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return filtered_lfa_list;
|
||||
}
|
||||
|
||||
void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
|
||||
struct isis_spftree *spftree,
|
||||
struct lfa_protected_resource *resource)
|
||||
{
|
||||
struct isis_vertex *vertex;
|
||||
struct listnode *vnode, *snode;
|
||||
int level = spftree->level;
|
||||
|
||||
resource->type = LFA_LINK_PROTECTION;
|
||||
|
||||
for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, vnode, vertex)) {
|
||||
struct list *lfa_list;
|
||||
struct list *filtered_lfa_list;
|
||||
struct isis_spf_adj *sadj_N;
|
||||
struct isis_vertex_adj *vadj_primary;
|
||||
struct isis_spf_adj *sadj_primary;
|
||||
bool allow_ecmp;
|
||||
uint32_t best_metric = UINT32_MAX;
|
||||
char buf[VID2STR_BUFFER];
|
||||
|
||||
if (!VTYPE_IP(vertex->type))
|
||||
continue;
|
||||
|
||||
vid2string(vertex, buf, sizeof(buf));
|
||||
|
||||
if (!spf_vertex_check_is_affected(vertex, spftree->sysid,
|
||||
resource)) {
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: route unaffected by %s",
|
||||
lfa_protected_resource2str(resource));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug("ISIS-LFA: checking %s %s w.r.t %s",
|
||||
vtype2string(vertex->type), buf,
|
||||
lfa_protected_resource2str(resource));
|
||||
|
||||
if (vertex->N.ip.priority
|
||||
> area->lfa_priority_limit[level - 1]) {
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: skipping computing LFAs due to low prefix priority");
|
||||
continue;
|
||||
}
|
||||
|
||||
vadj_primary = listnode_head(vertex->Adj_N);
|
||||
sadj_primary = vadj_primary->sadj;
|
||||
|
||||
/*
|
||||
* Loop over list of SPF adjacencies and compute a list of
|
||||
* preliminary LFAs.
|
||||
*/
|
||||
lfa_list = list_new();
|
||||
lfa_list->del = isis_vertex_adj_free;
|
||||
for (ALL_LIST_ELEMENTS_RO(spftree->sadj_list, snode, sadj_N)) {
|
||||
uint32_t lfa_metric;
|
||||
struct isis_vertex_adj *lfa;
|
||||
struct isis_prefix_sid *psid = NULL;
|
||||
bool last_hop = false;
|
||||
|
||||
/* Skip pseudonodes. */
|
||||
if (LSP_PSEUDO_ID(sadj_N->id))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Skip nexthops that are along a link whose cost is
|
||||
* infinite.
|
||||
*/
|
||||
if (CHECK_FLAG(sadj_N->flags,
|
||||
F_ISIS_SPF_ADJ_METRIC_INFINITY))
|
||||
continue;
|
||||
|
||||
/* Skip nexthops that have the overload bit set. */
|
||||
if (spftree->mtid != ISIS_MT_IPV4_UNICAST) {
|
||||
struct isis_mt_router_info *mt_router_info;
|
||||
|
||||
mt_router_info =
|
||||
isis_tlvs_lookup_mt_router_info(
|
||||
sadj_N->lsp->tlvs,
|
||||
spftree->mtid);
|
||||
if (mt_router_info && mt_router_info->overload)
|
||||
continue;
|
||||
} else if (ISIS_MASK_LSP_OL_BIT(
|
||||
sadj_N->lsp->hdr.lsp_bits))
|
||||
continue;
|
||||
|
||||
/* Skip primary nexthop. */
|
||||
if (spf_adj_check_is_affected(sadj_N, resource, NULL,
|
||||
false))
|
||||
continue;
|
||||
|
||||
/* Skip excluded interfaces as per the configuration. */
|
||||
if (circuit
|
||||
&& isis_lfa_excluded_iface_check(
|
||||
circuit, level,
|
||||
sadj_N->adj->circuit->interface->name))
|
||||
continue;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: checking candidate LFA %s",
|
||||
print_sys_hostname(sadj_N->id));
|
||||
|
||||
/* Check loop-free criterion. */
|
||||
if (!clfa_loop_free_check(spftree, vertex, sadj_primary,
|
||||
sadj_N, &lfa_metric)) {
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: LFA condition not met for %s",
|
||||
print_sys_hostname(sadj_N->id));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lfa_metric < best_metric)
|
||||
best_metric = lfa_metric;
|
||||
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: %s is a valid loop-free alternate",
|
||||
print_sys_hostname(sadj_N->id));
|
||||
|
||||
if (vertex->N.ip.sr.present) {
|
||||
psid = &vertex->N.ip.sr.sid;
|
||||
if (lfa_metric == sadj_N->metric)
|
||||
last_hop = true;
|
||||
}
|
||||
lfa = isis_vertex_adj_add(spftree, vertex, lfa_list,
|
||||
sadj_N, psid, last_hop);
|
||||
lfa->lfa_metric = lfa_metric;
|
||||
}
|
||||
|
||||
if (list_isempty(lfa_list)) {
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug("ISIS-LFA: no valid LFAs found");
|
||||
list_delete(&lfa_list);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check tie-breakers. */
|
||||
filtered_lfa_list =
|
||||
isis_lfa_tiebreakers(area, circuit, spftree, resource,
|
||||
vertex, sadj_primary, lfa_list);
|
||||
|
||||
/* Create backup route using the best LFAs. */
|
||||
allow_ecmp = area->lfa_load_sharing[level - 1];
|
||||
isis_route_create(&vertex->N.ip.p.dest, &vertex->N.ip.p.src,
|
||||
best_metric, vertex->depth, &vertex->N.ip.sr,
|
||||
filtered_lfa_list, allow_ecmp, area,
|
||||
spftree->route_table_backup);
|
||||
|
||||
list_delete(&filtered_lfa_list);
|
||||
list_delete(&lfa_list);
|
||||
}
|
||||
}
|
||||
|
||||
static void isis_spf_run_tilfa(struct isis_area *area,
|
||||
struct isis_circuit *circuit,
|
||||
struct isis_spftree *spftree,
|
||||
struct isis_spftree *spftree_reverse,
|
||||
struct lfa_protected_resource *resource)
|
||||
{
|
||||
struct isis_spftree *spftree_pc_link;
|
||||
struct isis_spftree *spftree_pc_node;
|
||||
|
||||
/* Compute node protecting repair paths first (if necessary). */
|
||||
if (circuit->tilfa_node_protection[spftree->level - 1]) {
|
||||
resource->type = LFA_NODE_PROTECTION;
|
||||
spftree_pc_node = isis_tilfa_compute(area, spftree,
|
||||
spftree_reverse, resource);
|
||||
isis_spftree_del(spftree_pc_node);
|
||||
}
|
||||
|
||||
/* Compute link protecting repair paths. */
|
||||
resource->type = LFA_LINK_PROTECTION;
|
||||
spftree_pc_link =
|
||||
isis_tilfa_compute(area, spftree, spftree_reverse, resource);
|
||||
isis_spftree_del(spftree_pc_link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the TI-LFA algorithm for all proctected interfaces.
|
||||
* Run the LFA/TI-LFA algorithms for all protected interfaces.
|
||||
*
|
||||
* @param area IS-IS area
|
||||
* @param spftree IS-IS SPF tree
|
||||
*/
|
||||
void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
|
||||
{
|
||||
struct isis_spftree *spftree_reverse;
|
||||
struct isis_spftree *spftree_reverse = NULL;
|
||||
struct isis_circuit *circuit;
|
||||
struct listnode *node;
|
||||
bool tilfa_configured;
|
||||
int level = spftree->level;
|
||||
|
||||
tilfa_configured = (area->tilfa_protected_links[level - 1] > 0);
|
||||
|
||||
/* Run reverse SPF locally. */
|
||||
spftree_reverse = isis_spf_reverse_run(spftree);
|
||||
if (tilfa_configured)
|
||||
spftree_reverse = isis_spf_reverse_run(spftree);
|
||||
|
||||
/* Run forward SPF on all adjacent routers. */
|
||||
isis_spf_run_neighbors(spftree);
|
||||
@ -1066,20 +1770,19 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
|
||||
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
|
||||
struct lfa_protected_resource resource = {};
|
||||
struct isis_adjacency *adj;
|
||||
struct isis_spftree *spftree_pc_link;
|
||||
struct isis_spftree *spftree_pc_node;
|
||||
static uint8_t null_sysid[ISIS_SYS_ID_LEN + 1];
|
||||
|
||||
if (!(circuit->is_type & spftree->level))
|
||||
if (!(circuit->is_type & level))
|
||||
continue;
|
||||
|
||||
if (!circuit->tilfa_protection[spftree->level - 1])
|
||||
if (!circuit->lfa_protection[level - 1]
|
||||
&& !circuit->tilfa_protection[level - 1])
|
||||
continue;
|
||||
|
||||
/* Fill in the protected resource. */
|
||||
switch (circuit->circ_type) {
|
||||
case CIRCUIT_T_BROADCAST:
|
||||
if (spftree->level == 1)
|
||||
if (level == ISIS_LEVEL1)
|
||||
memcpy(resource.adjacency,
|
||||
circuit->u.bc.l1_desig_is,
|
||||
ISIS_SYS_ID_LEN + 1);
|
||||
@ -1103,20 +1806,15 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compute node protecting repair paths first (if necessary). */
|
||||
if (circuit->tilfa_node_protection[spftree->level - 1]) {
|
||||
resource.type = LFA_NODE_PROTECTION;
|
||||
spftree_pc_node = isis_tilfa_compute(
|
||||
area, spftree, spftree_reverse, &resource);
|
||||
isis_spftree_del(spftree_pc_node);
|
||||
if (circuit->lfa_protection[level - 1])
|
||||
isis_lfa_compute(area, circuit, spftree, &resource);
|
||||
else if (circuit->tilfa_protection[level - 1]) {
|
||||
assert(spftree_reverse);
|
||||
isis_spf_run_tilfa(area, circuit, spftree,
|
||||
spftree_reverse, &resource);
|
||||
}
|
||||
|
||||
/* Compute link protecting repair paths. */
|
||||
resource.type = LFA_LINK_PROTECTION;
|
||||
spftree_pc_link = isis_tilfa_compute(
|
||||
area, spftree, spftree_reverse, &resource);
|
||||
isis_spftree_del(spftree_pc_link);
|
||||
}
|
||||
|
||||
isis_spftree_del(spftree_reverse);
|
||||
if (tilfa_configured)
|
||||
isis_spftree_del(spftree_reverse);
|
||||
}
|
||||
|
@ -20,6 +20,27 @@
|
||||
#ifndef _FRR_ISIS_LFA_H
|
||||
#define _FRR_ISIS_LFA_H
|
||||
|
||||
#include "lib/typesafe.h"
|
||||
|
||||
PREDECL_RBTREE_UNIQ(lfa_tiebreaker_tree)
|
||||
|
||||
enum lfa_tiebreaker_type {
|
||||
LFA_TIEBREAKER_DOWNSTREAM = 0,
|
||||
LFA_TIEBREAKER_LOWEST_METRIC,
|
||||
LFA_TIEBREAKER_NODE_PROTECTING,
|
||||
};
|
||||
|
||||
struct lfa_tiebreaker {
|
||||
struct lfa_tiebreaker_tree_item entry;
|
||||
uint8_t index;
|
||||
enum lfa_tiebreaker_type type;
|
||||
struct isis_area *area;
|
||||
};
|
||||
int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
|
||||
const struct lfa_tiebreaker *b);
|
||||
DECLARE_RBTREE_UNIQ(lfa_tiebreaker_tree, struct lfa_tiebreaker, entry,
|
||||
lfa_tiebreaker_cmp)
|
||||
|
||||
enum isis_tilfa_sid_type {
|
||||
TILFA_SID_PREFIX = 1,
|
||||
TILFA_SID_ADJ,
|
||||
@ -37,6 +58,20 @@ struct isis_tilfa_sid {
|
||||
} value;
|
||||
};
|
||||
|
||||
enum spf_prefix_priority {
|
||||
SPF_PREFIX_PRIO_CRITICAL = 0,
|
||||
SPF_PREFIX_PRIO_HIGH,
|
||||
SPF_PREFIX_PRIO_MEDIUM,
|
||||
SPF_PREFIX_PRIO_LOW,
|
||||
SPF_PREFIX_PRIO_MAX,
|
||||
};
|
||||
|
||||
struct spf_prefix_priority_acl {
|
||||
char *name;
|
||||
struct access_list *list_v4;
|
||||
struct access_list *list_v6;
|
||||
};
|
||||
|
||||
RB_HEAD(isis_spf_nodes, isis_spf_node);
|
||||
RB_PROTOTYPE(isis_spf_nodes, isis_spf_node, entry, isis_spf_node_compare)
|
||||
struct isis_spf_node {
|
||||
@ -89,14 +124,32 @@ struct isis_spf_node *isis_spf_node_new(struct isis_spf_nodes *nodes,
|
||||
const uint8_t *sysid);
|
||||
struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes,
|
||||
const uint8_t *sysid);
|
||||
void isis_lfa_tiebreakers_init(struct isis_area *area, int level);
|
||||
void isis_lfa_tiebreakers_clear(struct isis_area *area, int level);
|
||||
struct lfa_tiebreaker *isis_lfa_tiebreaker_add(struct isis_area *area,
|
||||
int level, uint8_t index,
|
||||
enum lfa_tiebreaker_type type);
|
||||
void isis_lfa_tiebreaker_delete(struct isis_area *area, int level,
|
||||
struct lfa_tiebreaker *tie_b);
|
||||
void isis_lfa_excluded_ifaces_init(struct isis_circuit *circuit, int level);
|
||||
void isis_lfa_excluded_ifaces_clear(struct isis_circuit *circuit, int level);
|
||||
void isis_lfa_excluded_iface_add(struct isis_circuit *circuit, int level,
|
||||
const char *ifname);
|
||||
void isis_lfa_excluded_iface_delete(struct isis_circuit *circuit, int level,
|
||||
const char *ifname);
|
||||
bool isis_lfa_excluded_iface_check(struct isis_circuit *circuit, int level,
|
||||
const char *ifname);
|
||||
bool isis_lfa_excise_adj_check(const struct isis_spftree *spftree,
|
||||
const uint8_t *id);
|
||||
bool isis_lfa_excise_node_check(const struct isis_spftree *spftree,
|
||||
const uint8_t *id);
|
||||
struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree);
|
||||
int isis_spf_run_neighbors(struct isis_spftree *spftree);
|
||||
void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
|
||||
struct isis_spftree *spftree,
|
||||
struct lfa_protected_resource *resource);
|
||||
void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree);
|
||||
int isis_lfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
|
||||
int isis_tilfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
|
||||
struct isis_spftree *
|
||||
isis_tilfa_compute(struct isis_area *area, struct isis_spftree *spftree,
|
||||
struct isis_spftree *spftree_reverse,
|
||||
|
@ -242,6 +242,8 @@ int main(int argc, char **argv, char **envp)
|
||||
*/
|
||||
isis_error_init();
|
||||
access_list_init();
|
||||
access_list_add_hook(isis_filter_update);
|
||||
access_list_delete_hook(isis_filter_update);
|
||||
isis_vrf_init();
|
||||
prefix_list_init();
|
||||
isis_init();
|
||||
|
@ -45,3 +45,4 @@ DEFINE_MTYPE(ISISD, ISIS_DICT_NODE, "ISIS dictionary node")
|
||||
DEFINE_MTYPE(ISISD, ISIS_EXT_ROUTE, "ISIS redistributed route")
|
||||
DEFINE_MTYPE(ISISD, ISIS_EXT_INFO, "ISIS redistributed route info")
|
||||
DEFINE_MTYPE(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters")
|
||||
DEFINE_MTYPE(ISISD, ISIS_ACL_NAME, "ISIS access-list name")
|
||||
|
@ -44,5 +44,6 @@ DECLARE_MTYPE(ISIS_DICT_NODE)
|
||||
DECLARE_MTYPE(ISIS_EXT_ROUTE)
|
||||
DECLARE_MTYPE(ISIS_EXT_INFO)
|
||||
DECLARE_MTYPE(ISIS_MPLS_TE)
|
||||
DECLARE_MTYPE(ISIS_ACL_NAME)
|
||||
|
||||
#endif /* _QUAGGA_ISIS_MEMORY_H */
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "linklist.h"
|
||||
#include "log.h"
|
||||
#include "bfd.h"
|
||||
#include "filter.h"
|
||||
#include "spf_backoff.h"
|
||||
#include "lib_errors.h"
|
||||
#include "vrf.h"
|
||||
@ -632,14 +633,22 @@ int isis_instance_spf_minimum_interval_level_2_modify(
|
||||
int isis_instance_spf_prefix_priorities_critical_access_list_name_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 *acl_name;
|
||||
struct spf_prefix_priority_acl *ppa;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
acl_name = yang_dnode_get_string(args->dnode, NULL);
|
||||
|
||||
ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_CRITICAL];
|
||||
XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
|
||||
ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
|
||||
ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
|
||||
ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -647,14 +656,19 @@ int isis_instance_spf_prefix_priorities_critical_access_list_name_modify(
|
||||
int isis_instance_spf_prefix_priorities_critical_access_list_name_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 spf_prefix_priority_acl *ppa;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
|
||||
ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_CRITICAL];
|
||||
XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
|
||||
ppa->list_v4 = NULL;
|
||||
ppa->list_v6 = NULL;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -665,14 +679,22 @@ int isis_instance_spf_prefix_priorities_critical_access_list_name_destroy(
|
||||
int isis_instance_spf_prefix_priorities_high_access_list_name_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 *acl_name;
|
||||
struct spf_prefix_priority_acl *ppa;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
acl_name = yang_dnode_get_string(args->dnode, NULL);
|
||||
|
||||
ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_HIGH];
|
||||
XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
|
||||
ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
|
||||
ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
|
||||
ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -680,14 +702,19 @@ int isis_instance_spf_prefix_priorities_high_access_list_name_modify(
|
||||
int isis_instance_spf_prefix_priorities_high_access_list_name_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 spf_prefix_priority_acl *ppa;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
|
||||
ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_HIGH];
|
||||
XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
|
||||
ppa->list_v4 = NULL;
|
||||
ppa->list_v6 = NULL;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -698,14 +725,22 @@ int isis_instance_spf_prefix_priorities_high_access_list_name_destroy(
|
||||
int isis_instance_spf_prefix_priorities_medium_access_list_name_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 *acl_name;
|
||||
struct spf_prefix_priority_acl *ppa;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
acl_name = yang_dnode_get_string(args->dnode, NULL);
|
||||
|
||||
ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_MEDIUM];
|
||||
XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
|
||||
ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
|
||||
ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
|
||||
ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -713,14 +748,19 @@ int isis_instance_spf_prefix_priorities_medium_access_list_name_modify(
|
||||
int isis_instance_spf_prefix_priorities_medium_access_list_name_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 spf_prefix_priority_acl *ppa;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
|
||||
ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_MEDIUM];
|
||||
XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
|
||||
ppa->list_v4 = NULL;
|
||||
ppa->list_v6 = NULL;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1395,14 +1435,14 @@ int isis_instance_multi_topology_ipv6_dstsrc_overload_modify(
|
||||
int isis_instance_fast_reroute_level_1_lfa_load_sharing_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;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
area->lfa_load_sharing[0] = yang_dnode_get_bool(args->dnode, NULL);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1413,14 +1453,14 @@ int isis_instance_fast_reroute_level_1_lfa_load_sharing_modify(
|
||||
int isis_instance_fast_reroute_level_1_lfa_priority_limit_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;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
area->lfa_priority_limit[0] = yang_dnode_get_enum(args->dnode, NULL);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1428,14 +1468,14 @@ int isis_instance_fast_reroute_level_1_lfa_priority_limit_modify(
|
||||
int isis_instance_fast_reroute_level_1_lfa_priority_limit_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);
|
||||
area->lfa_priority_limit[0] = SPF_PREFIX_PRIO_LOW;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1446,14 +1486,21 @@ int isis_instance_fast_reroute_level_1_lfa_priority_limit_destroy(
|
||||
int isis_instance_fast_reroute_level_1_lfa_tiebreaker_create(
|
||||
struct nb_cb_create_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;
|
||||
uint8_t index;
|
||||
enum lfa_tiebreaker_type type;
|
||||
struct lfa_tiebreaker *tie_b;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
index = yang_dnode_get_uint8(args->dnode, "./index");
|
||||
type = yang_dnode_get_enum(args->dnode, "./type");
|
||||
|
||||
tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL1, index, type);
|
||||
nb_running_set_entry(args->dnode, tie_b);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1461,14 +1508,16 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_create(
|
||||
int isis_instance_fast_reroute_level_1_lfa_tiebreaker_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 lfa_tiebreaker *tie_b;
|
||||
struct isis_area *area;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
tie_b = nb_running_unset_entry(args->dnode);
|
||||
area = tie_b->area;
|
||||
isis_lfa_tiebreaker_delete(area, ISIS_LEVEL1, tie_b);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1479,14 +1528,16 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_destroy(
|
||||
int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_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 lfa_tiebreaker *tie_b;
|
||||
struct isis_area *area;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
tie_b = nb_running_get_entry(args->dnode, NULL, true);
|
||||
area = tie_b->area;
|
||||
tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1497,14 +1548,13 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
|
||||
int isis_instance_fast_reroute_level_2_lfa_load_sharing_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;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
area->lfa_load_sharing[1] = yang_dnode_get_bool(args->dnode, NULL);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1515,14 +1565,13 @@ int isis_instance_fast_reroute_level_2_lfa_load_sharing_modify(
|
||||
int isis_instance_fast_reroute_level_2_lfa_priority_limit_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;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
area->lfa_priority_limit[1] = yang_dnode_get_enum(args->dnode, NULL);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1530,14 +1579,13 @@ int isis_instance_fast_reroute_level_2_lfa_priority_limit_modify(
|
||||
int isis_instance_fast_reroute_level_2_lfa_priority_limit_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);
|
||||
area->lfa_priority_limit[1] = SPF_PREFIX_PRIO_LOW;
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1548,14 +1596,21 @@ int isis_instance_fast_reroute_level_2_lfa_priority_limit_destroy(
|
||||
int isis_instance_fast_reroute_level_2_lfa_tiebreaker_create(
|
||||
struct nb_cb_create_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;
|
||||
uint8_t index;
|
||||
enum lfa_tiebreaker_type type;
|
||||
struct lfa_tiebreaker *tie_b;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
area = nb_running_get_entry(args->dnode, NULL, true);
|
||||
index = yang_dnode_get_uint8(args->dnode, "./index");
|
||||
type = yang_dnode_get_enum(args->dnode, "./type");
|
||||
|
||||
tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL2, index, type);
|
||||
nb_running_set_entry(args->dnode, tie_b);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1563,14 +1618,16 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_create(
|
||||
int isis_instance_fast_reroute_level_2_lfa_tiebreaker_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 lfa_tiebreaker *tie_b;
|
||||
struct isis_area *area;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
tie_b = nb_running_unset_entry(args->dnode);
|
||||
area = tie_b->area;
|
||||
isis_lfa_tiebreaker_delete(area, ISIS_LEVEL2, tie_b);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -1581,14 +1638,16 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_destroy(
|
||||
int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_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 lfa_tiebreaker *tie_b;
|
||||
struct isis_area *area;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
tie_b = nb_running_get_entry(args->dnode, NULL, true);
|
||||
area = tie_b->area;
|
||||
tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -3314,15 +3373,24 @@ int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args)
|
||||
int lib_interface_isis_fast_reroute_level_1_lfa_enable_modify(
|
||||
struct nb_cb_modify_args *args)
|
||||
{
|
||||
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->lfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
|
||||
if (circuit->lfa_protection[0])
|
||||
circuit->area->lfa_protected_links[0]++;
|
||||
else {
|
||||
assert(circuit->area->lfa_protected_links[0] > 0);
|
||||
circuit->area->lfa_protected_links[0]--;
|
||||
}
|
||||
|
||||
area = circuit->area;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
|
||||
@ -3333,14 +3401,19 @@ int lib_interface_isis_fast_reroute_level_1_lfa_enable_modify(
|
||||
int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_create(
|
||||
struct nb_cb_create_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;
|
||||
const char *exclude_ifname;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
circuit = nb_running_get_entry(args->dnode, NULL, true);
|
||||
exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
|
||||
|
||||
isis_lfa_excluded_iface_add(circuit, ISIS_LEVEL1, exclude_ifname);
|
||||
area = circuit->area;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -3348,14 +3421,19 @@ int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_create(
|
||||
int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_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;
|
||||
const char *exclude_ifname;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
circuit = nb_running_get_entry(args->dnode, NULL, true);
|
||||
exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
|
||||
|
||||
isis_lfa_excluded_iface_delete(circuit, ISIS_LEVEL1, exclude_ifname);
|
||||
area = circuit->area;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -3376,10 +3454,10 @@ int lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify(
|
||||
circuit = nb_running_get_entry(args->dnode, NULL, true);
|
||||
circuit->tilfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
|
||||
if (circuit->tilfa_protection[0])
|
||||
circuit->area->lfa_protected_links[0]++;
|
||||
circuit->area->tilfa_protected_links[0]++;
|
||||
else {
|
||||
assert(circuit->area->lfa_protected_links[0] > 0);
|
||||
circuit->area->lfa_protected_links[0]--;
|
||||
assert(circuit->area->tilfa_protected_links[0] > 0);
|
||||
circuit->area->tilfa_protected_links[0]--;
|
||||
}
|
||||
|
||||
area = circuit->area;
|
||||
@ -3418,15 +3496,24 @@ int lib_interface_isis_fast_reroute_level_1_ti_lfa_node_protection_modify(
|
||||
int lib_interface_isis_fast_reroute_level_2_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->lfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
|
||||
if (circuit->lfa_protection[1])
|
||||
circuit->area->lfa_protected_links[1]++;
|
||||
else {
|
||||
assert(circuit->area->lfa_protected_links[1] > 0);
|
||||
circuit->area->lfa_protected_links[1]--;
|
||||
}
|
||||
|
||||
area = circuit->area;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
|
||||
@ -3437,14 +3524,19 @@ int lib_interface_isis_fast_reroute_level_2_lfa_enable_modify(
|
||||
int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_create(
|
||||
struct nb_cb_create_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;
|
||||
const char *exclude_ifname;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
circuit = nb_running_get_entry(args->dnode, NULL, true);
|
||||
exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
|
||||
|
||||
isis_lfa_excluded_iface_add(circuit, ISIS_LEVEL2, exclude_ifname);
|
||||
area = circuit->area;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -3452,14 +3544,19 @@ int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_create(
|
||||
int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_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;
|
||||
const char *exclude_ifname;
|
||||
|
||||
if (args->event != NB_EV_APPLY)
|
||||
return NB_OK;
|
||||
|
||||
circuit = nb_running_get_entry(args->dnode, NULL, true);
|
||||
exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
|
||||
|
||||
isis_lfa_excluded_iface_delete(circuit, ISIS_LEVEL2, exclude_ifname);
|
||||
area = circuit->area;
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
@ -3480,10 +3577,10 @@ int lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify(
|
||||
circuit = nb_running_get_entry(args->dnode, NULL, true);
|
||||
circuit->tilfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
|
||||
if (circuit->tilfa_protection[1])
|
||||
circuit->area->lfa_protected_links[1]++;
|
||||
circuit->area->tilfa_protected_links[1]++;
|
||||
else {
|
||||
assert(circuit->area->lfa_protected_links[1] > 0);
|
||||
circuit->area->lfa_protected_links[1]--;
|
||||
assert(circuit->area->tilfa_protected_links[1] > 0);
|
||||
circuit->area->tilfa_protected_links[1]--;
|
||||
}
|
||||
|
||||
area = circuit->area;
|
||||
|
@ -183,7 +183,7 @@ static void isis_route_add_dummy_nexthops(struct isis_route_info *rinfo,
|
||||
static struct isis_route_info *
|
||||
isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
|
||||
uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
|
||||
struct list *adjacencies)
|
||||
struct list *adjacencies, bool allow_ecmp)
|
||||
{
|
||||
struct isis_route_info *rinfo;
|
||||
struct isis_vertex_adj *vadj;
|
||||
@ -205,6 +205,8 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
|
||||
if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) {
|
||||
isis_route_add_dummy_nexthops(rinfo, sadj->id, sr,
|
||||
label_stack);
|
||||
if (!allow_ecmp)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -233,6 +235,8 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
|
||||
}
|
||||
adjinfo2nexthop(prefix->family, rinfo->nexthops, adj, sr,
|
||||
label_stack);
|
||||
if (!allow_ecmp)
|
||||
break;
|
||||
}
|
||||
|
||||
rinfo->cost = cost;
|
||||
@ -339,8 +343,8 @@ static int isis_route_info_same(struct isis_route_info *new,
|
||||
struct isis_route_info *
|
||||
isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
|
||||
uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
|
||||
struct list *adjacencies, struct isis_area *area,
|
||||
struct route_table *table)
|
||||
struct list *adjacencies, bool allow_ecmp,
|
||||
struct isis_area *area, struct route_table *table)
|
||||
{
|
||||
struct route_node *route_node;
|
||||
struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL;
|
||||
@ -350,7 +354,7 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
|
||||
return NULL;
|
||||
|
||||
rinfo_new = isis_route_info_new(prefix, src_p, cost, depth, sr,
|
||||
adjacencies);
|
||||
adjacencies, allow_ecmp);
|
||||
route_node = srcdest_rnode_get(table, prefix, src_p);
|
||||
|
||||
rinfo_old = route_node->info;
|
||||
|
@ -61,8 +61,8 @@ void adjinfo2nexthop(int family, struct list *nexthops,
|
||||
struct isis_route_info *
|
||||
isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
|
||||
uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
|
||||
struct list *adjacencies, struct isis_area *area,
|
||||
struct route_table *table);
|
||||
struct list *adjacencies, bool allow_ecmp,
|
||||
struct isis_area *area, struct route_table *table);
|
||||
|
||||
/* Walk the given table and install new routes to zebra and remove old ones.
|
||||
* route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */
|
||||
|
152
isisd/isis_spf.c
152
isisd/isis_spf.c
@ -32,6 +32,7 @@
|
||||
#include "termtable.h"
|
||||
#include "memory.h"
|
||||
#include "prefix.h"
|
||||
#include "filter.h"
|
||||
#include "if.h"
|
||||
#include "hash.h"
|
||||
#include "table.h"
|
||||
@ -216,7 +217,7 @@ struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,
|
||||
return hash_lookup(spftree->prefix_sids, &lookup);
|
||||
}
|
||||
|
||||
static void isis_vertex_adj_free(void *arg)
|
||||
void isis_vertex_adj_free(void *arg)
|
||||
{
|
||||
struct isis_vertex_adj *vadj = arg;
|
||||
|
||||
@ -246,10 +247,10 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree,
|
||||
return vertex;
|
||||
}
|
||||
|
||||
static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
|
||||
struct isis_vertex *vertex,
|
||||
struct isis_spf_adj *sadj,
|
||||
struct isis_prefix_sid *psid)
|
||||
struct isis_vertex_adj *
|
||||
isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
|
||||
struct list *vadj_list, struct isis_spf_adj *sadj,
|
||||
struct isis_prefix_sid *psid, bool last_hop)
|
||||
{
|
||||
struct isis_vertex_adj *vadj;
|
||||
|
||||
@ -262,9 +263,6 @@ static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
|
||||
"ISIS-SPF: ignoring different Prefix-SID for route %pFX",
|
||||
&vertex->N.ip.p.dest);
|
||||
else {
|
||||
bool last_hop;
|
||||
|
||||
last_hop = (vertex->depth == 2);
|
||||
vadj->sr.sid = *psid;
|
||||
vadj->sr.label = sr_prefix_out_label(
|
||||
spftree->lspdb, vertex->N.ip.p.dest.family,
|
||||
@ -273,7 +271,7 @@ static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
|
||||
vadj->sr.present = true;
|
||||
}
|
||||
}
|
||||
listnode_add(vertex->Adj_N, vadj);
|
||||
listnode_add(vadj_list, vadj);
|
||||
|
||||
return vadj;
|
||||
}
|
||||
@ -528,6 +526,7 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
|
||||
{
|
||||
struct isis_vertex *vertex;
|
||||
struct listnode *node;
|
||||
bool last_hop;
|
||||
char buff[VID2STR_BUFFER];
|
||||
|
||||
vertex = isis_find_vertex(&spftree->paths, id, vtype);
|
||||
@ -593,14 +592,16 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
|
||||
if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC))
|
||||
vertex_update_firsthops(vertex, parent);
|
||||
|
||||
last_hop = (vertex->depth == 2);
|
||||
if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
|
||||
struct isis_vertex_adj *parent_vadj;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_vadj))
|
||||
isis_vertex_adj_add(spftree, vertex, parent_vadj->sadj,
|
||||
psid);
|
||||
isis_vertex_adj_add(spftree, vertex, vertex->Adj_N,
|
||||
parent_vadj->sadj, psid, last_hop);
|
||||
} else if (sadj) {
|
||||
isis_vertex_adj_add(spftree, vertex, sadj, psid);
|
||||
isis_vertex_adj_add(spftree, vertex, vertex->Adj_N, sadj, psid,
|
||||
last_hop);
|
||||
}
|
||||
|
||||
#ifdef EXTREME_DEBUG
|
||||
@ -628,9 +629,13 @@ static void isis_spf_add_local(struct isis_spftree *spftree,
|
||||
if (vertex) {
|
||||
/* C.2.5 c) */
|
||||
if (vertex->d_N == cost) {
|
||||
if (sadj)
|
||||
isis_vertex_adj_add(spftree, vertex, sadj,
|
||||
psid);
|
||||
if (sadj) {
|
||||
bool last_hop = (vertex->depth == 2);
|
||||
|
||||
isis_vertex_adj_add(spftree, vertex,
|
||||
vertex->Adj_N, sadj, psid,
|
||||
last_hop);
|
||||
}
|
||||
/* d) */
|
||||
if (!CHECK_FLAG(spftree->flags,
|
||||
F_SPFTREE_NO_ADJACENCIES)
|
||||
@ -718,11 +723,16 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
|
||||
struct isis_vertex_adj *parent_vadj;
|
||||
for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node,
|
||||
parent_vadj))
|
||||
if (!isis_vertex_adj_exists(spftree, vertex,
|
||||
parent_vadj->sadj))
|
||||
if (!isis_vertex_adj_exists(
|
||||
spftree, vertex,
|
||||
parent_vadj->sadj)) {
|
||||
bool last_hop = (vertex->depth == 2);
|
||||
|
||||
isis_vertex_adj_add(spftree, vertex,
|
||||
vertex->Adj_N,
|
||||
parent_vadj->sadj,
|
||||
psid);
|
||||
psid, last_hop);
|
||||
}
|
||||
if (CHECK_FLAG(spftree->flags,
|
||||
F_SPFTREE_HOPCOUNT_METRIC))
|
||||
vertex_update_firsthops(vertex, parent);
|
||||
@ -1276,6 +1286,10 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
|
||||
sadj->subtlvs = subtlvs;
|
||||
sadj->flags = flags;
|
||||
|
||||
if ((oldmetric && metric == ISIS_NARROW_METRIC_INFINITY)
|
||||
|| (!oldmetric && metric == ISIS_WIDE_METRIC_INFINITY))
|
||||
SET_FLAG(flags, F_ISIS_SPF_ADJ_METRIC_INFINITY);
|
||||
|
||||
/* Set real adjacency. */
|
||||
if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)
|
||||
&& !LSP_PSEUDO_ID(id)) {
|
||||
@ -1419,28 +1433,59 @@ static void init_spt(struct isis_spftree *spftree, int mtid)
|
||||
spftree->mtid = mtid;
|
||||
}
|
||||
|
||||
static enum spf_prefix_priority
|
||||
spf_prefix_priority(struct isis_spftree *spftree, struct isis_vertex *vertex)
|
||||
{
|
||||
struct isis_area *area = spftree->area;
|
||||
struct prefix *prefix = &vertex->N.ip.p.dest;
|
||||
|
||||
for (int priority = SPF_PREFIX_PRIO_CRITICAL;
|
||||
priority <= SPF_PREFIX_PRIO_MEDIUM; priority++) {
|
||||
struct spf_prefix_priority_acl *ppa;
|
||||
enum filter_type ret = FILTER_PERMIT;
|
||||
|
||||
ppa = &area->spf_prefix_priorities[priority];
|
||||
switch (spftree->family) {
|
||||
case AF_INET:
|
||||
ret = access_list_apply(ppa->list_v4, prefix);
|
||||
break;
|
||||
case AF_INET6:
|
||||
ret = access_list_apply(ppa->list_v6, prefix);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == FILTER_PERMIT)
|
||||
return priority;
|
||||
}
|
||||
|
||||
/* Assign medium priority to loopback prefixes by default. */
|
||||
if (is_host_route(prefix))
|
||||
return SPF_PREFIX_PRIO_MEDIUM;
|
||||
|
||||
return SPF_PREFIX_PRIO_LOW;
|
||||
}
|
||||
|
||||
static void spf_path_process(struct isis_spftree *spftree,
|
||||
struct isis_vertex *vertex)
|
||||
{
|
||||
struct isis_area *area = spftree->area;
|
||||
int level = spftree->level;
|
||||
char buff[VID2STR_BUFFER];
|
||||
|
||||
if (VTYPE_IS(vertex->type)
|
||||
if (spftree->type == SPF_TYPE_TI_LFA && VTYPE_IS(vertex->type)
|
||||
&& !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) {
|
||||
if (listcount(vertex->Adj_N) > 0) {
|
||||
if (spftree->type == SPF_TYPE_TI_LFA) {
|
||||
struct isis_adjacency *adj;
|
||||
struct isis_adjacency *adj;
|
||||
|
||||
if (isis_lfa_check(spftree, vertex) != 0)
|
||||
return;
|
||||
if (isis_tilfa_check(spftree, vertex) != 0)
|
||||
return;
|
||||
|
||||
adj = isis_adj_find(area, spftree->level,
|
||||
vertex->N.id);
|
||||
if (adj)
|
||||
sr_adj_sid_add_single(
|
||||
adj, spftree->family, true,
|
||||
vertex->Adj_N);
|
||||
}
|
||||
adj = isis_adj_find(area, level, vertex->N.id);
|
||||
if (adj)
|
||||
sr_adj_sid_add_single(adj, spftree->family,
|
||||
true, vertex->Adj_N);
|
||||
} else if (IS_DEBUG_SPF_EVENTS)
|
||||
zlog_debug(
|
||||
"ISIS-SPF: no adjacencies, do not install backup Adj-SID for %s depth %d dist %d",
|
||||
@ -1450,21 +1495,45 @@ static void spf_path_process(struct isis_spftree *spftree,
|
||||
|
||||
if (VTYPE_IP(vertex->type)
|
||||
&& !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ROUTES)) {
|
||||
enum spf_prefix_priority priority;
|
||||
|
||||
priority = spf_prefix_priority(spftree, vertex);
|
||||
vertex->N.ip.priority = priority;
|
||||
if (vertex->depth == 1 || listcount(vertex->Adj_N) > 0) {
|
||||
struct route_table *route_table;
|
||||
bool allow_ecmp;
|
||||
|
||||
if (spftree->type == SPF_TYPE_TI_LFA) {
|
||||
if (isis_lfa_check(spftree, vertex) != 0)
|
||||
return;
|
||||
route_table = spftree->lfa.old.spftree
|
||||
->route_table_backup;
|
||||
} else
|
||||
route_table = spftree->route_table;
|
||||
struct isis_spftree *pre_spftree;
|
||||
|
||||
isis_route_create(&vertex->N.ip.p.dest,
|
||||
&vertex->N.ip.p.src, vertex->d_N,
|
||||
vertex->depth, &vertex->N.ip.sr,
|
||||
vertex->Adj_N, area, route_table);
|
||||
if (priority
|
||||
> area->lfa_priority_limit[level - 1]) {
|
||||
if (IS_DEBUG_LFA)
|
||||
zlog_debug(
|
||||
"ISIS-LFA: skipping %s %s (low prefix priority)",
|
||||
vtype2string(
|
||||
vertex->type),
|
||||
vid2string(
|
||||
vertex, buff,
|
||||
sizeof(buff)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isis_tilfa_check(spftree, vertex) != 0)
|
||||
return;
|
||||
|
||||
pre_spftree = spftree->lfa.old.spftree;
|
||||
route_table = pre_spftree->route_table_backup;
|
||||
allow_ecmp = area->lfa_load_sharing[level - 1];
|
||||
} else {
|
||||
route_table = spftree->route_table;
|
||||
allow_ecmp = true;
|
||||
}
|
||||
|
||||
isis_route_create(
|
||||
&vertex->N.ip.p.dest, &vertex->N.ip.p.src,
|
||||
vertex->d_N, vertex->depth, &vertex->N.ip.sr,
|
||||
vertex->Adj_N, allow_ecmp, area, route_table);
|
||||
} else if (IS_DEBUG_SPF_EVENTS)
|
||||
zlog_debug(
|
||||
"ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d",
|
||||
@ -1638,7 +1707,8 @@ static void isis_run_spf_with_protection(struct isis_area *area,
|
||||
isis_run_spf(spftree);
|
||||
|
||||
/* Run LFA protection if configured. */
|
||||
if (area->lfa_protected_links[spftree->level - 1] > 0)
|
||||
if (area->lfa_protected_links[spftree->level - 1] > 0
|
||||
|| area->tilfa_protected_links[spftree->level - 1] > 0)
|
||||
isis_spf_run_lfa(area, spftree);
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@ struct isis_spf_adj {
|
||||
uint8_t flags;
|
||||
#define F_ISIS_SPF_ADJ_BROADCAST 0x01
|
||||
#define F_ISIS_SPF_ADJ_OLDMETRIC 0x02
|
||||
#define F_ISIS_SPF_ADJ_METRIC_INFINITY 0x04
|
||||
};
|
||||
|
||||
struct isis_spftree *isis_spftree_new(struct isis_area *area,
|
||||
|
@ -54,6 +54,7 @@ struct isis_vertex_adj {
|
||||
struct isis_spf_adj *sadj;
|
||||
struct isis_sr_psid_info sr;
|
||||
struct mpls_label_stack *label_stack;
|
||||
uint32_t lfa_metric;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -66,6 +67,7 @@ struct isis_vertex {
|
||||
struct {
|
||||
struct prefix_pair p;
|
||||
struct isis_sr_psid_info sr;
|
||||
enum spf_prefix_priority priority;
|
||||
} ip;
|
||||
} N;
|
||||
uint32_t d_N; /* d(N) Distance from this IS */
|
||||
@ -193,6 +195,11 @@ static void isis_vertex_del(struct isis_vertex *vertex)
|
||||
bool isis_vertex_adj_exists(const struct isis_spftree *spftree,
|
||||
const struct isis_vertex *vertex,
|
||||
const struct isis_spf_adj *sadj);
|
||||
void isis_vertex_adj_free(void *arg);
|
||||
struct isis_vertex_adj *
|
||||
isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
|
||||
struct list *vadj_list, struct isis_spf_adj *sadj,
|
||||
struct isis_prefix_sid *psid, bool last_hop);
|
||||
|
||||
__attribute__((__unused__))
|
||||
static void isis_vertex_queue_clear(struct isis_vertex_queue *queue)
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "linklist.h"
|
||||
#include "if.h"
|
||||
#include "hash.h"
|
||||
#include "filter.h"
|
||||
#include "stream.h"
|
||||
#include "prefix.h"
|
||||
#include "table.h"
|
||||
@ -310,6 +311,10 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
|
||||
area->lsp_frag_threshold = 90; /* not currently configurable */
|
||||
area->lsp_mtu =
|
||||
yang_get_default_uint16("/frr-isisd:isis/instance/lsp/mtu");
|
||||
area->lfa_load_sharing[0] = yang_get_default_bool(
|
||||
"/frr-isisd:isis/instance/fast-reroute/level-1/lfa/load-sharing");
|
||||
area->lfa_load_sharing[1] = yang_get_default_bool(
|
||||
"/frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing");
|
||||
#else
|
||||
area->max_lsp_lifetime[0] = DEFAULT_LSP_LIFETIME; /* 1200 */
|
||||
area->max_lsp_lifetime[1] = DEFAULT_LSP_LIFETIME; /* 1200 */
|
||||
@ -324,7 +329,13 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
|
||||
area->newmetric = 1;
|
||||
area->lsp_frag_threshold = 90;
|
||||
area->lsp_mtu = DEFAULT_LSP_MTU;
|
||||
area->lfa_load_sharing[0] = true;
|
||||
area->lfa_load_sharing[1] = true;
|
||||
#endif /* ifndef FABRICD */
|
||||
area->lfa_priority_limit[0] = SPF_PREFIX_PRIO_LOW;
|
||||
area->lfa_priority_limit[1] = SPF_PREFIX_PRIO_LOW;
|
||||
isis_lfa_tiebreakers_init(area, ISIS_LEVEL1);
|
||||
isis_lfa_tiebreakers_init(area, ISIS_LEVEL2);
|
||||
|
||||
area_mt_init(area);
|
||||
|
||||
@ -461,6 +472,16 @@ void isis_area_destroy(struct isis_area *area)
|
||||
}
|
||||
area->area_addrs = NULL;
|
||||
|
||||
for (int i = SPF_PREFIX_PRIO_CRITICAL; i <= SPF_PREFIX_PRIO_MEDIUM;
|
||||
i++) {
|
||||
struct spf_prefix_priority_acl *ppa;
|
||||
|
||||
ppa = &area->spf_prefix_priorities[i];
|
||||
XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
|
||||
}
|
||||
isis_lfa_tiebreakers_clear(area, ISIS_LEVEL1);
|
||||
isis_lfa_tiebreakers_clear(area, ISIS_LEVEL2);
|
||||
|
||||
thread_cancel(&area->t_tick);
|
||||
thread_cancel(&area->t_lsp_refresh[0]);
|
||||
thread_cancel(&area->t_lsp_refresh[1]);
|
||||
@ -605,6 +626,29 @@ void isis_terminate()
|
||||
isis_finish(isis);
|
||||
}
|
||||
|
||||
void isis_filter_update(struct access_list *access)
|
||||
{
|
||||
struct isis *isis;
|
||||
struct isis_area *area;
|
||||
struct listnode *node, *anode;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
|
||||
for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
|
||||
for (int i = SPF_PREFIX_PRIO_CRITICAL;
|
||||
i <= SPF_PREFIX_PRIO_MEDIUM; i++) {
|
||||
struct spf_prefix_priority_acl *ppa;
|
||||
|
||||
ppa = &area->spf_prefix_priorities[i];
|
||||
ppa->list_v4 =
|
||||
access_list_lookup(AFI_IP, ppa->name);
|
||||
ppa->list_v6 =
|
||||
access_list_lookup(AFI_IP6, ppa->name);
|
||||
}
|
||||
lsp_regenerate_schedule(area, area->is_type, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FABRICD
|
||||
static void area_set_mt_enabled(struct isis_area *area, uint16_t mtid,
|
||||
bool enabled)
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "isisd/isis_sr.h"
|
||||
#include "isis_flags.h"
|
||||
#include "isis_lsp.h"
|
||||
#include "isis_lfa.h"
|
||||
#include "isis_memory.h"
|
||||
#include "qobj.h"
|
||||
#include "ldp_sync.h"
|
||||
@ -188,8 +189,15 @@ struct isis_area {
|
||||
struct isis_sr_db srdb;
|
||||
int ipv6_circuits;
|
||||
bool purge_originator;
|
||||
/* SPF prefix priorities. */
|
||||
struct spf_prefix_priority_acl
|
||||
spf_prefix_priorities[SPF_PREFIX_PRIO_MAX];
|
||||
/* Fast Re-Route information. */
|
||||
size_t lfa_protected_links[ISIS_LEVELS];
|
||||
size_t lfa_load_sharing[ISIS_LEVELS];
|
||||
enum spf_prefix_priority lfa_priority_limit[ISIS_LEVELS];
|
||||
struct lfa_tiebreaker_tree_head lfa_tiebreakers[ISIS_LEVELS];
|
||||
size_t tilfa_protected_links[ISIS_LEVELS];
|
||||
/* Counters */
|
||||
uint32_t circuit_state_changes;
|
||||
struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT]
|
||||
@ -231,6 +239,7 @@ struct isis_area *isis_area_lookup_by_vrf(const char *area_tag,
|
||||
const char *vrf_name);
|
||||
int isis_area_get(struct vty *vty, const char *area_tag);
|
||||
void isis_area_destroy(struct isis_area *area);
|
||||
void isis_filter_update(struct access_list *access);
|
||||
void print_debug(struct vty *, int, int);
|
||||
struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
|
||||
struct isis *isis);
|
||||
|
Loading…
Reference in New Issue
Block a user