Skip to content

Commit 2f7cc7b

Browse files
committed
isisd: detect Prefix-SID collisions and handle them appropriately
isisd relies on its YANG module to prevent the same SID index from being configured multiple times for different prefixes. It's possible, however, to have different routers assigning the same SID index for different prefixes. When that happens, we say we have a Prefix-SID collision, which is ultimately a misconfiguration issue. The problem with Prefix-SID collisions is that the Prefix-SID that is processed later overwrites the previous ones. Then, once the Prefix-SID collision is fixed in the configuration, the overwritten Prefix-SID isn't reinstalled since it's already marked as installed and it didn't change. To prevent such inconsistency from happening, add a safeguard in the SPF code to detect Prefix-SID collisions and handle them appropriately (i.e. log a warning + ignore the Prefix-SID Sub-TLV since it's already in use by another prefix). That way, once the configuration is fixed, no Prefix-SID label entry will be missing in the LFIB. Reported-by: Emanuele Di Pascale <[email protected]> Signed-off-by: Renato Westphal <[email protected]>
1 parent 2831591 commit 2f7cc7b

File tree

6 files changed

+127
-7
lines changed

6 files changed

+127
-7
lines changed

isisd/isis_errors.c

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ static struct log_ref ferr_isis_err[] = {
4343
.description = "Isis has detected that a SID index falls outside of its associated SRGB range",
4444
.suggestion = "Configure a larger SRGB"
4545
},
46+
{
47+
.code = EC_ISIS_SID_COLLISION,
48+
.title = "SID collision",
49+
.description = "Isis has detected that two different prefixes share the same SID index",
50+
.suggestion = "Identify the routers that are advertising the same SID index and fix the collision accordingly"
51+
},
4652
{
4753
.code = END_FERR,
4854
}

isisd/isis_errors.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ enum isis_log_refs {
2727
EC_ISIS_PACKET = ISIS_FERR_START,
2828
EC_ISIS_CONFIG,
2929
EC_ISIS_SID_OVERFLOW,
30+
EC_ISIS_SID_COLLISION,
3031
};
3132

3233
extern void isis_error_init(void);

isisd/isis_nb_config.c

+48
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <zebra.h>
2424

25+
#include "printfrr.h"
2526
#include "northbound.h"
2627
#include "linklist.h"
2728
#include "log.h"
@@ -1739,20 +1740,27 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy(
17391740
int isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate(
17401741
struct nb_cb_pre_validate_args *args)
17411742
{
1743+
const struct lyd_node *area_dnode;
1744+
struct isis_area *area;
1745+
struct prefix prefix;
17421746
uint32_t srgb_lbound;
17431747
uint32_t srgb_ubound;
17441748
uint32_t srgb_range;
17451749
uint32_t sid;
17461750
enum sr_sid_value_type sid_type;
1751+
struct isis_prefix_sid psid = {};
17471752

1753+
yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
17481754
srgb_lbound = yang_dnode_get_uint32(args->dnode,
17491755
"../../srgb/lower-bound");
17501756
srgb_ubound = yang_dnode_get_uint32(args->dnode,
17511757
"../../srgb/upper-bound");
17521758
sid = yang_dnode_get_uint32(args->dnode, "./sid-value");
17531759
sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type");
17541760

1761+
/* Check for invalid indexes/labels. */
17551762
srgb_range = srgb_ubound - srgb_lbound + 1;
1763+
psid.value = sid;
17561764
switch (sid_type) {
17571765
case SR_SID_VALUE_TYPE_INDEX:
17581766
if (sid >= srgb_range) {
@@ -1766,9 +1774,49 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate(
17661774
zlog_warn("Invalid absolute SID %u", sid);
17671775
return NB_ERR_VALIDATION;
17681776
}
1777+
SET_FLAG(psid.flags, ISIS_PREFIX_SID_VALUE);
1778+
SET_FLAG(psid.flags, ISIS_PREFIX_SID_LOCAL);
17691779
break;
17701780
}
17711781

1782+
/* Check for Prefix-SID collisions. */
1783+
area_dnode = yang_dnode_get_parent(args->dnode, "instance");
1784+
area = nb_running_get_entry(area_dnode, NULL, false);
1785+
if (area) {
1786+
for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
1787+
for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2;
1788+
level++) {
1789+
struct isis_spftree *spftree;
1790+
struct isis_vertex *vertex_psid;
1791+
1792+
if (!(area->is_type & level))
1793+
continue;
1794+
spftree = area->spftree[tree][level - 1];
1795+
if (!spftree)
1796+
continue;
1797+
1798+
vertex_psid = isis_spf_prefix_sid_lookup(
1799+
spftree, &psid);
1800+
if (vertex_psid
1801+
&& !prefix_same(&vertex_psid->N.ip.p.dest,
1802+
&prefix)) {
1803+
snprintfrr(
1804+
args->errmsg, args->errmsg_len,
1805+
"Prefix-SID collision detected, SID %s %u is already in use by prefix %pFX (L%u)",
1806+
CHECK_FLAG(
1807+
psid.flags,
1808+
ISIS_PREFIX_SID_VALUE)
1809+
? "label"
1810+
: "index",
1811+
psid.value,
1812+
&vertex_psid->N.ip.p.dest,
1813+
level);
1814+
return NB_ERR_VALIDATION;
1815+
}
1816+
}
1817+
}
1818+
}
1819+
17721820
return NB_OK;
17731821
}
17741822

isisd/isis_spf.c

+69-7
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@
3333
#include "memory.h"
3434
#include "prefix.h"
3535
#include "if.h"
36+
#include "hash.h"
3637
#include "table.h"
3738
#include "spf_backoff.h"
3839
#include "srcdest_table.h"
3940
#include "vrf.h"
4041

42+
#include "isis_errors.h"
4143
#include "isis_constants.h"
4244
#include "isis_common.h"
4345
#include "isis_flags.h"
@@ -184,6 +186,36 @@ const char *vid2string(const struct isis_vertex *vertex, char *buff, int size)
184186
return "UNKNOWN";
185187
}
186188

