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