2 * This is an implementation of Segment Routing for IS-IS as per RFC 8667
4 * Copyright (C) 2019 Orange http://www.orange.com
6 * Author: Olivier Dugeon <olivier.dugeon@orange.com>
7 * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
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)
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
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
30 #include "termtable.h"
34 #include "srcdest_table.h"
37 #include "lib/lib_errors.h"
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"
51 /* Local variables and functions */
52 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SR_INFO
, "ISIS segment routing information")
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
);
60 /* --- RB-Tree Management functions ----------------------------------------- */
63 * Configured SR Prefix comparison for RB-Tree.
65 * @param a First SR prefix
66 * @param b Second SR prefix
68 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
70 static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg
*a
,
71 const struct sr_prefix_cfg
*b
)
73 return prefix_cmp(&a
->prefix
, &b
->prefix
);
75 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg
, struct sr_prefix_cfg
, entry
,
76 sr_prefix_sid_cfg_compare
)
79 * Find SRGB associated to a System ID.
81 * @param area IS-IS LSP database
82 * @param sysid System ID to lookup
84 * @return Pointer to SRGB if found, NULL otherwise
86 struct isis_sr_block
*isis_sr_find_srgb(struct lspdb_head
*lspdb
,
91 lsp
= isis_root_system_lsp(lspdb
, sysid
);
95 if (!lsp
->tlvs
->router_cap
96 || lsp
->tlvs
->router_cap
->srgb
.range_size
== 0)
99 return &lsp
->tlvs
->router_cap
->srgb
;
103 * Compute input label for the given Prefix-SID.
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
109 * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow
111 mpls_label_t
sr_prefix_in_label(struct isis_area
*area
,
112 struct isis_prefix_sid
*psid
, bool local
)
115 * No need to assign a label for local Prefix-SIDs unless the no-PHP
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
;
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
))
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
;
137 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
138 return (area
->srdb
.config
.srgb_lower_bound
+ psid
->value
);
142 * Compute output label for the given Prefix-SID.
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
150 * @return MPLS label or MPLS_INVALID_LABEL in case of error
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
)
156 struct isis_sr_block
*nh_srgb
;
159 if (!CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_NO_PHP
))
160 return MPLS_LABEL_IMPLICIT_NULL
;
162 if (CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_EXPLICIT_NULL
)) {
163 if (family
== AF_INET
)
164 return MPLS_LABEL_IPV4_EXPLICIT_NULL
;
166 return MPLS_LABEL_IPV6_EXPLICIT_NULL
;
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
)) {
175 * V/L SIDs have local significance, so only adjacent routers
176 * can use them (RFC8667 section #2.1.1.1)
179 return MPLS_INVALID_LABEL
;
183 /* Check that SID index falls inside the SRGB */
184 nh_srgb
= isis_sr_find_srgb(lspdb
, nh_sysid
);
186 return MPLS_INVALID_LABEL
;
189 * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
192 if ((family
== AF_INET
&& !IS_SR_IPV4(nh_srgb
))
193 || (family
== AF_INET6
&& !IS_SR_IPV6(nh_srgb
)))
194 return MPLS_INVALID_LABEL
;
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
;
203 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
204 return (nh_srgb
->lower_bound
+ psid
->value
);
207 /* --- Functions used for Yang model and CLI to configure Segment Routing --- */
210 * Check if prefix correspond to a Node SID.
212 * @param ifp Interface
213 * @param prefix Prefix to be checked
215 * @return True if the interface/address pair corresponds to a Node-SID
217 static bool sr_prefix_is_node_sid(const struct interface
*ifp
,
218 const struct prefix
*prefix
)
220 return (if_is_loopback(ifp
) && is_host_route(prefix
));
224 * Update local SRGB configuration. SRGB is reserved though Label Manager.
225 * This function trigger the update of local Prefix-SID installation.
227 * @param area IS-IS area
228 * @param lower_bound Lower bound of SRGB
229 * @param upper_bound Upper bound of SRGB
231 * @return 0 on success, -1 otherwise
233 int isis_sr_cfg_srgb_update(struct isis_area
*area
, uint32_t lower_bound
,
234 uint32_t upper_bound
)
236 struct isis_sr_db
*srdb
= &area
->srdb
;
238 sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
239 area
->area_tag
, lower_bound
, upper_bound
);
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
;
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;
256 srdb
->config
.srgb_lower_bound
= lower_bound
;
257 srdb
->config
.srgb_upper_bound
= upper_bound
;
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;
268 srdb
->srgb_active
= true;
271 sr_debug(" |- Got new SRGB [%u/%u]",
272 srdb
->config
.srgb_lower_bound
,
273 srdb
->config
.srgb_upper_bound
);
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. */
285 * Update Segment Routing Local Block range which is reserved though the
286 * Label Manager. This function trigger the update of local Adjacency-SID
289 * @param area IS-IS area
290 * @param lower_bound Lower bound of SRLB
291 * @param upper_bound Upper bound of SRLB
293 * @return 0 on success, -1 otherwise
295 int isis_sr_cfg_srlb_update(struct isis_area
*area
, uint32_t lower_bound
,
296 uint32_t upper_bound
)
298 struct isis_sr_db
*srdb
= &area
->srdb
;
299 struct listnode
*node
;
300 struct sr_adjacency
*sra
;
302 sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
303 area
->area_tag
, lower_bound
, upper_bound
);
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
;
313 /* LM is ready, start by deleting the old SRLB */
314 sr_local_block_delete(area
);
316 srdb
->config
.srlb_lower_bound
= lower_bound
;
317 srdb
->config
.srlb_upper_bound
= upper_bound
;
320 /* Initialize new SRLB */
321 if (sr_local_block_init(area
) != 0)
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
);
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. */
339 * Add new Prefix-SID configuration to the SRDB.
341 * @param area IS-IS area
342 * @param prefix Prefix to be added
344 * @return Newly added Prefix-SID configuration structure
346 struct sr_prefix_cfg
*isis_sr_cfg_prefix_add(struct isis_area
*area
,
347 const struct prefix
*prefix
)
349 struct sr_prefix_cfg
*pcfg
;
350 struct interface
*ifp
;
352 sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area
->area_tag
, prefix
);
354 pcfg
= XCALLOC(MTYPE_ISIS_SR_INFO
, sizeof(*pcfg
));
355 pcfg
->prefix
= *prefix
;
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
);
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;
369 /* Save prefix-sid configuration. */
370 srdb_prefix_cfg_add(&area
->srdb
.config
.prefix_sids
, pcfg
);
376 * Removal of locally configured Prefix-SID.
378 * @param pcfg Configured Prefix-SID
380 void isis_sr_cfg_prefix_del(struct sr_prefix_cfg
*pcfg
)
382 struct isis_area
*area
= pcfg
->area
;
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",
389 srdb_prefix_cfg_del(&area
->srdb
.config
.prefix_sids
, pcfg
);
390 XFREE(MTYPE_ISIS_SR_INFO
, pcfg
);
394 * Lookup for Prefix-SID in the local configuration.
396 * @param area IS-IS area
397 * @param prefix Prefix to lookup
399 * @return Configured Prefix-SID structure if found, NULL otherwise
401 struct sr_prefix_cfg
*isis_sr_cfg_prefix_find(struct isis_area
*area
,
402 union prefixconstptr prefix
)
404 struct sr_prefix_cfg pcfg
= {};
406 prefix_copy(&pcfg
.prefix
, prefix
.p
);
407 return srdb_prefix_cfg_find(&area
->srdb
.config
.prefix_sids
, &pcfg
);
411 * Fill in Prefix-SID Sub-TLV according to the corresponding configuration.
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
417 void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg
*pcfg
, bool external
,
418 struct isis_prefix_sid
*psid
)
420 /* Set SID algorithm. */
421 psid
->algorithm
= SR_ALGORITHM_SPF
;
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
);
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
);
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
);
440 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_READVERTISED
);
442 SET_FLAG(psid
->flags
, ISIS_PREFIX_SID_NODE
);
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
);
453 * Delete all backup Adj-SIDs.
455 * @param area IS-IS area
456 * @param level IS-IS level
458 void isis_area_delete_backup_adj_sids(struct isis_area
*area
, int level
)
460 struct sr_adjacency
*sra
;
461 struct listnode
*node
, *nnode
;
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
))
469 /* --- Segment Routing Local Block management functions --------------------- */
472 * Initialize Segment Routing Local Block from SRDB configuration and reserve
473 * block of bits to manage label allocation.
475 * @param area IS-IS area
477 static int sr_local_block_init(struct isis_area
*area
)
479 struct isis_sr_db
*srdb
= &area
->srdb
;
480 struct sr_local_block
*srlb
= &srdb
->srlb
;
482 /* Check if SRLB is not already configured */
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.
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;
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
);
501 /* Initialize the SRLB */
502 srlb
->start
= srdb
->config
.srlb_lower_bound
;
503 srlb
->end
= srdb
->config
.srlb_upper_bound
;
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)
509 srlb
->used_mark
= XCALLOC(MTYPE_ISIS_SR_INFO
,
510 srlb
->max_block
* SRLB_BLOCK_SIZE
);
517 * Remove Segment Routing Local Block.
519 * @param area IS-IS area
521 static void sr_local_block_delete(struct isis_area
*area
)
523 struct isis_sr_db
*srdb
= &area
->srdb
;
524 struct sr_local_block
*srlb
= &srdb
->srlb
;
526 /* Check if SRLB is not already delete */
530 sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area
->area_tag
,
531 srlb
->start
, srlb
->end
);
533 /* First release the label block */
534 isis_zebra_release_label_range(srdb
->config
.srlb_lower_bound
,
535 srdb
->config
.srlb_upper_bound
);
537 /* Then reset SRLB structure */
538 if (srlb
->used_mark
!= NULL
)
539 XFREE(MTYPE_ISIS_SR_INFO
, srlb
->used_mark
);
540 srlb
->active
= false;
544 * Request a label from the Segment Routing Local Block.
546 * @param srlb Segment Routing Local Block
548 * @return First available label on success or MPLS_INVALID_LABEL if the
549 * block of labels is full
551 static mpls_label_t
sr_local_block_request_label(struct sr_local_block
*srlb
)
558 /* Check if we ran out of available labels */
559 if (srlb
->current
>= srlb
->end
)
560 return MPLS_INVALID_LABEL
;
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
;
568 /* Jump to the next free position */
570 pos
= srlb
->current
% SRLB_BLOCK_SIZE
;
571 while (srlb
->current
< srlb
->end
) {
574 if (!((1ULL << pos
) & srlb
->used_mark
[index
]))
578 pos
= srlb
->current
% SRLB_BLOCK_SIZE
;
586 * Release label in the Segment Routing Local Block.
588 * @param srlb Segment Routing Local Block
589 * @param label Label to be release
591 * @return 0 on success or -1 if label falls outside SRLB
593 static int sr_local_block_release_label(struct sr_local_block
*srlb
,
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
);
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
])) {
616 index
* SRLB_BLOCK_SIZE
+ pos
;
626 /* --- Segment Routing Adjacency-SID management functions ------------------- */
629 * Add new local Adjacency-SID.
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)
636 void sr_adj_sid_add_single(struct isis_adjacency
*adj
, int family
, bool backup
,
637 struct list
*nexthops
)
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
= {};
646 mpls_label_t input_label
;
648 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area
->area_tag
,
649 backup
? "Backup" : "Primary");
651 /* Determine nexthop IP address */
654 if (!circuit
->ip_router
|| !adj
->ipv4_address_count
)
657 nexthop
.ipv4
= adj
->ipv4_addresses
[0];
660 if (!circuit
->ipv6_router
|| !adj
->ipv6_address_count
)
663 nexthop
.ipv6
= adj
->ipv6_addresses
[0];
666 flog_err(EC_LIB_DEVELOPMENT
,
667 "%s: unexpected address-family: %u", __func__
, family
);
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
);
676 SET_FLAG(flags
, EXT_SUBTLV_LINK_ADJ_SID_BFLG
);
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
)
683 if (circuit
->ext
== NULL
)
684 circuit
->ext
= isis_alloc_ext_subtlvs();
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
;
692 if (backup
&& nexthops
) {
693 struct isis_vertex_adj
*vadj
;
694 struct listnode
*node
;
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
;
701 label_stack
= vadj
->label_stack
;
702 adjinfo2nexthop(family
, sra
->backup_nexthops
, adj
, NULL
,
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
;
720 /* Adjacency-SID for Point to Point interface section #2.2.1 */
722 adj_sid
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*adj_sid
));
723 adj_sid
->family
= family
;
724 adj_sid
->flags
= flags
;
726 adj_sid
->sid
= input_label
;
727 isis_tlvs_add_adj_sid(circuit
->ext
, adj_sid
);
728 sra
->u
.adj_sid
= adj_sid
;
731 flog_err(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
732 __func__
, circuit
->circ_type
);
736 /* Add Adjacency-SID in SRDB */
738 listnode_add(area
->srdb
.adj_sids
, sra
);
739 listnode_add(adj
->adj_sids
, sra
);
741 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD
, sra
);
745 * Add Primary and Backup local Adjacency SID.
747 * @param adj IS-IS Adjacency
748 * @param family Inet Family (IPv4 or IPv6)
750 static void sr_adj_sid_add(struct isis_adjacency
*adj
, int family
)
752 sr_adj_sid_add_single(adj
, family
, false, NULL
);
755 static void sr_adj_sid_update(struct sr_adjacency
*sra
,
756 struct sr_local_block
*srlb
)
758 struct isis_circuit
*circuit
= sra
->adj
->circuit
;
760 /* First remove the old MPLS Label */
761 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE
, sra
);
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
)
768 switch (circuit
->circ_type
) {
769 case CIRCUIT_T_BROADCAST
:
770 sra
->u
.ladj_sid
->sid
= sra
->input_label
;
773 sra
->u
.adj_sid
->sid
= sra
->input_label
;
776 flog_warn(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
777 __func__
, circuit
->circ_type
);
781 /* Finally configure the new MPLS Label */
782 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD
, sra
);
786 * Delete local Adj-SID.
788 * @param sra Segment Routing Adjacency
790 static void sr_adj_sid_del(struct sr_adjacency
*sra
)
792 struct isis_circuit
*circuit
= sra
->adj
->circuit
;
793 struct isis_area
*area
= circuit
->area
;
795 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area
->area_tag
);
797 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE
, sra
);
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
);
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
);
812 flog_err(EC_LIB_DEVELOPMENT
, "%s: unexpected circuit type: %u",
813 __func__
, circuit
->circ_type
);
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
);
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
);
830 * Lookup Segment Routing Adj-SID by family and type.
832 * @param adj IS-IS Adjacency
833 * @param family Inet Family (IPv4 or IPv6)
834 * @param type Adjacency SID type
836 struct sr_adjacency
*isis_sr_adj_sid_find(struct isis_adjacency
*adj
,
837 int family
, enum sr_adj_type type
)
839 struct sr_adjacency
*sra
;
840 struct listnode
*node
;
842 for (ALL_LIST_ELEMENTS_RO(adj
->adj_sids
, node
, sra
))
843 if (sra
->nexthop
.family
== family
&& sra
->type
== type
)
850 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
852 * @param adj IS-IS Adjacency
856 static int sr_adj_state_change(struct isis_adjacency
*adj
)
858 struct sr_adjacency
*sra
;
859 struct listnode
*node
, *nnode
;
861 if (!adj
->circuit
->area
->srdb
.enabled
)
864 if (adj
->adj_state
== ISIS_ADJ_UP
)
867 for (ALL_LIST_ELEMENTS(adj
->adj_sids
, node
, nnode
, sra
))
874 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
875 * IPv6 address to corresponding Adjacency-SID accordingly.
877 * @param adj IS-IS Adjacency
878 * @param family Inet Family (IPv4 or IPv6)
882 static int sr_adj_ip_enabled(struct isis_adjacency
*adj
, int family
)
884 if (!adj
->circuit
->area
->srdb
.enabled
)
887 sr_adj_sid_add(adj
, family
);
893 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
894 * delete the corresponding Adjacency-SID(s) accordingly.
896 * @param adj IS-IS Adjacency
897 * @param family Inet Family (IPv4 or IPv6)
901 static int sr_adj_ip_disabled(struct isis_adjacency
*adj
, int family
)
903 struct sr_adjacency
*sra
;
904 struct listnode
*node
, *nnode
;
906 if (!adj
->circuit
->area
->srdb
.enabled
)
909 for (ALL_LIST_ELEMENTS(adj
->adj_sids
, node
, nnode
, sra
))
910 if (sra
->nexthop
.family
== family
)
917 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
919 * @param ifp Loopback Interface
923 static int sr_if_new_hook(struct interface
*ifp
)
925 struct isis_circuit
*circuit
;
926 struct isis_area
*area
;
927 struct connected
*connected
;
928 struct listnode
*node
;
930 /* Get corresponding circuit */
931 circuit
= circuit_scan_by_ifp(ifp
);
935 area
= circuit
->area
;
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.
944 FOR_ALL_INTERFACES_ADDRESSES (ifp
, connected
, node
) {
945 struct sr_prefix_cfg
*pcfg
;
947 pcfg
= isis_sr_cfg_prefix_find(area
, connected
->address
);
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);
962 * Show LFIB operation in human readable format.
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
969 * @return String containing LFIB operation in human readable format
971 char *sr_op2str(char *buf
, size_t size
, mpls_label_t label_in
,
972 mpls_label_t label_out
)
977 if (label_in
== MPLS_INVALID_LABEL
) {
978 snprintf(buf
, size
, "no-op.");
983 case MPLS_LABEL_IMPLICIT_NULL
:
984 snprintf(buf
, size
, "Pop(%u)", label_in
);
986 case MPLS_LABEL_IPV4_EXPLICIT_NULL
:
987 case MPLS_LABEL_IPV6_EXPLICIT_NULL
:
988 snprintf(buf
, size
, "Swap(%u, null)", label_in
);
990 case MPLS_INVALID_LABEL
:
991 snprintf(buf
, size
, "no-op.");
994 snprintf(buf
, size
, "Swap(%u, %u)", label_in
, label_out
);
1001 * Show Segment Routing Node.
1003 * @param vty VTY output
1004 * @param area IS-IS area
1005 * @param level IS-IS level
1007 static void show_node(struct vty
*vty
, struct isis_area
*area
, int level
)
1009 struct isis_lsp
*lsp
;
1012 vty_out(vty
, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level
));
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
= '+';
1020 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1022 frr_each (lspdb
, &area
->lspdb
[level
- 1], lsp
) {
1023 struct isis_router_cap
*cap
;
1027 cap
= lsp
->tlvs
->router_cap
;
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",
1041 /* Dump the generated table. */
1042 if (tt
->nrows
> 1) {
1045 table
= ttable_dump(tt
, "\n");
1046 vty_out(vty
, "%s\n", table
);
1047 XFREE(MTYPE_TMP
, table
);
1052 DEFUN(show_sr_node
, show_sr_node_cmd
,
1053 "show isis segment-routing node",
1056 "Segment-Routing node\n")
1058 struct listnode
*node
, *inode
;
1059 struct isis_area
*area
;
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");
1067 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
;
1069 show_node(vty
, area
, level
);
1076 /* --- IS-IS Segment Routing Management function ---------------------------- */
1079 * Thread function to re-attempt connection to the Label Manager and thus be
1080 * able to start Segment Routing.
1082 * @param start Thread structure that contains area as argument
1084 * @return 1 on success
1086 static int sr_start_label_manager(struct thread
*start
)
1088 struct isis_area
*area
;
1090 area
= THREAD_ARG(start
);
1092 /* re-attempt to start SR & Label Manager connection */
1093 isis_sr_start(area
);
1099 * Enable SR on the given IS-IS area.
1101 * @param area IS-IS area
1103 * @return 0 on success, -1 otherwise
1105 int isis_sr_start(struct isis_area
*area
)
1107 struct isis_sr_db
*srdb
= &area
->srdb
;
1108 struct isis_adjacency
*adj
;
1109 struct listnode
*node
;
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
);
1120 /* Label Manager is ready, initialize the SRLB */
1121 if (sr_local_block_init(area
) < 0)
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.
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)
1135 srdb
->srgb_active
= false;
1138 srdb
->srgb_active
= true;
1141 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
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
);
1152 area
->srdb
.enabled
= true;
1154 /* Regenerate LSPs to advertise Segment Routing capabilities. */
1155 lsp_regenerate_schedule(area
, area
->is_type
, 0);
1161 * Disable SR on the given IS-IS area.
1163 * @param area IS-IS area
1165 void isis_sr_stop(struct isis_area
*area
)
1167 struct isis_sr_db
*srdb
= &area
->srdb
;
1168 struct sr_adjacency
*sra
;
1169 struct listnode
*node
, *nnode
;
1171 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
1174 /* Disable any re-attempt to connect to Label Manager */
1175 thread_cancel(&srdb
->t_start_lm
);
1177 /* Uninstall all local Adjacency-SIDs. */
1178 for (ALL_LIST_ELEMENTS(area
->srdb
.adj_sids
, node
, nnode
, sra
))
1179 sr_adj_sid_del(sra
);
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;
1189 sr_local_block_delete(area
);
1191 area
->srdb
.enabled
= false;
1193 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
1194 lsp_regenerate_schedule(area
, area
->is_type
, 0);
1198 * IS-IS Segment Routing initialization for given area.
1200 * @param area IS-IS area
1202 void isis_sr_area_init(struct isis_area
*area
)
1204 struct isis_sr_db
*srdb
= &area
->srdb
;
1206 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
1209 /* Initialize Segment Routing Data Base */
1210 memset(srdb
, 0, sizeof(*srdb
));
1211 srdb
->adj_sids
= list_new();
1213 /* Pull defaults from the YANG module. */
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
);
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
;
1231 srdb
->config
.msd
= 0;
1232 srdb_prefix_cfg_init(&srdb
->config
.prefix_sids
);
1236 * Terminate IS-IS Segment Routing for the given area.
1238 * @param area IS-IS area
1240 void isis_sr_area_term(struct isis_area
*area
)
1242 struct isis_sr_db
*srdb
= &area
->srdb
;
1244 /* Stop Segment Routing */
1245 if (area
->srdb
.enabled
)
1248 /* Clear Prefix-SID configuration. */
1249 while (srdb_prefix_cfg_count(&srdb
->config
.prefix_sids
) > 0) {
1250 struct sr_prefix_cfg
*pcfg
;
1252 pcfg
= srdb_prefix_cfg_first(&srdb
->config
.prefix_sids
);
1253 isis_sr_cfg_prefix_del(pcfg
);
1258 * IS-IS Segment Routing global initialization.
1260 void isis_sr_init(void)
1262 install_element(VIEW_NODE
, &show_sr_node_cmd
);
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
);
1272 * IS-IS Segment Routing global terminate.
1274 void isis_sr_term(void)
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
);