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
)
60 return prefix_cmp(&a
->prefix
, &b
->prefix
);
62 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg
, struct sr_prefix_cfg
, entry
,
63 sr_prefix_sid_cfg_compare
);
66 * Find SRGB associated to a System ID.
68 * @param area IS-IS LSP database
69 * @param sysid System ID to lookup
71 * @return Pointer to SRGB if found, NULL otherwise
73 struct isis_sr_block
*isis_sr_find_srgb(struct lspdb_head
*lspdb
,
78 lsp
= isis_root_system_lsp(lspdb
, sysid
);
82 if (!lsp
->tlvs
->router_cap
83 || lsp
->tlvs
->router_cap
->srgb
.range_size
== 0)
86 return &lsp
->tlvs
->router_cap
->srgb
;
90 * Compute input label for the given Prefix-SID.
92 * @param area IS-IS area
93 * @param psid IS-IS Prefix-SID Sub-TLV
94 * @param local Indicates whether the Prefix-SID is local or not
96 * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow
98 mpls_label_t
sr_prefix_in_label(struct isis_area
*area
,
99 struct isis_prefix_sid
*psid
, bool local
)
102 * No need to assign a label for local Prefix-SIDs unless the no-PHP
106 && (!CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
)
107 || CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
)))
108 return MPLS_INVALID_LABEL
;
110 /* Return SID value as MPLS label if it is an Absolute SID */
111 if (CHECK_FLAG(psid
->flags
,
112 ISIS_PREFIX_SID_VALUE
| ISIS_PREFIX_SID_LOCAL
))
115 /* Check that SID index falls inside the SRGB */
116 if (psid
->value
>= (area
->srdb
.config
.srgb_upper_bound
117 - area
->srdb
.config
.srgb_lower_bound
+ 1)) {
118 flog_warn(EC_ISIS_SID_OVERFLOW
,
119 "%s: SID index %u falls outside local SRGB range",
120 __func__
, psid
->value
);
121 return MPLS_INVALID_LABEL
;
124 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
125 return (area
->srdb
.config
.srgb_lower_bound
+ psid
->value
);
129 * Compute output label for the given Prefix-SID.
131 * @param lspdb IS-IS LSP database
132 * @param family Prefix-SID address family
133 * @param psid Prefix-SID Sub-TLV
134 * @param nh_sysid System ID of the nexthop node
135 * @param last_hop Indicates whether the nexthop node is the last hop
137 * @return MPLS label or MPLS_INVALID_LABEL in case of error
139 mpls_label_t
sr_prefix_out_label(struct lspdb_head
*lspdb
, int family
,
140 struct isis_prefix_sid
*psid
,
141 const uint8_t *nh_sysid
, bool last_hop
)
143 struct isis_sr_block
*nh_srgb
;
146 if (!CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
))
147 return MPLS_LABEL_IMPLICIT_NULL
;
149 if (CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
)) {
150 if (family
== AF_INET
)
151 return MPLS_LABEL_IPV4_EXPLICIT_NULL
;
153 return MPLS_LABEL_IPV6_EXPLICIT_NULL
;
158 /* Return SID value as MPLS label if it is an Absolute SID */
159 if (CHECK_FLAG(psid
->flags
,
160 ISIS_PREFIX_SID_VALUE
| ISIS_PREFIX_SID_LOCAL
)) {
162 * V/L SIDs have local significance, so only adjacent routers
163 * can use them (RFC8667 section #2.1.1.1)
166 return MPLS_INVALID_LABEL
;
170 /* Check that SID index falls inside the SRGB */
171 nh_srgb
= isis_sr_find_srgb(lspdb
, nh_sysid
);
173 return MPLS_INVALID_LABEL
;
176 * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
179 if ((family
== AF_INET
&& !IS_SR_IPV4(nh_srgb
))
180 || (family
== AF_INET6
&& !IS_SR_IPV6(nh_srgb
)))
181 return MPLS_INVALID_LABEL
;
183 if (psid
->value
>= nh_srgb
->range_size
) {
184 flog_warn(EC_ISIS_SID_OVERFLOW
,
185 "%s: SID index %u falls outside remote SRGB range",
186 __func__
, psid
->value
);
187 return MPLS_INVALID_LABEL
;
190 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
191 return (nh_srgb
->lower_bound
+ psid
->value
);
194 /* --- Functions used for Yang model and CLI to configure Segment Routing --- */
197 * Check if prefix correspond to a Node SID.
199 * @param ifp Interface
200 * @param prefix Prefix to be checked
202 * @return True if the interface/address pair corresponds to a Node-SID
204 static bool sr_prefix_is_node_sid(const struct interface
*ifp
,
205 const struct prefix
*prefix
)
207 return (if_is_loopback(ifp
) && is_host_route(prefix
));
211 * Update local SRGB configuration. SRGB is reserved though Label Manager.
212 * This function trigger the update of local Prefix-SID installation.
214 * @param area IS-IS area
215 * @param lower_bound Lower bound of SRGB
216 * @param upper_bound Upper bound of SRGB
218 * @return 0 on success, -1 otherwise
220 int isis_sr_cfg_srgb_update(struct isis_area
*area
, uint32_t lower_bound
,
221 uint32_t upper_bound
)
223 struct isis_sr_db
*srdb
= &area
->srdb
;
225 sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
226 area
->area_tag
, lower_bound
, upper_bound
);
228 /* Just store new SRGB values if Label Manager is not available.
229 * SRGB will be configured later when SR start */
230 if (!isis_zebra_label_manager_ready()) {
231 srdb
->config
.srgb_lower_bound
= lower_bound
;
232 srdb
->config
.srgb_upper_bound
= upper_bound
;
236 /* Label Manager is ready, start by releasing the old SRGB. */
237 if (srdb
->srgb_active
) {
238 isis_zebra_release_label_range(srdb
->config
.srgb_lower_bound
,
239 srdb
->config
.srgb_upper_bound
);
240 srdb
->srgb_active
= false;
243 srdb
->config
.srgb_lower_bound
= lower_bound
;
244 srdb
->config
.srgb_upper_bound
= upper_bound
;
247 /* then request new SRGB if SR is enabled. */
248 if (isis_zebra_request_label_range(
249 srdb
->config
.srgb_lower_bound
,
250 srdb
->config
.srgb_upper_bound
251 - srdb
->config
.srgb_lower_bound
+ 1) < 0) {
252 srdb
->srgb_active
= false;
255 srdb
->srgb_active
= true;
258 sr_debug(" |- Got new SRGB [%u/%u]",
259 srdb
->config
.srgb_lower_bound
,
260 srdb
->config
.srgb_upper_bound
);
262 lsp_regenerate_schedule(area
, area
->is_type
, 0);
263 } else if (srdb
->config
.enabled
) {
264 /* Try to enable SR again using the new SRGB. */
272 * Update Segment Routing Local Block range which is reserved though the
273 * Label Manager. This function trigger the update of local Adjacency-SID
276 * @param area IS-IS area
277 * @param lower_bound Lower bound of SRLB
278 * @param upper_bound Upper bound of SRLB
280 * @return 0 on success, -1 otherwise
282 int isis_sr_cfg_srlb_update(struct isis_area
*area
, uint32_t lower_bound
,
283 uint32_t upper_bound
)
285 struct isis_sr_db
*srdb
= &area
->srdb
;
286 struct listnode
*node
;
287 struct sr_adjacency
*sra
;
289 sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
290 area
->area_tag
, lower_bound
, upper_bound
);
292 /* Just store new SRLB values if Label Manager is not available.
293 * SRLB will be configured later when SR start */
294 if (!isis_zebra_label_manager_ready()) {
295 srdb
->config
.srlb_lower_bound
= lower_bound
;
296 srdb
->config
.srlb_upper_bound
= upper_bound
;
300 /* LM is ready, start by deleting the old SRLB */
301 sr_local_block_delete(area
);
303 srdb
->config
.srlb_lower_bound
= lower_bound
;
304 srdb
->config
.srlb_upper_bound
= upper_bound
;
307 /* Initialize new SRLB */
308 if (sr_local_block_init(area
) != 0)
311 /* Reinstall local Adjacency-SIDs with new labels. */
312 for (ALL_LIST_ELEMENTS_RO(area
->srdb
.adj_sids
, node
, sra
))
313 sr_adj_sid_update(sra
, &srdb
->srlb
);
315 /* Update and Flood LSP */
316 lsp_regenerate_schedule(area
, area
->is_type
, 0);
317 } else if (srdb
->config
.enabled
) {
318 /* Try to enable SR again using the new SRLB. */
326 * Add new Prefix-SID configuration to the SRDB.
328 * @param area IS-IS area
329 * @param prefix Prefix to be added
331 * @return Newly added Prefix-SID configuration structure
333 struct sr_prefix_cfg
*isis_sr_cfg_prefix_add(struct isis_area
*area
,
334 const struct prefix
*prefix
)
336 struct sr_prefix_cfg
*pcfg
;
337 struct interface
*ifp
;
339 sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area
->area_tag
, prefix
);
341 pcfg
= XCALLOC(MTYPE_ISIS_SR_INFO
, sizeof(*pcfg
));
342 pcfg
->prefix
= *prefix
;
345 /* Pull defaults from the YANG module. */
346 pcfg
->sid_type
= yang_get_default_enum(
347 "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR
);
348 pcfg
->last_hop_behavior
= yang_get_default_enum(
349 "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR
);
351 /* Mark as node Sid if the prefix is host and configured in loopback */
352 ifp
= if_lookup_prefix(prefix
, VRF_DEFAULT
);
353 if (ifp
&& sr_prefix_is_node_sid(ifp
, prefix
))
354 pcfg
->node_sid
= true;
356 /* Save prefix-sid configuration. */
357 srdb_prefix_cfg_add(&area
->srdb
.config
.prefix_sids
, pcfg
);
363 * Removal of locally configured Prefix-SID.
365 * @param pcfg Configured Prefix-SID
367 void isis_sr_cfg_prefix_del(struct sr_prefix_cfg
*pcfg
)
369 struct isis_area
*area
= pcfg
->area
;
371 sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u",
372 area
->area_tag
, &pcfg
->prefix
,
373 pcfg
->sid_type
== SR_SID_VALUE_TYPE_INDEX
? "index" : "label",
376 srdb_prefix_cfg_del(&area
->srdb
.config
.prefix_sids
, pcfg
);
377 XFREE(MTYPE_ISIS_SR_INFO
, pcfg
);
381 * Lookup for Prefix-SID in the local configuration.
383 * @param area IS-IS area
384 * @param prefix Prefix to lookup
386 * @return Configured Prefix-SID structure if found, NULL otherwise
388 struct sr_prefix_cfg
*isis_sr_cfg_prefix_find(struct isis_area
*area
,
389 union prefixconstptr prefix
)
391 struct sr_prefix_cfg pcfg
= {};
393 prefix_copy(&pcfg
.prefix
, prefix
.p
);
394 return srdb_prefix_cfg_find(&area
->srdb
.config
.prefix_sids
, &pcfg
);
398 * Fill in Prefix-SID Sub-TLV according to the corresponding configuration.
400 * @param pcfg Prefix-SID configuration
401 * @param external False if prefix is locally configured, true otherwise
402 * @param psid Prefix-SID sub-TLV to be updated
404 void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg
*pcfg
, bool external
,
405 struct isis_prefix_sid
*psid
)
407 /* Set SID algorithm. */
408 psid
->algorithm
= SR_ALGORITHM_SPF
;
412 switch (pcfg
->last_hop_behavior
) {
413 case SR_LAST_HOP_BEHAVIOR_EXP_NULL
:
414 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
);
415 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
);
417 case SR_LAST_HOP_BEHAVIOR_NO_PHP
:
418 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
);
419 UNSET_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
);
421 case SR_LAST_HOP_BEHAVIOR_PHP
:
422 UNSET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
);
423 UNSET_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
);
427 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_READVERTISED
);
428 if (pcfg
->node_sid
&& !pcfg
->n_flag_clear
)
429 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NODE
);
432 psid
->value
= pcfg
->sid
;
433 if (pcfg
->sid_type
== SR_SID_VALUE_TYPE_ABSOLUTE
) {
434 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_VALUE
);
435 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_LOCAL
);
440 * Delete all backup Adj-SIDs.
442 * @param area IS-IS area
443 * @param level IS-IS level
445 void isis_area_delete_backup_adj_sids(struct isis_area
*area
, int level
)
447 struct sr_adjacency
*sra
;
448 struct listnode
*node
, *nnode
;
450 for (ALL_LIST_ELEMENTS(area
->srdb
.adj_sids
, node
, nnode
, sra
))
451 if (sra
->type
== ISIS_SR_LAN_BACKUP
452 && (sra
->adj
->level
& level
))
456 /* --- Segment Routing Local Block management functions --------------------- */
459 * Initialize Segment Routing Local Block from SRDB configuration and reserve
460 * block of bits to manage label allocation.
462 * @param area IS-IS area
464 static int sr_local_block_init(struct isis_area
*area
)
466 struct isis_sr_db
*srdb
= &area
->srdb
;
467 struct sr_local_block
*srlb
= &srdb
->srlb
;
469 /* Check if SRLB is not already configured */
474 * Request SRLB to the label manager. If the allocation fails, return
475 * an error to disable SR until a new SRLB is successfully allocated.
477 if (isis_zebra_request_label_range(
478 srdb
->config
.srlb_lower_bound
,
479 srdb
->config
.srlb_upper_bound
480 - srdb
->config
.srlb_lower_bound
+ 1)) {
481 srlb
->active
= false;
485 sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area
->area_tag
,
486 srdb
->config
.srlb_lower_bound
, srdb
->config
.srlb_upper_bound
);
488 /* Initialize the SRLB */
489 srlb
->start
= srdb
->config
.srlb_lower_bound
;
490 srlb
->end
= srdb
->config
.srlb_upper_bound
;
492 /* Compute the needed Used Mark number and allocate them */
493 srlb
->max_block
= (srlb
->end
- srlb
->start
+ 1) / SRLB_BLOCK_SIZE
;
494 if (((srlb
->end
- srlb
->start
+ 1) % SRLB_BLOCK_SIZE
) != 0)
496 srlb
->used_mark
= XCALLOC(MTYPE_ISIS_SR_INFO
,
497 srlb
->max_block
* SRLB_BLOCK_SIZE
);
504 * Remove Segment Routing Local Block.
506 * @param area IS-IS area
508 static void sr_local_block_delete(struct isis_area
*area
)
510 struct isis_sr_db
*srdb
= &area
->srdb
;
511 struct sr_local_block
*srlb
= &srdb
->srlb
;
513 /* Check if SRLB is not already delete */
517 sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area
->area_tag
,
518 srlb
->start
, srlb
->end
);
520 /* First release the label block */
521 isis_zebra_release_label_range(srdb
->config
.srlb_lower_bound
,
522 srdb
->config
.srlb_upper_bound
);
524 /* Then reset SRLB structure */
525 if (srlb
->used_mark
!= NULL
)
526 XFREE(MTYPE_ISIS_SR_INFO
, srlb
->used_mark
);
527 srlb
->active
= false;
531 * Request a label from the Segment Routing Local Block.
533 * @param srlb Segment Routing Local Block
535 * @return First available label on success or MPLS_INVALID_LABEL if the
536 * block of labels is full
538 static mpls_label_t
sr_local_block_request_label(struct sr_local_block
*srlb
)
543 uint32_t size
= srlb
->end
- srlb
->start
+ 1;
545 /* Check if we ran out of available labels */
546 if (srlb
->current
>= size
)
547 return MPLS_INVALID_LABEL
;
549 /* Get first available label and mark it used */
550 label
= srlb
->current
+ srlb
->start
;
551 index
= srlb
->current
/ SRLB_BLOCK_SIZE
;
552 pos
= 1ULL << (srlb
->current
% SRLB_BLOCK_SIZE
);
553 srlb
->used_mark
[index
] |= pos
;
555 /* Jump to the next free position */
557 pos
= srlb
->current
% SRLB_BLOCK_SIZE
;
558 while (srlb
->current
< size
) {
561 if (!((1ULL << pos
) & srlb
->used_mark
[index
]))
565 pos
= srlb
->current
% SRLB_BLOCK_SIZE
;
569 if (srlb
->current
== size
)
571 "SR: Warning, SRLB is depleted and next label request will fail");
577 * Release label in the Segment Routing Local Block.
579 * @param srlb Segment Routing Local Block
580 * @param label Label to be release
582 * @return 0 on success or -1 if label falls outside SRLB
584 static int sr_local_block_release_label(struct sr_local_block
*srlb
,
590 /* Check that label falls inside the SRLB */
591 if ((label
< srlb
->start
) || (label
> srlb
->end
)) {
592 flog_warn(EC_ISIS_SID_OVERFLOW
,
593 "%s: Returning label %u is outside SRLB [%u/%u]",
594 __func__
, label
, srlb
->start
, srlb
->end
);
598 index
= (label
- srlb
->start
) / SRLB_BLOCK_SIZE
;
599 pos
= 1ULL << ((label
- srlb
->start
) % SRLB_BLOCK_SIZE
);
600 srlb
->used_mark
[index
] &= ~pos
;
601 /* Reset current to the first available position */
602 for (index
= 0; index
< srlb
->max_block
; index
++) {
603 if (srlb
->used_mark
[index
] != 0xFFFFFFFFFFFFFFFF) {
604 for (pos
= 0; pos
< SRLB_BLOCK_SIZE
; pos
++)
605 if (!((1ULL << pos
) & srlb
->used_mark
[index
])) {
607 index
* SRLB_BLOCK_SIZE
+ pos
;
617 /* --- Segment Routing Adjacency-SID management functions ------------------- */
620 * Add new local Adjacency-SID.
622 * @param adj IS-IS Adjacency
623 * @param family Inet Family (IPv4 or IPv6)
624 * @param backup True to initialize backup Adjacency SID
625 * @param nexthops List of backup nexthops (for backup Adj-SIDs only)
627 void sr_adj_sid_add_single(struct isis_adjacency
*adj
, int family
, bool backup
,
628 struct list
*nexthops
)
630 struct isis_circuit
*circuit
= adj
->circuit
;
631 struct isis_area
*area
= circuit
->area
;
632 struct sr_adjacency
*sra
;
633 struct isis_adj_sid
*adj_sid
;
634 struct isis_lan_adj_sid
*ladj_sid
;
635 union g_addr nexthop
= {};
637 mpls_label_t input_label
;
639 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area
->area_tag
,
640 backup
? "Backup" : "Primary");
642 /* Determine nexthop IP address */
645 if (!circuit
->ip_router
|| !adj
->ipv4_address_count
)
648 nexthop
.ipv4
= adj
->ipv4_addresses
[0];
651 if (!circuit
->ipv6_router
|| !adj
->ll_ipv6_count
)
654 nexthop
.ipv6
= adj
->ll_ipv6_addrs
[0];
657 flog_err(EC_LIB_DEVELOPMENT
,
658 "%s: unexpected address-family: %u", __func__
, family
);
662 /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */
663 flags
= EXT_SUBTLV_LINK_ADJ_SID_VFLG
| EXT_SUBTLV_LINK_ADJ_SID_LFLG
;
664 if (family
== AF_INET6
)
665 SET_FLAG(flags
, EXT_SUBTLV_LINK_ADJ_SID_FFLG
);
667 SET_FLAG(flags
, EXT_SUBTLV_LINK_ADJ_SID_BFLG
);
669 /* Get a label from the SRLB for this Adjacency */
670 input_label
= sr_local_block_request_label(&area
->srdb
.srlb
);
671 if (input_label
== MPLS_INVALID_LABEL
)
674 if (circuit
->ext
== NULL
)
675 circuit
->ext
= isis_alloc_ext_subtlvs();
677 sra
= XCALLOC(MTYPE_ISIS_SR_INFO
, sizeof(*sra
));
678 sra
->type
= backup
? ISIS_SR_LAN_BACKUP
: ISIS_SR_ADJ_NORMAL
;
679 sra
->input_label
= input_label
;
680 sra
->nexthop
.family
= family
;
681 sra
->nexthop
.address
= nexthop
;
683 if (backup
&& nexthops
) {
684 struct isis_vertex_adj
*vadj
;
685 struct listnode
*node
;
687 sra
->backup_nexthops
= list_new();
688 for (ALL_LIST_ELEMENTS_RO(nexthops
, node
, vadj
)) {
689 struct isis_adjacency
*adj
= vadj
->sadj
->adj
;
690 struct mpls_label_stack
*label_stack
;
692 label_stack
= vadj
->label_stack
;
693 adjinfo2nexthop(family
, sra
->backup_nexthops
, adj
, NULL
,
698 switch (circuit
->circ_type
) {
699 /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */
700 case CIRCUIT_T_BROADCAST
:
701 ladj_sid
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*ladj_sid
));
702 ladj_sid
->family
= family
;
703 ladj_sid
->flags
= flags
;
704 ladj_sid
->weight
= 0;
705 memcpy(ladj_sid
->neighbor_id
, adj
->sysid
,
706 sizeof(ladj_sid
->neighbor_id
));
707 ladj_sid
->sid
= input_label
;
708 isis_tlvs_add_lan_adj_sid(circuit
->ext
, ladj_sid
);
709 sra
->u
.ladj_sid
= ladj_sid
;
711 /* Adjacency-SID for Point to Point interface section #2.2.1 */
713 adj_sid
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*adj_sid
));
714 adj_sid
->family
= family
;
715 adj_sid
->flags
= flags
;
717 adj_sid
->sid
= input_label
;
718 isis_tlvs_add_adj_sid(circuit
->ext
, adj_sid
);
719 sra
->u
.adj_sid
= adj_sid
;
722 flog_err(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
723 __func__
, circuit
->circ_type
);
727 /* Add Adjacency-SID in SRDB */
729 listnode_add(area
->srdb
.adj_sids
, sra
);
730 listnode_add(adj
->adj_sids
, sra
);
732 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD
, sra
);
736 * Add Primary and Backup local Adjacency SID.
738 * @param adj IS-IS Adjacency
739 * @param family Inet Family (IPv4 or IPv6)
741 static void sr_adj_sid_add(struct isis_adjacency
*adj
, int family
)
743 sr_adj_sid_add_single(adj
, family
, false, NULL
);
746 static void sr_adj_sid_update(struct sr_adjacency
*sra
,
747 struct sr_local_block
*srlb
)
749 struct isis_circuit
*circuit
= sra
->adj
->circuit
;
751 /* First remove the old MPLS Label */
752 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE
, sra
);
754 /* Got new label in the new SRLB */
755 sra
->input_label
= sr_local_block_request_label(srlb
);
756 if (sra
->input_label
== MPLS_INVALID_LABEL
)
759 switch (circuit
->circ_type
) {
760 case CIRCUIT_T_BROADCAST
:
761 sra
->u
.ladj_sid
->sid
= sra
->input_label
;
764 sra
->u
.adj_sid
->sid
= sra
->input_label
;
767 flog_warn(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
768 __func__
, circuit
->circ_type
);
772 /* Finally configure the new MPLS Label */
773 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD
, sra
);
777 * Delete local Adj-SID.
779 * @param sra Segment Routing Adjacency
781 static void sr_adj_sid_del(struct sr_adjacency
*sra
)
783 struct isis_circuit
*circuit
= sra
->adj
->circuit
;
784 struct isis_area
*area
= circuit
->area
;
786 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area
->area_tag
);
788 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE
, sra
);
790 /* Release dynamic label and remove subTLVs */
791 switch (circuit
->circ_type
) {
792 case CIRCUIT_T_BROADCAST
:
793 sr_local_block_release_label(&area
->srdb
.srlb
,
794 sra
->u
.ladj_sid
->sid
);
795 isis_tlvs_del_lan_adj_sid(circuit
->ext
, sra
->u
.ladj_sid
);
798 sr_local_block_release_label(&area
->srdb
.srlb
,
799 sra
->u
.adj_sid
->sid
);
800 isis_tlvs_del_adj_sid(circuit
->ext
, sra
->u
.adj_sid
);
803 flog_err(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
804 __func__
, circuit
->circ_type
);
808 if (sra
->type
== ISIS_SR_LAN_BACKUP
&& sra
->backup_nexthops
) {
809 sra
->backup_nexthops
->del
=
810 (void (*)(void *))isis_nexthop_delete
;
811 list_delete(&sra
->backup_nexthops
);
814 /* Remove Adjacency-SID from the SRDB */
815 listnode_delete(area
->srdb
.adj_sids
, sra
);
816 listnode_delete(sra
->adj
->adj_sids
, sra
);
817 XFREE(MTYPE_ISIS_SR_INFO
, sra
);
821 * Lookup Segment Routing Adj-SID by family and type.
823 * @param adj IS-IS Adjacency
824 * @param family Inet Family (IPv4 or IPv6)
825 * @param type Adjacency SID type
827 struct sr_adjacency
*isis_sr_adj_sid_find(struct isis_adjacency
*adj
,
828 int family
, enum sr_adj_type type
)
830 struct sr_adjacency
*sra
;
831 struct listnode
*node
;
833 for (ALL_LIST_ELEMENTS_RO(adj
->adj_sids
, node
, sra
))
834 if (sra
->nexthop
.family
== family
&& sra
->type
== type
)
841 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
843 * @param adj IS-IS Adjacency
847 static int sr_adj_state_change(struct isis_adjacency
*adj
)
849 struct sr_adjacency
*sra
;
850 struct listnode
*node
, *nnode
;
852 if (!adj
->circuit
->area
->srdb
.enabled
)
855 if (adj
->adj_state
== ISIS_ADJ_UP
)
858 for (ALL_LIST_ELEMENTS(adj
->adj_sids
, node
, nnode
, sra
))
865 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
866 * IPv6 address to corresponding Adjacency-SID accordingly.
868 * @param adj IS-IS Adjacency
869 * @param family Inet Family (IPv4 or IPv6)
870 * @param global Indicate if it concerns the Local or Global IPv6 addresses
874 static int sr_adj_ip_enabled(struct isis_adjacency
*adj
, int family
,
877 if (!adj
->circuit
->area
->srdb
.enabled
|| global
)
880 sr_adj_sid_add(adj
, family
);
886 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
887 * delete the corresponding Adjacency-SID(s) accordingly.
889 * @param adj IS-IS Adjacency
890 * @param family Inet Family (IPv4 or IPv6)
891 * @param global Indicate if it concerns the Local or Global IPv6 addresses
895 static int sr_adj_ip_disabled(struct isis_adjacency
*adj
, int family
,
898 struct sr_adjacency
*sra
;
899 struct listnode
*node
, *nnode
;
901 if (!adj
->circuit
->area
->srdb
.enabled
|| global
)
904 for (ALL_LIST_ELEMENTS(adj
->adj_sids
, node
, nnode
, sra
))
905 if (sra
->nexthop
.family
== family
)
912 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
914 * @param ifp Loopback Interface
918 static int sr_if_new_hook(struct interface
*ifp
)
920 struct isis_circuit
*circuit
;
921 struct isis_area
*area
;
922 struct connected
*connected
;
923 struct listnode
*node
;
925 /* Get corresponding circuit */
926 circuit
= circuit_scan_by_ifp(ifp
);
930 area
= circuit
->area
;
935 * Update the Node-SID flag of the configured Prefix-SID mappings if
936 * necessary. This needs to be done here since isisd reads the startup
937 * configuration before receiving interface information from zebra.
939 FOR_ALL_INTERFACES_ADDRESSES (ifp
, connected
, node
) {
940 struct sr_prefix_cfg
*pcfg
;
942 pcfg
= isis_sr_cfg_prefix_find(area
, connected
->address
);
946 if (sr_prefix_is_node_sid(ifp
, &pcfg
->prefix
)) {
947 pcfg
->node_sid
= true;
948 lsp_regenerate_schedule(area
, area
->is_type
, 0);
956 * Show LFIB operation in human readable format.
958 * @param buf Buffer to store string output. Must be pre-allocate
959 * @param size Size of the buffer
960 * @param label_in Input Label
961 * @param label_out Output Label
963 * @return String containing LFIB operation in human readable format
965 char *sr_op2str(char *buf
, size_t size
, mpls_label_t label_in
,
966 mpls_label_t label_out
)
971 if (label_in
== MPLS_INVALID_LABEL
) {
972 snprintf(buf
, size
, "no-op.");
977 case MPLS_LABEL_IMPLICIT_NULL
:
978 snprintf(buf
, size
, "Pop(%u)", label_in
);
980 case MPLS_LABEL_IPV4_EXPLICIT_NULL
:
981 case MPLS_LABEL_IPV6_EXPLICIT_NULL
:
982 snprintf(buf
, size
, "Swap(%u, null)", label_in
);
984 case MPLS_INVALID_LABEL
:
985 snprintf(buf
, size
, "no-op.");
988 snprintf(buf
, size
, "Swap(%u, %u)", label_in
, label_out
);
995 * Show Segment Routing Node.
997 * @param vty VTY output
998 * @param area IS-IS area
999 * @param level IS-IS level
1001 static void show_node(struct vty
*vty
, struct isis_area
*area
, int level
)
1003 struct isis_lsp
*lsp
;
1006 vty_out(vty
, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level
));
1008 /* Prepare table. */
1009 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1010 ttable_add_row(tt
, "System ID|SRGB|SRLB|Algorithm|MSD");
1011 tt
->style
.cell
.rpad
= 2;
1012 tt
->style
.corner
= '+';
1014 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1016 frr_each (lspdb
, &area
->lspdb
[level
- 1], lsp
) {
1017 struct isis_router_cap
*cap
;
1021 cap
= lsp
->tlvs
->router_cap
;
1026 tt
, "%s|%u - %u|%u - %u|%s|%u",
1027 sysid_print(lsp
->hdr
.lsp_id
), cap
->srgb
.lower_bound
,
1028 cap
->srgb
.lower_bound
+ cap
->srgb
.range_size
- 1,
1029 cap
->srlb
.lower_bound
,
1030 cap
->srlb
.lower_bound
+ cap
->srlb
.range_size
- 1,
1031 cap
->algo
[0] == SR_ALGORITHM_SPF
? "SPF" : "S-SPF",
1035 /* Dump the generated table. */
1036 if (tt
->nrows
> 1) {
1039 table
= ttable_dump(tt
, "\n");
1040 vty_out(vty
, "%s\n", table
);
1041 XFREE(MTYPE_TMP
, table
);
1046 DEFUN(show_sr_node
, show_sr_node_cmd
,
1047 "show " PROTO_NAME
" segment-routing node",
1051 "Segment-Routing node\n")
1053 struct listnode
*node
, *inode
;
1054 struct isis_area
*area
;
1057 for (ALL_LIST_ELEMENTS_RO(im
->isis
, inode
, isis
)) {
1058 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
1059 vty_out(vty
, "Area %s:\n",
1060 area
->area_tag
? area
->area_tag
: "null");
1061 if (!area
->srdb
.enabled
) {
1062 vty_out(vty
, " Segment Routing is disabled\n");
1065 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
;
1067 show_node(vty
, area
, level
);
1074 /* --- IS-IS Segment Routing Management function ---------------------------- */
1077 * Thread function to re-attempt connection to the Label Manager and thus be
1078 * able to start Segment Routing.
1080 * @param start Thread structure that contains area as argument
1082 * @return 1 on success
1084 static void sr_start_label_manager(struct event
*start
)
1086 struct isis_area
*area
;
1088 area
= EVENT_ARG(start
);
1090 /* re-attempt to start SR & Label Manager connection */
1091 isis_sr_start(area
);
1095 * Enable SR on the given IS-IS area.
1097 * @param area IS-IS area
1099 * @return 0 on success, -1 otherwise
1101 int isis_sr_start(struct isis_area
*area
)
1103 struct isis_sr_db
*srdb
= &area
->srdb
;
1104 struct isis_adjacency
*adj
;
1105 struct listnode
*node
;
1107 /* First start Label Manager if not ready */
1108 if (!isis_zebra_label_manager_ready())
1109 if (isis_zebra_label_manager_connect() < 0) {
1110 /* Re-attempt to connect to Label Manager in 1 sec. */
1111 event_add_timer(master
, sr_start_label_manager
, area
, 1,
1116 /* Label Manager is ready, initialize the SRLB */
1117 if (sr_local_block_init(area
) < 0)
1121 * Request SGRB to the label manager if not already active. If the
1122 * allocation fails, return an error to disable SR until a new SRGB
1123 * is successfully allocated.
1125 if (!srdb
->srgb_active
) {
1126 if (isis_zebra_request_label_range(
1127 srdb
->config
.srgb_lower_bound
,
1128 srdb
->config
.srgb_upper_bound
1129 - srdb
->config
.srgb_lower_bound
+ 1)
1131 srdb
->srgb_active
= false;
1134 srdb
->srgb_active
= true;
1137 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
1140 /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */
1141 for (ALL_LIST_ELEMENTS_RO(area
->adjacency_list
, node
, adj
)) {
1142 if (adj
->ipv4_address_count
> 0)
1143 sr_adj_sid_add(adj
, AF_INET
);
1144 if (adj
->ll_ipv6_count
> 0)
1145 sr_adj_sid_add(adj
, AF_INET6
);
1148 area
->srdb
.enabled
= true;
1150 /* Regenerate LSPs to advertise Segment Routing capabilities. */
1151 lsp_regenerate_schedule(area
, area
->is_type
, 0);
1157 * Disable SR on the given IS-IS area.
1159 * @param area IS-IS area
1161 void isis_sr_stop(struct isis_area
*area
)
1163 struct isis_sr_db
*srdb
= &area
->srdb
;
1164 struct sr_adjacency
*sra
;
1165 struct listnode
*node
, *nnode
;
1167 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
1170 /* Disable any re-attempt to connect to Label Manager */
1171 EVENT_OFF(srdb
->t_start_lm
);
1173 /* Uninstall all local Adjacency-SIDs. */
1174 for (ALL_LIST_ELEMENTS(area
->srdb
.adj_sids
, node
, nnode
, sra
))
1175 sr_adj_sid_del(sra
);
1177 /* Release SRGB if active. */
1178 if (srdb
->srgb_active
) {
1179 isis_zebra_release_label_range(srdb
->config
.srgb_lower_bound
,
1180 srdb
->config
.srgb_upper_bound
);
1181 srdb
->srgb_active
= false;
1185 sr_local_block_delete(area
);
1187 area
->srdb
.enabled
= false;
1189 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
1190 lsp_regenerate_schedule(area
, area
->is_type
, 0);
1194 * IS-IS Segment Routing initialization for given area.
1196 * @param area IS-IS area
1198 void isis_sr_area_init(struct isis_area
*area
)
1200 struct isis_sr_db
*srdb
= &area
->srdb
;
1202 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
1205 /* Initialize Segment Routing Data Base */
1206 memset(srdb
, 0, sizeof(*srdb
));
1207 srdb
->adj_sids
= list_new();
1209 /* Pull defaults from the YANG module. */
1211 srdb
->config
.enabled
= yang_get_default_bool("%s/enabled", ISIS_SR
);
1212 srdb
->config
.srgb_lower_bound
= yang_get_default_uint32(
1213 "%s/label-blocks/srgb/lower-bound", ISIS_SR
);
1214 srdb
->config
.srgb_upper_bound
= yang_get_default_uint32(
1215 "%s/label-blocks/srgb/upper-bound", ISIS_SR
);
1216 srdb
->config
.srlb_lower_bound
= yang_get_default_uint32(
1217 "%s/label-blocks/srlb/lower-bound", ISIS_SR
);
1218 srdb
->config
.srlb_upper_bound
= yang_get_default_uint32(
1219 "%s/label-blocks/srlb/upper-bound", ISIS_SR
);
1221 srdb
->config
.enabled
= false;
1222 srdb
->config
.srgb_lower_bound
= SRGB_LOWER_BOUND
;
1223 srdb
->config
.srgb_upper_bound
= SRGB_UPPER_BOUND
;
1224 srdb
->config
.srlb_lower_bound
= SRLB_LOWER_BOUND
;
1225 srdb
->config
.srlb_upper_bound
= SRLB_UPPER_BOUND
;
1227 srdb
->config
.msd
= 0;
1228 srdb_prefix_cfg_init(&srdb
->config
.prefix_sids
);
1232 * Terminate IS-IS Segment Routing for the given area.
1234 * @param area IS-IS area
1236 void isis_sr_area_term(struct isis_area
*area
)
1238 struct isis_sr_db
*srdb
= &area
->srdb
;
1240 /* Stop Segment Routing */
1241 if (area
->srdb
.enabled
)
1244 /* Free Adjacency SID list */
1245 list_delete(&srdb
->adj_sids
);
1247 /* Clear Prefix-SID configuration. */
1248 while (srdb_prefix_cfg_count(&srdb
->config
.prefix_sids
) > 0) {
1249 struct sr_prefix_cfg
*pcfg
;
1251 pcfg
= srdb_prefix_cfg_first(&srdb
->config
.prefix_sids
);
1252 isis_sr_cfg_prefix_del(pcfg
);
1257 * IS-IS Segment Routing global initialization.
1259 void isis_sr_init(void)
1261 install_element(VIEW_NODE
, &show_sr_node_cmd
);
1263 /* Register hooks. */
1264 hook_register(isis_adj_state_change_hook
, sr_adj_state_change
);
1265 hook_register(isis_adj_ip_enabled_hook
, sr_adj_ip_enabled
);
1266 hook_register(isis_adj_ip_disabled_hook
, sr_adj_ip_disabled
);
1267 hook_register(isis_if_new_hook
, sr_if_new_hook
);
1271 * IS-IS Segment Routing global terminate.
1273 void isis_sr_term(void)
1275 /* Unregister hooks. */
1276 hook_unregister(isis_adj_state_change_hook
, sr_adj_state_change
);
1277 hook_unregister(isis_adj_ip_enabled_hook
, sr_adj_ip_enabled
);
1278 hook_unregister(isis_adj_ip_disabled_hook
, sr_adj_ip_disabled
);
1279 hook_unregister(isis_if_new_hook
, sr_if_new_hook
);