]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_sr.c
Merge pull request #7375 from vishaldhingra/static
[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 /* Set the N-flag when appropriate. */
365 ifp = if_lookup_prefix(prefix, VRF_DEFAULT);
366 if (ifp && sr_prefix_is_node_sid(ifp, prefix) && !pcfg->n_flag_clear)
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)
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
554 mpls_label_t label;
555 uint32_t index;
556 uint32_t pos;
557
558 /* Check if we ran out of available labels */
559 if (srlb->current >= srlb->end)
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 < srlb->end) {
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 return label;
583 }
584
585 /**
586 * Release label in the Segment Routing Local Block.
587 *
588 * @param srlb Segment Routing Local Block
589 * @param label Label to be release
590 *
591 * @return 0 on success or -1 if label falls outside SRLB
592 */
593 static int sr_local_block_release_label(struct sr_local_block *srlb,
594 mpls_label_t label)
595 {
596 uint32_t index;
597 uint32_t pos;
598
599 /* Check that label falls inside the SRLB */
600 if ((label < srlb->start) || (label > srlb->end)) {
601 flog_warn(EC_ISIS_SID_OVERFLOW,
602 "%s: Returning label %u is outside SRLB [%u/%u]",
603 __func__, label, srlb->start, srlb->end);
604 return -1;
605 }
606
607 index = (label - srlb->start) / SRLB_BLOCK_SIZE;
608 pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE);
609 srlb->used_mark[index] &= ~pos;
610 /* Reset current to the first available position */
611 for (index = 0; index < srlb->max_block; index++) {
612 if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) {
613 for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++)
614 if (!((1ULL << pos) & srlb->used_mark[index])) {
615 srlb->current =
616 index * SRLB_BLOCK_SIZE + pos;
617 break;
618 }
619 break;
620 }
621 }
622
623 return 0;
624 }
625
626 /* --- Segment Routing Adjacency-SID management functions ------------------- */
627
628 /**
629 * Add new local Adjacency-SID.
630 *
631 * @param adj IS-IS Adjacency
632 * @param family Inet Family (IPv4 or IPv6)
633 * @param backup True to initialize backup Adjacency SID
634 * @param nexthops List of backup nexthops (for backup Adj-SIDs only)
635 */
636 void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, bool backup,
637 struct list *nexthops)
638 {
639 struct isis_circuit *circuit = adj->circuit;
640 struct isis_area *area = circuit->area;
641 struct sr_adjacency *sra;
642 struct isis_adj_sid *adj_sid;
643 struct isis_lan_adj_sid *ladj_sid;
644 union g_addr nexthop = {};
645 uint8_t flags;
646 mpls_label_t input_label;
647
648 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag,
649 backup ? "Backup" : "Primary");
650
651 /* Determine nexthop IP address */
652 switch (family) {
653 case AF_INET:
654 if (!circuit->ip_router || !adj->ipv4_address_count)
655 return;
656
657 nexthop.ipv4 = adj->ipv4_addresses[0];
658 break;
659 case AF_INET6:
660 if (!circuit->ipv6_router || !adj->ipv6_address_count)
661 return;
662
663 nexthop.ipv6 = adj->ipv6_addresses[0];
664 break;
665 default:
666 flog_err(EC_LIB_DEVELOPMENT,
667 "%s: unexpected address-family: %u", __func__, family);
668 exit(1);
669 }
670
671 /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */
672 flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG;
673 if (family == AF_INET6)
674 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG);
675 if (backup)
676 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
677
678 /* Get a label from the SRLB for this Adjacency */
679 input_label = sr_local_block_request_label(&area->srdb.srlb);
680 if (input_label == MPLS_INVALID_LABEL)
681 return;
682
683 if (circuit->ext == NULL)
684 circuit->ext = isis_alloc_ext_subtlvs();
685
686 sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra));
687 sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL;
688 sra->input_label = input_label;
689 sra->nexthop.family = family;
690 sra->nexthop.address = nexthop;
691
692 if (backup && nexthops) {
693 struct isis_vertex_adj *vadj;
694 struct listnode *node;
695
696 sra->backup_nexthops = list_new();
697 for (ALL_LIST_ELEMENTS_RO(nexthops, node, vadj)) {
698 struct isis_adjacency *adj = vadj->sadj->adj;
699 struct mpls_label_stack *label_stack;
700
701 label_stack = vadj->label_stack;
702 adjinfo2nexthop(family, sra->backup_nexthops, adj, NULL,
703 label_stack);
704 }
705 }
706
707 switch (circuit->circ_type) {
708 /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */
709 case CIRCUIT_T_BROADCAST:
710 ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid));
711 ladj_sid->family = family;
712 ladj_sid->flags = flags;
713 ladj_sid->weight = 0;
714 memcpy(ladj_sid->neighbor_id, adj->sysid,
715 sizeof(ladj_sid->neighbor_id));
716 ladj_sid->sid = input_label;
717 isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid);
718 sra->u.ladj_sid = ladj_sid;
719 break;
720 /* Adjacency-SID for Point to Point interface section #2.2.1 */
721 case CIRCUIT_T_P2P:
722 adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
723 adj_sid->family = family;
724 adj_sid->flags = flags;
725 adj_sid->weight = 0;
726 adj_sid->sid = input_label;
727 isis_tlvs_add_adj_sid(circuit->ext, adj_sid);
728 sra->u.adj_sid = adj_sid;
729 break;
730 default:
731 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
732 __func__, circuit->circ_type);
733 exit(1);
734 }
735
736 /* Add Adjacency-SID in SRDB */
737 sra->adj = adj;
738 listnode_add(area->srdb.adj_sids, sra);
739 listnode_add(adj->adj_sids, sra);
740
741 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
742 }
743
744 /**
745 * Add Primary and Backup local Adjacency SID.
746 *
747 * @param adj IS-IS Adjacency
748 * @param family Inet Family (IPv4 or IPv6)
749 */
750 static void sr_adj_sid_add(struct isis_adjacency *adj, int family)
751 {
752 sr_adj_sid_add_single(adj, family, false, NULL);
753 }
754
755 static void sr_adj_sid_update(struct sr_adjacency *sra,
756 struct sr_local_block *srlb)
757 {
758 struct isis_circuit *circuit = sra->adj->circuit;
759
760 /* First remove the old MPLS Label */
761 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
762
763 /* Got new label in the new SRLB */
764 sra->input_label = sr_local_block_request_label(srlb);
765 if (sra->input_label == MPLS_INVALID_LABEL)
766 return;
767
768 switch (circuit->circ_type) {
769 case CIRCUIT_T_BROADCAST:
770 sra->u.ladj_sid->sid = sra->input_label;
771 break;
772 case CIRCUIT_T_P2P:
773 sra->u.adj_sid->sid = sra->input_label;
774 break;
775 default:
776 flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
777 __func__, circuit->circ_type);
778 break;
779 }
780
781 /* Finally configure the new MPLS Label */
782 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
783 }
784
785 /**
786 * Delete local Adj-SID.
787 *
788 * @param sra Segment Routing Adjacency
789 */
790 static void sr_adj_sid_del(struct sr_adjacency *sra)
791 {
792 struct isis_circuit *circuit = sra->adj->circuit;
793 struct isis_area *area = circuit->area;
794
795 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag);
796
797 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
798
799 /* Release dynamic label and remove subTLVs */
800 switch (circuit->circ_type) {
801 case CIRCUIT_T_BROADCAST:
802 sr_local_block_release_label(&area->srdb.srlb,
803 sra->u.ladj_sid->sid);
804 isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid);
805 break;
806 case CIRCUIT_T_P2P:
807 sr_local_block_release_label(&area->srdb.srlb,
808 sra->u.adj_sid->sid);
809 isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid);
810 break;
811 default:
812 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
813 __func__, circuit->circ_type);
814 exit(1);
815 }
816
817 if (sra->type == ISIS_SR_LAN_BACKUP && sra->backup_nexthops) {
818 sra->backup_nexthops->del =
819 (void (*)(void *))isis_nexthop_delete;
820 list_delete(&sra->backup_nexthops);
821 }
822
823 /* Remove Adjacency-SID from the SRDB */
824 listnode_delete(area->srdb.adj_sids, sra);
825 listnode_delete(sra->adj->adj_sids, sra);
826 XFREE(MTYPE_ISIS_SR_INFO, sra);
827 }
828
829 /**
830 * Lookup Segment Routing Adj-SID by family and type.
831 *
832 * @param adj IS-IS Adjacency
833 * @param family Inet Family (IPv4 or IPv6)
834 * @param type Adjacency SID type
835 */
836 struct sr_adjacency *isis_sr_adj_sid_find(struct isis_adjacency *adj,
837 int family, enum sr_adj_type type)
838 {
839 struct sr_adjacency *sra;
840 struct listnode *node;
841
842 for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, node, sra))
843 if (sra->nexthop.family == family && sra->type == type)
844 return sra;
845
846 return NULL;
847 }
848
849 /**
850 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
851 *
852 * @param adj IS-IS Adjacency
853 *
854 * @return 0
855 */
856 static int sr_adj_state_change(struct isis_adjacency *adj)
857 {
858 struct sr_adjacency *sra;
859 struct listnode *node, *nnode;
860
861 if (!adj->circuit->area->srdb.enabled)
862 return 0;
863
864 if (adj->adj_state == ISIS_ADJ_UP)
865 return 0;
866
867 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
868 sr_adj_sid_del(sra);
869
870 return 0;
871 }
872
873 /**
874 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
875 * IPv6 address to corresponding Adjacency-SID accordingly.
876 *
877 * @param adj IS-IS Adjacency
878 * @param family Inet Family (IPv4 or IPv6)
879 *
880 * @return 0
881 */
882 static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family)
883 {
884 if (!adj->circuit->area->srdb.enabled)
885 return 0;
886
887 sr_adj_sid_add(adj, family);
888
889 return 0;
890 }
891
892 /**
893 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
894 * delete the corresponding Adjacency-SID(s) accordingly.
895 *
896 * @param adj IS-IS Adjacency
897 * @param family Inet Family (IPv4 or IPv6)
898 *
899 * @return 0
900 */
901 static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family)
902 {
903 struct sr_adjacency *sra;
904 struct listnode *node, *nnode;
905
906 if (!adj->circuit->area->srdb.enabled)
907 return 0;
908
909 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
910 if (sra->nexthop.family == family)
911 sr_adj_sid_del(sra);
912
913 return 0;
914 }
915
916 /**
917 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
918 *
919 * @param ifp Loopback Interface
920 *
921 * @return 0
922 */
923 static int sr_if_new_hook(struct interface *ifp)
924 {
925 struct isis_circuit *circuit;
926 struct isis_area *area;
927 struct connected *connected;
928 struct listnode *node;
929
930 /* Get corresponding circuit */
931 circuit = circuit_scan_by_ifp(ifp);
932 if (!circuit)
933 return 0;
934
935 area = circuit->area;
936 if (!area)
937 return 0;
938
939 /*
940 * Update the Node-SID flag of the configured Prefix-SID mappings if
941 * necessary. This needs to be done here since isisd reads the startup
942 * configuration before receiving interface information from zebra.
943 */
944 FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
945 struct sr_prefix_cfg *pcfg;
946
947 pcfg = isis_sr_cfg_prefix_find(area, connected->address);
948 if (!pcfg)
949 continue;
950
951 if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)
952 && !pcfg->n_flag_clear) {
953 pcfg->node_sid = true;
954 lsp_regenerate_schedule(area, area->is_type, 0);
955 }
956 }
957
958 return 0;
959 }
960
961 /**
962 * Show LFIB operation in human readable format.
963 *
964 * @param buf Buffer to store string output. Must be pre-allocate
965 * @param size Size of the buffer
966 * @param label_in Input Label
967 * @param label_out Output Label
968 *
969 * @return String containing LFIB operation in human readable format
970 */
971 char *sr_op2str(char *buf, size_t size, mpls_label_t label_in,
972 mpls_label_t label_out)
973 {
974 if (size < 24)
975 return NULL;
976
977 if (label_in == MPLS_INVALID_LABEL) {
978 snprintf(buf, size, "no-op.");
979 return buf;
980 }
981
982 switch (label_out) {
983 case MPLS_LABEL_IMPLICIT_NULL:
984 snprintf(buf, size, "Pop(%u)", label_in);
985 break;
986 case MPLS_LABEL_IPV4_EXPLICIT_NULL:
987 case MPLS_LABEL_IPV6_EXPLICIT_NULL:
988 snprintf(buf, size, "Swap(%u, null)", label_in);
989 break;
990 case MPLS_INVALID_LABEL:
991 snprintf(buf, size, "no-op.");
992 break;
993 default:
994 snprintf(buf, size, "Swap(%u, %u)", label_in, label_out);
995 break;
996 }
997 return buf;
998 }
999
1000 /**
1001 * Show Segment Routing Node.
1002 *
1003 * @param vty VTY output
1004 * @param area IS-IS area
1005 * @param level IS-IS level
1006 */
1007 static void show_node(struct vty *vty, struct isis_area *area, int level)
1008 {
1009 struct isis_lsp *lsp;
1010 struct ttable *tt;
1011
1012 vty_out(vty, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level));
1013
1014 /* Prepare table. */
1015 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1016 ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD");
1017 tt->style.cell.rpad = 2;
1018 tt->style.corner = '+';
1019 ttable_restyle(tt);
1020 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1021
1022 frr_each (lspdb, &area->lspdb[level - 1], lsp) {
1023 struct isis_router_cap *cap;
1024
1025 if (!lsp->tlvs)
1026 continue;
1027 cap = lsp->tlvs->router_cap;
1028 if (!cap)
1029 continue;
1030
1031 ttable_add_row(
1032 tt, "%s|%u - %u|%u - %u|%s|%u",
1033 sysid_print(lsp->hdr.lsp_id), cap->srgb.lower_bound,
1034 cap->srgb.lower_bound + cap->srgb.range_size - 1,
1035 cap->srlb.lower_bound,
1036 cap->srlb.lower_bound + cap->srlb.range_size - 1,
1037 cap->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF",
1038 cap->msd);
1039 }
1040
1041 /* Dump the generated table. */
1042 if (tt->nrows > 1) {
1043 char *table;
1044
1045 table = ttable_dump(tt, "\n");
1046 vty_out(vty, "%s\n", table);
1047 XFREE(MTYPE_TMP, table);
1048 }
1049 ttable_del(tt);
1050 }
1051
1052 DEFUN(show_sr_node, show_sr_node_cmd,
1053 "show isis segment-routing node",
1054 SHOW_STR PROTO_HELP
1055 "Segment-Routing\n"
1056 "Segment-Routing node\n")
1057 {
1058 struct listnode *node, *inode;
1059 struct isis_area *area;
1060 struct isis *isis;
1061
1062 for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
1063 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
1064 vty_out(vty, "Area %s:\n",
1065 area->area_tag ? area->area_tag : "null");
1066
1067 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
1068 level++)
1069 show_node(vty, area, level);
1070 }
1071 }
1072
1073 return CMD_SUCCESS;
1074 }
1075
1076 /* --- IS-IS Segment Routing Management function ---------------------------- */
1077
1078 /**
1079 * Thread function to re-attempt connection to the Label Manager and thus be
1080 * able to start Segment Routing.
1081 *
1082 * @param start Thread structure that contains area as argument
1083 *
1084 * @return 1 on success
1085 */
1086 static int sr_start_label_manager(struct thread *start)
1087 {
1088 struct isis_area *area;
1089
1090 area = THREAD_ARG(start);
1091
1092 /* re-attempt to start SR & Label Manager connection */
1093 isis_sr_start(area);
1094
1095 return 1;
1096 }
1097
1098 /**
1099 * Enable SR on the given IS-IS area.
1100 *
1101 * @param area IS-IS area
1102 *
1103 * @return 0 on success, -1 otherwise
1104 */
1105 int isis_sr_start(struct isis_area *area)
1106 {
1107 struct isis_sr_db *srdb = &area->srdb;
1108 struct isis_adjacency *adj;
1109 struct listnode *node;
1110
1111 /* First start Label Manager if not ready */
1112 if (!isis_zebra_label_manager_ready())
1113 if (isis_zebra_label_manager_connect() < 0) {
1114 /* Re-attempt to connect to Label Manager in 1 sec. */
1115 thread_add_timer(master, sr_start_label_manager, area,
1116 1, &srdb->t_start_lm);
1117 return -1;
1118 }
1119
1120 /* Label Manager is ready, initialize the SRLB */
1121 if (sr_local_block_init(area) < 0)
1122 return -1;
1123
1124 /*
1125 * Request SGRB to the label manager if not already active. If the
1126 * allocation fails, return an error to disable SR until a new SRGB
1127 * is successfully allocated.
1128 */
1129 if (!srdb->srgb_active) {
1130 if (isis_zebra_request_label_range(
1131 srdb->config.srgb_lower_bound,
1132 srdb->config.srgb_upper_bound
1133 - srdb->config.srgb_lower_bound + 1)
1134 < 0) {
1135 srdb->srgb_active = false;
1136 return -1;
1137 } else
1138 srdb->srgb_active = true;
1139 }
1140
1141 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
1142 area->area_tag);
1143
1144 /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */
1145 for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
1146 if (adj->ipv4_address_count > 0)
1147 sr_adj_sid_add(adj, AF_INET);
1148 if (adj->ipv6_address_count > 0)
1149 sr_adj_sid_add(adj, AF_INET6);
1150 }
1151
1152 area->srdb.enabled = true;
1153
1154 /* Regenerate LSPs to advertise Segment Routing capabilities. */
1155 lsp_regenerate_schedule(area, area->is_type, 0);
1156
1157 return 0;
1158 }
1159
1160 /**
1161 * Disable SR on the given IS-IS area.
1162 *
1163 * @param area IS-IS area
1164 */
1165 void isis_sr_stop(struct isis_area *area)
1166 {
1167 struct isis_sr_db *srdb = &area->srdb;
1168 struct sr_adjacency *sra;
1169 struct listnode *node, *nnode;
1170
1171 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
1172 area->area_tag);
1173
1174 /* Disable any re-attempt to connect to Label Manager */
1175 thread_cancel(&srdb->t_start_lm);
1176
1177 /* Uninstall all local Adjacency-SIDs. */
1178 for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
1179 sr_adj_sid_del(sra);
1180
1181 /* Release SRGB if active. */
1182 if (srdb->srgb_active) {
1183 isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
1184 srdb->config.srgb_upper_bound);
1185 srdb->srgb_active = false;
1186 }
1187
1188 /* Delete SRLB */
1189 sr_local_block_delete(area);
1190
1191 area->srdb.enabled = false;
1192
1193 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
1194 lsp_regenerate_schedule(area, area->is_type, 0);
1195 }
1196
1197 /**
1198 * IS-IS Segment Routing initialization for given area.
1199 *
1200 * @param area IS-IS area
1201 */
1202 void isis_sr_area_init(struct isis_area *area)
1203 {
1204 struct isis_sr_db *srdb = &area->srdb;
1205
1206 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
1207 area->area_tag);
1208
1209 /* Initialize Segment Routing Data Base */
1210 memset(srdb, 0, sizeof(*srdb));
1211 srdb->adj_sids = list_new();
1212
1213 /* Pull defaults from the YANG module. */
1214 #ifndef FABRICD
1215 srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR);
1216 srdb->config.srgb_lower_bound =
1217 yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR);
1218 srdb->config.srgb_upper_bound =
1219 yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR);
1220 srdb->config.srlb_lower_bound =
1221 yang_get_default_uint32("%s/srlb/lower-bound", ISIS_SR);
1222 srdb->config.srlb_upper_bound =
1223 yang_get_default_uint32("%s/srlb/upper-bound", ISIS_SR);
1224 #else
1225 srdb->config.enabled = false;
1226 srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND;
1227 srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND;
1228 srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND;
1229 srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND;
1230 #endif
1231 srdb->config.msd = 0;
1232 srdb_prefix_cfg_init(&srdb->config.prefix_sids);
1233 }
1234
1235 /**
1236 * Terminate IS-IS Segment Routing for the given area.
1237 *
1238 * @param area IS-IS area
1239 */
1240 void isis_sr_area_term(struct isis_area *area)
1241 {
1242 struct isis_sr_db *srdb = &area->srdb;
1243
1244 /* Stop Segment Routing */
1245 if (area->srdb.enabled)
1246 isis_sr_stop(area);
1247
1248 /* Clear Prefix-SID configuration. */
1249 while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) {
1250 struct sr_prefix_cfg *pcfg;
1251
1252 pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids);
1253 isis_sr_cfg_prefix_del(pcfg);
1254 }
1255 }
1256
1257 /**
1258 * IS-IS Segment Routing global initialization.
1259 */
1260 void isis_sr_init(void)
1261 {
1262 install_element(VIEW_NODE, &show_sr_node_cmd);
1263
1264 /* Register hooks. */
1265 hook_register(isis_adj_state_change_hook, sr_adj_state_change);
1266 hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
1267 hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
1268 hook_register(isis_if_new_hook, sr_if_new_hook);
1269 }
1270
1271 /**
1272 * IS-IS Segment Routing global terminate.
1273 */
1274 void isis_sr_term(void)
1275 {
1276 /* Unregister hooks. */
1277 hook_unregister(isis_adj_state_change_hook, sr_adj_state_change);
1278 hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
1279 hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
1280 hook_unregister(isis_if_new_hook, sr_if_new_hook);
1281 }