]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_sr.c
Merge pull request #12837 from donaldsharp/unlikely_routemap
[mirror_frr.git] / isisd / isis_sr.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * This is an implementation of Segment Routing for IS-IS as per RFC 8667
4 *
5 * Copyright (C) 2019 Orange http://www.orange.com
6 *
7 * Author: Olivier Dugeon <olivier.dugeon@orange.com>
8 * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
9 */
10
11 #include <zebra.h>
12
13 #include "if.h"
14 #include "linklist.h"
15 #include "log.h"
16 #include "command.h"
17 #include "termtable.h"
18 #include "memory.h"
19 #include "prefix.h"
20 #include "table.h"
21 #include "srcdest_table.h"
22 #include "vty.h"
23 #include "zclient.h"
24 #include "lib/lib_errors.h"
25
26 #include "isisd/isisd.h"
27 #include "isisd/isis_spf.h"
28 #include "isisd/isis_spf_private.h"
29 #include "isisd/isis_adjacency.h"
30 #include "isisd/isis_route.h"
31 #include "isisd/isis_mt.h"
32 #include "isisd/isis_sr.h"
33 #include "isisd/isis_tlvs.h"
34 #include "isisd/isis_misc.h"
35 #include "isisd/isis_zebra.h"
36 #include "isisd/isis_errors.h"
37
38 /* Local variables and functions */
39 DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information");
40
41 static void sr_local_block_delete(struct isis_area *area);
42 static int sr_local_block_init(struct isis_area *area);
43 static void sr_adj_sid_update(struct sr_adjacency *sra,
44 struct sr_local_block *srlb);
45 static void sr_adj_sid_del(struct sr_adjacency *sra);
46
47 /* --- RB-Tree Management functions ----------------------------------------- */
48
49 /**
50 * Configured SR Prefix comparison for RB-Tree.
51 *
52 * @param a First SR prefix
53 * @param b Second SR prefix
54 *
55 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
56 */
57 static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a,
58 const struct sr_prefix_cfg *b)
59 {
60 return prefix_cmp(&a->prefix, &b->prefix);
61 }
62 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry,
63 sr_prefix_sid_cfg_compare);
64
65 /**
66 * Find SRGB associated to a System ID.
67 *
68 * @param area IS-IS LSP database
69 * @param sysid System ID to lookup
70 *
71 * @return Pointer to SRGB if found, NULL otherwise
72 */
73 struct isis_sr_block *isis_sr_find_srgb(struct lspdb_head *lspdb,
74 const uint8_t *sysid)
75 {
76 struct isis_lsp *lsp;
77
78 lsp = isis_root_system_lsp(lspdb, sysid);
79 if (!lsp)
80 return NULL;
81
82 if (!lsp->tlvs->router_cap
83 || lsp->tlvs->router_cap->srgb.range_size == 0)
84 return NULL;
85
86 return &lsp->tlvs->router_cap->srgb;
87 }
88
89 /**
90 * Compute input label for the given Prefix-SID.
91 *
92 * @param area IS-IS area
93 * @param psid IS-IS Prefix-SID Sub-TLV
94 * @param local Indicates whether the Prefix-SID is local or not
95 *
96 * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow
97 */
98 mpls_label_t sr_prefix_in_label(struct isis_area *area,
99 struct isis_prefix_sid *psid, bool local)
100 {
101 /*
102 * No need to assign a label for local Prefix-SIDs unless the no-PHP
103 * flag is set.
104 */
105 if (local
106 && (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP)
107 || CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL)))
108 return MPLS_INVALID_LABEL;
109
110 /* Return SID value as MPLS label if it is an Absolute SID */
111 if (CHECK_FLAG(psid->flags,
112 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL))
113 return psid->value;
114
115 /* Check that SID index falls inside the SRGB */
116 if (psid->value >= (area->srdb.config.srgb_upper_bound
117 - area->srdb.config.srgb_lower_bound + 1)) {
118 flog_warn(EC_ISIS_SID_OVERFLOW,
119 "%s: SID index %u falls outside local SRGB range",
120 __func__, psid->value);
121 return MPLS_INVALID_LABEL;
122 }
123
124 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
125 return (area->srdb.config.srgb_lower_bound + psid->value);
126 }
127
128 /**
129 * Compute output label for the given Prefix-SID.
130 *
131 * @param lspdb IS-IS LSP database
132 * @param family Prefix-SID address family
133 * @param psid Prefix-SID Sub-TLV
134 * @param nh_sysid System ID of the nexthop node
135 * @param last_hop Indicates whether the nexthop node is the last hop
136 *
137 * @return MPLS label or MPLS_INVALID_LABEL in case of error
138 */
139 mpls_label_t sr_prefix_out_label(struct lspdb_head *lspdb, int family,
140 struct isis_prefix_sid *psid,
141 const uint8_t *nh_sysid, bool last_hop)
142 {
143 struct isis_sr_block *nh_srgb;
144
145 if (last_hop) {
146 if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP))
147 return MPLS_LABEL_IMPLICIT_NULL;
148
149 if (CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) {
150 if (family == AF_INET)
151 return MPLS_LABEL_IPV4_EXPLICIT_NULL;
152 else
153 return MPLS_LABEL_IPV6_EXPLICIT_NULL;
154 }
155 /* Fallthrough */
156 }
157
158 /* Return SID value as MPLS label if it is an Absolute SID */
159 if (CHECK_FLAG(psid->flags,
160 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) {
161 /*
162 * V/L SIDs have local significance, so only adjacent routers
163 * can use them (RFC8667 section #2.1.1.1)
164 */
165 if (!last_hop)
166 return MPLS_INVALID_LABEL;
167 return psid->value;
168 }
169
170 /* Check that SID index falls inside the SRGB */
171 nh_srgb = isis_sr_find_srgb(lspdb, nh_sysid);
172 if (!nh_srgb)
173 return MPLS_INVALID_LABEL;
174
175 /*
176 * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
177 * IPv6 packets.
178 */
179 if ((family == AF_INET && !IS_SR_IPV4(nh_srgb))
180 || (family == AF_INET6 && !IS_SR_IPV6(nh_srgb)))
181 return MPLS_INVALID_LABEL;
182
183 if (psid->value >= nh_srgb->range_size) {
184 flog_warn(EC_ISIS_SID_OVERFLOW,
185 "%s: SID index %u falls outside remote SRGB range",
186 __func__, psid->value);
187 return MPLS_INVALID_LABEL;
188 }
189
190 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
191 return (nh_srgb->lower_bound + psid->value);
192 }
193
194 /* --- Functions used for Yang model and CLI to configure Segment Routing --- */
195
196 /**
197 * Check if prefix correspond to a Node SID.
198 *
199 * @param ifp Interface
200 * @param prefix Prefix to be checked
201 *
202 * @return True if the interface/address pair corresponds to a Node-SID
203 */
204 static bool sr_prefix_is_node_sid(const struct interface *ifp,
205 const struct prefix *prefix)
206 {
207 return (if_is_loopback(ifp) && is_host_route(prefix));
208 }
209
210 /**
211 * Update local SRGB configuration. SRGB is reserved though Label Manager.
212 * This function trigger the update of local Prefix-SID installation.
213 *
214 * @param area IS-IS area
215 * @param lower_bound Lower bound of SRGB
216 * @param upper_bound Upper bound of SRGB
217 *
218 * @return 0 on success, -1 otherwise
219 */
220 int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
221 uint32_t upper_bound)
222 {
223 struct isis_sr_db *srdb = &area->srdb;
224
225 sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
226 area->area_tag, lower_bound, upper_bound);
227
228 /* Just store new SRGB values if Label Manager is not available.
229 * SRGB will be configured later when SR start */
230 if (!isis_zebra_label_manager_ready()) {
231 srdb->config.srgb_lower_bound = lower_bound;
232 srdb->config.srgb_upper_bound = upper_bound;
233 return 0;
234 }
235
236 /* Label Manager is ready, start by releasing the old SRGB. */
237 if (srdb->srgb_active) {
238 isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
239 srdb->config.srgb_upper_bound);
240 srdb->srgb_active = false;
241 }
242
243 srdb->config.srgb_lower_bound = lower_bound;
244 srdb->config.srgb_upper_bound = upper_bound;
245
246 if (srdb->enabled) {
247 /* then request new SRGB if SR is enabled. */
248 if (isis_zebra_request_label_range(
249 srdb->config.srgb_lower_bound,
250 srdb->config.srgb_upper_bound
251 - srdb->config.srgb_lower_bound + 1) < 0) {
252 srdb->srgb_active = false;
253 return -1;
254 } else
255 srdb->srgb_active = true;
256
257
258 sr_debug(" |- Got new SRGB [%u/%u]",
259 srdb->config.srgb_lower_bound,
260 srdb->config.srgb_upper_bound);
261
262 lsp_regenerate_schedule(area, area->is_type, 0);
263 } else if (srdb->config.enabled) {
264 /* Try to enable SR again using the new SRGB. */
265 isis_sr_start(area);
266 }
267
268 return 0;
269 }
270
271 /**
272 * Update Segment Routing Local Block range which is reserved though the
273 * Label Manager. This function trigger the update of local Adjacency-SID
274 * installation.
275 *
276 * @param area IS-IS area
277 * @param lower_bound Lower bound of SRLB
278 * @param upper_bound Upper bound of SRLB
279 *
280 * @return 0 on success, -1 otherwise
281 */
282 int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound,
283 uint32_t upper_bound)
284 {
285 struct isis_sr_db *srdb = &area->srdb;
286 struct listnode *node;
287 struct sr_adjacency *sra;
288
289 sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
290 area->area_tag, lower_bound, upper_bound);
291
292 /* Just store new SRLB values if Label Manager is not available.
293 * SRLB will be configured later when SR start */
294 if (!isis_zebra_label_manager_ready()) {
295 srdb->config.srlb_lower_bound = lower_bound;
296 srdb->config.srlb_upper_bound = upper_bound;
297 return 0;
298 }
299
300 /* LM is ready, start by deleting the old SRLB */
301 sr_local_block_delete(area);
302
303 srdb->config.srlb_lower_bound = lower_bound;
304 srdb->config.srlb_upper_bound = upper_bound;
305
306 if (srdb->enabled) {
307 /* Initialize new SRLB */
308 if (sr_local_block_init(area) != 0)
309 return -1;
310
311 /* Reinstall local Adjacency-SIDs with new labels. */
312 for (ALL_LIST_ELEMENTS_RO(area->srdb.adj_sids, node, sra))
313 sr_adj_sid_update(sra, &srdb->srlb);
314
315 /* Update and Flood LSP */
316 lsp_regenerate_schedule(area, area->is_type, 0);
317 } else if (srdb->config.enabled) {
318 /* Try to enable SR again using the new SRLB. */
319 isis_sr_start(area);
320 }
321
322 return 0;
323 }
324
325 /**
326 * Add new Prefix-SID configuration to the SRDB.
327 *
328 * @param area IS-IS area
329 * @param prefix Prefix to be added
330 *
331 * @return Newly added Prefix-SID configuration structure
332 */
333 struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
334 const struct prefix *prefix)
335 {
336 struct sr_prefix_cfg *pcfg;
337 struct interface *ifp;
338
339 sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix);
340
341 pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg));
342 pcfg->prefix = *prefix;
343 pcfg->area = area;
344
345 /* Pull defaults from the YANG module. */
346 pcfg->sid_type = yang_get_default_enum(
347 "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR);
348 pcfg->last_hop_behavior = yang_get_default_enum(
349 "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR);
350
351 /* Mark as node Sid if the prefix is host and configured in loopback */
352 ifp = if_lookup_prefix(prefix, VRF_DEFAULT);
353 if (ifp && sr_prefix_is_node_sid(ifp, prefix))
354 pcfg->node_sid = true;
355
356 /* Save prefix-sid configuration. */
357 srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg);
358
359 return pcfg;
360 }
361
362 /**
363 * Removal of locally configured Prefix-SID.
364 *
365 * @param pcfg Configured Prefix-SID
366 */
367 void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg)
368 {
369 struct isis_area *area = pcfg->area;
370
371 sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u",
372 area->area_tag, &pcfg->prefix,
373 pcfg->sid_type == SR_SID_VALUE_TYPE_INDEX ? "index" : "label",
374 pcfg->sid);
375
376 srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg);
377 XFREE(MTYPE_ISIS_SR_INFO, pcfg);
378 }
379
380 /**
381 * Lookup for Prefix-SID in the local configuration.
382 *
383 * @param area IS-IS area
384 * @param prefix Prefix to lookup
385 *
386 * @return Configured Prefix-SID structure if found, NULL otherwise
387 */
388 struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area,
389 union prefixconstptr prefix)
390 {
391 struct sr_prefix_cfg pcfg = {};
392
393 prefix_copy(&pcfg.prefix, prefix.p);
394 return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg);
395 }
396
397 /**
398 * Fill in Prefix-SID Sub-TLV according to the corresponding configuration.
399 *
400 * @param pcfg Prefix-SID configuration
401 * @param external False if prefix is locally configured, true otherwise
402 * @param psid Prefix-SID sub-TLV to be updated
403 */
404 void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external,
405 struct isis_prefix_sid *psid)
406 {
407 /* Set SID algorithm. */
408 psid->algorithm = SR_ALGORITHM_SPF;
409
410 /* Set SID flags. */
411 psid->flags = 0;
412 switch (pcfg->last_hop_behavior) {
413 case SR_LAST_HOP_BEHAVIOR_EXP_NULL:
414 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
415 SET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
416 break;
417 case SR_LAST_HOP_BEHAVIOR_NO_PHP:
418 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
419 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
420 break;
421 case SR_LAST_HOP_BEHAVIOR_PHP:
422 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
423 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
424 break;
425 }
426 if (external)
427 SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED);
428 if (pcfg->node_sid && !pcfg->n_flag_clear)
429 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE);
430
431 /* Set SID value. */
432 psid->value = pcfg->sid;
433 if (pcfg->sid_type == SR_SID_VALUE_TYPE_ABSOLUTE) {
434 SET_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE);
435 SET_FLAG(psid->flags, ISIS_PREFIX_SID_LOCAL);
436 }
437 }
438
439 /**
440 * Delete all backup Adj-SIDs.
441 *
442 * @param area IS-IS area
443 * @param level IS-IS level
444 */
445 void isis_area_delete_backup_adj_sids(struct isis_area *area, int level)
446 {
447 struct sr_adjacency *sra;
448 struct listnode *node, *nnode;
449
450 for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
451 if (sra->type == ISIS_SR_LAN_BACKUP
452 && (sra->adj->level & level))
453 sr_adj_sid_del(sra);
454 }
455
456 /* --- Segment Routing Local Block management functions --------------------- */
457
458 /**
459 * Initialize Segment Routing Local Block from SRDB configuration and reserve
460 * block of bits to manage label allocation.
461 *
462 * @param area IS-IS area
463 */
464 static int sr_local_block_init(struct isis_area *area)
465 {
466 struct isis_sr_db *srdb = &area->srdb;
467 struct sr_local_block *srlb = &srdb->srlb;
468
469 /* Check if SRLB is not already configured */
470 if (srlb->active)
471 return 0;
472
473 /*
474 * Request SRLB to the label manager. If the allocation fails, return
475 * an error to disable SR until a new SRLB is successfully allocated.
476 */
477 if (isis_zebra_request_label_range(
478 srdb->config.srlb_lower_bound,
479 srdb->config.srlb_upper_bound
480 - srdb->config.srlb_lower_bound + 1)) {
481 srlb->active = false;
482 return -1;
483 }
484
485 sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area->area_tag,
486 srdb->config.srlb_lower_bound, srdb->config.srlb_upper_bound);
487
488 /* Initialize the SRLB */
489 srlb->start = srdb->config.srlb_lower_bound;
490 srlb->end = srdb->config.srlb_upper_bound;
491 srlb->current = 0;
492 /* Compute the needed Used Mark number and allocate them */
493 srlb->max_block = (srlb->end - srlb->start + 1) / SRLB_BLOCK_SIZE;
494 if (((srlb->end - srlb->start + 1) % SRLB_BLOCK_SIZE) != 0)
495 srlb->max_block++;
496 srlb->used_mark = XCALLOC(MTYPE_ISIS_SR_INFO,
497 srlb->max_block * SRLB_BLOCK_SIZE);
498 srlb->active = true;
499
500 return 0;
501 }
502
503 /**
504 * Remove Segment Routing Local Block.
505 *
506 * @param area IS-IS area
507 */
508 static void sr_local_block_delete(struct isis_area *area)
509 {
510 struct isis_sr_db *srdb = &area->srdb;
511 struct sr_local_block *srlb = &srdb->srlb;
512
513 /* Check if SRLB is not already delete */
514 if (!srlb->active)
515 return;
516
517 sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area->area_tag,
518 srlb->start, srlb->end);
519
520 /* First release the label block */
521 isis_zebra_release_label_range(srdb->config.srlb_lower_bound,
522 srdb->config.srlb_upper_bound);
523
524 /* Then reset SRLB structure */
525 if (srlb->used_mark != NULL)
526 XFREE(MTYPE_ISIS_SR_INFO, srlb->used_mark);
527 srlb->active = false;
528 }
529
530 /**
531 * Request a label from the Segment Routing Local Block.
532 *
533 * @param srlb Segment Routing Local Block
534 *
535 * @return First available label on success or MPLS_INVALID_LABEL if the
536 * block of labels is full
537 */
538 static mpls_label_t sr_local_block_request_label(struct sr_local_block *srlb)
539 {
540 mpls_label_t label;
541 uint32_t index;
542 uint32_t pos;
543 uint32_t size = srlb->end - srlb->start + 1;
544
545 /* Check if we ran out of available labels */
546 if (srlb->current >= size)
547 return MPLS_INVALID_LABEL;
548
549 /* Get first available label and mark it used */
550 label = srlb->current + srlb->start;
551 index = srlb->current / SRLB_BLOCK_SIZE;
552 pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE);
553 srlb->used_mark[index] |= pos;
554
555 /* Jump to the next free position */
556 srlb->current++;
557 pos = srlb->current % SRLB_BLOCK_SIZE;
558 while (srlb->current < size) {
559 if (pos == 0)
560 index++;
561 if (!((1ULL << pos) & srlb->used_mark[index]))
562 break;
563 else {
564 srlb->current++;
565 pos = srlb->current % SRLB_BLOCK_SIZE;
566 }
567 }
568
569 if (srlb->current == size)
570 zlog_warn(
571 "SR: Warning, SRLB is depleted and next label request will fail");
572
573 return label;
574 }
575
576 /**
577 * Release label in the Segment Routing Local Block.
578 *
579 * @param srlb Segment Routing Local Block
580 * @param label Label to be release
581 *
582 * @return 0 on success or -1 if label falls outside SRLB
583 */
584 static int sr_local_block_release_label(struct sr_local_block *srlb,
585 mpls_label_t label)
586 {
587 uint32_t index;
588 uint32_t pos;
589
590 /* Check that label falls inside the SRLB */
591 if ((label < srlb->start) || (label > srlb->end)) {
592 flog_warn(EC_ISIS_SID_OVERFLOW,
593 "%s: Returning label %u is outside SRLB [%u/%u]",
594 __func__, label, srlb->start, srlb->end);
595 return -1;
596 }
597
598 index = (label - srlb->start) / SRLB_BLOCK_SIZE;
599 pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE);
600 srlb->used_mark[index] &= ~pos;
601 /* Reset current to the first available position */
602 for (index = 0; index < srlb->max_block; index++) {
603 if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) {
604 for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++)
605 if (!((1ULL << pos) & srlb->used_mark[index])) {
606 srlb->current =
607 index * SRLB_BLOCK_SIZE + pos;
608 break;
609 }
610 break;
611 }
612 }
613
614 return 0;
615 }
616
617 /* --- Segment Routing Adjacency-SID management functions ------------------- */
618
619 /**
620 * Add new local Adjacency-SID.
621 *
622 * @param adj IS-IS Adjacency
623 * @param family Inet Family (IPv4 or IPv6)
624 * @param backup True to initialize backup Adjacency SID
625 * @param nexthops List of backup nexthops (for backup Adj-SIDs only)
626 */
627 void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, bool backup,
628 struct list *nexthops)
629 {
630 struct isis_circuit *circuit = adj->circuit;
631 struct isis_area *area = circuit->area;
632 struct sr_adjacency *sra;
633 struct isis_adj_sid *adj_sid;
634 struct isis_lan_adj_sid *ladj_sid;
635 union g_addr nexthop = {};
636 uint8_t flags;
637 mpls_label_t input_label;
638
639 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag,
640 backup ? "Backup" : "Primary");
641
642 /* Determine nexthop IP address */
643 switch (family) {
644 case AF_INET:
645 if (!circuit->ip_router || !adj->ipv4_address_count)
646 return;
647
648 nexthop.ipv4 = adj->ipv4_addresses[0];
649 break;
650 case AF_INET6:
651 if (!circuit->ipv6_router || !adj->ll_ipv6_count)
652 return;
653
654 nexthop.ipv6 = adj->ll_ipv6_addrs[0];
655 break;
656 default:
657 flog_err(EC_LIB_DEVELOPMENT,
658 "%s: unexpected address-family: %u", __func__, family);
659 exit(1);
660 }
661
662 /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */
663 flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG;
664 if (family == AF_INET6)
665 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG);
666 if (backup)
667 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
668
669 /* Get a label from the SRLB for this Adjacency */
670 input_label = sr_local_block_request_label(&area->srdb.srlb);
671 if (input_label == MPLS_INVALID_LABEL)
672 return;
673
674 if (circuit->ext == NULL)
675 circuit->ext = isis_alloc_ext_subtlvs();
676
677 sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra));
678 sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL;
679 sra->input_label = input_label;
680 sra->nexthop.family = family;
681 sra->nexthop.address = nexthop;
682
683 if (backup && nexthops) {
684 struct isis_vertex_adj *vadj;
685 struct listnode *node;
686
687 sra->backup_nexthops = list_new();
688 for (ALL_LIST_ELEMENTS_RO(nexthops, node, vadj)) {
689 struct isis_adjacency *adj = vadj->sadj->adj;
690 struct mpls_label_stack *label_stack;
691
692 label_stack = vadj->label_stack;
693 adjinfo2nexthop(family, sra->backup_nexthops, adj, NULL,
694 label_stack);
695 }
696 }
697
698 switch (circuit->circ_type) {
699 /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */
700 case CIRCUIT_T_BROADCAST:
701 ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid));
702 ladj_sid->family = family;
703 ladj_sid->flags = flags;
704 ladj_sid->weight = 0;
705 memcpy(ladj_sid->neighbor_id, adj->sysid,
706 sizeof(ladj_sid->neighbor_id));
707 ladj_sid->sid = input_label;
708 isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid);
709 sra->u.ladj_sid = ladj_sid;
710 break;
711 /* Adjacency-SID for Point to Point interface section #2.2.1 */
712 case CIRCUIT_T_P2P:
713 adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
714 adj_sid->family = family;
715 adj_sid->flags = flags;
716 adj_sid->weight = 0;
717 adj_sid->sid = input_label;
718 isis_tlvs_add_adj_sid(circuit->ext, adj_sid);
719 sra->u.adj_sid = adj_sid;
720 break;
721 default:
722 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
723 __func__, circuit->circ_type);
724 exit(1);
725 }
726
727 /* Add Adjacency-SID in SRDB */
728 sra->adj = adj;
729 listnode_add(area->srdb.adj_sids, sra);
730 listnode_add(adj->adj_sids, sra);
731
732 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
733 }
734
735 /**
736 * Add Primary and Backup local Adjacency SID.
737 *
738 * @param adj IS-IS Adjacency
739 * @param family Inet Family (IPv4 or IPv6)
740 */
741 static void sr_adj_sid_add(struct isis_adjacency *adj, int family)
742 {
743 sr_adj_sid_add_single(adj, family, false, NULL);
744 }
745
746 static void sr_adj_sid_update(struct sr_adjacency *sra,
747 struct sr_local_block *srlb)
748 {
749 struct isis_circuit *circuit = sra->adj->circuit;
750
751 /* First remove the old MPLS Label */
752 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
753
754 /* Got new label in the new SRLB */
755 sra->input_label = sr_local_block_request_label(srlb);
756 if (sra->input_label == MPLS_INVALID_LABEL)
757 return;
758
759 switch (circuit->circ_type) {
760 case CIRCUIT_T_BROADCAST:
761 sra->u.ladj_sid->sid = sra->input_label;
762 break;
763 case CIRCUIT_T_P2P:
764 sra->u.adj_sid->sid = sra->input_label;
765 break;
766 default:
767 flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
768 __func__, circuit->circ_type);
769 break;
770 }
771
772 /* Finally configure the new MPLS Label */
773 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
774 }
775
776 /**
777 * Delete local Adj-SID.
778 *
779 * @param sra Segment Routing Adjacency
780 */
781 static void sr_adj_sid_del(struct sr_adjacency *sra)
782 {
783 struct isis_circuit *circuit = sra->adj->circuit;
784 struct isis_area *area = circuit->area;
785
786 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag);
787
788 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
789
790 /* Release dynamic label and remove subTLVs */
791 switch (circuit->circ_type) {
792 case CIRCUIT_T_BROADCAST:
793 sr_local_block_release_label(&area->srdb.srlb,
794 sra->u.ladj_sid->sid);
795 isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid);
796 break;
797 case CIRCUIT_T_P2P:
798 sr_local_block_release_label(&area->srdb.srlb,
799 sra->u.adj_sid->sid);
800 isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid);
801 break;
802 default:
803 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
804 __func__, circuit->circ_type);
805 exit(1);
806 }
807
808 if (sra->type == ISIS_SR_LAN_BACKUP && sra->backup_nexthops) {
809 sra->backup_nexthops->del =
810 (void (*)(void *))isis_nexthop_delete;
811 list_delete(&sra->backup_nexthops);
812 }
813
814 /* Remove Adjacency-SID from the SRDB */
815 listnode_delete(area->srdb.adj_sids, sra);
816 listnode_delete(sra->adj->adj_sids, sra);
817 XFREE(MTYPE_ISIS_SR_INFO, sra);
818 }
819
820 /**
821 * Lookup Segment Routing Adj-SID by family and type.
822 *
823 * @param adj IS-IS Adjacency
824 * @param family Inet Family (IPv4 or IPv6)
825 * @param type Adjacency SID type
826 */
827 struct sr_adjacency *isis_sr_adj_sid_find(struct isis_adjacency *adj,
828 int family, enum sr_adj_type type)
829 {
830 struct sr_adjacency *sra;
831 struct listnode *node;
832
833 for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, node, sra))
834 if (sra->nexthop.family == family && sra->type == type)
835 return sra;
836
837 return NULL;
838 }
839
840 /**
841 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
842 *
843 * @param adj IS-IS Adjacency
844 *
845 * @return 0
846 */
847 static int sr_adj_state_change(struct isis_adjacency *adj)
848 {
849 struct sr_adjacency *sra;
850 struct listnode *node, *nnode;
851
852 if (!adj->circuit->area->srdb.enabled)
853 return 0;
854
855 if (adj->adj_state == ISIS_ADJ_UP)
856 return 0;
857
858 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
859 sr_adj_sid_del(sra);
860
861 return 0;
862 }
863
864 /**
865 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
866 * IPv6 address to corresponding Adjacency-SID accordingly.
867 *
868 * @param adj IS-IS Adjacency
869 * @param family Inet Family (IPv4 or IPv6)
870 * @param global Indicate if it concerns the Local or Global IPv6 addresses
871 *
872 * @return 0
873 */
874 static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family,
875 bool global)
876 {
877 if (!adj->circuit->area->srdb.enabled || global)
878 return 0;
879
880 sr_adj_sid_add(adj, family);
881
882 return 0;
883 }
884
885 /**
886 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
887 * delete the corresponding Adjacency-SID(s) accordingly.
888 *
889 * @param adj IS-IS Adjacency
890 * @param family Inet Family (IPv4 or IPv6)
891 * @param global Indicate if it concerns the Local or Global IPv6 addresses
892 *
893 * @return 0
894 */
895 static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family,
896 bool global)
897 {
898 struct sr_adjacency *sra;
899 struct listnode *node, *nnode;
900
901 if (!adj->circuit->area->srdb.enabled || global)
902 return 0;
903
904 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
905 if (sra->nexthop.family == family)
906 sr_adj_sid_del(sra);
907
908 return 0;
909 }
910
911 /**
912 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
913 *
914 * @param ifp Loopback Interface
915 *
916 * @return 0
917 */
918 static int sr_if_new_hook(struct interface *ifp)
919 {
920 struct isis_circuit *circuit;
921 struct isis_area *area;
922 struct connected *connected;
923 struct listnode *node;
924
925 /* Get corresponding circuit */
926 circuit = circuit_scan_by_ifp(ifp);
927 if (!circuit)
928 return 0;
929
930 area = circuit->area;
931 if (!area)
932 return 0;
933
934 /*
935 * Update the Node-SID flag of the configured Prefix-SID mappings if
936 * necessary. This needs to be done here since isisd reads the startup
937 * configuration before receiving interface information from zebra.
938 */
939 FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
940 struct sr_prefix_cfg *pcfg;
941
942 pcfg = isis_sr_cfg_prefix_find(area, connected->address);
943 if (!pcfg)
944 continue;
945
946 if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)) {
947 pcfg->node_sid = true;
948 lsp_regenerate_schedule(area, area->is_type, 0);
949 }
950 }
951
952 return 0;
953 }
954
955 /**
956 * Show LFIB operation in human readable format.
957 *
958 * @param buf Buffer to store string output. Must be pre-allocate
959 * @param size Size of the buffer
960 * @param label_in Input Label
961 * @param label_out Output Label
962 *
963 * @return String containing LFIB operation in human readable format
964 */
965 char *sr_op2str(char *buf, size_t size, mpls_label_t label_in,
966 mpls_label_t label_out)
967 {
968 if (size < 24)
969 return NULL;
970
971 if (label_in == MPLS_INVALID_LABEL) {
972 snprintf(buf, size, "no-op.");
973 return buf;
974 }
975
976 switch (label_out) {
977 case MPLS_LABEL_IMPLICIT_NULL:
978 snprintf(buf, size, "Pop(%u)", label_in);
979 break;
980 case MPLS_LABEL_IPV4_EXPLICIT_NULL:
981 case MPLS_LABEL_IPV6_EXPLICIT_NULL:
982 snprintf(buf, size, "Swap(%u, null)", label_in);
983 break;
984 case MPLS_INVALID_LABEL:
985 snprintf(buf, size, "no-op.");
986 break;
987 default:
988 snprintf(buf, size, "Swap(%u, %u)", label_in, label_out);
989 break;
990 }
991 return buf;
992 }
993
994 /**
995 * Show Segment Routing Node.
996 *
997 * @param vty VTY output
998 * @param area IS-IS area
999 * @param level IS-IS level
1000 */
1001 static void show_node(struct vty *vty, struct isis_area *area, int level)
1002 {
1003 struct isis_lsp *lsp;
1004 struct ttable *tt;
1005
1006 vty_out(vty, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level));
1007
1008 /* Prepare table. */
1009 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1010 ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD");
1011 tt->style.cell.rpad = 2;
1012 tt->style.corner = '+';
1013 ttable_restyle(tt);
1014 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1015
1016 frr_each (lspdb, &area->lspdb[level - 1], lsp) {
1017 struct isis_router_cap *cap;
1018
1019 if (!lsp->tlvs)
1020 continue;
1021 cap = lsp->tlvs->router_cap;
1022 if (!cap)
1023 continue;
1024
1025 ttable_add_row(
1026 tt, "%s|%u - %u|%u - %u|%s|%u",
1027 sysid_print(lsp->hdr.lsp_id), cap->srgb.lower_bound,
1028 cap->srgb.lower_bound + cap->srgb.range_size - 1,
1029 cap->srlb.lower_bound,
1030 cap->srlb.lower_bound + cap->srlb.range_size - 1,
1031 cap->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF",
1032 cap->msd);
1033 }
1034
1035 /* Dump the generated table. */
1036 if (tt->nrows > 1) {
1037 char *table;
1038
1039 table = ttable_dump(tt, "\n");
1040 vty_out(vty, "%s\n", table);
1041 XFREE(MTYPE_TMP, table);
1042 }
1043 ttable_del(tt);
1044 }
1045
1046 DEFUN(show_sr_node, show_sr_node_cmd,
1047 "show " PROTO_NAME " segment-routing node",
1048 SHOW_STR
1049 PROTO_HELP
1050 "Segment-Routing\n"
1051 "Segment-Routing node\n")
1052 {
1053 struct listnode *node, *inode;
1054 struct isis_area *area;
1055 struct isis *isis;
1056
1057 for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
1058 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
1059 vty_out(vty, "Area %s:\n",
1060 area->area_tag ? area->area_tag : "null");
1061 if (!area->srdb.enabled) {
1062 vty_out(vty, " Segment Routing is disabled\n");
1063 continue;
1064 }
1065 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
1066 level++)
1067 show_node(vty, area, level);
1068 }
1069 }
1070
1071 return CMD_SUCCESS;
1072 }
1073
1074 /* --- IS-IS Segment Routing Management function ---------------------------- */
1075
1076 /**
1077 * Thread function to re-attempt connection to the Label Manager and thus be
1078 * able to start Segment Routing.
1079 *
1080 * @param start Thread structure that contains area as argument
1081 *
1082 * @return 1 on success
1083 */
1084 static void sr_start_label_manager(struct event *start)
1085 {
1086 struct isis_area *area;
1087
1088 area = EVENT_ARG(start);
1089
1090 /* re-attempt to start SR & Label Manager connection */
1091 isis_sr_start(area);
1092 }
1093
1094 /**
1095 * Enable SR on the given IS-IS area.
1096 *
1097 * @param area IS-IS area
1098 *
1099 * @return 0 on success, -1 otherwise
1100 */
1101 int isis_sr_start(struct isis_area *area)
1102 {
1103 struct isis_sr_db *srdb = &area->srdb;
1104 struct isis_adjacency *adj;
1105 struct listnode *node;
1106
1107 /* First start Label Manager if not ready */
1108 if (!isis_zebra_label_manager_ready())
1109 if (isis_zebra_label_manager_connect() < 0) {
1110 /* Re-attempt to connect to Label Manager in 1 sec. */
1111 event_add_timer(master, sr_start_label_manager, area, 1,
1112 &srdb->t_start_lm);
1113 return -1;
1114 }
1115
1116 /* Label Manager is ready, initialize the SRLB */
1117 if (sr_local_block_init(area) < 0)
1118 return -1;
1119
1120 /*
1121 * Request SGRB to the label manager if not already active. If the
1122 * allocation fails, return an error to disable SR until a new SRGB
1123 * is successfully allocated.
1124 */
1125 if (!srdb->srgb_active) {
1126 if (isis_zebra_request_label_range(
1127 srdb->config.srgb_lower_bound,
1128 srdb->config.srgb_upper_bound
1129 - srdb->config.srgb_lower_bound + 1)
1130 < 0) {
1131 srdb->srgb_active = false;
1132 return -1;
1133 } else
1134 srdb->srgb_active = true;
1135 }
1136
1137 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
1138 area->area_tag);
1139
1140 /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */
1141 for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
1142 if (adj->ipv4_address_count > 0)
1143 sr_adj_sid_add(adj, AF_INET);
1144 if (adj->ll_ipv6_count > 0)
1145 sr_adj_sid_add(adj, AF_INET6);
1146 }
1147
1148 area->srdb.enabled = true;
1149
1150 /* Regenerate LSPs to advertise Segment Routing capabilities. */
1151 lsp_regenerate_schedule(area, area->is_type, 0);
1152
1153 return 0;
1154 }
1155
1156 /**
1157 * Disable SR on the given IS-IS area.
1158 *
1159 * @param area IS-IS area
1160 */
1161 void isis_sr_stop(struct isis_area *area)
1162 {
1163 struct isis_sr_db *srdb = &area->srdb;
1164 struct sr_adjacency *sra;
1165 struct listnode *node, *nnode;
1166
1167 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
1168 area->area_tag);
1169
1170 /* Disable any re-attempt to connect to Label Manager */
1171 EVENT_OFF(srdb->t_start_lm);
1172
1173 /* Uninstall all local Adjacency-SIDs. */
1174 for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
1175 sr_adj_sid_del(sra);
1176
1177 /* Release SRGB if active. */
1178 if (srdb->srgb_active) {
1179 isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
1180 srdb->config.srgb_upper_bound);
1181 srdb->srgb_active = false;
1182 }
1183
1184 /* Delete SRLB */
1185 sr_local_block_delete(area);
1186
1187 area->srdb.enabled = false;
1188
1189 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
1190 lsp_regenerate_schedule(area, area->is_type, 0);
1191 }
1192
1193 /**
1194 * IS-IS Segment Routing initialization for given area.
1195 *
1196 * @param area IS-IS area
1197 */
1198 void isis_sr_area_init(struct isis_area *area)
1199 {
1200 struct isis_sr_db *srdb = &area->srdb;
1201
1202 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
1203 area->area_tag);
1204
1205 /* Initialize Segment Routing Data Base */
1206 memset(srdb, 0, sizeof(*srdb));
1207 srdb->adj_sids = list_new();
1208
1209 /* Pull defaults from the YANG module. */
1210 #ifndef FABRICD
1211 srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR);
1212 srdb->config.srgb_lower_bound = yang_get_default_uint32(
1213 "%s/label-blocks/srgb/lower-bound", ISIS_SR);
1214 srdb->config.srgb_upper_bound = yang_get_default_uint32(
1215 "%s/label-blocks/srgb/upper-bound", ISIS_SR);
1216 srdb->config.srlb_lower_bound = yang_get_default_uint32(
1217 "%s/label-blocks/srlb/lower-bound", ISIS_SR);
1218 srdb->config.srlb_upper_bound = yang_get_default_uint32(
1219 "%s/label-blocks/srlb/upper-bound", ISIS_SR);
1220 #else
1221 srdb->config.enabled = false;
1222 srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND;
1223 srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND;
1224 srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND;
1225 srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND;
1226 #endif
1227 srdb->config.msd = 0;
1228 srdb_prefix_cfg_init(&srdb->config.prefix_sids);
1229 }
1230
1231 /**
1232 * Terminate IS-IS Segment Routing for the given area.
1233 *
1234 * @param area IS-IS area
1235 */
1236 void isis_sr_area_term(struct isis_area *area)
1237 {
1238 struct isis_sr_db *srdb = &area->srdb;
1239
1240 /* Stop Segment Routing */
1241 if (area->srdb.enabled)
1242 isis_sr_stop(area);
1243
1244 /* Free Adjacency SID list */
1245 list_delete(&srdb->adj_sids);
1246
1247 /* Clear Prefix-SID configuration. */
1248 while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) {
1249 struct sr_prefix_cfg *pcfg;
1250
1251 pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids);
1252 isis_sr_cfg_prefix_del(pcfg);
1253 }
1254 }
1255
1256 /**
1257 * IS-IS Segment Routing global initialization.
1258 */
1259 void isis_sr_init(void)
1260 {
1261 install_element(VIEW_NODE, &show_sr_node_cmd);
1262
1263 /* Register hooks. */
1264 hook_register(isis_adj_state_change_hook, sr_adj_state_change);
1265 hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
1266 hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
1267 hook_register(isis_if_new_hook, sr_if_new_hook);
1268 }
1269
1270 /**
1271 * IS-IS Segment Routing global terminate.
1272 */
1273 void isis_sr_term(void)
1274 {
1275 /* Unregister hooks. */
1276 hook_unregister(isis_adj_state_change_hook, sr_adj_state_change);
1277 hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
1278 hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
1279 hook_unregister(isis_if_new_hook, sr_if_new_hook);
1280 }