189+
static bool prefix_sid_cmp(const void *value1, const void *value2)
190+
{
191+
const struct isis_vertex *c1 = value1;
192+
const struct isis_vertex *c2 = value2;
193+
194+
if (CHECK_FLAG(c1->N.ip.sr.sid.flags,
195+
ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)
196+
!= CHECK_FLAG(c2->N.ip.sr.sid.flags,
197+
ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL))
198+
return false;
199+
200+
return c1->N.ip.sr.sid.value == c2->N.ip.sr.sid.value;
201+
}
202+
203+
static unsigned int prefix_sid_key_make(const void *value)
204+
{
205+
const struct isis_vertex *vertex = value;
206+
207+
return jhash_1word(vertex->N.ip.sr.sid.value, 0);
208+
}
209+
210+
struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,
211+
struct isis_prefix_sid *psid)
212+
{
213+
struct isis_vertex lookup = {};
214+
215+
lookup.N.ip.sr.sid = *psid;
216+
return hash_lookup(spftree->prefix_sids, &lookup);
217+
}
218+
187219
static void isis_vertex_adj_free(void *arg)
188220
{
189221
struct isis_vertex_adj *vadj = arg;
@@ -310,6 +342,8 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area,
310342
tree->route_table_backup->cleanup = isis_route_node_cleanup;
311343
tree->area = area;
312344
tree->lspdb = lspdb;
345+
tree->prefix_sids = hash_create(prefix_sid_key_make, prefix_sid_cmp,
346+
"SR Prefix-SID Entries");
313347
tree->sadj_list = list_new();
314348
tree->sadj_list->del = isis_spf_adj_free;
315349
tree->last_run_timestamp = 0;
@@ -332,6 +366,8 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area,
332366

333367
void isis_spftree_del(struct isis_spftree *spftree)
334368
{
369+
hash_clean(spftree->prefix_sids, NULL);
370+
hash_free(spftree->prefix_sids);
335371
if (spftree->type == SPF_TYPE_TI_LFA) {
336372
isis_spf_node_list_clear(&spftree->lfa.q_space);
337373
isis_spf_node_list_clear(&spftree->lfa.p_space);
@@ -515,14 +551,39 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
515551
vertex->d_N = cost;
516552
vertex->depth = depth;
517553
if (VTYPE_IP(vtype) && psid) {
518-
bool local;
554+
struct isis_area *area = spftree->area;
555+
struct isis_vertex *vertex_psid;
519556

520-
local = (vertex->depth == 1);
521-
vertex->N.ip.sr.sid = *psid;
522-
vertex->N.ip.sr.label =
523-
sr_prefix_in_label(spftree->area, psid, local);
524-
if (vertex->N.ip.sr.label != MPLS_INVALID_LABEL)
525-
vertex->N.ip.sr.present = true;
557+
/*
558+
* Check if the Prefix-SID is already in use by another prefix.
559+
*/
560+
vertex_psid = isis_spf_prefix_sid_lookup(spftree, psid);
561+
if (vertex_psid
562+
&& !prefix_same(&vertex_psid->N.ip.p.dest,
563+
&vertex->N.ip.p.dest)) {
564+
flog_warn(
565+
EC_ISIS_SID_COLLISION,
566+
"ISIS-Sr (%s): collision detected, prefixes %pFX and %pFX share the same SID %s (%u)",
567+
area->area_tag, &vertex->N.ip.p.dest,
568+
&vertex_psid->N.ip.p.dest,
569+
CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE)
570+
? "label"
571+
: "index",
572+
psid->value);
573+
psid = NULL;
574+
} else {
575+
bool local;
576+
577+
local = (vertex->depth == 1);
578+
vertex->N.ip.sr.sid = *psid;
579+
vertex->N.ip.sr.label =
580+
sr_prefix_in_label(area, psid, local);
581+
if (vertex->N.ip.sr.label != MPLS_INVALID_LABEL)
582+
vertex->N.ip.sr.present = true;
583+
584+
hash_get(spftree->prefix_sids, vertex,
585+
hash_alloc_intern);
586+
}
526587
}
527588

528589
if (parent) {
@@ -1352,6 +1413,7 @@ static void add_to_paths(struct isis_spftree *spftree,
13521413
static void init_spt(struct isis_spftree *spftree, int mtid)
13531414
{
13541415
/* Clear data from previous run. */
1416+
hash_clean(spftree->prefix_sids, NULL);
13551417
isis_spf_node_list_clear(&spftree->adj_nodes);
13561418
list_delete_all_node(spftree->sadj_list);
13571419
isis_vertex_queue_clear(&spftree->tents);

isisd/isis_spf.h

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area,
5353
const uint8_t *sysid, int level,
5454
enum spf_tree_id tree_id,
5555
enum spf_type type, uint8_t flags);
56+
struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,
57+
struct isis_prefix_sid *psid);
5658
void isis_spf_invalidate_routes(struct isis_spftree *tree);
5759
void isis_spf_verify_routes(struct isis_area *area,
5860
struct isis_spftree **trees);

isisd/isis_spf_private.h

+1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ struct isis_spftree {
313313
struct route_table *route_table;
314314
struct route_table *route_table_backup;
315315
struct lspdb_head *lspdb; /* link-state db */
316+
struct hash *prefix_sids; /* SR Prefix-SIDs. */
316317
struct list *sadj_list;
317318
struct isis_spf_nodes adj_nodes;
318319
struct isis_area *area; /* back pointer to area */

0 commit comments

Comments
 (0)