]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_sr.c
*: unify thread/event cancel macros
[mirror_frr.git] / isisd / isis_sr.c
1 /*
2 * This is an implementation of Segment Routing for IS-IS as per RFC 8667
3 *
4 * Copyright (C) 2019 Orange http://www.orange.com
5 *
6 * Author: Olivier Dugeon <olivier.dugeon@orange.com>
7 * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; see the file COPYING; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include <zebra.h>
25
26 #include "if.h"
27 #include "linklist.h"
28 #include "log.h"
29 #include "command.h"
30 #include "termtable.h"
31 #include "memory.h"
32 #include "prefix.h"
33 #include "table.h"
34 #include "vty.h"
35 #include "zclient.h"
36 #include "lib/lib_errors.h"
37
38 #include "isisd/isisd.h"
39 #include "isisd/isis_spf.h"
40 #include "isisd/isis_spf_private.h"
41 #include "isisd/isis_adjacency.h"
42 #include "isisd/isis_route.h"
43 #include "isisd/isis_mt.h"
44 #include "isisd/isis_sr.h"
45 #include "isisd/isis_tlvs.h"
46 #include "isisd/isis_misc.h"
47 #include "isisd/isis_zebra.h"
48 #include "isisd/isis_errors.h"
49
50 /* Local variables and functions */
51 DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information")
52
53 static void sr_prefix_uninstall(struct sr_prefix *srp);
54 static void sr_prefix_reinstall(struct sr_prefix *srp, bool make_before_break);
55 static void sr_local_block_delete(struct isis_area *area);
56 static int sr_local_block_init(struct isis_area *area);
57 static void sr_adj_sid_update(struct sr_adjacency *sra,
58 struct sr_local_block *srlb);
59 static void sr_adj_sid_del(struct sr_adjacency *sra);
60
61 /* --- RB-Tree Management functions ----------------------------------------- */
62
63 /**
64 * SR Prefix comparison for RB-Tree.
65 *
66 * @param a First SR prefix
67 * @param b Second SR prefix
68 *
69 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
70 */
71 static inline int sr_prefix_sid_compare(const struct sr_prefix *a,
72 const struct sr_prefix *b)
73 {
74 return prefix_cmp(&a->prefix, &b->prefix);
75 }
76 DECLARE_RBTREE_UNIQ(srdb_node_prefix, struct sr_prefix, node_entry,
77 sr_prefix_sid_compare)
78 DECLARE_RBTREE_UNIQ(srdb_area_prefix, struct sr_prefix, area_entry,
79 sr_prefix_sid_compare)
80
81 /**
82 * Configured SR Prefix comparison for RB-Tree.
83 *
84 * @param a First SR prefix
85 * @param b Second SR prefix
86 *
87 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
88 */
89 static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a,
90 const struct sr_prefix_cfg *b)
91 {
92 return prefix_cmp(&a->prefix, &b->prefix);
93 }
94 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry,
95 sr_prefix_sid_cfg_compare)
96
97 /**
98 * SR Node comparison for RB-Tree.
99 *
100 * @param a First SR node
101 * @param b Second SR node
102 *
103 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
104 */
105 static inline int sr_node_compare(const struct sr_node *a,
106 const struct sr_node *b)
107 {
108 return memcmp(a->sysid, b->sysid, ISIS_SYS_ID_LEN);
109 }
110 DECLARE_RBTREE_UNIQ(srdb_node, struct sr_node, entry, sr_node_compare)
111
112 /* --- Functions used for Yang model and CLI to configure Segment Routing --- */
113
114 /**
115 * Check if prefix correspond to a Node SID.
116 *
117 * @param ifp Interface
118 * @param prefix Prefix to be checked
119 *
120 * @return True if the interface/address pair corresponds to a Node-SID
121 */
122 static bool sr_prefix_is_node_sid(const struct interface *ifp,
123 const struct prefix *prefix)
124 {
125 return (if_is_loopback(ifp) && is_host_route(prefix));
126 }
127
128 /**
129 * Update local SRGB configuration. SRGB is reserved though Label Manager.
130 * This function trigger the update of local Prefix-SID installation.
131 *
132 * @param area IS-IS area
133 * @param lower_bound Lower bound of SRGB
134 * @param upper_bound Upper bound of SRGB
135 *
136 * @return 0 on success, -1 otherwise
137 */
138 int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
139 uint32_t upper_bound)
140 {
141 struct isis_sr_db *srdb = &area->srdb;
142
143 sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
144 area->area_tag, lower_bound, upper_bound);
145
146 /* Just store new SRGB values if Label Manager is not available.
147 * SRGB will be configured later when SR start */
148 if (!isis_zebra_label_manager_ready()) {
149 srdb->config.srgb_lower_bound = lower_bound;
150 srdb->config.srgb_upper_bound = upper_bound;
151 return 0;
152 }
153
154 /* Label Manager is ready, start by releasing the old SRGB. */
155 if (srdb->srgb_active) {
156 isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
157 srdb->config.srgb_upper_bound);
158 srdb->srgb_active = false;
159 }
160
161 srdb->config.srgb_lower_bound = lower_bound;
162 srdb->config.srgb_upper_bound = upper_bound;
163
164 if (srdb->enabled) {
165 struct sr_prefix *srp;
166
167 /* then request new SRGB if SR is enabled. */
168 if (isis_zebra_request_label_range(
169 srdb->config.srgb_lower_bound,
170 srdb->config.srgb_upper_bound
171 - srdb->config.srgb_lower_bound + 1) < 0) {
172 srdb->srgb_active = false;
173 return -1;
174 } else
175 srdb->srgb_active = true;
176
177
178 sr_debug(" |- Got new SRGB [%u/%u]",
179 srdb->config.srgb_lower_bound,
180 srdb->config.srgb_upper_bound);
181
182 /* Reinstall local Prefix-SIDs to update their input labels. */
183 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
184 frr_each (srdb_area_prefix,
185 &area->srdb.prefix_sids[level - 1], srp) {
186 sr_prefix_reinstall(srp, false);
187 }
188 }
189
190 lsp_regenerate_schedule(area, area->is_type, 0);
191 } else if (srdb->config.enabled) {
192 /* Try to enable SR again using the new SRGB. */
193 isis_sr_start(area);
194 }
195
196 return 0;
197 }
198
199 /**
200 * Update Segment Routing Local Block range which is reserved though the
201 * Label Manager. This function trigger the update of local Adjacency-SID
202 * installation.
203 *
204 * @param area IS-IS area
205 * @param lower_bound Lower bound of SRLB
206 * @param upper_bound Upper bound of SRLB
207 *
208 * @return 0 on success, -1 otherwise
209 */
210 int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound,
211 uint32_t upper_bound)
212 {
213 struct isis_sr_db *srdb = &area->srdb;
214 struct listnode *node;
215 struct sr_adjacency *sra;
216
217 sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
218 area->area_tag, lower_bound, upper_bound);
219
220 /* Just store new SRLB values if Label Manager is not available.
221 * SRLB will be configured later when SR start */
222 if (!isis_zebra_label_manager_ready()) {
223 srdb->config.srlb_lower_bound = lower_bound;
224 srdb->config.srlb_upper_bound = upper_bound;
225 return 0;
226 }
227
228 /* LM is ready, start by deleting the old SRLB */
229 sr_local_block_delete(area);
230
231 srdb->config.srlb_lower_bound = lower_bound;
232 srdb->config.srlb_upper_bound = upper_bound;
233
234 if (srdb->enabled) {
235 /* Initialize new SRLB */
236 if (sr_local_block_init(area) != 0)
237 return -1;
238
239 /* Reinstall local Adjacency-SIDs with new labels. */
240 for (ALL_LIST_ELEMENTS_RO(area->srdb.adj_sids, node, sra))
241 sr_adj_sid_update(sra, &srdb->srlb);
242
243 /* Update and Flood LSP */
244 lsp_regenerate_schedule(area, area->is_type, 0);
245 } else if (srdb->config.enabled) {
246 /* Try to enable SR again using the new SRLB. */
247 isis_sr_start(area);
248 }
249
250 return 0;
251 }
252
253 /**
254 * Add new Prefix-SID configuration to the SRDB.
255 *
256 * @param area IS-IS area
257 * @param prefix Prefix to be added
258 *
259 * @return Newly added Prefix-SID configuration structure
260 */
261 struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
262 const struct prefix *prefix)
263 {
264 struct sr_prefix_cfg *pcfg;
265 struct interface *ifp;
266
267 sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix);
268
269 pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg));
270 pcfg->prefix = *prefix;
271 pcfg->area = area;
272
273 /* Pull defaults from the YANG module. */
274 pcfg->sid_type = yang_get_default_enum(
275 "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR);
276 pcfg->last_hop_behavior = yang_get_default_enum(
277 "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR);
278
279 /* Set the N-flag when appropriate. */
280 ifp = if_lookup_prefix(prefix, VRF_DEFAULT);
281 if (ifp && sr_prefix_is_node_sid(ifp, prefix))
282 pcfg->node_sid = true;
283
284 /* Save prefix-sid configuration. */
285 srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg);
286
287 return pcfg;
288 }
289
290 /**
291 * Removal of locally configured Prefix-SID.
292 *
293 * @param pcfg Configured Prefix-SID
294 */
295 void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg)
296 {
297 struct isis_area *area = pcfg->area;
298
299 sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u",
300 area->area_tag, &pcfg->prefix,
301 pcfg->sid_type == SR_SID_VALUE_TYPE_INDEX ? "index" : "label",
302 pcfg->sid);
303
304 srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg);
305 XFREE(MTYPE_ISIS_SR_INFO, pcfg);
306 }
307
308 /**
309 * Lookup for Prefix-SID in the local configuration.
310 *
311 * @param area IS-IS area
312 * @param prefix Prefix to lookup
313 *
314 * @return Configured Prefix-SID structure if found, NULL otherwise
315 */
316 struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area,
317 union prefixconstptr prefix)
318 {
319 struct sr_prefix_cfg pcfg = {};
320
321 prefix_copy(&pcfg.prefix, prefix.p);
322 return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg);
323 }
324
325 /**
326 * Fill in Prefix-SID Sub-TLV according to the corresponding configuration.
327 *
328 * @param pcfg Prefix-SID configuration
329 * @param external False if prefix is locally configured, true otherwise
330 * @param psid Prefix-SID sub-TLV to be updated
331 */
332 void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external,
333 struct isis_prefix_sid *psid)
334 {
335 /* Set SID algorithm. */
336 psid->algorithm = SR_ALGORITHM_SPF;
337
338 /* Set SID flags. */
339 psid->flags = 0;
340 switch (pcfg->last_hop_behavior) {
341 case SR_LAST_HOP_BEHAVIOR_EXP_NULL:
342 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
343 SET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
344 break;
345 case SR_LAST_HOP_BEHAVIOR_NO_PHP:
346 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
347 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
348 break;
349 case SR_LAST_HOP_BEHAVIOR_PHP:
350 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
351 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
352 break;
353 }
354 if (external)
355 SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED);
356 if (pcfg->node_sid)
357 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE);
358
359 /* Set SID value. */
360 psid->value = pcfg->sid;
361 if (pcfg->sid_type == SR_SID_VALUE_TYPE_ABSOLUTE) {
362 SET_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE);
363 SET_FLAG(psid->flags, ISIS_PREFIX_SID_LOCAL);
364 }
365 }
366
367 /* --- Segment Routing Prefix Management functions -------------------------- */
368
369 /**
370 * Add Segment Routing Prefix to a given Segment Routing Node.
371 *
372 * @param area IS-IS area
373 * @param srn Segment Routing Node
374 * @param prefix Prefix to be added
375 * @param local True if prefix is locally configured, false otherwise
376 * @param psid Prefix-SID sub-TLVs
377 *
378 * @return New Segment Routing Prefix structure
379 */
380 static struct sr_prefix *sr_prefix_add(struct isis_area *area,
381 struct sr_node *srn,
382 union prefixconstptr prefix, bool local,
383 const struct isis_prefix_sid *psid)
384 {
385 struct sr_prefix *srp;
386
387 srp = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srp));
388 prefix_copy(&srp->prefix, prefix.p);
389 srp->sid = *psid;
390 srp->input_label = MPLS_INVALID_LABEL;
391 if (local) {
392 srp->type = ISIS_SR_PREFIX_LOCAL;
393 isis_sr_nexthop_reset(&srp->u.local.info);
394 } else {
395 srp->type = ISIS_SR_PREFIX_REMOTE;
396 srp->u.remote.rinfo = NULL;
397 }
398 srp->srn = srn;
399 srdb_node_prefix_add(&srn->prefix_sids, srp);
400 /* TODO: this might fail if we have Anycast SIDs in the IS-IS area. */
401 srdb_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp);
402
403 sr_debug(" |- Added new SR Prefix-SID %pFX %s %u to SR Node %s",
404 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
405 srp->sid.value, sysid_print(srn->sysid));
406
407 return srp;
408 }
409
410 /**
411 * Remove given Segment Prefix from given Segment Routing Node.
412 * Prefix-SID is un-installed first.
413 *
414 * @param area IS-IS area
415 * @param srn Segment Routing Node
416 * @param srp Segment Routing Prefix
417 */
418 static void sr_prefix_del(struct isis_area *area, struct sr_node *srn,
419 struct sr_prefix *srp)
420 {
421 sr_debug(" |- Delete SR Prefix-SID %pFX %s %u to SR Node %s",
422 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
423 srp->sid.value, sysid_print(srn->sysid));
424
425 sr_prefix_uninstall(srp);
426 srdb_node_prefix_del(&srn->prefix_sids, srp);
427 srdb_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp);
428 XFREE(MTYPE_ISIS_SR_INFO, srp);
429 }
430
431 /**
432 * Find Segment Routing Prefix by Area.
433 *
434 * @param area IS-IS area
435 * @param level IS-IS level
436 * @param prefix Prefix to lookup
437 *
438 * @return Segment Routing Prefix structure if found, NULL otherwise
439 */
440 static struct sr_prefix *sr_prefix_find_by_area(struct isis_area *area,
441 int level,
442 union prefixconstptr prefix)
443 {
444 struct sr_prefix srp = {};
445
446 prefix_copy(&srp.prefix, prefix.p);
447 return srdb_area_prefix_find(&area->srdb.prefix_sids[level - 1], &srp);
448 }
449
450 /**
451 * Find Segment Routing Prefix by Segment Routing Node.
452 *
453 * @param srn Segment Routing Node
454 * @param prefix Prefix to lookup
455 *
456 * @return Segment Routing Prefix structure if found, NULL otherwise
457 */
458 static struct sr_prefix *sr_prefix_find_by_node(struct sr_node *srn,
459 union prefixconstptr prefix)
460 {
461 struct sr_prefix srp = {};
462
463 prefix_copy(&srp.prefix, prefix.p);
464 return srdb_node_prefix_find(&srn->prefix_sids, &srp);
465 }
466
467 /* --- Segment Routing Node Management functions ---------------------------- */
468
469 /**
470 * Add Segment Routing Node to the Segment Routing Data Base.
471 *
472 * @param area IS-IS area
473 * @param level IS-IS level
474 * @param sysid Node System ID
475 * @param cap Segment Routing Capability sub-TLVs
476 *
477 * @return New Segment Routing Node structure
478 */
479 static struct sr_node *sr_node_add(struct isis_area *area, int level,
480 const uint8_t *sysid)
481 {
482 struct sr_node *srn;
483
484 srn = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srn));
485 srn->level = level;
486 memcpy(srn->sysid, sysid, ISIS_SYS_ID_LEN);
487 srn->area = area;
488 srdb_node_prefix_init(&srn->prefix_sids);
489 srdb_node_add(&area->srdb.sr_nodes[level - 1], srn);
490
491 sr_debug(" |- Added new SR Node %s", sysid_print(srn->sysid));
492
493 return srn;
494 }
495
496 static void sr_node_del(struct isis_area *area, int level, struct sr_node *srn)
497 /**
498 * Remove Segment Routing Node from the Segment Routing Data Base.
499 * All Prefix-SID attached to this Segment Routing Node are removed first.
500 *
501 * @param area IS-IS area
502 * @param level IS-IS level
503 * @param srn Segment Routing Node to be deleted
504 */
505 {
506
507 sr_debug(" |- Delete SR Node %s", sysid_print(srn->sysid));
508
509 /* Remove and uninstall Prefix-SIDs. */
510 while (srdb_node_prefix_count(&srn->prefix_sids) > 0) {
511 struct sr_prefix *srp;
512
513 srp = srdb_node_prefix_first(&srn->prefix_sids);
514 sr_prefix_del(area, srn, srp);
515 }
516
517 srdb_node_del(&area->srdb.sr_nodes[level - 1], srn);
518 XFREE(MTYPE_ISIS_SR_INFO, srn);
519 }
520
521 /**
522 * Find Segment Routing Node in the Segment Routing Data Base per system ID.
523 *
524 * @param area IS-IS area
525 * @param level IS-IS level
526 * @param sysid Node System ID to lookup
527 *
528 * @return Segment Routing Node structure if found, NULL otherwise
529 */
530 static struct sr_node *sr_node_find(struct isis_area *area, int level,
531 const uint8_t *sysid)
532 {
533 struct sr_node srn = {};
534
535 memcpy(srn.sysid, sysid, ISIS_SYS_ID_LEN);
536 return srdb_node_find(&area->srdb.sr_nodes[level - 1], &srn);
537 }
538
539 /**
540 * Update Segment Routing Node following an SRGB update. This function
541 * is called when a neighbor SR Node has updated its SRGB.
542 *
543 * @param area IS-IS area
544 * @param level IS-IS level
545 * @param sysid Segment Routing Node system ID
546 */
547 static void sr_node_srgb_update(struct isis_area *area, int level,
548 uint8_t *sysid)
549 {
550 struct sr_prefix *srp;
551
552 sr_debug("ISIS-Sr (%s): Update neighbors SR Node with new SRGB",
553 area->area_tag);
554
555 frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) {
556 struct listnode *node;
557 struct isis_nexthop *nh;
558
559 if (srp->type == ISIS_SR_PREFIX_LOCAL)
560 continue;
561
562 if (srp->u.remote.rinfo == NULL)
563 continue;
564
565 for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
566 nh)) {
567 if (memcmp(nh->sysid, sysid, ISIS_SYS_ID_LEN) != 0)
568 continue;
569
570 /*
571 * The Prefix-SID input label hasn't changed. We could
572 * re-install all Prefix-SID with "Make Before Break"
573 * option. Zebra layer will update output label(s) by
574 * adding new entry before removing the old one(s).
575 */
576 sr_prefix_reinstall(srp, true);
577 break;
578 }
579 }
580 }
581
582 /* --- Segment Routing Nexthop information Management functions ------------- */
583
584 /**
585 * Update Segment Routing Nexthop.
586 *
587 * @param srnh Segment Routing next hop
588 * @param label Output MPLS label
589 */
590 void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, mpls_label_t label)
591 {
592 srnh->label = label;
593 if (srnh->uptime == 0)
594 srnh->uptime = time(NULL);
595 }
596
597 /**
598 * Reset Segment Routing Nexthop.
599 *
600 * @param srnh Segment Routing Nexthop
601 */
602 void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh)
603 {
604 srnh->label = MPLS_INVALID_LABEL;
605 srnh->uptime = 0;
606 }
607
608 /* --- Segment Routing Prefix-SID Management functions to configure LFIB ---- */
609
610 /**
611 * Lookup IS-IS route in the Shortest Path Tree.
612 *
613 * @param area IS-IS area
614 * @param tree_id Shortest Path Tree identifier
615 * @param srp Segment Routing Prefix to lookup
616 *
617 * @return Route Information for this prefix if found, NULL otherwise
618 */
619 static struct isis_route_info *sr_prefix_lookup_route(struct isis_area *area,
620 enum spf_tree_id tree_id,
621 struct sr_prefix *srp)
622 {
623 struct route_node *rn;
624 int level = srp->srn->level;
625
626 rn = route_node_lookup(area->spftree[tree_id][level - 1]->route_table,
627 &srp->prefix);
628 if (rn) {
629 route_unlock_node(rn);
630 if (rn->info)
631 return rn->info;
632 }
633
634 return NULL;
635 }
636
637 /**
638 * Compute input label for the given Prefix-SID.
639 *
640 * @param srp Segment Routing Prefix
641 *
642 * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow
643 */
644 static mpls_label_t sr_prefix_in_label(const struct sr_prefix *srp)
645 {
646 const struct sr_node *srn = srp->srn;
647 struct isis_area *area = srn->area;
648
649 /* Return SID value as MPLS label if it is an Absolute SID */
650 if (CHECK_FLAG(srp->sid.flags,
651 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL))
652 return srp->sid.value;
653
654 /* Check that SID index falls inside the SRGB */
655 if (srp->sid.value >= (area->srdb.config.srgb_upper_bound
656 - area->srdb.config.srgb_lower_bound + 1)) {
657 flog_warn(EC_ISIS_SID_OVERFLOW,
658 "%s: SID index %u falls outside local SRGB range",
659 __func__, srp->sid.value);
660 return MPLS_INVALID_LABEL;
661 }
662
663 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
664 return (area->srdb.config.srgb_lower_bound + srp->sid.value);
665 }
666
667 /**
668 * Compute output label for the given Prefix-SID.
669 *
670 * @param srp Segment Routing Prefix
671 * @param srn_nexthop Segment Routing nexthop node
672 * @param sysid System ID of the SR node which advertised the Prefix-SID
673 *
674 * @return MPLS label or MPLS_INVALID_LABEL in case of error
675 */
676 static mpls_label_t sr_prefix_out_label(const struct sr_prefix *srp,
677 const struct sr_node *srn_nexthop,
678 const uint8_t *sysid)
679 {
680 const struct sr_node *srn = srp->srn;
681
682 /* Check if the nexthop SR Node is the last hop? */
683 if (memcmp(sysid, srn->sysid, ISIS_SYS_ID_LEN) == 0) {
684 /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */
685 if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP))
686 return MPLS_LABEL_IMPLICIT_NULL;
687
688 /* SR-Node requests Implicit NULL Label */
689 if (CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) {
690 if (srp->prefix.family == AF_INET)
691 return MPLS_LABEL_IPV4_EXPLICIT_NULL;
692 else
693 return MPLS_LABEL_IPV6_EXPLICIT_NULL;
694 }
695 /* Fallthrough */
696 }
697
698 /* Return SID value as MPLS label if it is an Absolute SID */
699 if (CHECK_FLAG(srp->sid.flags,
700 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) {
701 /*
702 * V/L SIDs have local significance, so only adjacent routers
703 * can use them (RFC8667 section #2.1.1.1)
704 */
705 if (srp->srn != srn_nexthop)
706 return MPLS_INVALID_LABEL;
707 return srp->sid.value;
708 }
709
710 /* Check that SID index falls inside the SRGB */
711 if (srp->sid.value >= srn_nexthop->cap.srgb.range_size) {
712 flog_warn(EC_ISIS_SID_OVERFLOW,
713 "%s: SID index %u falls outside remote SRGB range",
714 __func__, srp->sid.value);
715 return MPLS_INVALID_LABEL;
716 }
717
718 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
719 return (srn_nexthop->cap.srgb.lower_bound + srp->sid.value);
720 }
721
722 /**
723 * Process local Prefix-SID and install it if possible. Input label is
724 * computed before installing it in LFIB.
725 *
726 * @param srp Segment Routing Prefix
727 *
728 * @return 0 on success, -1 otherwise
729 */
730 static int sr_prefix_install_local(struct sr_prefix *srp)
731 {
732 mpls_label_t input_label;
733 const struct sr_node *srn = srp->srn;
734
735 /*
736 * No need to install Label for local Prefix-SID unless the
737 * no-PHP option is configured.
738 */
739 if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP)
740 || CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL))
741 return -1;
742
743 sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s) with nexthop self",
744 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
745 srp->sid.value, circuit_t2string(srn->level));
746
747 /* Compute input label and check that is valid. */
748 input_label = sr_prefix_in_label(srp);
749 if (input_label == MPLS_INVALID_LABEL)
750 return -1;
751
752 /* Update internal state. */
753 srp->input_label = input_label;
754 isis_sr_nexthop_update(&srp->u.local.info, MPLS_LABEL_IMPLICIT_NULL);
755
756 /* Install Prefix-SID in the forwarding plane. */
757 isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp);
758
759 return 0;
760 }
761
762 /**
763 * Process remote Prefix-SID and install it if possible. Input and Output
764 * labels are computed before installing them in LFIB.
765 *
766 * @param srp Segment Routing Prefix
767 *
768 * @return 0 on success, -1 otherwise
769 */
770 static int sr_prefix_install_remote(struct sr_prefix *srp)
771 {
772 const struct sr_node *srn = srp->srn;
773 struct isis_area *area = srn->area;
774 enum spf_tree_id tree_id;
775 struct listnode *node;
776 struct isis_nexthop *nexthop;
777 mpls_label_t input_label;
778 size_t nexthop_num = 0;
779
780 /* Lookup to associated IS-IS route. */
781 tree_id = (srp->prefix.family == AF_INET) ? SPFTREE_IPV4 : SPFTREE_IPV6;
782 srp->u.remote.rinfo = sr_prefix_lookup_route(area, tree_id, srp);
783 if (!srp->u.remote.rinfo)
784 /* SPF hasn't converged for this route yet. */
785 return -1;
786
787 /* Compute input label and check that is valid. */
788 input_label = sr_prefix_in_label(srp);
789 if (input_label == MPLS_INVALID_LABEL)
790 return -1;
791
792 sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s)", &srp->prefix,
793 IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
794 srp->sid.value, circuit_t2string(srn->level));
795
796 /* Process all SPF nexthops */
797 for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
798 nexthop)) {
799 struct sr_node *srn_nexthop;
800 mpls_label_t output_label;
801
802 /* Check if the nexthop advertised a SRGB. */
803 srn_nexthop = sr_node_find(area, srn->level, nexthop->sysid);
804 if (!srn_nexthop)
805 goto next;
806
807 /*
808 * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
809 * IPv6 packets.
810 */
811 if ((nexthop->family == AF_INET
812 && !IS_SR_IPV4(srn_nexthop->cap.srgb))
813 || (nexthop->family == AF_INET6
814 && !IS_SR_IPV6(srn_nexthop->cap.srgb)))
815 goto next;
816
817 /* Compute output label and check if it is valid */
818 output_label =
819 sr_prefix_out_label(srp, srn_nexthop, nexthop->sysid);
820 if (output_label == MPLS_INVALID_LABEL)
821 goto next;
822
823 if (IS_DEBUG_SR) {
824 static char buf[INET6_ADDRSTRLEN];
825
826 inet_ntop(nexthop->family, &nexthop->ip, buf,
827 sizeof(buf));
828 zlog_debug(" |- nexthop %s label %u", buf,
829 output_label);
830 }
831
832 isis_sr_nexthop_update(&nexthop->sr, output_label);
833 nexthop_num++;
834 continue;
835 next:
836 isis_sr_nexthop_reset(&nexthop->sr);
837 }
838
839 /* Check that we found at least one valid nexthop */
840 if (nexthop_num == 0) {
841 sr_debug(" |- no valid nexthops");
842 return -1;
843 }
844
845 /* Update internal state. */
846 srp->input_label = input_label;
847
848 /* Install Prefix-SID in the forwarding plane. */
849 isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp);
850
851 return 0;
852 }
853
854 /**
855 * Process local or remote Prefix-SID and install it if possible.
856 *
857 * @param srp Segment Routing Prefix
858 */
859 static void sr_prefix_install(struct sr_prefix *srp)
860 {
861 const struct sr_node *srn = srp->srn;
862 struct isis_area *area = srn->area;
863 int ret;
864
865 sr_debug("ISIS-Sr (%s): Install Prefix-SID %pFX %s %u", area->area_tag,
866 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
867 srp->sid.value);
868
869 /* L1 routes are preferred over the L2 ones. */
870 if (area->is_type == IS_LEVEL_1_AND_2) {
871 struct sr_prefix *srp_l1, *srp_l2;
872
873 switch (srn->level) {
874 case ISIS_LEVEL1:
875 srp_l2 = sr_prefix_find_by_area(area, ISIS_LEVEL2,
876 &srp->prefix);
877 if (srp_l2)
878 sr_prefix_uninstall(srp_l2);
879 break;
880 case ISIS_LEVEL2:
881 srp_l1 = sr_prefix_find_by_area(area, ISIS_LEVEL1,
882 &srp->prefix);
883 if (srp_l1)
884 return;
885 break;
886 default:
887 break;
888 }
889 }
890
891 /* Install corresponding LFIB entry */
892 if (srp->type == ISIS_SR_PREFIX_LOCAL)
893 ret = sr_prefix_install_local(srp);
894 else
895 ret = sr_prefix_install_remote(srp);
896 if (ret != 0)
897 sr_prefix_uninstall(srp);
898 }
899
900 /**
901 * Uninstall local or remote Prefix-SID.
902 *
903 * @param srp Segment Routing Prefix
904 */
905 static void sr_prefix_uninstall(struct sr_prefix *srp)
906 {
907 struct listnode *node;
908 struct isis_nexthop *nexthop;
909
910 /* Check that Input Label is valid */
911 if (srp->input_label == MPLS_INVALID_LABEL)
912 return;
913
914 sr_debug("ISIS-Sr: Un-install Prefix-SID %pFX %s %u", &srp->prefix,
915 IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
916 srp->sid.value);
917
918 /* Uninstall Prefix-SID from the forwarding plane. */
919 isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_DELETE, srp);
920
921 /* Reset internal state. */
922 srp->input_label = MPLS_INVALID_LABEL;
923 switch (srp->type) {
924 case ISIS_SR_PREFIX_LOCAL:
925 isis_sr_nexthop_reset(&srp->u.local.info);
926 break;
927 case ISIS_SR_PREFIX_REMOTE:
928 if (srp->u.remote.rinfo) {
929 for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops,
930 node, nexthop))
931 isis_sr_nexthop_reset(&nexthop->sr);
932 }
933 break;
934 }
935 }
936
937 /**
938 * Reinstall local or remote Prefix-SID.
939 *
940 * @param srp Segment Routing Prefix
941 */
942 static inline void sr_prefix_reinstall(struct sr_prefix *srp,
943 bool make_before_break)
944 {
945 /*
946 * Make Before Break can be used only when we know for sure that
947 * the Prefix-SID input label hasn't changed. Otherwise we need to
948 * uninstall the Prefix-SID first using the old input label before
949 * reinstalling it.
950 */
951 if (!make_before_break)
952 sr_prefix_uninstall(srp);
953
954 /* New input label is computed in sr_prefix_install() function */
955 sr_prefix_install(srp);
956 }
957
958 /* --- IS-IS LSP Parse functions -------------------------------------------- */
959
960 /**
961 * Compare Router Capabilities. Only Flags, SRGB and Algorithm are used for the
962 * comparison. MSD and SRLB modification must not trigger and SR-Prefix update.
963 *
964 * @param r1 First Router Capabilities to compare
965 * @param r2 Second Router Capabilities to compare
966 * @return 0 if r1 and r2 are equal or -1 otherwise
967 */
968 static int router_cap_cmp(const struct isis_router_cap *r1,
969 const struct isis_router_cap *r2)
970 {
971 if (r1->flags == r2->flags
972 && r1->srgb.lower_bound == r2->srgb.lower_bound
973 && r1->srgb.range_size == r2->srgb.range_size
974 && r1->algo[0] == r2->algo[0])
975 return 0;
976 else
977 return -1;
978 }
979
980 /**
981 * Parse all SR-related information from the given Router Capabilities TLV.
982 *
983 * @param area IS-IS area
984 * @param level IS-IS level
985 * @param sysid System ID of the LSP
986 * @param router_cap Router Capability subTLVs
987 *
988 * @return Segment Routing Node structure for this System ID
989 */
990 static struct sr_node *
991 parse_router_cap_tlv(struct isis_area *area, int level, const uint8_t *sysid,
992 const struct isis_router_cap *router_cap)
993 {
994 struct sr_node *srn;
995
996 if (!router_cap || router_cap->srgb.range_size == 0)
997 return NULL;
998
999 sr_debug("ISIS-Sr (%s): Parse Router Capability TLV", area->area_tag);
1000
1001 srn = sr_node_find(area, level, sysid);
1002 if (srn) {
1003 if (router_cap_cmp(&srn->cap, router_cap) != 0) {
1004 srn->state = SRDB_STATE_MODIFIED;
1005 } else
1006 srn->state = SRDB_STATE_UNCHANGED;
1007 sr_debug(" |- Found %s SR Node %s",
1008 srn->state == SRDB_STATE_MODIFIED ? "Modified"
1009 : "Unchanged",
1010 sysid_print(srn->sysid));
1011 } else {
1012 srn = sr_node_add(area, level, sysid);
1013 srn->state = SRDB_STATE_NEW;
1014 }
1015
1016 /*
1017 * Update Router Capabilities in any case as SRLB or MSD
1018 * modification are not take into account for comparison.
1019 */
1020 srn->cap = *router_cap;
1021
1022 return srn;
1023 }
1024
1025 /**
1026 * Parse list of Prefix-SID Sub-TLVs.
1027 *
1028 * @param srn Segment Routing Node
1029 * @param prefix Prefix to be parsed
1030 * @param local True if prefix comes from own LSP, false otherwise
1031 * @param prefix_sids Prefix SID subTLVs
1032 */
1033 static void parse_prefix_sid_subtlvs(struct sr_node *srn,
1034 union prefixconstptr prefix, bool local,
1035 struct isis_item_list *prefix_sids)
1036 {
1037 struct isis_area *area = srn->area;
1038 struct isis_item *i;
1039
1040 sr_debug("ISIS-Sr (%s): Parse Prefix SID TLV", area->area_tag);
1041
1042 /* Parse list of Prefix SID subTLVs */
1043 for (i = prefix_sids->head; i; i = i->next) {
1044 struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i;
1045 struct sr_prefix *srp;
1046
1047 /* Only SPF algorithm is supported right now */
1048 if (psid->algorithm != SR_ALGORITHM_SPF)
1049 continue;
1050
1051 /* Compute corresponding Segment Routing Prefix */
1052 srp = sr_prefix_find_by_node(srn, prefix);
1053 if (srp) {
1054 if (srp->sid.flags != psid->flags
1055 || srp->sid.algorithm != psid->algorithm
1056 || srp->sid.value != psid->value) {
1057 srp->sid = *psid;
1058 srp->state = SRDB_STATE_MODIFIED;
1059 } else if (srp->state == SRDB_STATE_VALIDATED)
1060 srp->state = SRDB_STATE_UNCHANGED;
1061 sr_debug(" |- Found %s Prefix-SID %pFX",
1062 srp->state == SRDB_STATE_MODIFIED
1063 ? "Modified"
1064 : "Unchanged",
1065 &srp->prefix);
1066
1067 } else {
1068 srp = sr_prefix_add(area, srn, prefix, local, psid);
1069 srp->state = SRDB_STATE_NEW;
1070 }
1071 /*
1072 * Stop the Prefix-SID iteration since we only support the SPF
1073 * algorithm for now.
1074 */
1075 break;
1076 }
1077 }
1078
1079 /**
1080 * Parse all SR-related information from the given LSP.
1081 *
1082 * @param area IS-IS area
1083 * @param level IS-IS level
1084 * @param srn Segment Routing Node
1085 * @param lsp IS-IS LSP
1086 */
1087 static void parse_lsp(struct isis_area *area, int level, struct sr_node **srn,
1088 struct isis_lsp *lsp)
1089 {
1090 struct isis_item_list *items;
1091 struct isis_item *i;
1092 bool local = lsp->own_lsp;
1093
1094 /* Check LSP sequence number */
1095 if (lsp->hdr.seqno == 0) {
1096 zlog_warn("%s: lsp with 0 seq_num - ignore", __func__);
1097 return;
1098 }
1099
1100 sr_debug("ISIS-Sr (%s): Parse LSP from node %s", area->area_tag,
1101 sysid_print(lsp->hdr.lsp_id));
1102
1103 /* Parse the Router Capability TLV. */
1104 if (*srn == NULL) {
1105 *srn = parse_router_cap_tlv(area, level, lsp->hdr.lsp_id,
1106 lsp->tlvs->router_cap);
1107 if (!*srn)
1108 return;
1109 }
1110
1111 /* Parse the Extended IP Reachability TLV. */
1112 items = &lsp->tlvs->extended_ip_reach;
1113 for (i = items->head; i; i = i->next) {
1114 struct isis_extended_ip_reach *ir;
1115
1116 ir = (struct isis_extended_ip_reach *)i;
1117 if (!ir->subtlvs)
1118 continue;
1119
1120 parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
1121 &ir->subtlvs->prefix_sids);
1122 }
1123
1124 /* Parse Multi Topology Reachable IPv6 Prefixes TLV. */
1125 items = isis_lookup_mt_items(&lsp->tlvs->mt_ipv6_reach,
1126 ISIS_MT_IPV6_UNICAST);
1127 for (i = items ? items->head : NULL; i; i = i->next) {
1128 struct isis_ipv6_reach *ir;
1129
1130 ir = (struct isis_ipv6_reach *)i;
1131 if (!ir->subtlvs)
1132 continue;
1133
1134 parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
1135 &ir->subtlvs->prefix_sids);
1136 }
1137 }
1138
1139 /**
1140 * Parse all SR-related information from the entire LSPDB.
1141 *
1142 * @param area IS-IS area
1143 */
1144 static void parse_lspdb(struct isis_area *area)
1145 {
1146 struct isis_lsp *lsp;
1147
1148 sr_debug("ISIS-Sr (%s): Parse LSP Data Base", area->area_tag);
1149
1150 /* Process all LSP from Level 1 & 2 */
1151 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
1152 frr_each (lspdb, &area->lspdb[level - 1], lsp) {
1153 struct isis_lsp *frag;
1154 struct listnode *node;
1155 struct sr_node *srn = NULL;
1156
1157 /* Skip Pseudo ID LSP and LSP without TLVs */
1158 if (LSP_PSEUDO_ID(lsp->hdr.lsp_id))
1159 continue;
1160 if (!lsp->tlvs)
1161 continue;
1162
1163 /* Parse LSP, then fragment */
1164 parse_lsp(area, level, &srn, lsp);
1165 for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag))
1166 parse_lsp(area, level, &srn, frag);
1167 }
1168 }
1169 }
1170
1171 /**
1172 * Process any new/deleted/modified Prefix-SID in the LSPDB.
1173 *
1174 * @param srn Segment Routing Node
1175 * @param srp Segment Routing Prefix
1176 */
1177 static void process_prefix_changes(struct sr_node *srn, struct sr_prefix *srp)
1178 {
1179 struct isis_area *area = srn->area;
1180
1181 /* Install/reinstall/uninstall Prefix-SID if necessary. */
1182 switch (srp->state) {
1183 case SRDB_STATE_NEW:
1184 sr_debug("ISIS-Sr (%s): Created Prefix-SID %pFX for SR node %s",
1185 area->area_tag, &srp->prefix, sysid_print(srn->sysid));
1186 sr_prefix_install(srp);
1187 break;
1188 case SRDB_STATE_MODIFIED:
1189 sr_debug(
1190 "ISIS-Sr (%s): Modified Prefix-SID %pFX for SR node %s",
1191 area->area_tag, &srp->prefix, sysid_print(srn->sysid));
1192 sr_prefix_reinstall(srp, false);
1193 break;
1194 case SRDB_STATE_UNCHANGED:
1195 break;
1196 default:
1197 sr_debug("ISIS-Sr (%s): Removed Prefix-SID %pFX for SR node %s",
1198 area->area_tag, &srp->prefix, sysid_print(srn->sysid));
1199 sr_prefix_del(area, srn, srp);
1200 return;
1201 }
1202
1203 /* Validate SRDB State for next LSPDB parsing */
1204 srp->state = SRDB_STATE_VALIDATED;
1205 }
1206
1207 /**
1208 * Process any new/deleted/modified SRGB in the LSPDB.
1209 *
1210 * @param area IS-IS area
1211 * @param level IS-IS level
1212 * @param srn Segment Routing Node
1213 */
1214 static void process_node_changes(struct isis_area *area, int level,
1215 struct sr_node *srn)
1216 {
1217 struct sr_prefix *srp;
1218 uint8_t sysid[ISIS_SYS_ID_LEN];
1219 bool adjacent;
1220
1221 memcpy(sysid, srn->sysid, sizeof(sysid));
1222
1223 /*
1224 * If an neighbor router's SRGB was changed or created, then reinstall
1225 * all Prefix-SIDs from all nodes that use this neighbor as nexthop.
1226 */
1227 adjacent = !!isis_adj_find(area, level, sysid);
1228 switch (srn->state) {
1229 case SRDB_STATE_NEW:
1230 case SRDB_STATE_MODIFIED:
1231 sr_debug("ISIS-Sr (%s): Create/Update SR node %s",
1232 area->area_tag, sysid_print(srn->sysid));
1233 if (adjacent)
1234 sr_node_srgb_update(area, level, sysid);
1235 break;
1236 case SRDB_STATE_UNCHANGED:
1237 break;
1238 default:
1239 /* SR capabilities have been removed. Delete SR-Node */
1240 sr_debug("ISIS-Sr (%s): Remove SR node %s", area->area_tag,
1241 sysid_print(srn->sysid));
1242
1243 sr_node_del(area, level, srn);
1244 /* and Update remaining Prefix-SID from all remaining SR Node */
1245 if (adjacent)
1246 sr_node_srgb_update(area, level, sysid);
1247 return;
1248 }
1249
1250 /* Validate SRDB State for next LSPDB parsing */
1251 srn->state = SRDB_STATE_VALIDATED;
1252
1253 /* Finally, process all Prefix-SID of this SR Node */
1254 frr_each_safe (srdb_node_prefix, &srn->prefix_sids, srp)
1255 process_prefix_changes(srn, srp);
1256 }
1257
1258 /**
1259 * Delete all backup Adj-SIDs.
1260 *
1261 * @param area IS-IS area
1262 * @param level IS-IS level
1263 */
1264 void isis_area_delete_backup_adj_sids(struct isis_area *area, int level)
1265 {
1266 struct sr_adjacency *sra;
1267 struct listnode *node, *nnode;
1268
1269 for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
1270 if (sra->type == ISIS_SR_LAN_BACKUP
1271 && (sra->adj->level & level))
1272 sr_adj_sid_del(sra);
1273 }
1274
1275 /**
1276 * Parse and process all SR-related Sub-TLVs after running the SPF algorithm.
1277 *
1278 * @param area IS-IS area
1279 */
1280 void isis_area_verify_sr(struct isis_area *area)
1281 {
1282 struct sr_node *srn;
1283
1284 if (!area->srdb.enabled)
1285 return;
1286
1287 /* Parse LSPDB to detect new/deleted/modified SR (sub-)TLVs. */
1288 parse_lspdb(area);
1289
1290 /* Process possible SR-related changes in the LDPSB. */
1291 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
1292 frr_each_safe (srdb_node, &area->srdb.sr_nodes[level - 1], srn)
1293 process_node_changes(area, level, srn);
1294 }
1295 }
1296
1297 /**
1298 * Once a route is updated in the SPT, reinstall or uninstall its corresponding
1299 * Prefix-SID (if any).
1300 *
1301 * @param area IS-IS area
1302 * @param prefix Prefix to be updated
1303 * @param route_info New Route Information
1304 *
1305 * @return 0
1306 */
1307 static int sr_route_update(struct isis_area *area, struct prefix *prefix,
1308 struct isis_route_info *route_info)
1309 {
1310 struct sr_prefix *srp;
1311
1312 if (!area->srdb.enabled)
1313 return 0;
1314
1315 sr_debug("ISIS-Sr (%s): Update route for prefix %pFX", area->area_tag,
1316 prefix);
1317
1318 /* Lookup to Segment Routing Prefix for this prefix */
1319 switch (area->is_type) {
1320 case IS_LEVEL_1:
1321 srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix);
1322 break;
1323 case IS_LEVEL_2:
1324 srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix);
1325 break;
1326 case IS_LEVEL_1_AND_2:
1327 srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix);
1328 if (!srp)
1329 srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix);
1330 break;
1331 default:
1332 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown area level",
1333 __func__);
1334 exit(1);
1335 }
1336
1337 /* Skip NULL or local Segment Routing Prefix */
1338 if (!srp || srp->type == ISIS_SR_PREFIX_LOCAL)
1339 return 0;
1340
1341 /* Install or unintall Prefix-SID if route is Active or not */
1342 if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) {
1343 /*
1344 * The Prefix-SID input label hasn't changed. We could use the
1345 * "Make Before Break" option. Zebra layer will update output
1346 * label by adding new label(s) before removing old one(s).
1347 */
1348 sr_prefix_reinstall(srp, true);
1349 srp->u.remote.rinfo = route_info;
1350 } else {
1351 sr_prefix_uninstall(srp);
1352 srp->u.remote.rinfo = NULL;
1353 }
1354
1355 return 0;
1356 }
1357
1358 /* --- Segment Routing Local Block management functions --------------------- */
1359
1360 /**
1361 * Initialize Segment Routing Local Block from SRDB configuration and reserve
1362 * block of bits to manage label allocation.
1363 *
1364 * @param area IS-IS area
1365 */
1366 static int sr_local_block_init(struct isis_area *area)
1367 {
1368 struct isis_sr_db *srdb = &area->srdb;
1369 struct sr_local_block *srlb = &srdb->srlb;
1370
1371 /* Check if SRLB is not already configured */
1372 if (srlb->active)
1373 return 0;
1374
1375 /*
1376 * Request SRLB to the label manager. If the allocation fails, return
1377 * an error to disable SR until a new SRLB is successfully allocated.
1378 */
1379 if (isis_zebra_request_label_range(
1380 srdb->config.srlb_lower_bound,
1381 srdb->config.srlb_upper_bound
1382 - srdb->config.srlb_lower_bound + 1)) {
1383 srlb->active = false;
1384 return -1;
1385 }
1386
1387 sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area->area_tag,
1388 srdb->config.srlb_lower_bound, srdb->config.srlb_upper_bound);
1389
1390 /* Initialize the SRLB */
1391 srlb->start = srdb->config.srlb_lower_bound;
1392 srlb->end = srdb->config.srlb_upper_bound;
1393 srlb->current = 0;
1394 /* Compute the needed Used Mark number and allocate them */
1395 srlb->max_block = (srlb->end - srlb->start + 1) / SRLB_BLOCK_SIZE;
1396 if (((srlb->end - srlb->start + 1) % SRLB_BLOCK_SIZE) != 0)
1397 srlb->max_block++;
1398 srlb->used_mark = XCALLOC(MTYPE_ISIS_SR_INFO,
1399 srlb->max_block * SRLB_BLOCK_SIZE);
1400 srlb->active = true;
1401
1402 return 0;
1403 }
1404
1405 /**
1406 * Remove Segment Routing Local Block.
1407 *
1408 * @param area IS-IS area
1409 */
1410 static void sr_local_block_delete(struct isis_area *area)
1411 {
1412 struct isis_sr_db *srdb = &area->srdb;
1413 struct sr_local_block *srlb = &srdb->srlb;
1414
1415 /* Check if SRLB is not already delete */
1416 if (!srlb->active)
1417 return;
1418
1419 sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area->area_tag,
1420 srlb->start, srlb->end);
1421
1422 /* First release the label block */
1423 isis_zebra_release_label_range(srdb->config.srlb_lower_bound,
1424 srdb->config.srlb_upper_bound);
1425
1426 /* Then reset SRLB structure */
1427 if (srlb->used_mark != NULL)
1428 XFREE(MTYPE_ISIS_SR_INFO, srlb->used_mark);
1429 srlb->active = false;
1430 }
1431
1432 /**
1433 * Request a label from the Segment Routing Local Block.
1434 *
1435 * @param srlb Segment Routing Local Block
1436 *
1437 * @return First available label on success or MPLS_INVALID_LABEL if the
1438 * block of labels is full
1439 */
1440 static mpls_label_t sr_local_block_request_label(struct sr_local_block *srlb)
1441 {
1442
1443 mpls_label_t label;
1444 uint32_t index;
1445 uint32_t pos;
1446
1447 /* Check if we ran out of available labels */
1448 if (srlb->current >= srlb->end)
1449 return MPLS_INVALID_LABEL;
1450
1451 /* Get first available label and mark it used */
1452 label = srlb->current + srlb->start;
1453 index = srlb->current / SRLB_BLOCK_SIZE;
1454 pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE);
1455 srlb->used_mark[index] |= pos;
1456
1457 /* Jump to the next free position */
1458 srlb->current++;
1459 pos = srlb->current % SRLB_BLOCK_SIZE;
1460 while (srlb->current < srlb->end) {
1461 if (pos == 0)
1462 index++;
1463 if (!((1ULL << pos) & srlb->used_mark[index]))
1464 break;
1465 else {
1466 srlb->current++;
1467 pos = srlb->current % SRLB_BLOCK_SIZE;
1468 }
1469 }
1470
1471 return label;
1472 }
1473
1474 /**
1475 * Release label in the Segment Routing Local Block.
1476 *
1477 * @param srlb Segment Routing Local Block
1478 * @param label Label to be release
1479 *
1480 * @return 0 on success or -1 if label falls outside SRLB
1481 */
1482 static int sr_local_block_release_label(struct sr_local_block *srlb,
1483 mpls_label_t label)
1484 {
1485 uint32_t index;
1486 uint32_t pos;
1487
1488 /* Check that label falls inside the SRLB */
1489 if ((label < srlb->start) || (label > srlb->end)) {
1490 flog_warn(EC_ISIS_SID_OVERFLOW,
1491 "%s: Returning label %u is outside SRLB [%u/%u]",
1492 __func__, label, srlb->start, srlb->end);
1493 return -1;
1494 }
1495
1496 index = (label - srlb->start) / SRLB_BLOCK_SIZE;
1497 pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE);
1498 srlb->used_mark[index] &= ~pos;
1499 /* Reset current to the first available position */
1500 for (index = 0; index < srlb->max_block; index++) {
1501 if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) {
1502 for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++)
1503 if (!((1ULL << pos) & srlb->used_mark[index])) {
1504 srlb->current =
1505 index * SRLB_BLOCK_SIZE + pos;
1506 break;
1507 }
1508 break;
1509 }
1510 }
1511
1512 return 0;
1513 }
1514
1515 /* --- Segment Routing Adjacency-SID management functions ------------------- */
1516
1517 /**
1518 * Add new local Adjacency-SID.
1519 *
1520 * @param adj IS-IS Adjacency
1521 * @param family Inet Family (IPv4 or IPv6)
1522 * @param backup True to initialize backup Adjacency SID
1523 * @param nexthops List of backup nexthops (for backup Adj-SIDs only)
1524 */
1525 void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, bool backup,
1526 struct list *nexthops)
1527 {
1528 struct isis_circuit *circuit = adj->circuit;
1529 struct isis_area *area = circuit->area;
1530 struct sr_adjacency *sra;
1531 struct isis_adj_sid *adj_sid;
1532 struct isis_lan_adj_sid *ladj_sid;
1533 union g_addr nexthop = {};
1534 uint8_t flags;
1535 mpls_label_t input_label;
1536
1537 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag,
1538 backup ? "Backup" : "Primary");
1539
1540 /* Determine nexthop IP address */
1541 switch (family) {
1542 case AF_INET:
1543 if (!circuit->ip_router || !adj->ipv4_address_count)
1544 return;
1545
1546 nexthop.ipv4 = adj->ipv4_addresses[0];
1547 break;
1548 case AF_INET6:
1549 if (!circuit->ipv6_router || !adj->ipv6_address_count)
1550 return;
1551
1552 nexthop.ipv6 = adj->ipv6_addresses[0];
1553 break;
1554 default:
1555 flog_err(EC_LIB_DEVELOPMENT,
1556 "%s: unexpected address-family: %u", __func__, family);
1557 exit(1);
1558 }
1559
1560 /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */
1561 flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG;
1562 if (family == AF_INET6)
1563 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG);
1564 if (backup)
1565 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
1566
1567 /* Get a label from the SRLB for this Adjacency */
1568 input_label = sr_local_block_request_label(&area->srdb.srlb);
1569 if (input_label == MPLS_INVALID_LABEL)
1570 return;
1571
1572 if (circuit->ext == NULL)
1573 circuit->ext = isis_alloc_ext_subtlvs();
1574
1575 sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra));
1576 sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL;
1577 sra->input_label = input_label;
1578 sra->nexthop.family = family;
1579 sra->nexthop.address = nexthop;
1580
1581 if (backup && nexthops) {
1582 struct isis_vertex_adj *vadj;
1583 struct listnode *node;
1584
1585 sra->backup_nexthops = list_new();
1586 for (ALL_LIST_ELEMENTS_RO(nexthops, node, vadj)) {
1587 struct isis_adjacency *adj = vadj->sadj->adj;
1588 struct mpls_label_stack *label_stack;
1589
1590 label_stack = vadj->label_stack;
1591 adjinfo2nexthop(family, sra->backup_nexthops, adj,
1592 label_stack);
1593 }
1594 }
1595
1596 switch (circuit->circ_type) {
1597 /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */
1598 case CIRCUIT_T_BROADCAST:
1599 ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid));
1600 ladj_sid->family = family;
1601 ladj_sid->flags = flags;
1602 ladj_sid->weight = 0;
1603 memcpy(ladj_sid->neighbor_id, adj->sysid,
1604 sizeof(ladj_sid->neighbor_id));
1605 ladj_sid->sid = input_label;
1606 isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid);
1607 sra->u.ladj_sid = ladj_sid;
1608 break;
1609 /* Adjacency-SID for Point to Point interface section #2.2.1 */
1610 case CIRCUIT_T_P2P:
1611 adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
1612 adj_sid->family = family;
1613 adj_sid->flags = flags;
1614 adj_sid->weight = 0;
1615 adj_sid->sid = input_label;
1616 isis_tlvs_add_adj_sid(circuit->ext, adj_sid);
1617 sra->u.adj_sid = adj_sid;
1618 break;
1619 default:
1620 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
1621 __func__, circuit->circ_type);
1622 exit(1);
1623 }
1624
1625 /* Add Adjacency-SID in SRDB */
1626 sra->adj = adj;
1627 listnode_add(area->srdb.adj_sids, sra);
1628 listnode_add(adj->adj_sids, sra);
1629
1630 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
1631 }
1632
1633 /**
1634 * Add Primary and Backup local Adjacency SID.
1635 *
1636 * @param adj IS-IS Adjacency
1637 * @param family Inet Family (IPv4 or IPv6)
1638 */
1639 static void sr_adj_sid_add(struct isis_adjacency *adj, int family)
1640 {
1641 sr_adj_sid_add_single(adj, family, false, NULL);
1642 }
1643
1644 static void sr_adj_sid_update(struct sr_adjacency *sra,
1645 struct sr_local_block *srlb)
1646 {
1647 struct isis_circuit *circuit = sra->adj->circuit;
1648
1649 /* First remove the old MPLS Label */
1650 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
1651
1652 /* Got new label in the new SRLB */
1653 sra->input_label = sr_local_block_request_label(srlb);
1654 if (sra->input_label == MPLS_INVALID_LABEL)
1655 return;
1656
1657 switch (circuit->circ_type) {
1658 case CIRCUIT_T_BROADCAST:
1659 sra->u.ladj_sid->sid = sra->input_label;
1660 break;
1661 case CIRCUIT_T_P2P:
1662 sra->u.adj_sid->sid = sra->input_label;
1663 break;
1664 default:
1665 flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
1666 __func__, circuit->circ_type);
1667 break;
1668 }
1669
1670 /* Finally configure the new MPLS Label */
1671 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
1672 }
1673
1674 /**
1675 * Delete local Adj-SID.
1676 *
1677 * @param sra Segment Routing Adjacency
1678 */
1679 static void sr_adj_sid_del(struct sr_adjacency *sra)
1680 {
1681 struct isis_circuit *circuit = sra->adj->circuit;
1682 struct isis_area *area = circuit->area;
1683
1684 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag);
1685
1686 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
1687
1688 /* Release dynamic label and remove subTLVs */
1689 switch (circuit->circ_type) {
1690 case CIRCUIT_T_BROADCAST:
1691 sr_local_block_release_label(&area->srdb.srlb,
1692 sra->u.ladj_sid->sid);
1693 isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid);
1694 break;
1695 case CIRCUIT_T_P2P:
1696 sr_local_block_release_label(&area->srdb.srlb,
1697 sra->u.adj_sid->sid);
1698 isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid);
1699 break;
1700 default:
1701 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
1702 __func__, circuit->circ_type);
1703 exit(1);
1704 }
1705
1706 if (sra->type == ISIS_SR_LAN_BACKUP && sra->backup_nexthops) {
1707 sra->backup_nexthops->del =
1708 (void (*)(void *))isis_nexthop_delete;
1709 list_delete(&sra->backup_nexthops);
1710 }
1711
1712 /* Remove Adjacency-SID from the SRDB */
1713 listnode_delete(area->srdb.adj_sids, sra);
1714 listnode_delete(sra->adj->adj_sids, sra);
1715 XFREE(MTYPE_ISIS_SR_INFO, sra);
1716 }
1717
1718 /**
1719 * Lookup Segment Routing Adj-SID by family and type.
1720 *
1721 * @param adj IS-IS Adjacency
1722 * @param family Inet Family (IPv4 or IPv6)
1723 * @param type Adjacency SID type
1724 */
1725 struct sr_adjacency *isis_sr_adj_sid_find(struct isis_adjacency *adj,
1726 int family, enum sr_adj_type type)
1727 {
1728 struct sr_adjacency *sra;
1729 struct listnode *node;
1730
1731 for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, node, sra))
1732 if (sra->nexthop.family == family && sra->type == type)
1733 return sra;
1734
1735 return NULL;
1736 }
1737
1738 /**
1739 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
1740 *
1741 * @param adj IS-IS Adjacency
1742 *
1743 * @return 0
1744 */
1745 static int sr_adj_state_change(struct isis_adjacency *adj)
1746 {
1747 struct sr_adjacency *sra;
1748 struct listnode *node, *nnode;
1749
1750 if (!adj->circuit->area->srdb.enabled)
1751 return 0;
1752
1753 if (adj->adj_state == ISIS_ADJ_UP)
1754 return 0;
1755
1756 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
1757 sr_adj_sid_del(sra);
1758
1759 return 0;
1760 }
1761
1762 /**
1763 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
1764 * IPv6 address to corresponding Adjacency-SID accordingly.
1765 *
1766 * @param adj IS-IS Adjacency
1767 * @param family Inet Family (IPv4 or IPv6)
1768 *
1769 * @return 0
1770 */
1771 static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family)
1772 {
1773 if (!adj->circuit->area->srdb.enabled)
1774 return 0;
1775
1776 sr_adj_sid_add(adj, family);
1777
1778 return 0;
1779 }
1780
1781 /**
1782 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
1783 * delete the corresponding Adjacency-SID(s) accordingly.
1784 *
1785 * @param adj IS-IS Adjacency
1786 * @param family Inet Family (IPv4 or IPv6)
1787 *
1788 * @return 0
1789 */
1790 static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family)
1791 {
1792 struct sr_adjacency *sra;
1793 struct listnode *node, *nnode;
1794
1795 if (!adj->circuit->area->srdb.enabled)
1796 return 0;
1797
1798 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
1799 if (sra->nexthop.family == family)
1800 sr_adj_sid_del(sra);
1801
1802 return 0;
1803 }
1804
1805 /**
1806 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
1807 *
1808 * @param ifp Loopback Interface
1809 *
1810 * @return 0
1811 */
1812 static int sr_if_new_hook(struct interface *ifp)
1813 {
1814 struct isis_circuit *circuit;
1815 struct isis_area *area;
1816 struct connected *connected;
1817 struct listnode *node;
1818
1819 /* Get corresponding circuit */
1820 circuit = circuit_scan_by_ifp(ifp);
1821 if (!circuit)
1822 return 0;
1823
1824 area = circuit->area;
1825 if (!area)
1826 return 0;
1827
1828 /*
1829 * Update the Node-SID flag of the configured Prefix-SID mappings if
1830 * necessary. This needs to be done here since isisd reads the startup
1831 * configuration before receiving interface information from zebra.
1832 */
1833 FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
1834 struct sr_prefix_cfg *pcfg;
1835
1836 pcfg = isis_sr_cfg_prefix_find(area, connected->address);
1837 if (!pcfg)
1838 continue;
1839
1840 if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)
1841 && !pcfg->node_sid) {
1842 pcfg->node_sid = true;
1843 lsp_regenerate_schedule(area, area->is_type, 0);
1844 }
1845 }
1846
1847 return 0;
1848 }
1849
1850 /* --- Segment Routing Show information functions --------------------------- */
1851
1852 /**
1853 * Show LFIB operation in human readable format.
1854 *
1855 * @param buf Buffer to store string output. Must be pre-allocate
1856 * @param size Size of the buffer
1857 * @param label_in Input Label
1858 * @param label_out Output Label
1859 * @param label_stack Output Label Stack (TI-LFA)
1860 *
1861 * @return String containing LFIB operation in human readable format
1862 */
1863 static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in,
1864 mpls_label_t label_out,
1865 const struct mpls_label_stack *label_stack)
1866 {
1867 if (size < 24)
1868 return NULL;
1869
1870 if (label_in == MPLS_INVALID_LABEL) {
1871 snprintf(buf, size, "no-op.");
1872 return buf;
1873 }
1874
1875 if (label_stack) {
1876 char buf_labels[256];
1877
1878 mpls_label2str(label_stack->num_labels, &label_stack->label[0],
1879 buf_labels, sizeof(buf_labels), 1);
1880
1881 snprintf(buf, size, "Swap(%u, %s)", label_in, buf_labels);
1882 return buf;
1883 }
1884
1885 switch (label_out) {
1886 case MPLS_LABEL_IMPLICIT_NULL:
1887 snprintf(buf, size, "Pop(%u)", label_in);
1888 break;
1889 case MPLS_LABEL_IPV4_EXPLICIT_NULL:
1890 case MPLS_LABEL_IPV6_EXPLICIT_NULL:
1891 snprintf(buf, size, "Swap(%u, null)", label_in);
1892 break;
1893 case MPLS_INVALID_LABEL:
1894 snprintf(buf, size, "no-op.");
1895 break;
1896 default:
1897 snprintf(buf, size, "Swap(%u, %u)", label_in, label_out);
1898 break;
1899 }
1900 return buf;
1901 }
1902
1903 /**
1904 * Show Local Prefix-SID.
1905 *
1906 * @param vty VTY output
1907 * @param tt Table format
1908 * @param area IS-IS area
1909 * @param srp Segment Routing Prefix
1910 */
1911 static void show_prefix_sid_local(struct vty *vty, struct ttable *tt,
1912 const struct isis_area *area,
1913 const struct sr_prefix *srp)
1914 {
1915 const struct sr_nexthop_info *srnh = &srp->u.local.info;
1916 char buf_prefix[BUFSIZ];
1917 char buf_oper[BUFSIZ];
1918 char buf_iface[BUFSIZ];
1919 char buf_uptime[BUFSIZ];
1920
1921 if (srnh->label != MPLS_INVALID_LABEL) {
1922 struct interface *ifp;
1923 ifp = if_lookup_prefix(&srp->prefix, VRF_DEFAULT);
1924 if (ifp)
1925 strlcpy(buf_iface, ifp->name, sizeof(buf_iface));
1926 else
1927 snprintf(buf_iface, sizeof(buf_iface), "-");
1928 log_uptime(srnh->uptime, buf_uptime, sizeof(buf_uptime));
1929 } else {
1930 snprintf(buf_iface, sizeof(buf_iface), "-");
1931 snprintf(buf_uptime, sizeof(buf_uptime), "-");
1932 }
1933 sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label,
1934 MPLS_LABEL_IMPLICIT_NULL, NULL);
1935
1936 ttable_add_row(tt, "%s|%u|%s|-|%s|%s",
1937 prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)),
1938 srp->sid.value, buf_oper, buf_iface, buf_uptime);
1939 }
1940
1941 /**
1942 * Show Remote Prefix-SID.
1943 *
1944 * @param vty VTY output
1945 * @param tt Table format
1946 * @param area IS-IS area
1947 * @param srp Segment Routing Prefix
1948 */
1949 static void show_prefix_sid_remote(struct vty *vty, struct ttable *tt,
1950 const struct isis_area *area,
1951 const struct sr_prefix *srp, bool backup)
1952 {
1953 struct isis_nexthop *nexthop;
1954 struct listnode *node;
1955 char buf_prefix[BUFSIZ];
1956 char buf_oper[BUFSIZ];
1957 char buf_nhop[BUFSIZ];
1958 char buf_iface[BUFSIZ];
1959 char buf_uptime[BUFSIZ];
1960 bool first = true;
1961 struct isis_route_info *rinfo;
1962
1963 (void)prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix));
1964
1965 rinfo = srp->u.remote.rinfo;
1966 if (rinfo && backup)
1967 rinfo = rinfo->backup;
1968 if (!rinfo) {
1969 ttable_add_row(tt, "%s|%u|%s|-|-|-", buf_prefix, srp->sid.value,
1970 sr_op2str(buf_oper, sizeof(buf_oper),
1971 srp->input_label,
1972 MPLS_LABEL_IMPLICIT_NULL, NULL));
1973 return;
1974 }
1975
1976 for (ALL_LIST_ELEMENTS_RO(rinfo->nexthops, node, nexthop)) {
1977 struct interface *ifp;
1978
1979 inet_ntop(nexthop->family, &nexthop->ip, buf_nhop,
1980 sizeof(buf_nhop));
1981 ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT);
1982 if (ifp)
1983 strlcpy(buf_iface, ifp->name, sizeof(buf_iface));
1984 else
1985 snprintf(buf_iface, sizeof(buf_iface), "ifindex %u",
1986 nexthop->ifindex);
1987 if (nexthop->sr.label == MPLS_INVALID_LABEL)
1988 snprintf(buf_uptime, sizeof(buf_uptime), "-");
1989 else
1990 log_uptime(nexthop->sr.uptime, buf_uptime,
1991 sizeof(buf_uptime));
1992 sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label,
1993 nexthop->sr.label, nexthop->label_stack);
1994
1995 if (first)
1996 ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", buf_prefix,
1997 srp->sid.value, buf_oper, buf_nhop,
1998 buf_iface, buf_uptime);
1999 else
2000 ttable_add_row(tt, "|||%s|%s|%s|%s", buf_oper, buf_nhop,
2001 buf_iface, buf_uptime);
2002 first = false;
2003 }
2004 }
2005
2006 /**
2007 * Show Prefix-SIDs.
2008 *
2009 * @param vty VTY output
2010 * @param area IS-IS area
2011 * @param level IS-IS level
2012 */
2013 static void show_prefix_sids(struct vty *vty, struct isis_area *area, int level,
2014 bool backup)
2015 {
2016 struct sr_prefix *srp;
2017 struct ttable *tt;
2018
2019 if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0)
2020 return;
2021
2022 vty_out(vty, " IS-IS %s Prefix-SIDs:\n\n", circuit_t2string(level));
2023
2024 /* Prepare table. */
2025 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
2026 ttable_add_row(tt, "Prefix|SID|Label Op.|Nexthop|Interface|Uptime");
2027 tt->style.cell.rpad = 2;
2028 tt->style.corner = '+';
2029 ttable_restyle(tt);
2030 ttable_rowseps(tt, 0, BOTTOM, true, '-');
2031
2032 /* Process all Prefix-SID from the SRDB */
2033 frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) {
2034 switch (srp->type) {
2035 case ISIS_SR_PREFIX_LOCAL:
2036 show_prefix_sid_local(vty, tt, area, srp);
2037 break;
2038 case ISIS_SR_PREFIX_REMOTE:
2039 show_prefix_sid_remote(vty, tt, area, srp, backup);
2040 break;
2041 }
2042 }
2043
2044 /* Dump the generated table. */
2045 if (tt->nrows > 1) {
2046 char *table;
2047
2048 table = ttable_dump(tt, "\n");
2049 vty_out(vty, "%s\n", table);
2050 XFREE(MTYPE_TMP, table);
2051 }
2052 ttable_del(tt);
2053 }
2054
2055 /**
2056 * Declaration of new show commands.
2057 */
2058 DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd,
2059 "show isis [vrf <NAME|all>] segment-routing prefix-sids [backup]",
2060 SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
2061 "All VRFs\n"
2062 "Segment-Routing\n"
2063 "Segment-Routing Prefix-SIDs\n"
2064 "Show backup Prefix-SIDs\n")
2065 {
2066 struct listnode *node, *inode;
2067 struct isis_area *area;
2068 struct isis *isis = NULL;
2069 const char *vrf_name = VRF_DEFAULT_NAME;
2070 bool all_vrf = false;
2071 bool backup = false;
2072 int idx = 0;
2073
2074 ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf);
2075 if (argv_find(argv, argc, "backup", &idx))
2076 backup = true;
2077
2078 if (vrf_name) {
2079 if (all_vrf) {
2080 for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
2081 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node,
2082 area)) {
2083 vty_out(vty, "Area %s:\n",
2084 area->area_tag ? area->area_tag
2085 : "null");
2086 for (int level = ISIS_LEVEL1;
2087 level <= ISIS_LEVELS; level++)
2088 show_prefix_sids(vty, area,
2089 level, backup);
2090 }
2091 }
2092 return 0;
2093 }
2094 isis = isis_lookup_by_vrfname(vrf_name);
2095 if (isis != NULL) {
2096 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node,
2097 area)) {
2098 vty_out(vty, "Area %s:\n",
2099 area->area_tag ? area->area_tag
2100 : "null");
2101 for (int level = ISIS_LEVEL1;
2102 level <= ISIS_LEVELS; level++)
2103 show_prefix_sids(vty, area, level,
2104 backup);
2105 }
2106 }
2107 }
2108
2109 return CMD_SUCCESS;
2110 }
2111
2112 /**
2113 * Show Segment Routing Node.
2114 *
2115 * @param vty VTY output
2116 * @param area IS-IS area
2117 * @param level IS-IS level
2118 */
2119 static void show_node(struct vty *vty, struct isis_area *area, int level)
2120 {
2121 struct sr_node *srn;
2122 struct ttable *tt;
2123
2124 if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0)
2125 return;
2126
2127 vty_out(vty, " IS-IS %s SR-Node:\n\n", circuit_t2string(level));
2128
2129 /* Prepare table. */
2130 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
2131 ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD");
2132 tt->style.cell.rpad = 2;
2133 tt->style.corner = '+';
2134 ttable_restyle(tt);
2135 ttable_rowseps(tt, 0, BOTTOM, true, '-');
2136
2137 /* Process all SR-Node from the SRDB */
2138 frr_each (srdb_node, &area->srdb.sr_nodes[level - 1], srn) {
2139 ttable_add_row(
2140 tt, "%s|%u - %u|%u - %u|%s|%u",
2141 sysid_print(srn->sysid),
2142 srn->cap.srgb.lower_bound,
2143 srn->cap.srgb.lower_bound + srn->cap.srgb.range_size
2144 - 1,
2145 srn->cap.srlb.lower_bound,
2146 srn->cap.srlb.lower_bound + srn->cap.srlb.range_size
2147 - 1,
2148 srn->cap.algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF",
2149 srn->cap.msd);
2150 }
2151
2152 /* Dump the generated table. */
2153 if (tt->nrows > 1) {
2154 char *table;
2155
2156 table = ttable_dump(tt, "\n");
2157 vty_out(vty, "%s\n", table);
2158 XFREE(MTYPE_TMP, table);
2159 }
2160 ttable_del(tt);
2161 }
2162
2163 DEFUN(show_sr_node, show_sr_node_cmd,
2164 "show isis segment-routing node",
2165 SHOW_STR PROTO_HELP
2166 "Segment-Routing\n"
2167 "Segment-Routing node\n")
2168 {
2169 struct listnode *node, *inode;
2170 struct isis_area *area;
2171 struct isis *isis;
2172
2173 for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
2174 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
2175 vty_out(vty, "Area %s:\n",
2176 area->area_tag ? area->area_tag : "null");
2177
2178 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
2179 level++)
2180 show_node(vty, area, level);
2181 }
2182 }
2183
2184 return CMD_SUCCESS;
2185 }
2186
2187
2188 /* --- IS-IS Segment Routing Management function ---------------------------- */
2189
2190 /**
2191 * Thread function to re-attempt connection to the Label Manager and thus be
2192 * able to start Segment Routing.
2193 *
2194 * @param start Thread structure that contains area as argument
2195 *
2196 * @return 1 on success
2197 */
2198 static int sr_start_label_manager(struct thread *start)
2199 {
2200 struct isis_area *area;
2201
2202 area = THREAD_ARG(start);
2203
2204 /* re-attempt to start SR & Label Manager connection */
2205 isis_sr_start(area);
2206
2207 return 1;
2208 }
2209
2210 /**
2211 * Enable SR on the given IS-IS area.
2212 *
2213 * @param area IS-IS area
2214 *
2215 * @return 0 on success, -1 otherwise
2216 */
2217 int isis_sr_start(struct isis_area *area)
2218 {
2219 struct isis_sr_db *srdb = &area->srdb;
2220 struct isis_adjacency *adj;
2221 struct listnode *node;
2222
2223 /* First start Label Manager if not ready */
2224 if (!isis_zebra_label_manager_ready())
2225 if (isis_zebra_label_manager_connect() < 0) {
2226 /* Re-attempt to connect to Label Manager in 1 sec. */
2227 thread_add_timer(master, sr_start_label_manager, area,
2228 1, &srdb->t_start_lm);
2229 return -1;
2230 }
2231
2232 /* Label Manager is ready, initialize the SRLB */
2233 if (sr_local_block_init(area) < 0)
2234 return -1;
2235
2236 /*
2237 * Request SGRB to the label manager if not already active. If the
2238 * allocation fails, return an error to disable SR until a new SRGB
2239 * is successfully allocated.
2240 */
2241 if (!srdb->srgb_active) {
2242 if (isis_zebra_request_label_range(
2243 srdb->config.srgb_lower_bound,
2244 srdb->config.srgb_upper_bound
2245 - srdb->config.srgb_lower_bound + 1)
2246 < 0) {
2247 srdb->srgb_active = false;
2248 return -1;
2249 } else
2250 srdb->srgb_active = true;
2251 }
2252
2253 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
2254 area->area_tag);
2255
2256 /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */
2257 for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
2258 if (adj->ipv4_address_count > 0)
2259 sr_adj_sid_add(adj, AF_INET);
2260 if (adj->ipv6_address_count > 0)
2261 sr_adj_sid_add(adj, AF_INET6);
2262 }
2263
2264 area->srdb.enabled = true;
2265
2266 /* Regenerate LSPs to advertise Segment Routing capabilities. */
2267 lsp_regenerate_schedule(area, area->is_type, 0);
2268
2269 return 0;
2270 }
2271
2272 /**
2273 * Disable SR on the given IS-IS area.
2274 *
2275 * @param area IS-IS area
2276 */
2277 void isis_sr_stop(struct isis_area *area)
2278 {
2279 struct isis_sr_db *srdb = &area->srdb;
2280 struct sr_adjacency *sra;
2281 struct listnode *node, *nnode;
2282
2283 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
2284 area->area_tag);
2285
2286 /* Disable any re-attempt to connect to Label Manager */
2287 thread_cancel(&srdb->t_start_lm);
2288
2289 /* Uninstall all local Adjacency-SIDs. */
2290 for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
2291 sr_adj_sid_del(sra);
2292
2293 /* Uninstall all Prefix-SIDs from all SR Node. */
2294 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
2295 while (srdb_node_count(&srdb->sr_nodes[level - 1]) > 0) {
2296 struct sr_node *srn;
2297
2298 srn = srdb_node_first(&srdb->sr_nodes[level - 1]);
2299 sr_node_del(area, level, srn);
2300 }
2301 }
2302
2303 /* Release SRGB if active. */
2304 if (srdb->srgb_active) {
2305 isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
2306 srdb->config.srgb_upper_bound);
2307 srdb->srgb_active = false;
2308 }
2309
2310 /* Delete SRLB */
2311 sr_local_block_delete(area);
2312
2313 area->srdb.enabled = false;
2314
2315 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
2316 lsp_regenerate_schedule(area, area->is_type, 0);
2317 }
2318
2319 /**
2320 * IS-IS Segment Routing initialization for given area.
2321 *
2322 * @param area IS-IS area
2323 */
2324 void isis_sr_area_init(struct isis_area *area)
2325 {
2326 struct isis_sr_db *srdb = &area->srdb;
2327
2328 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
2329 area->area_tag);
2330
2331 /* Initialize Segment Routing Data Base */
2332 memset(srdb, 0, sizeof(*srdb));
2333 srdb->adj_sids = list_new();
2334
2335 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
2336 srdb_node_init(&srdb->sr_nodes[level - 1]);
2337 srdb_area_prefix_init(&srdb->prefix_sids[level - 1]);
2338 }
2339
2340 /* Pull defaults from the YANG module. */
2341 #ifndef FABRICD
2342 srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR);
2343 srdb->config.srgb_lower_bound =
2344 yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR);
2345 srdb->config.srgb_upper_bound =
2346 yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR);
2347 srdb->config.srlb_lower_bound =
2348 yang_get_default_uint32("%s/srlb/lower-bound", ISIS_SR);
2349 srdb->config.srlb_upper_bound =
2350 yang_get_default_uint32("%s/srlb/upper-bound", ISIS_SR);
2351 #else
2352 srdb->config.enabled = false;
2353 srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND;
2354 srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND;
2355 srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND;
2356 srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND;
2357 #endif
2358 srdb->config.msd = 0;
2359 srdb_prefix_cfg_init(&srdb->config.prefix_sids);
2360 }
2361
2362 /**
2363 * Terminate IS-IS Segment Routing for the given area.
2364 *
2365 * @param area IS-IS area
2366 */
2367 void isis_sr_area_term(struct isis_area *area)
2368 {
2369 struct isis_sr_db *srdb = &area->srdb;
2370
2371 /* Stop Segment Routing */
2372 if (area->srdb.enabled)
2373 isis_sr_stop(area);
2374
2375 /* Clear Prefix-SID configuration. */
2376 while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) {
2377 struct sr_prefix_cfg *pcfg;
2378
2379 pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids);
2380 isis_sr_cfg_prefix_del(pcfg);
2381 }
2382 }
2383
2384 /**
2385 * IS-IS Segment Routing global initialization.
2386 */
2387 void isis_sr_init(void)
2388 {
2389 install_element(VIEW_NODE, &show_sr_prefix_sids_cmd);
2390 install_element(VIEW_NODE, &show_sr_node_cmd);
2391
2392 /* Register hooks. */
2393 hook_register(isis_adj_state_change_hook, sr_adj_state_change);
2394 hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
2395 hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
2396 hook_register(isis_route_update_hook, sr_route_update);
2397 hook_register(isis_if_new_hook, sr_if_new_hook);
2398 }
2399
2400 /**
2401 * IS-IS Segment Routing global terminate.
2402 */
2403 void isis_sr_term(void)
2404 {
2405 /* Unregister hooks. */
2406 hook_unregister(isis_adj_state_change_hook, sr_adj_state_change);
2407 hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
2408 hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
2409 hook_unregister(isis_route_update_hook, sr_route_update);
2410 hook_unregister(isis_if_new_hook, sr_if_new_hook);
2411 }