1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * This is an implementation of Segment Routing for IS-IS as per RFC 8667
5 * Copyright (C) 2019 Orange http://www.orange.com
7 * Author: Olivier Dugeon <olivier.dugeon@orange.com>
8 * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
17 #include "termtable.h"
21 #include "srcdest_table.h"
24 #include "lib/lib_errors.h"
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"
38 /* Local variables and functions */
39 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SR_INFO
, "ISIS segment routing information");
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
);
47 /* --- RB-Tree Management functions ----------------------------------------- */
50 * Configured SR Prefix comparison for RB-Tree.
52 * @param a First SR prefix
53 * @param b Second SR prefix
55 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
57 static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg
*a
,
58 const struct sr_prefix_cfg
*b
)
62 ret
= prefix_cmp(&a
->prefix
, &b
->prefix
);
66 ret
= a
->algorithm
- b
->algorithm
;
72 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg
, struct sr_prefix_cfg
, entry
,
73 sr_prefix_sid_cfg_compare
);
76 * Find SRGB associated to a System ID.
78 * @param area IS-IS LSP database
79 * @param sysid System ID to lookup
81 * @return Pointer to SRGB if found, NULL otherwise
83 struct isis_sr_block
*isis_sr_find_srgb(struct lspdb_head
*lspdb
,
88 lsp
= isis_root_system_lsp(lspdb
, sysid
);
92 if (!lsp
->tlvs
->router_cap
93 || lsp
->tlvs
->router_cap
->srgb
.range_size
== 0)
96 return &lsp
->tlvs
->router_cap
->srgb
;
100 * Compute input label for the given Prefix-SID.
102 * @param area IS-IS area
103 * @param psid IS-IS Prefix-SID Sub-TLV
104 * @param local Indicates whether the Prefix-SID is local or not
106 * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow
108 mpls_label_t
sr_prefix_in_label(struct isis_area
*area
,
109 struct isis_prefix_sid
*psid
, bool local
)
112 * No need to assign a label for local Prefix-SIDs unless the no-PHP
116 && (!CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
)
117 || CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
)))
118 return MPLS_INVALID_LABEL
;
120 /* Return SID value as MPLS label if it is an Absolute SID */
121 if (CHECK_FLAG(psid
->flags
,
122 ISIS_PREFIX_SID_VALUE
| ISIS_PREFIX_SID_LOCAL
))
125 /* Check that SID index falls inside the SRGB */
126 if (psid
->value
>= (area
->srdb
.config
.srgb_upper_bound
127 - area
->srdb
.config
.srgb_lower_bound
+ 1)) {
128 flog_warn(EC_ISIS_SID_OVERFLOW
,
129 "%s: SID index %u falls outside local SRGB range",
130 __func__
, psid
->value
);
131 return MPLS_INVALID_LABEL
;
134 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
135 return (area
->srdb
.config
.srgb_lower_bound
+ psid
->value
);
139 * Compute output label for the given Prefix-SID.
141 * @param lspdb IS-IS LSP database
142 * @param family Prefix-SID address family
143 * @param psid Prefix-SID Sub-TLV
144 * @param nh_sysid System ID of the nexthop node
145 * @param last_hop Indicates whether the nexthop node is the last hop
147 * @return MPLS label or MPLS_INVALID_LABEL in case of error
149 mpls_label_t
sr_prefix_out_label(struct lspdb_head
*lspdb
, int family
,
150 struct isis_prefix_sid
*psid
,
151 const uint8_t *nh_sysid
, bool last_hop
)
153 struct isis_sr_block
*nh_srgb
;
156 if (!CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
))
157 return MPLS_LABEL_IMPLICIT_NULL
;
159 if (CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
)) {
160 if (family
== AF_INET
)
161 return MPLS_LABEL_IPV4_EXPLICIT_NULL
;
163 return MPLS_LABEL_IPV6_EXPLICIT_NULL
;
168 /* Return SID value as MPLS label if it is an Absolute SID */
169 if (CHECK_FLAG(psid
->flags
,
170 ISIS_PREFIX_SID_VALUE
| ISIS_PREFIX_SID_LOCAL
)) {
172 * V/L SIDs have local significance, so only adjacent routers
173 * can use them (RFC8667 section #2.1.1.1)
176 return MPLS_INVALID_LABEL
;
180 /* Check that SID index falls inside the SRGB */
181 nh_srgb
= isis_sr_find_srgb(lspdb
, nh_sysid
);
183 return MPLS_INVALID_LABEL
;
186 * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
189 if ((family
== AF_INET
&& !IS_SR_IPV4(nh_srgb
))
190 || (family
== AF_INET6
&& !IS_SR_IPV6(nh_srgb
)))
191 return MPLS_INVALID_LABEL
;
193 if (psid
->value
>= nh_srgb
->range_size
) {
194 flog_warn(EC_ISIS_SID_OVERFLOW
,
195 "%s: SID index %u falls outside remote SRGB range",
196 __func__
, psid
->value
);
197 return MPLS_INVALID_LABEL
;
200 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
201 return (nh_srgb
->lower_bound
+ psid
->value
);
204 /* --- Functions used for Yang model and CLI to configure Segment Routing --- */
207 * Check if prefix correspond to a Node SID.
209 * @param ifp Interface
210 * @param prefix Prefix to be checked
212 * @return True if the interface/address pair corresponds to a Node-SID
214 static bool sr_prefix_is_node_sid(const struct interface
*ifp
,
215 const struct prefix
*prefix
)
217 return (if_is_loopback(ifp
) && is_host_route(prefix
));
221 * Update local SRGB configuration. SRGB is reserved though Label Manager.
222 * This function trigger the update of local Prefix-SID installation.
224 * @param area IS-IS area
225 * @param lower_bound Lower bound of SRGB
226 * @param upper_bound Upper bound of SRGB
228 * @return 0 on success, -1 otherwise
230 int isis_sr_cfg_srgb_update(struct isis_area
*area
, uint32_t lower_bound
,
231 uint32_t upper_bound
)
233 struct isis_sr_db
*srdb
= &area
->srdb
;
235 sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
236 area
->area_tag
, lower_bound
, upper_bound
);
238 /* Just store new SRGB values if Label Manager is not available.
239 * SRGB will be configured later when SR start */
240 if (!isis_zebra_label_manager_ready()) {
241 srdb
->config
.srgb_lower_bound
= lower_bound
;
242 srdb
->config
.srgb_upper_bound
= upper_bound
;
246 /* Label Manager is ready, start by releasing the old SRGB. */
247 if (srdb
->srgb_active
) {
248 isis_zebra_release_label_range(srdb
->config
.srgb_lower_bound
,
249 srdb
->config
.srgb_upper_bound
);
250 srdb
->srgb_active
= false;
253 srdb
->config
.srgb_lower_bound
= lower_bound
;
254 srdb
->config
.srgb_upper_bound
= upper_bound
;
257 /* then request new SRGB if SR is enabled. */
258 if (isis_zebra_request_label_range(
259 srdb
->config
.srgb_lower_bound
,
260 srdb
->config
.srgb_upper_bound
261 - srdb
->config
.srgb_lower_bound
+ 1) < 0) {
262 srdb
->srgb_active
= false;
265 srdb
->srgb_active
= true;
268 sr_debug(" |- Got new SRGB [%u/%u]",
269 srdb
->config
.srgb_lower_bound
,
270 srdb
->config
.srgb_upper_bound
);
272 lsp_regenerate_schedule(area
, area
->is_type
, 0);
273 } else if (srdb
->config
.enabled
) {
274 /* Try to enable SR again using the new SRGB. */
282 * Update Segment Routing Local Block range which is reserved though the
283 * Label Manager. This function trigger the update of local Adjacency-SID
286 * @param area IS-IS area
287 * @param lower_bound Lower bound of SRLB
288 * @param upper_bound Upper bound of SRLB
290 * @return 0 on success, -1 otherwise
292 int isis_sr_cfg_srlb_update(struct isis_area
*area
, uint32_t lower_bound
,
293 uint32_t upper_bound
)
295 struct isis_sr_db
*srdb
= &area
->srdb
;
296 struct listnode
*node
;
297 struct sr_adjacency
*sra
;
299 sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
300 area
->area_tag
, lower_bound
, upper_bound
);
302 /* Just store new SRLB values if Label Manager is not available.
303 * SRLB will be configured later when SR start */
304 if (!isis_zebra_label_manager_ready()) {
305 srdb
->config
.srlb_lower_bound
= lower_bound
;
306 srdb
->config
.srlb_upper_bound
= upper_bound
;
310 /* LM is ready, start by deleting the old SRLB */
311 sr_local_block_delete(area
);
313 srdb
->config
.srlb_lower_bound
= lower_bound
;
314 srdb
->config
.srlb_upper_bound
= upper_bound
;
317 /* Initialize new SRLB */
318 if (sr_local_block_init(area
) != 0)
321 /* Reinstall local Adjacency-SIDs with new labels. */
322 for (ALL_LIST_ELEMENTS_RO(area
->srdb
.adj_sids
, node
, sra
))
323 sr_adj_sid_update(sra
, &srdb
->srlb
);
325 /* Update and Flood LSP */
326 lsp_regenerate_schedule(area
, area
->is_type
, 0);
327 } else if (srdb
->config
.enabled
) {
328 /* Try to enable SR again using the new SRLB. */
336 * Add new Prefix-SID configuration to the SRDB.
338 * @param area IS-IS area
339 * @param prefix Prefix to be added
341 * @return Newly added Prefix-SID configuration structure
343 struct sr_prefix_cfg
*isis_sr_cfg_prefix_add(struct isis_area
*area
,
344 const struct prefix
*prefix
,
347 struct sr_prefix_cfg
*pcfg
;
348 struct interface
*ifp
;
350 sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area
->area_tag
, prefix
);
352 pcfg
= XCALLOC(MTYPE_ISIS_SR_INFO
, sizeof(*pcfg
));
353 pcfg
->prefix
= *prefix
;
355 pcfg
->algorithm
= algorithm
;
357 /* Pull defaults from the YANG module. */
358 pcfg
->sid_type
= yang_get_default_enum(
359 "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR
);
360 pcfg
->last_hop_behavior
= yang_get_default_enum(
361 "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR
);
363 /* Mark as node Sid if the prefix is host and configured in loopback */
364 ifp
= if_lookup_prefix(prefix
, VRF_DEFAULT
);
365 if (ifp
&& sr_prefix_is_node_sid(ifp
, prefix
))
366 pcfg
->node_sid
= true;
368 /* Save prefix-sid configuration. */
369 srdb_prefix_cfg_add(&area
->srdb
.config
.prefix_sids
, pcfg
);
375 * Removal of locally configured Prefix-SID.
377 * @param pcfg Configured Prefix-SID
379 void isis_sr_cfg_prefix_del(struct sr_prefix_cfg
*pcfg
)
381 struct isis_area
*area
= pcfg
->area
;
383 sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u",
384 area
->area_tag
, &pcfg
->prefix
,
385 pcfg
->sid_type
== SR_SID_VALUE_TYPE_INDEX
? "index" : "label",
388 srdb_prefix_cfg_del(&area
->srdb
.config
.prefix_sids
, pcfg
);
389 XFREE(MTYPE_ISIS_SR_INFO
, pcfg
);
393 * Lookup for Prefix-SID in the local configuration.
395 * @param area IS-IS area
396 * @param prefix Prefix to lookup
398 * @return Configured Prefix-SID structure if found, NULL otherwise
400 struct sr_prefix_cfg
*isis_sr_cfg_prefix_find(struct isis_area
*area
,
401 union prefixconstptr prefix
,
404 struct sr_prefix_cfg pcfg
= {};
406 prefix_copy(&pcfg
.prefix
, prefix
.p
);
407 pcfg
.algorithm
= algorithm
;
408 return srdb_prefix_cfg_find(&area
->srdb
.config
.prefix_sids
, &pcfg
);
412 * Fill in Prefix-SID Sub-TLV according to the corresponding configuration.
414 * @param pcfg Prefix-SID configuration
415 * @param external False if prefix is locally configured, true otherwise
416 * @param psid Prefix-SID sub-TLV to be updated
418 void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg
*pcfg
, bool external
,
419 struct isis_prefix_sid
*psid
)
421 /* Set SID algorithm. */
422 psid
->algorithm
= pcfg
->algorithm
;
426 switch (pcfg
->last_hop_behavior
) {
427 case SR_LAST_HOP_BEHAVIOR_EXP_NULL
:
428 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
);
429 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
);
431 case SR_LAST_HOP_BEHAVIOR_NO_PHP
:
432 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
);
433 UNSET_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
);
435 case SR_LAST_HOP_BEHAVIOR_PHP
:
436 UNSET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
);
437 UNSET_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
);
441 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_READVERTISED
);
442 if (pcfg
->node_sid
&& !pcfg
->n_flag_clear
)
443 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NODE
);
446 psid
->value
= pcfg
->sid
;
447 if (pcfg
->sid_type
== SR_SID_VALUE_TYPE_ABSOLUTE
) {
448 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_VALUE
);
449 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_LOCAL
);
454 * Delete all backup Adj-SIDs.
456 * @param area IS-IS area
457 * @param level IS-IS level
459 void isis_area_delete_backup_adj_sids(struct isis_area
*area
, int level
)
461 struct sr_adjacency
*sra
;
462 struct listnode
*node
, *nnode
;
464 for (ALL_LIST_ELEMENTS(area
->srdb
.adj_sids
, node
, nnode
, sra
))
465 if (sra
->type
== ISIS_SR_LAN_BACKUP
466 && (sra
->adj
->level
& level
))
470 /* --- Segment Routing Local Block management functions --------------------- */
473 * Initialize Segment Routing Local Block from SRDB configuration and reserve
474 * block of bits to manage label allocation.
476 * @param area IS-IS area
478 static int sr_local_block_init(struct isis_area
*area
)
480 struct isis_sr_db
*srdb
= &area
->srdb
;
481 struct sr_local_block
*srlb
= &srdb
->srlb
;
483 /* Check if SRLB is not already configured */
488 * Request SRLB to the label manager. If the allocation fails, return
489 * an error to disable SR until a new SRLB is successfully allocated.
491 if (isis_zebra_request_label_range(
492 srdb
->config
.srlb_lower_bound
,
493 srdb
->config
.srlb_upper_bound
494 - srdb
->config
.srlb_lower_bound
+ 1)) {
495 srlb
->active
= false;
499 sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area
->area_tag
,
500 srdb
->config
.srlb_lower_bound
, srdb
->config
.srlb_upper_bound
);
502 /* Initialize the SRLB */
503 srlb
->start
= srdb
->config
.srlb_lower_bound
;
504 srlb
->end
= srdb
->config
.srlb_upper_bound
;
506 /* Compute the needed Used Mark number and allocate them */
507 srlb
->max_block
= (srlb
->end
- srlb
->start
+ 1) / SRLB_BLOCK_SIZE
;
508 if (((srlb
->end
- srlb
->start
+ 1) % SRLB_BLOCK_SIZE
) != 0)
510 srlb
->used_mark
= XCALLOC(MTYPE_ISIS_SR_INFO
,
511 srlb
->max_block
* SRLB_BLOCK_SIZE
);
518 * Remove Segment Routing Local Block.
520 * @param area IS-IS area
522 static void sr_local_block_delete(struct isis_area
*area
)
524 struct isis_sr_db
*srdb
= &area
->srdb
;
525 struct sr_local_block
*srlb
= &srdb
->srlb
;
527 /* Check if SRLB is not already delete */
531 sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area
->area_tag
,
532 srlb
->start
, srlb
->end
);
534 /* First release the label block */
535 isis_zebra_release_label_range(srdb
->config
.srlb_lower_bound
,
536 srdb
->config
.srlb_upper_bound
);
538 /* Then reset SRLB structure */
539 if (srlb
->used_mark
!= NULL
)
540 XFREE(MTYPE_ISIS_SR_INFO
, srlb
->used_mark
);
541 srlb
->active
= false;
545 * Request a label from the Segment Routing Local Block.
547 * @param srlb Segment Routing Local Block
549 * @return First available label on success or MPLS_INVALID_LABEL if the
550 * block of labels is full
552 static mpls_label_t
sr_local_block_request_label(struct sr_local_block
*srlb
)
557 uint32_t size
= srlb
->end
- srlb
->start
+ 1;
559 /* Check if we ran out of available labels */
560 if (srlb
->current
>= size
)
561 return MPLS_INVALID_LABEL
;
563 /* Get first available label and mark it used */
564 label
= srlb
->current
+ srlb
->start
;
565 index
= srlb
->current
/ SRLB_BLOCK_SIZE
;
566 pos
= 1ULL << (srlb
->current
% SRLB_BLOCK_SIZE
);
567 srlb
->used_mark
[index
] |= pos
;
569 /* Jump to the next free position */
571 pos
= srlb
->current
% SRLB_BLOCK_SIZE
;
572 while (srlb
->current
< size
) {
575 if (!((1ULL << pos
) & srlb
->used_mark
[index
]))
579 pos
= srlb
->current
% SRLB_BLOCK_SIZE
;
583 if (srlb
->current
== size
)
585 "SR: Warning, SRLB is depleted and next label request will fail");
591 * Release label in the Segment Routing Local Block.
593 * @param srlb Segment Routing Local Block
594 * @param label Label to be release
596 * @return 0 on success or -1 if label falls outside SRLB
598 static int sr_local_block_release_label(struct sr_local_block
*srlb
,
604 /* Check that label falls inside the SRLB */
605 if ((label
< srlb
->start
) || (label
> srlb
->end
)) {
606 flog_warn(EC_ISIS_SID_OVERFLOW
,
607 "%s: Returning label %u is outside SRLB [%u/%u]",
608 __func__
, label
, srlb
->start
, srlb
->end
);
612 index
= (label
- srlb
->start
) / SRLB_BLOCK_SIZE
;
613 pos
= 1ULL << ((label
- srlb
->start
) % SRLB_BLOCK_SIZE
);
614 srlb
->used_mark
[index
] &= ~pos
;
615 /* Reset current to the first available position */
616 for (index
= 0; index
< srlb
->max_block
; index
++) {
617 if (srlb
->used_mark
[index
] != 0xFFFFFFFFFFFFFFFF) {
618 for (pos
= 0; pos
< SRLB_BLOCK_SIZE
; pos
++)
619 if (!((1ULL << pos
) & srlb
->used_mark
[index
])) {
621 index
* SRLB_BLOCK_SIZE
+ pos
;
631 /* --- Segment Routing Adjacency-SID management functions ------------------- */
634 * Add new local Adjacency-SID.
636 * @param adj IS-IS Adjacency
637 * @param family Inet Family (IPv4 or IPv6)
638 * @param backup True to initialize backup Adjacency SID
639 * @param nexthops List of backup nexthops (for backup Adj-SIDs only)
641 void sr_adj_sid_add_single(struct isis_adjacency
*adj
, int family
, bool backup
,
642 struct list
*nexthops
)
644 struct isis_circuit
*circuit
= adj
->circuit
;
645 struct isis_area
*area
= circuit
->area
;
646 struct sr_adjacency
*sra
;
647 struct isis_adj_sid
*adj_sid
;
648 struct isis_lan_adj_sid
*ladj_sid
;
649 union g_addr nexthop
= {};
651 mpls_label_t input_label
;
653 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area
->area_tag
,
654 backup
? "Backup" : "Primary");
656 /* Determine nexthop IP address */
659 if (!circuit
->ip_router
|| !adj
->ipv4_address_count
)
662 nexthop
.ipv4
= adj
->ipv4_addresses
[0];
665 if (!circuit
->ipv6_router
|| !adj
->ll_ipv6_count
)
668 nexthop
.ipv6
= adj
->ll_ipv6_addrs
[0];
671 flog_err(EC_LIB_DEVELOPMENT
,
672 "%s: unexpected address-family: %u", __func__
, family
);
676 /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */
677 flags
= EXT_SUBTLV_LINK_ADJ_SID_VFLG
| EXT_SUBTLV_LINK_ADJ_SID_LFLG
;
678 if (family
== AF_INET6
)
679 SET_FLAG(flags
, EXT_SUBTLV_LINK_ADJ_SID_FFLG
);
681 SET_FLAG(flags
, EXT_SUBTLV_LINK_ADJ_SID_BFLG
);
683 /* Get a label from the SRLB for this Adjacency */
684 input_label
= sr_local_block_request_label(&area
->srdb
.srlb
);
685 if (input_label
== MPLS_INVALID_LABEL
)
688 if (circuit
->ext
== NULL
)
689 circuit
->ext
= isis_alloc_ext_subtlvs();
691 sra
= XCALLOC(MTYPE_ISIS_SR_INFO
, sizeof(*sra
));
692 sra
->type
= backup
? ISIS_SR_LAN_BACKUP
: ISIS_SR_ADJ_NORMAL
;
693 sra
->input_label
= input_label
;
694 sra
->nexthop
.family
= family
;
695 sra
->nexthop
.address
= nexthop
;
697 if (backup
&& nexthops
) {
698 struct isis_vertex_adj
*vadj
;
699 struct listnode
*node
;
701 sra
->backup_nexthops
= list_new();
702 for (ALL_LIST_ELEMENTS_RO(nexthops
, node
, vadj
)) {
703 struct isis_adjacency
*adj
= vadj
->sadj
->adj
;
704 struct mpls_label_stack
*label_stack
;
706 label_stack
= vadj
->label_stack
;
707 adjinfo2nexthop(family
, sra
->backup_nexthops
, adj
, NULL
,
712 switch (circuit
->circ_type
) {
713 /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */
714 case CIRCUIT_T_BROADCAST
:
715 ladj_sid
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*ladj_sid
));
716 ladj_sid
->family
= family
;
717 ladj_sid
->flags
= flags
;
718 ladj_sid
->weight
= 0;
719 memcpy(ladj_sid
->neighbor_id
, adj
->sysid
,
720 sizeof(ladj_sid
->neighbor_id
));
721 ladj_sid
->sid
= input_label
;
722 isis_tlvs_add_lan_adj_sid(circuit
->ext
, ladj_sid
);
723 sra
->u
.ladj_sid
= ladj_sid
;
725 /* Adjacency-SID for Point to Point interface section #2.2.1 */
727 adj_sid
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*adj_sid
));
728 adj_sid
->family
= family
;
729 adj_sid
->flags
= flags
;
731 adj_sid
->sid
= input_label
;
732 isis_tlvs_add_adj_sid(circuit
->ext
, adj_sid
);
733 sra
->u
.adj_sid
= adj_sid
;
736 flog_err(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
737 __func__
, circuit
->circ_type
);
741 /* Add Adjacency-SID in SRDB */
743 listnode_add(area
->srdb
.adj_sids
, sra
);
744 listnode_add(adj
->adj_sids
, sra
);
746 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD
, sra
);
750 * Add Primary and Backup local Adjacency SID.
752 * @param adj IS-IS Adjacency
753 * @param family Inet Family (IPv4 or IPv6)
755 static void sr_adj_sid_add(struct isis_adjacency
*adj
, int family
)
757 sr_adj_sid_add_single(adj
, family
, false, NULL
);
760 static void sr_adj_sid_update(struct sr_adjacency
*sra
,
761 struct sr_local_block
*srlb
)
763 struct isis_circuit
*circuit
= sra
->adj
->circuit
;
765 /* First remove the old MPLS Label */
766 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE
, sra
);
768 /* Got new label in the new SRLB */
769 sra
->input_label
= sr_local_block_request_label(srlb
);
770 if (sra
->input_label
== MPLS_INVALID_LABEL
)
773 switch (circuit
->circ_type
) {
774 case CIRCUIT_T_BROADCAST
:
775 sra
->u
.ladj_sid
->sid
= sra
->input_label
;
778 sra
->u
.adj_sid
->sid
= sra
->input_label
;
781 flog_warn(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
782 __func__
, circuit
->circ_type
);
786 /* Finally configure the new MPLS Label */
787 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD
, sra
);
791 * Delete local Adj-SID.
793 * @param sra Segment Routing Adjacency
795 static void sr_adj_sid_del(struct sr_adjacency
*sra
)
797 struct isis_circuit
*circuit
= sra
->adj
->circuit
;
798 struct isis_area
*area
= circuit
->area
;
800 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area
->area_tag
);
802 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE
, sra
);
804 /* Release dynamic label and remove subTLVs */
805 switch (circuit
->circ_type
) {
806 case CIRCUIT_T_BROADCAST
:
807 sr_local_block_release_label(&area
->srdb
.srlb
,
808 sra
->u
.ladj_sid
->sid
);
809 isis_tlvs_del_lan_adj_sid(circuit
->ext
, sra
->u
.ladj_sid
);
812 sr_local_block_release_label(&area
->srdb
.srlb
,
813 sra
->u
.adj_sid
->sid
);
814 isis_tlvs_del_adj_sid(circuit
->ext
, sra
->u
.adj_sid
);
817 flog_err(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
818 __func__
, circuit
->circ_type
);
822 if (sra
->type
== ISIS_SR_LAN_BACKUP
&& sra
->backup_nexthops
) {
823 sra
->backup_nexthops
->del
=
824 (void (*)(void *))isis_nexthop_delete
;
825 list_delete(&sra
->backup_nexthops
);
828 /* Remove Adjacency-SID from the SRDB */
829 listnode_delete(area
->srdb
.adj_sids
, sra
);
830 listnode_delete(sra
->adj
->adj_sids
, sra
);
831 XFREE(MTYPE_ISIS_SR_INFO
, sra
);
835 * Lookup Segment Routing Adj-SID by family and type.
837 * @param adj IS-IS Adjacency
838 * @param family Inet Family (IPv4 or IPv6)
839 * @param type Adjacency SID type
841 struct sr_adjacency
*isis_sr_adj_sid_find(struct isis_adjacency
*adj
,
842 int family
, enum sr_adj_type type
)
844 struct sr_adjacency
*sra
;
845 struct listnode
*node
;
847 for (ALL_LIST_ELEMENTS_RO(adj
->adj_sids
, node
, sra
))
848 if (sra
->nexthop
.family
== family
&& sra
->type
== type
)
855 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
857 * @param adj IS-IS Adjacency
861 static int sr_adj_state_change(struct isis_adjacency
*adj
)
863 struct sr_adjacency
*sra
;
864 struct listnode
*node
, *nnode
;
866 if (!adj
->circuit
->area
->srdb
.enabled
)
869 if (adj
->adj_state
== ISIS_ADJ_UP
)
872 for (ALL_LIST_ELEMENTS(adj
->adj_sids
, node
, nnode
, sra
))
879 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
880 * IPv6 address to corresponding Adjacency-SID accordingly.
882 * @param adj IS-IS Adjacency
883 * @param family Inet Family (IPv4 or IPv6)
884 * @param global Indicate if it concerns the Local or Global IPv6 addresses
888 static int sr_adj_ip_enabled(struct isis_adjacency
*adj
, int family
,
891 if (!adj
->circuit
->area
->srdb
.enabled
|| global
)
894 sr_adj_sid_add(adj
, family
);
900 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
901 * delete the corresponding Adjacency-SID(s) accordingly.
903 * @param adj IS-IS Adjacency
904 * @param family Inet Family (IPv4 or IPv6)
905 * @param global Indicate if it concerns the Local or Global IPv6 addresses
909 static int sr_adj_ip_disabled(struct isis_adjacency
*adj
, int family
,
912 struct sr_adjacency
*sra
;
913 struct listnode
*node
, *nnode
;
915 if (!adj
->circuit
->area
->srdb
.enabled
|| global
)
918 for (ALL_LIST_ELEMENTS(adj
->adj_sids
, node
, nnode
, sra
))
919 if (sra
->nexthop
.family
== family
)
926 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
928 * @param ifp Loopback Interface
932 static int sr_if_new_hook(struct interface
*ifp
)
934 struct sr_prefix_cfg
*pcfgs
[SR_ALGORITHM_COUNT
] = {NULL
};
935 struct isis_circuit
*circuit
;
936 struct isis_area
*area
;
937 struct connected
*connected
;
938 struct listnode
*node
;
939 bool need_lsp_regenerate
= false;
941 /* Get corresponding circuit */
942 circuit
= circuit_scan_by_ifp(ifp
);
946 area
= circuit
->area
;
951 * Update the Node-SID flag of the configured Prefix-SID mappings if
952 * necessary. This needs to be done here since isisd reads the startup
953 * configuration before receiving interface information from zebra.
955 FOR_ALL_INTERFACES_ADDRESSES (ifp
, connected
, node
) {
957 for (int i
= 0; i
< SR_ALGORITHM_COUNT
; i
++) {
958 pcfgs
[i
] = isis_sr_cfg_prefix_find(
959 area
, connected
->address
, i
);
964 if (sr_prefix_is_node_sid(ifp
, &pcfgs
[i
]->prefix
)) {
965 pcfgs
[i
]->node_sid
= true;
966 need_lsp_regenerate
= true;
971 if (need_lsp_regenerate
)
972 lsp_regenerate_schedule(area
, area
->is_type
, 0);
978 * Show LFIB operation in human readable format.
980 * @param buf Buffer to store string output. Must be pre-allocate
981 * @param size Size of the buffer
982 * @param label_in Input Label
983 * @param label_out Output Label
985 * @return String containing LFIB operation in human readable format
987 char *sr_op2str(char *buf
, size_t size
, mpls_label_t label_in
,
988 mpls_label_t label_out
)
993 if (label_in
== MPLS_INVALID_LABEL
) {
994 snprintf(buf
, size
, "no-op.");
999 case MPLS_LABEL_IMPLICIT_NULL
:
1000 snprintf(buf
, size
, "Pop(%u)", label_in
);
1002 case MPLS_LABEL_IPV4_EXPLICIT_NULL
:
1003 case MPLS_LABEL_IPV6_EXPLICIT_NULL
:
1004 snprintf(buf
, size
, "Swap(%u, null)", label_in
);
1006 case MPLS_INVALID_LABEL
:
1007 snprintf(buf
, size
, "no-op.");
1010 snprintf(buf
, size
, "Swap(%u, %u)", label_in
, label_out
);
1017 * Show Segment Routing Node.
1019 * @param vty VTY output
1020 * @param area IS-IS area
1021 * @param level IS-IS level
1023 static void show_node(struct vty
*vty
, struct isis_area
*area
, int level
,
1026 struct isis_lsp
*lsp
;
1030 vty_out(vty
, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level
));
1032 /* Prepare table. */
1033 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1034 ttable_add_row(tt
, "System ID|SRGB|SRLB|Algorithm|MSD");
1035 tt
->style
.cell
.rpad
= 2;
1036 tt
->style
.corner
= '+';
1038 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1040 frr_each (lspdb
, &area
->lspdb
[level
- 1], lsp
) {
1041 struct isis_router_cap
*cap
;
1045 cap
= lsp
->tlvs
->router_cap
;
1048 if (cap
->algo
[algo
] == SR_ALGORITHM_UNSET
)
1051 if (cap
->algo
[algo
] == SR_ALGORITHM_SPF
)
1052 snprintf(buf
, sizeof(buf
), "SPF");
1053 else if (cap
->algo
[algo
] == SR_ALGORITHM_STRICT_SPF
)
1054 snprintf(buf
, sizeof(buf
), "S-SPF");
1057 snprintf(buf
, sizeof(buf
), "Flex-Algo %d", algo
);
1058 #endif /* ifndef FABRICD */
1060 ttable_add_row(tt
, "%pSY|%u - %u|%u - %u|%s|%u",
1061 lsp
->hdr
.lsp_id
, cap
->srgb
.lower_bound
,
1062 cap
->srgb
.lower_bound
+ cap
->srgb
.range_size
- 1,
1063 cap
->srlb
.lower_bound
,
1064 cap
->srlb
.lower_bound
+ cap
->srlb
.range_size
- 1,
1068 /* Dump the generated table. */
1069 if (tt
->nrows
> 1) {
1072 table
= ttable_dump(tt
, "\n");
1073 vty_out(vty
, "%s\n", table
);
1074 XFREE(MTYPE_TMP
, table
);
1079 DEFUN(show_sr_node
, show_sr_node_cmd
,
1081 " segment-routing node"
1083 " [algorithm (128-255)]"
1084 #endif /* ifndef FABRICD */
1088 "Segment-Routing node\n"
1090 "Show Flex-algo nodes\n"
1091 "Algorithm number\n"
1092 #endif /* ifndef FABRICD */
1095 struct listnode
*node
, *inode
;
1096 struct isis_area
*area
;
1097 uint8_t algorithm
= SR_ALGORITHM_SPF
;
1102 if (argv_find(argv
, argc
, "algorithm", &idx
))
1103 algorithm
= (uint8_t)strtoul(argv
[idx
+ 1]->arg
, NULL
, 10);
1104 #endif /* ifndef FABRICD */
1106 for (ALL_LIST_ELEMENTS_RO(im
->isis
, inode
, isis
)) {
1107 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
1108 vty_out(vty
, "Area %s:\n",
1109 area
->area_tag
? area
->area_tag
: "null");
1110 if (!area
->srdb
.enabled
) {
1111 vty_out(vty
, " Segment Routing is disabled\n");
1114 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
;
1116 show_node(vty
, area
, level
, algorithm
);
1123 /* --- IS-IS Segment Routing Management function ---------------------------- */
1126 * Thread function to re-attempt connection to the Label Manager and thus be
1127 * able to start Segment Routing.
1129 * @param start Thread structure that contains area as argument
1131 * @return 1 on success
1133 static void sr_start_label_manager(struct event
*start
)
1135 struct isis_area
*area
;
1137 area
= EVENT_ARG(start
);
1139 /* re-attempt to start SR & Label Manager connection */
1140 isis_sr_start(area
);
1144 * Enable SR on the given IS-IS area.
1146 * @param area IS-IS area
1148 * @return 0 on success, -1 otherwise
1150 int isis_sr_start(struct isis_area
*area
)
1152 struct isis_sr_db
*srdb
= &area
->srdb
;
1153 struct isis_adjacency
*adj
;
1154 struct listnode
*node
;
1156 /* First start Label Manager if not ready */
1157 if (!isis_zebra_label_manager_ready())
1158 if (isis_zebra_label_manager_connect() < 0) {
1159 /* Re-attempt to connect to Label Manager in 1 sec. */
1160 event_add_timer(master
, sr_start_label_manager
, area
, 1,
1165 /* Label Manager is ready, initialize the SRLB */
1166 if (sr_local_block_init(area
) < 0)
1170 * Request SGRB to the label manager if not already active. If the
1171 * allocation fails, return an error to disable SR until a new SRGB
1172 * is successfully allocated.
1174 if (!srdb
->srgb_active
) {
1175 if (isis_zebra_request_label_range(
1176 srdb
->config
.srgb_lower_bound
,
1177 srdb
->config
.srgb_upper_bound
1178 - srdb
->config
.srgb_lower_bound
+ 1)
1180 srdb
->srgb_active
= false;
1183 srdb
->srgb_active
= true;
1186 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
1189 /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */
1190 for (ALL_LIST_ELEMENTS_RO(area
->adjacency_list
, node
, adj
)) {
1191 if (adj
->ipv4_address_count
> 0)
1192 sr_adj_sid_add(adj
, AF_INET
);
1193 if (adj
->ll_ipv6_count
> 0)
1194 sr_adj_sid_add(adj
, AF_INET6
);
1197 area
->srdb
.enabled
= true;
1199 /* Regenerate LSPs to advertise Segment Routing capabilities. */
1200 lsp_regenerate_schedule(area
, area
->is_type
, 0);
1206 * Disable SR on the given IS-IS area.
1208 * @param area IS-IS area
1210 void isis_sr_stop(struct isis_area
*area
)
1212 struct isis_sr_db
*srdb
= &area
->srdb
;
1213 struct sr_adjacency
*sra
;
1214 struct listnode
*node
, *nnode
;
1216 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
1219 /* Disable any re-attempt to connect to Label Manager */
1220 EVENT_OFF(srdb
->t_start_lm
);
1222 /* Uninstall all local Adjacency-SIDs. */
1223 for (ALL_LIST_ELEMENTS(area
->srdb
.adj_sids
, node
, nnode
, sra
))
1224 sr_adj_sid_del(sra
);
1226 /* Release SRGB if active. */
1227 if (srdb
->srgb_active
) {
1228 isis_zebra_release_label_range(srdb
->config
.srgb_lower_bound
,
1229 srdb
->config
.srgb_upper_bound
);
1230 srdb
->srgb_active
= false;
1234 sr_local_block_delete(area
);
1236 area
->srdb
.enabled
= false;
1238 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
1239 lsp_regenerate_schedule(area
, area
->is_type
, 0);
1243 * IS-IS Segment Routing initialization for given area.
1245 * @param area IS-IS area
1247 void isis_sr_area_init(struct isis_area
*area
)
1249 struct isis_sr_db
*srdb
= &area
->srdb
;
1251 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
1254 /* Initialize Segment Routing Data Base */
1255 memset(srdb
, 0, sizeof(*srdb
));
1256 srdb
->adj_sids
= list_new();
1258 /* Pull defaults from the YANG module. */
1260 srdb
->config
.enabled
= yang_get_default_bool("%s/enabled", ISIS_SR
);
1261 srdb
->config
.srgb_lower_bound
= yang_get_default_uint32(
1262 "%s/label-blocks/srgb/lower-bound", ISIS_SR
);
1263 srdb
->config
.srgb_upper_bound
= yang_get_default_uint32(
1264 "%s/label-blocks/srgb/upper-bound", ISIS_SR
);
1265 srdb
->config
.srlb_lower_bound
= yang_get_default_uint32(
1266 "%s/label-blocks/srlb/lower-bound", ISIS_SR
);
1267 srdb
->config
.srlb_upper_bound
= yang_get_default_uint32(
1268 "%s/label-blocks/srlb/upper-bound", ISIS_SR
);
1270 srdb
->config
.enabled
= false;
1271 srdb
->config
.srgb_lower_bound
= SRGB_LOWER_BOUND
;
1272 srdb
->config
.srgb_upper_bound
= SRGB_UPPER_BOUND
;
1273 srdb
->config
.srlb_lower_bound
= SRLB_LOWER_BOUND
;
1274 srdb
->config
.srlb_upper_bound
= SRLB_UPPER_BOUND
;
1276 srdb
->config
.msd
= 0;
1277 srdb_prefix_cfg_init(&srdb
->config
.prefix_sids
);
1281 * Terminate IS-IS Segment Routing for the given area.
1283 * @param area IS-IS area
1285 void isis_sr_area_term(struct isis_area
*area
)
1287 struct isis_sr_db
*srdb
= &area
->srdb
;
1289 /* Stop Segment Routing */
1290 if (area
->srdb
.enabled
)
1293 /* Free Adjacency SID list */
1294 list_delete(&srdb
->adj_sids
);
1296 /* Clear Prefix-SID configuration. */
1297 while (srdb_prefix_cfg_count(&srdb
->config
.prefix_sids
) > 0) {
1298 struct sr_prefix_cfg
*pcfg
;
1300 pcfg
= srdb_prefix_cfg_first(&srdb
->config
.prefix_sids
);
1301 isis_sr_cfg_prefix_del(pcfg
);
1306 * IS-IS Segment Routing global initialization.
1308 void isis_sr_init(void)
1310 install_element(VIEW_NODE
, &show_sr_node_cmd
);
1312 /* Register hooks. */
1313 hook_register(isis_adj_state_change_hook
, sr_adj_state_change
);
1314 hook_register(isis_adj_ip_enabled_hook
, sr_adj_ip_enabled
);
1315 hook_register(isis_adj_ip_disabled_hook
, sr_adj_ip_disabled
);
1316 hook_register(isis_if_new_hook
, sr_if_new_hook
);
1320 * IS-IS Segment Routing global terminate.
1322 void isis_sr_term(void)
1324 /* Unregister hooks. */
1325 hook_unregister(isis_adj_state_change_hook
, sr_adj_state_change
);
1326 hook_unregister(isis_adj_ip_enabled_hook
, sr_adj_ip_enabled
);
1327 hook_unregister(isis_adj_ip_disabled_hook
, sr_adj_ip_disabled
);
1328 hook_unregister(isis_if_new_hook
, sr_if_new_hook
);