summaryrefslogtreecommitdiff
path: root/isisd/isis_sr.c
diff options
context:
space:
mode:
Diffstat (limited to 'isisd/isis_sr.c')
-rw-r--r--isisd/isis_sr.c424
1 files changed, 387 insertions, 37 deletions
diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c
index c24c0608b2..f27aca991f 100644
--- a/isisd/isis_sr.c
+++ b/isisd/isis_sr.c
@@ -52,6 +52,10 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information")
static void sr_prefix_uninstall(struct sr_prefix *srp);
static void sr_prefix_reinstall(struct sr_prefix *srp, bool make_before_break);
+static void sr_local_block_delete(struct isis_area *area);
+static int sr_local_block_init(struct isis_area *area);
+static void sr_adj_sid_update(struct sr_adjacency *sra,
+ struct sr_local_block *srlb);
/* --- RB-Tree Management functions ----------------------------------------- */
@@ -135,12 +139,23 @@ int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
{
struct isis_sr_db *srdb = &area->srdb;
- sr_debug("ISIS-Sr (%s): Update SRGB", area->area_tag);
+ sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
+ area->area_tag, lower_bound, upper_bound);
- /* First release the old SRGB. */
- if (srdb->config.enabled)
- isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
+ /* Just store new SRGB values if Label Manager is not available.
+ * SRGB will be configured later when SR start */
+ if (!isis_zebra_label_manager_ready()) {
+ srdb->config.srgb_lower_bound = lower_bound;
+ srdb->config.srgb_upper_bound = upper_bound;
+ return 0;
+ }
+
+ /* Label Manager is ready, start by releasing the old SRGB. */
+ if (srdb->srgb_active) {
+ isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
srdb->config.srgb_upper_bound);
+ srdb->srgb_active = false;
+ }
srdb->config.srgb_lower_bound = lower_bound;
srdb->config.srgb_upper_bound = upper_bound;
@@ -148,14 +163,18 @@ int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
if (srdb->enabled) {
struct sr_prefix *srp;
- /* Request new SRGB if SR is enabled. */
+ /* then request new SRGB if SR is enabled. */
if (isis_zebra_request_label_range(
srdb->config.srgb_lower_bound,
srdb->config.srgb_upper_bound
- - srdb->config.srgb_lower_bound + 1))
+ - srdb->config.srgb_lower_bound + 1) < 0) {
+ srdb->srgb_active = false;
return -1;
+ } else
+ srdb->srgb_active = true;
- sr_debug(" |- Got new SRGB %u/%u",
+
+ sr_debug(" |- Got new SRGB [%u/%u]",
srdb->config.srgb_lower_bound,
srdb->config.srgb_upper_bound);
@@ -170,8 +189,61 @@ int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
lsp_regenerate_schedule(area, area->is_type, 0);
} else if (srdb->config.enabled) {
/* Try to enable SR again using the new SRGB. */
- if (isis_sr_start(area) == 0)
- area->srdb.enabled = true;
+ isis_sr_start(area);
+ }
+
+ return 0;
+}
+
+/**
+ * Update Segment Routing Local Block range which is reserved though the
+ * Label Manager. This function trigger the update of local Adjacency-SID
+ * installation.
+ *
+ * @param area IS-IS area
+ * @param lower_bound Lower bound of SRLB
+ * @param upper_bound Upper bound of SRLB
+ *
+ * @return 0 on success, -1 otherwise
+ */
+int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound,
+ uint32_t upper_bound)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+ struct listnode *node, *nnode;
+ struct sr_adjacency *sra;
+
+ sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
+ area->area_tag, lower_bound, upper_bound);
+
+ /* Just store new SRLB values if Label Manager is not available.
+ * SRLB will be configured later when SR start */
+ if (!isis_zebra_label_manager_ready()) {
+ srdb->config.srlb_lower_bound = lower_bound;
+ srdb->config.srlb_upper_bound = upper_bound;
+ return 0;
+ }
+
+ /* LM is ready, start by deleting the old SRLB */
+ sr_local_block_delete(area);
+
+ srdb->config.srlb_lower_bound = lower_bound;
+ srdb->config.srlb_upper_bound = upper_bound;
+
+ if (srdb->enabled) {
+ /* Initialize new SRLB */
+ if (sr_local_block_init(area) != 0)
+ return -1;
+
+ /* Reinstall local Adjacency-SIDs with new labels. */
+ for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
+ sr_adj_sid_update(sra, &srdb->srlb);
+
+ /* Update and Flood LSP */
+ lsp_regenerate_schedule(area, area->is_type, 0);
+ } else if (srdb->config.enabled) {
+ /* Try to enable SR again using the new SRLB. */
+ isis_sr_start(area);
}
return 0;
@@ -404,15 +476,13 @@ static struct sr_prefix *sr_prefix_find_by_node(struct sr_node *srn,
* @return New Segment Routing Node structure
*/
static struct sr_node *sr_node_add(struct isis_area *area, int level,
- const uint8_t *sysid,
- const struct isis_router_cap *cap)
+ const uint8_t *sysid)
{
struct sr_node *srn;
srn = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srn));
srn->level = level;
memcpy(srn->sysid, sysid, ISIS_SYS_ID_LEN);
- srn->cap = *cap;
srn->area = area;
srdb_node_prefix_init(&srn->prefix_sids);
srdb_node_add(&area->srdb.sr_nodes[level - 1], srn);
@@ -887,6 +957,26 @@ static inline void sr_prefix_reinstall(struct sr_prefix *srp,
/* --- IS-IS LSP Parse functions -------------------------------------------- */
/**
+ * Compare Router Capabilities. Only Flags, SRGB and Algorithm are used for the
+ * comparison. MSD and SRLB modification must not trigger and SR-Prefix update.
+ *
+ * @param r1 First Router Capabilities to compare
+ * @param r2 Second Router Capabilities to compare
+ * @return 0 if r1 and r2 are equal or -1 otherwise
+ */
+static int router_cap_cmp(const struct isis_router_cap *r1,
+ const struct isis_router_cap *r2)
+{
+ if (r1->flags == r2->flags
+ && r1->srgb.lower_bound == r2->srgb.lower_bound
+ && r1->srgb.range_size == r2->srgb.range_size
+ && r1->algo[0] == r2->algo[0])
+ return 0;
+ else
+ return -1;
+}
+
+/**
* Parse all SR-related information from the given Router Capabilities TLV.
*
* @param area IS-IS area
@@ -909,8 +999,7 @@ parse_router_cap_tlv(struct isis_area *area, int level, const uint8_t *sysid,
srn = sr_node_find(area, level, sysid);
if (srn) {
- if (memcmp(&srn->cap, router_cap, sizeof(srn->cap)) != 0) {
- srn->cap = *router_cap;
+ if (router_cap_cmp(&srn->cap, router_cap) != 0) {
srn->state = SRDB_STATE_MODIFIED;
} else
srn->state = SRDB_STATE_UNCHANGED;
@@ -919,10 +1008,16 @@ parse_router_cap_tlv(struct isis_area *area, int level, const uint8_t *sysid,
: "Unchanged",
sysid_print(srn->sysid));
} else {
- srn = sr_node_add(area, level, sysid, router_cap);
+ srn = sr_node_add(area, level, sysid);
srn->state = SRDB_STATE_NEW;
}
+ /*
+ * Update Router Capabilities in any case as SRLB or MSD
+ * modification are not take into account for comparison.
+ */
+ srn->cap = *router_cap;
+
return srn;
}
@@ -1242,6 +1337,163 @@ static int sr_route_update(struct isis_area *area, struct prefix *prefix,
return 0;
}
+/* --- Segment Routing Local Block management functions --------------------- */
+
+/**
+ * Initialize Segment Routing Local Block from SRDB configuration and reserve
+ * block of bits to manage label allocation.
+ *
+ * @param area IS-IS area
+ */
+static int sr_local_block_init(struct isis_area *area)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+ struct sr_local_block *srlb = &srdb->srlb;
+
+ /* Check if SRLB is not already configured */
+ if (srlb->active)
+ return 0;
+
+ /*
+ * Request SRLB to the label manager. If the allocation fails, return
+ * an error to disable SR until a new SRLB is successfully allocated.
+ */
+ if (isis_zebra_request_label_range(
+ srdb->config.srlb_lower_bound,
+ srdb->config.srlb_upper_bound
+ - srdb->config.srlb_lower_bound + 1)) {
+ srlb->active = false;
+ return -1;
+ }
+
+ sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area->area_tag,
+ srdb->config.srlb_lower_bound, srdb->config.srlb_upper_bound);
+
+ /* Initialize the SRLB */
+ srlb->start = srdb->config.srlb_lower_bound;
+ srlb->end = srdb->config.srlb_upper_bound;
+ srlb->current = 0;
+ /* Compute the needed Used Mark number and allocate them */
+ srlb->max_block = (srlb->end - srlb->start + 1) / SRLB_BLOCK_SIZE;
+ if (((srlb->end - srlb->start + 1) % SRLB_BLOCK_SIZE) != 0)
+ srlb->max_block++;
+ srlb->used_mark = XCALLOC(MTYPE_ISIS_SR_INFO,
+ srlb->max_block * SRLB_BLOCK_SIZE);
+ srlb->active = true;
+
+ return 0;
+}
+
+/**
+ * Remove Segment Routing Local Block.
+ *
+ * @param area IS-IS area
+ */
+static void sr_local_block_delete(struct isis_area *area)
+{
+ struct isis_sr_db *srdb = &area->srdb;
+ struct sr_local_block *srlb = &srdb->srlb;
+
+ /* Check if SRLB is not already delete */
+ if (!srlb->active)
+ return;
+
+ sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area->area_tag,
+ srlb->start, srlb->end);
+
+ /* First release the label block */
+ isis_zebra_release_label_range(srdb->config.srlb_lower_bound,
+ srdb->config.srlb_upper_bound);
+
+ /* Then reset SRLB structure */
+ if (srlb->used_mark != NULL)
+ XFREE(MTYPE_ISIS_SR_INFO, srlb->used_mark);
+ srlb->active = false;
+}
+
+/**
+ * Request a label from the Segment Routing Local Block.
+ *
+ * @param srlb Segment Routing Local Block
+ *
+ * @return First available label on success or MPLS_INVALID_LABEL if the
+ * block of labels is full
+ */
+static mpls_label_t sr_local_block_request_label(struct sr_local_block *srlb)
+{
+
+ mpls_label_t label;
+ uint32_t index;
+ uint32_t pos;
+
+ /* Check if we ran out of available labels */
+ if (srlb->current >= srlb->end)
+ return MPLS_INVALID_LABEL;
+
+ /* Get first available label and mark it used */
+ label = srlb->current + srlb->start;
+ index = srlb->current / SRLB_BLOCK_SIZE;
+ pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE);
+ srlb->used_mark[index] |= pos;
+
+ /* Jump to the next free position */
+ srlb->current++;
+ pos = srlb->current % SRLB_BLOCK_SIZE;
+ while (srlb->current < srlb->end) {
+ if (pos == 0)
+ index++;
+ if (!((1ULL << pos) & srlb->used_mark[index]))
+ break;
+ else {
+ srlb->current++;
+ pos = srlb->current % SRLB_BLOCK_SIZE;
+ }
+ }
+
+ return label;
+}
+
+/**
+ * Release label in the Segment Routing Local Block.
+ *
+ * @param srlb Segment Routing Local Block
+ * @param label Label to be release
+ *
+ * @return 0 on success or -1 if label falls outside SRLB
+ */
+static int sr_local_block_release_label(struct sr_local_block *srlb,
+ mpls_label_t label)
+{
+ uint32_t index;
+ uint32_t pos;
+
+ /* Check that label falls inside the SRLB */
+ if ((label < srlb->start) || (label > srlb->end)) {
+ flog_warn(EC_ISIS_SID_OVERFLOW,
+ "%s: Returning label %u is outside SRLB [%u/%u]",
+ __func__, label, srlb->start, srlb->end);
+ return -1;
+ }
+
+ index = (label - srlb->start) / SRLB_BLOCK_SIZE;
+ pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE);
+ srlb->used_mark[index] &= ~pos;
+ /* Reset current to the first available position */
+ for (index = 0; index < srlb->max_block; index++) {
+ if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) {
+ for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++)
+ if (!((1ULL << pos) & srlb->used_mark[index])) {
+ srlb->current =
+ index * SRLB_BLOCK_SIZE + pos;
+ break;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
/* --- Segment Routing Adjacency-SID management functions ------------------- */
/**
@@ -1293,7 +1545,11 @@ static void sr_adj_sid_add_single(struct isis_adjacency *adj, int family,
if (backup)
SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
- input_label = isis_zebra_request_dynamic_label();
+ /* Get a label from the SRLB for this Adjacency */
+ input_label = sr_local_block_request_label(&area->srdb.srlb);
+ if (input_label == MPLS_INVALID_LABEL)
+ return;
+
if (circuit->ext == NULL)
circuit->ext = isis_alloc_ext_subtlvs();
@@ -1351,6 +1607,36 @@ static void sr_adj_sid_add(struct isis_adjacency *adj, int family)
sr_adj_sid_add_single(adj, family, true);
}
+static void sr_adj_sid_update(struct sr_adjacency *sra,
+ struct sr_local_block *srlb)
+{
+ struct isis_circuit *circuit = sra->adj->circuit;
+
+ /* First remove the old MPLS Label */
+ isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
+
+ /* Got new label in the new SRLB */
+ sra->nexthop.label = sr_local_block_request_label(srlb);
+ if (sra->nexthop.label == MPLS_INVALID_LABEL)
+ return;
+
+ switch (circuit->circ_type) {
+ case CIRCUIT_T_BROADCAST:
+ sra->u.ladj_sid->sid = sra->nexthop.label;
+ break;
+ case CIRCUIT_T_P2P:
+ sra->u.adj_sid->sid = sra->nexthop.label;
+ break;
+ default:
+ flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
+ __func__, circuit->circ_type);
+ break;
+ }
+
+ /* Finally configure the new MPLS Label */
+ isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
+}
+
/**
* Delete local Adj-SID.
*
@@ -1368,11 +1654,13 @@ static void sr_adj_sid_del(struct sr_adjacency *sra)
/* Release dynamic label and remove subTLVs */
switch (circuit->circ_type) {
case CIRCUIT_T_BROADCAST:
- isis_zebra_release_dynamic_label(sra->u.ladj_sid->sid);
+ sr_local_block_release_label(&area->srdb.srlb,
+ sra->u.ladj_sid->sid);
isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid);
break;
case CIRCUIT_T_P2P:
- isis_zebra_release_dynamic_label(sra->u.adj_sid->sid);
+ sr_local_block_release_label(&area->srdb.srlb,
+ sra->u.adj_sid->sid);
isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid);
break;
default:
@@ -1730,7 +2018,7 @@ static void show_node(struct vty *vty, struct isis_area *area, int level)
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
- ttable_add_row(tt, "System ID|SRGB|Algorithm|MSD");
+ ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
@@ -1738,13 +2026,17 @@ static void show_node(struct vty *vty, struct isis_area *area, int level)
/* Process all SR-Node from the SRDB */
frr_each (srdb_node, &area->srdb.sr_nodes[level - 1], srn) {
- ttable_add_row(tt, "%s|%u - %u|%s|%u", sysid_print(srn->sysid),
- srn->cap.srgb.lower_bound,
- srn->cap.srgb.lower_bound
- + srn->cap.srgb.range_size - 1,
- srn->cap.algo[0] == SR_ALGORITHM_SPF ? "SPF"
- : "S-SPF",
- srn->cap.msd);
+ ttable_add_row(
+ tt, "%s|%u - %u|%u - %u|%s|%u",
+ sysid_print(srn->sysid),
+ srn->cap.srgb.lower_bound,
+ srn->cap.srgb.lower_bound + srn->cap.srgb.range_size
+ - 1,
+ srn->cap.srlb.lower_bound,
+ srn->cap.srlb.lower_bound + srn->cap.srlb.range_size
+ - 1,
+ srn->cap.algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF",
+ srn->cap.msd);
}
/* Dump the generated table. */
@@ -1782,6 +2074,26 @@ DEFUN(show_sr_node, show_sr_node_cmd,
/* --- IS-IS Segment Routing Management function ---------------------------- */
/**
+ * Thread function to re-attempt connection to the Label Manager and thus be
+ * able to start Segment Routing.
+ *
+ * @param start Thread structure that contains area as argument
+ *
+ * @return 1 on success
+ */
+static int sr_start_label_manager(struct thread *start)
+{
+ struct isis_area *area;
+
+ area = THREAD_ARG(start);
+
+ /* re-attempt to start SR & Label Manager connection */
+ isis_sr_start(area);
+
+ return 1;
+}
+
+/**
* Enable SR on the given IS-IS area.
*
* @param area IS-IS area
@@ -1794,15 +2106,35 @@ int isis_sr_start(struct isis_area *area)
struct isis_circuit *circuit;
struct listnode *node;
+ /* First start Label Manager if not ready */
+ if (!isis_zebra_label_manager_ready())
+ if (isis_zebra_label_manager_connect() < 0) {
+ /* Re-attempt to connect to Label Manager in 1 sec. */
+ thread_add_timer(master, sr_start_label_manager, area,
+ 1, &srdb->t_start_lm);
+ return -1;
+ }
+
+ /* Label Manager is ready, initialize the SRLB */
+ if (sr_local_block_init(area) < 0)
+ return -1;
+
/*
- * Request SGRB to the label manager. If the allocation fails, return
- * an error to disable SR until a new SRGB is successfully allocated.
+ * Request SGRB to the label manager if not already active. If the
+ * allocation fails, return an error to disable SR until a new SRGB
+ * is successfully allocated.
*/
- if (isis_zebra_request_label_range(
- srdb->config.srgb_lower_bound,
- srdb->config.srgb_upper_bound
- - srdb->config.srgb_lower_bound + 1))
- return -1;
+ if (!srdb->srgb_active) {
+ if (isis_zebra_request_label_range(
+ srdb->config.srgb_lower_bound,
+ srdb->config.srgb_upper_bound
+ - srdb->config.srgb_lower_bound + 1)
+ < 0) {
+ srdb->srgb_active = false;
+ return -1;
+ } else
+ srdb->srgb_active = true;
+ }
sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
area->area_tag);
@@ -1838,6 +2170,8 @@ int isis_sr_start(struct isis_area *area)
}
}
+ area->srdb.enabled = true;
+
/* Regenerate LSPs to advertise Segment Routing capabilities. */
lsp_regenerate_schedule(area, area->is_type, 0);
@@ -1858,6 +2192,9 @@ void isis_sr_stop(struct isis_area *area)
sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
area->area_tag);
+ /* Disable any re-attempt to connect to Label Manager */
+ THREAD_TIMER_OFF(srdb->t_start_lm);
+
/* Uninstall all local Adjacency-SIDs. */
for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
sr_adj_sid_del(sra);
@@ -1872,9 +2209,17 @@ void isis_sr_stop(struct isis_area *area)
}
}
- /* Release SRGB. */
- isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
- srdb->config.srgb_upper_bound);
+ /* Release SRGB if active. */
+ if (srdb->srgb_active) {
+ isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
+ srdb->config.srgb_upper_bound);
+ srdb->srgb_active = false;
+ }
+
+ /* Delete SRLB */
+ sr_local_block_delete(area);
+
+ area->srdb.enabled = false;
/* Regenerate LSPs to advertise that the Node is no more SR enable. */
lsp_regenerate_schedule(area, area->is_type, 0);
@@ -1894,7 +2239,6 @@ void isis_sr_area_init(struct isis_area *area)
/* Initialize Segment Routing Data Base */
memset(srdb, 0, sizeof(*srdb));
- srdb->enabled = false;
srdb->adj_sids = list_new();
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
@@ -1909,10 +2253,16 @@ void isis_sr_area_init(struct isis_area *area)
yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR);
srdb->config.srgb_upper_bound =
yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR);
+ srdb->config.srlb_lower_bound =
+ yang_get_default_uint32("%s/srlb/lower-bound", ISIS_SR);
+ srdb->config.srlb_upper_bound =
+ yang_get_default_uint32("%s/srlb/upper-bound", ISIS_SR);
#else
srdb->config.enabled = false;
srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND;
srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND;
+ srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND;
+ srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND;
#endif
srdb->config.msd = 0;
srdb_prefix_cfg_init(&srdb->config.prefix_sids);