]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_sr.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / isisd / isis_sr.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * This is an implementation of Segment Routing for IS-IS as per RFC 8667
4 *
5 * Copyright (C) 2019 Orange http://www.orange.com
6 *
7 * Author: Olivier Dugeon <olivier.dugeon@orange.com>
8 * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
9 */
10
11 #include <zebra.h>
12
13 #include "if.h"
14 #include "linklist.h"
15 #include "log.h"
16 #include "command.h"
17 #include "termtable.h"
18 #include "memory.h"
19 #include "prefix.h"
20 #include "table.h"
21 #include "srcdest_table.h"
22 #include "vty.h"
23 #include "zclient.h"
24 #include "lib/lib_errors.h"
25
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"
37
38 /* Local variables and functions */
39 DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information");
40
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);
46
47 /* --- RB-Tree Management functions ----------------------------------------- */
48
49 /**
50 * Configured SR Prefix comparison for RB-Tree.
51 *
52 * @param a First SR prefix
53 * @param b Second SR prefix
54 *
55 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
56 */
57 static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a,
58 const struct sr_prefix_cfg *b)
59 {
60 int ret;
61
62 ret = prefix_cmp(&a->prefix, &b->prefix);
63 if (ret != 0)
64 return ret;
65
66 ret = a->algorithm - b->algorithm;
67 if (ret != 0)
68 return ret;
69
70 return 0;
71 }
72 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry,
73 sr_prefix_sid_cfg_compare);
74
75 /**
76 * Find SRGB associated to a System ID.
77 *
78 * @param area IS-IS LSP database
79 * @param sysid System ID to lookup
80 *
81 * @return Pointer to SRGB if found, NULL otherwise
82 */
83 struct isis_sr_block *isis_sr_find_srgb(struct lspdb_head *lspdb,
84 const uint8_t *sysid)
85 {
86 struct isis_lsp *lsp;
87
88 lsp = isis_root_system_lsp(lspdb, sysid);
89 if (!lsp)
90 return NULL;
91
92 if (!lsp->tlvs->router_cap
93 || lsp->tlvs->router_cap->srgb.range_size == 0)
94 return NULL;
95
96 return &lsp->tlvs->router_cap->srgb;
97 }
98
99 /**
100 * Compute input label for the given Prefix-SID.
101 *
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
105 *
106 * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow
107 */
108 mpls_label_t sr_prefix_in_label(struct isis_area *area,
109 struct isis_prefix_sid *psid, bool local)
110 {
111 /*
112 * No need to assign a label for local Prefix-SIDs unless the no-PHP
113 * flag is set.
114 */
115 if (local
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;
119
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))
123 return psid->value;
124
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;
132 }
133
134 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
135 return (area->srdb.config.srgb_lower_bound + psid->value);
136 }
137
138 /**
139 * Compute output label for the given Prefix-SID.
140 *
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
146 *
147 * @return MPLS label or MPLS_INVALID_LABEL in case of error
148 */
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)
152 {
153 struct isis_sr_block *nh_srgb;
154
155 if (last_hop) {
156 if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP))
157 return MPLS_LABEL_IMPLICIT_NULL;
158
159 if (CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) {
160 if (family == AF_INET)
161 return MPLS_LABEL_IPV4_EXPLICIT_NULL;
162 else
163 return MPLS_LABEL_IPV6_EXPLICIT_NULL;
164 }
165 /* Fallthrough */
166 }
167
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)) {
171 /*
172 * V/L SIDs have local significance, so only adjacent routers
173 * can use them (RFC8667 section #2.1.1.1)
174 */
175 if (!last_hop)
176 return MPLS_INVALID_LABEL;
177 return psid->value;
178 }
179
180 /* Check that SID index falls inside the SRGB */
181 nh_srgb = isis_sr_find_srgb(lspdb, nh_sysid);
182 if (!nh_srgb)
183 return MPLS_INVALID_LABEL;
184
185 /*
186 * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
187 * IPv6 packets.
188 */
189 if ((family == AF_INET && !IS_SR_IPV4(nh_srgb))
190 || (family == AF_INET6 && !IS_SR_IPV6(nh_srgb)))
191 return MPLS_INVALID_LABEL;
192
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;
198 }
199
200 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
201 return (nh_srgb->lower_bound + psid->value);
202 }
203
204 /* --- Functions used for Yang model and CLI to configure Segment Routing --- */
205
206 /**
207 * Check if prefix correspond to a Node SID.
208 *
209 * @param ifp Interface
210 * @param prefix Prefix to be checked
211 *
212 * @return True if the interface/address pair corresponds to a Node-SID
213 */
214 static bool sr_prefix_is_node_sid(const struct interface *ifp,
215 const struct prefix *prefix)
216 {
217 return (if_is_loopback(ifp) && is_host_route(prefix));
218 }
219
220 /**
221 * Update local SRGB configuration. SRGB is reserved though Label Manager.
222 * This function trigger the update of local Prefix-SID installation.
223 *
224 * @param area IS-IS area
225 * @param lower_bound Lower bound of SRGB
226 * @param upper_bound Upper bound of SRGB
227 *
228 * @return 0 on success, -1 otherwise
229 */
230 int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
231 uint32_t upper_bound)
232 {
233 struct isis_sr_db *srdb = &area->srdb;
234
235 sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
236 area->area_tag, lower_bound, upper_bound);
237
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;
243 return 0;
244 }
245
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;
251 }
252
253 srdb->config.srgb_lower_bound = lower_bound;
254 srdb->config.srgb_upper_bound = upper_bound;
255
256 if (srdb->enabled) {
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;
263 return -1;
264 } else
265 srdb->srgb_active = true;
266
267
268 sr_debug(" |- Got new SRGB [%u/%u]",
269 srdb->config.srgb_lower_bound,
270 srdb->config.srgb_upper_bound);
271
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. */
275 isis_sr_start(area);
276 }
277
278 return 0;
279 }
280
281 /**
282 * Update Segment Routing Local Block range which is reserved though the
283 * Label Manager. This function trigger the update of local Adjacency-SID
284 * installation.
285 *
286 * @param area IS-IS area
287 * @param lower_bound Lower bound of SRLB
288 * @param upper_bound Upper bound of SRLB
289 *
290 * @return 0 on success, -1 otherwise
291 */
292 int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound,
293 uint32_t upper_bound)
294 {
295 struct isis_sr_db *srdb = &area->srdb;
296 struct listnode *node;
297 struct sr_adjacency *sra;
298
299 sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
300 area->area_tag, lower_bound, upper_bound);
301
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;
307 return 0;
308 }
309
310 /* LM is ready, start by deleting the old SRLB */
311 sr_local_block_delete(area);
312
313 srdb->config.srlb_lower_bound = lower_bound;
314 srdb->config.srlb_upper_bound = upper_bound;
315
316 if (srdb->enabled) {
317 /* Initialize new SRLB */
318 if (sr_local_block_init(area) != 0)
319 return -1;
320
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);
324
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. */
329 isis_sr_start(area);
330 }
331
332 return 0;
333 }
334
335 /**
336 * Add new Prefix-SID configuration to the SRDB.
337 *
338 * @param area IS-IS area
339 * @param prefix Prefix to be added
340 *
341 * @return Newly added Prefix-SID configuration structure
342 */
343 struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
344 const struct prefix *prefix,
345 uint8_t algorithm)
346 {
347 struct sr_prefix_cfg *pcfg;
348 struct interface *ifp;
349
350 sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix);
351
352 pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg));
353 pcfg->prefix = *prefix;
354 pcfg->area = area;
355 pcfg->algorithm = algorithm;
356
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);
362
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;
367
368 /* Save prefix-sid configuration. */
369 srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg);
370
371 return pcfg;
372 }
373
374 /**
375 * Removal of locally configured Prefix-SID.
376 *
377 * @param pcfg Configured Prefix-SID
378 */
379 void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg)
380 {
381 struct isis_area *area = pcfg->area;
382
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",
386 pcfg->sid);
387
388 srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg);
389 XFREE(MTYPE_ISIS_SR_INFO, pcfg);
390 }
391
392 /**
393 * Lookup for Prefix-SID in the local configuration.
394 *
395 * @param area IS-IS area
396 * @param prefix Prefix to lookup
397 *
398 * @return Configured Prefix-SID structure if found, NULL otherwise
399 */
400 struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area,
401 union prefixconstptr prefix,
402 uint8_t algorithm)
403 {
404 struct sr_prefix_cfg pcfg = {};
405
406 prefix_copy(&pcfg.prefix, prefix.p);
407 pcfg.algorithm = algorithm;
408 return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg);
409 }
410
411 /**
412 * Fill in Prefix-SID Sub-TLV according to the corresponding configuration.
413 *
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
417 */
418 void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external,
419 struct isis_prefix_sid *psid)
420 {
421 /* Set SID algorithm. */
422 psid->algorithm = pcfg->algorithm;
423
424 /* Set SID flags. */
425 psid->flags = 0;
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);
430 break;
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);
434 break;
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);
438 break;
439 }
440 if (external)
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);
444
445 /* Set SID value. */
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);
450 }
451 }
452
453 /**
454 * Delete all backup Adj-SIDs.
455 *
456 * @param area IS-IS area
457 * @param level IS-IS level
458 */
459 void isis_area_delete_backup_adj_sids(struct isis_area *area, int level)
460 {
461 struct sr_adjacency *sra;
462 struct listnode *node, *nnode;
463
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))
467 sr_adj_sid_del(sra);
468 }
469
470 /* --- Segment Routing Local Block management functions --------------------- */
471
472 /**
473 * Initialize Segment Routing Local Block from SRDB configuration and reserve
474 * block of bits to manage label allocation.
475 *
476 * @param area IS-IS area
477 */
478 static int sr_local_block_init(struct isis_area *area)
479 {
480 struct isis_sr_db *srdb = &area->srdb;
481 struct sr_local_block *srlb = &srdb->srlb;
482
483 /* Check if SRLB is not already configured */
484 if (srlb->active)
485 return 0;
486
487 /*
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.
490 */
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;
496 return -1;
497 }
498
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);
501
502 /* Initialize the SRLB */
503 srlb->start = srdb->config.srlb_lower_bound;
504 srlb->end = srdb->config.srlb_upper_bound;
505 srlb->current = 0;
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)
509 srlb->max_block++;
510 srlb->used_mark = XCALLOC(MTYPE_ISIS_SR_INFO,
511 srlb->max_block * SRLB_BLOCK_SIZE);
512 srlb->active = true;
513
514 return 0;
515 }
516
517 /**
518 * Remove Segment Routing Local Block.
519 *
520 * @param area IS-IS area
521 */
522 static void sr_local_block_delete(struct isis_area *area)
523 {
524 struct isis_sr_db *srdb = &area->srdb;
525 struct sr_local_block *srlb = &srdb->srlb;
526
527 /* Check if SRLB is not already delete */
528 if (!srlb->active)
529 return;
530
531 sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area->area_tag,
532 srlb->start, srlb->end);
533
534 /* First release the label block */
535 isis_zebra_release_label_range(srdb->config.srlb_lower_bound,
536 srdb->config.srlb_upper_bound);
537
538 /* Then reset SRLB structure */
539 if (srlb->used_mark != NULL)
540 XFREE(MTYPE_ISIS_SR_INFO, srlb->used_mark);
541 srlb->active = false;
542 }
543
544 /**
545 * Request a label from the Segment Routing Local Block.
546 *
547 * @param srlb Segment Routing Local Block
548 *
549 * @return First available label on success or MPLS_INVALID_LABEL if the
550 * block of labels is full
551 */
552 static mpls_label_t sr_local_block_request_label(struct sr_local_block *srlb)
553 {
554 mpls_label_t label;
555 uint32_t index;
556 uint32_t pos;
557 uint32_t size = srlb->end - srlb->start + 1;
558
559 /* Check if we ran out of available labels */
560 if (srlb->current >= size)
561 return MPLS_INVALID_LABEL;
562
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;
568
569 /* Jump to the next free position */
570 srlb->current++;
571 pos = srlb->current % SRLB_BLOCK_SIZE;
572 while (srlb->current < size) {
573 if (pos == 0)
574 index++;
575 if (!((1ULL << pos) & srlb->used_mark[index]))
576 break;
577 else {
578 srlb->current++;
579 pos = srlb->current % SRLB_BLOCK_SIZE;
580 }
581 }
582
583 if (srlb->current == size)
584 zlog_warn(
585 "SR: Warning, SRLB is depleted and next label request will fail");
586
587 return label;
588 }
589
590 /**
591 * Release label in the Segment Routing Local Block.
592 *
593 * @param srlb Segment Routing Local Block
594 * @param label Label to be release
595 *
596 * @return 0 on success or -1 if label falls outside SRLB
597 */
598 static int sr_local_block_release_label(struct sr_local_block *srlb,
599 mpls_label_t label)
600 {
601 uint32_t index;
602 uint32_t pos;
603
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);
609 return -1;
610 }
611
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])) {
620 srlb->current =
621 index * SRLB_BLOCK_SIZE + pos;
622 break;
623 }
624 break;
625 }
626 }
627
628 return 0;
629 }
630
631 /* --- Segment Routing Adjacency-SID management functions ------------------- */
632
633 /**
634 * Add new local Adjacency-SID.
635 *
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)
640 */
641 void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, bool backup,
642 struct list *nexthops)
643 {
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 = {};
650 uint8_t flags;
651 mpls_label_t input_label;
652
653 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag,
654 backup ? "Backup" : "Primary");
655
656 /* Determine nexthop IP address */
657 switch (family) {
658 case AF_INET:
659 if (!circuit->ip_router || !adj->ipv4_address_count)
660 return;
661
662 nexthop.ipv4 = adj->ipv4_addresses[0];
663 break;
664 case AF_INET6:
665 if (!circuit->ipv6_router || !adj->ll_ipv6_count)
666 return;
667
668 nexthop.ipv6 = adj->ll_ipv6_addrs[0];
669 break;
670 default:
671 flog_err(EC_LIB_DEVELOPMENT,
672 "%s: unexpected address-family: %u", __func__, family);
673 exit(1);
674 }
675
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);
680 if (backup)
681 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
682
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)
686 return;
687
688 if (circuit->ext == NULL)
689 circuit->ext = isis_alloc_ext_subtlvs();
690
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;
696
697 if (backup && nexthops) {
698 struct isis_vertex_adj *vadj;
699 struct listnode *node;
700
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;
705
706 label_stack = vadj->label_stack;
707 adjinfo2nexthop(family, sra->backup_nexthops, adj, NULL,
708 label_stack);
709 }
710 }
711
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;
724 break;
725 /* Adjacency-SID for Point to Point interface section #2.2.1 */
726 case CIRCUIT_T_P2P:
727 adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
728 adj_sid->family = family;
729 adj_sid->flags = flags;
730 adj_sid->weight = 0;
731 adj_sid->sid = input_label;
732 isis_tlvs_add_adj_sid(circuit->ext, adj_sid);
733 sra->u.adj_sid = adj_sid;
734 break;
735 default:
736 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
737 __func__, circuit->circ_type);
738 exit(1);
739 }
740
741 /* Add Adjacency-SID in SRDB */
742 sra->adj = adj;
743 listnode_add(area->srdb.adj_sids, sra);
744 listnode_add(adj->adj_sids, sra);
745
746 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
747 }
748
749 /**
750 * Add Primary and Backup local Adjacency SID.
751 *
752 * @param adj IS-IS Adjacency
753 * @param family Inet Family (IPv4 or IPv6)
754 */
755 static void sr_adj_sid_add(struct isis_adjacency *adj, int family)
756 {
757 sr_adj_sid_add_single(adj, family, false, NULL);
758 }
759
760 static void sr_adj_sid_update(struct sr_adjacency *sra,
761 struct sr_local_block *srlb)
762 {
763 struct isis_circuit *circuit = sra->adj->circuit;
764
765 /* First remove the old MPLS Label */
766 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
767
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)
771 return;
772
773 switch (circuit->circ_type) {
774 case CIRCUIT_T_BROADCAST:
775 sra->u.ladj_sid->sid = sra->input_label;
776 break;
777 case CIRCUIT_T_P2P:
778 sra->u.adj_sid->sid = sra->input_label;
779 break;
780 default:
781 flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
782 __func__, circuit->circ_type);
783 break;
784 }
785
786 /* Finally configure the new MPLS Label */
787 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
788 }
789
790 /**
791 * Delete local Adj-SID.
792 *
793 * @param sra Segment Routing Adjacency
794 */
795 static void sr_adj_sid_del(struct sr_adjacency *sra)
796 {
797 struct isis_circuit *circuit = sra->adj->circuit;
798 struct isis_area *area = circuit->area;
799
800 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag);
801
802 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
803
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);
810 break;
811 case CIRCUIT_T_P2P:
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);
815 break;
816 default:
817 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
818 __func__, circuit->circ_type);
819 exit(1);
820 }
821
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);
826 }
827
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);
832 }
833
834 /**
835 * Lookup Segment Routing Adj-SID by family and type.
836 *
837 * @param adj IS-IS Adjacency
838 * @param family Inet Family (IPv4 or IPv6)
839 * @param type Adjacency SID type
840 */
841 struct sr_adjacency *isis_sr_adj_sid_find(struct isis_adjacency *adj,
842 int family, enum sr_adj_type type)
843 {
844 struct sr_adjacency *sra;
845 struct listnode *node;
846
847 for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, node, sra))
848 if (sra->nexthop.family == family && sra->type == type)
849 return sra;
850
851 return NULL;
852 }
853
854 /**
855 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
856 *
857 * @param adj IS-IS Adjacency
858 *
859 * @return 0
860 */
861 static int sr_adj_state_change(struct isis_adjacency *adj)
862 {
863 struct sr_adjacency *sra;
864 struct listnode *node, *nnode;
865
866 if (!adj->circuit->area->srdb.enabled)
867 return 0;
868
869 if (adj->adj_state == ISIS_ADJ_UP)
870 return 0;
871
872 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
873 sr_adj_sid_del(sra);
874
875 return 0;
876 }
877
878 /**
879 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
880 * IPv6 address to corresponding Adjacency-SID accordingly.
881 *
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
885 *
886 * @return 0
887 */
888 static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family,
889 bool global)
890 {
891 if (!adj->circuit->area->srdb.enabled || global)
892 return 0;
893
894 sr_adj_sid_add(adj, family);
895
896 return 0;
897 }
898
899 /**
900 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
901 * delete the corresponding Adjacency-SID(s) accordingly.
902 *
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
906 *
907 * @return 0
908 */
909 static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family,
910 bool global)
911 {
912 struct sr_adjacency *sra;
913 struct listnode *node, *nnode;
914
915 if (!adj->circuit->area->srdb.enabled || global)
916 return 0;
917
918 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
919 if (sra->nexthop.family == family)
920 sr_adj_sid_del(sra);
921
922 return 0;
923 }
924
925 /**
926 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
927 *
928 * @param ifp Loopback Interface
929 *
930 * @return 0
931 */
932 static int sr_if_new_hook(struct interface *ifp)
933 {
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;
940
941 /* Get corresponding circuit */
942 circuit = circuit_scan_by_ifp(ifp);
943 if (!circuit)
944 return 0;
945
946 area = circuit->area;
947 if (!area)
948 return 0;
949
950 /*
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.
954 */
955 FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
956
957 for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
958 pcfgs[i] = isis_sr_cfg_prefix_find(
959 area, connected->address, i);
960
961 if (!pcfgs[i])
962 continue;
963
964 if (sr_prefix_is_node_sid(ifp, &pcfgs[i]->prefix)) {
965 pcfgs[i]->node_sid = true;
966 need_lsp_regenerate = true;
967 }
968 }
969 }
970
971 if (need_lsp_regenerate)
972 lsp_regenerate_schedule(area, area->is_type, 0);
973
974 return 0;
975 }
976
977 /**
978 * Show LFIB operation in human readable format.
979 *
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
984 *
985 * @return String containing LFIB operation in human readable format
986 */
987 char *sr_op2str(char *buf, size_t size, mpls_label_t label_in,
988 mpls_label_t label_out)
989 {
990 if (size < 24)
991 return NULL;
992
993 if (label_in == MPLS_INVALID_LABEL) {
994 snprintf(buf, size, "no-op.");
995 return buf;
996 }
997
998 switch (label_out) {
999 case MPLS_LABEL_IMPLICIT_NULL:
1000 snprintf(buf, size, "Pop(%u)", label_in);
1001 break;
1002 case MPLS_LABEL_IPV4_EXPLICIT_NULL:
1003 case MPLS_LABEL_IPV6_EXPLICIT_NULL:
1004 snprintf(buf, size, "Swap(%u, null)", label_in);
1005 break;
1006 case MPLS_INVALID_LABEL:
1007 snprintf(buf, size, "no-op.");
1008 break;
1009 default:
1010 snprintf(buf, size, "Swap(%u, %u)", label_in, label_out);
1011 break;
1012 }
1013 return buf;
1014 }
1015
1016 /**
1017 * Show Segment Routing Node.
1018 *
1019 * @param vty VTY output
1020 * @param area IS-IS area
1021 * @param level IS-IS level
1022 */
1023 static void show_node(struct vty *vty, struct isis_area *area, int level,
1024 uint8_t algo)
1025 {
1026 struct isis_lsp *lsp;
1027 struct ttable *tt;
1028 char buf[128];
1029
1030 vty_out(vty, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level));
1031
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 = '+';
1037 ttable_restyle(tt);
1038 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1039
1040 frr_each (lspdb, &area->lspdb[level - 1], lsp) {
1041 struct isis_router_cap *cap;
1042
1043 if (!lsp->tlvs)
1044 continue;
1045 cap = lsp->tlvs->router_cap;
1046 if (!cap)
1047 continue;
1048 if (cap->algo[algo] == SR_ALGORITHM_UNSET)
1049 continue;
1050
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");
1055 #ifndef FABRICD
1056 else
1057 snprintf(buf, sizeof(buf), "Flex-Algo %d", algo);
1058 #endif /* ifndef FABRICD */
1059
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,
1065 buf, cap->msd);
1066 }
1067
1068 /* Dump the generated table. */
1069 if (tt->nrows > 1) {
1070 char *table;
1071
1072 table = ttable_dump(tt, "\n");
1073 vty_out(vty, "%s\n", table);
1074 XFREE(MTYPE_TMP, table);
1075 }
1076 ttable_del(tt);
1077 }
1078
1079 DEFUN(show_sr_node, show_sr_node_cmd,
1080 "show " PROTO_NAME
1081 " segment-routing node"
1082 #ifndef FABRICD
1083 " [algorithm (128-255)]"
1084 #endif /* ifndef FABRICD */
1085 ,
1086 SHOW_STR PROTO_HELP
1087 "Segment-Routing\n"
1088 "Segment-Routing node\n"
1089 #ifndef FABRICD
1090 "Show Flex-algo nodes\n"
1091 "Algorithm number\n"
1092 #endif /* ifndef FABRICD */
1093 )
1094 {
1095 struct listnode *node, *inode;
1096 struct isis_area *area;
1097 uint8_t algorithm = SR_ALGORITHM_SPF;
1098 struct isis *isis;
1099 #ifndef FABRICD
1100 int idx = 0;
1101
1102 if (argv_find(argv, argc, "algorithm", &idx))
1103 algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
1104 #endif /* ifndef FABRICD */
1105
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");
1112 continue;
1113 }
1114 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
1115 level++)
1116 show_node(vty, area, level, algorithm);
1117 }
1118 }
1119
1120 return CMD_SUCCESS;
1121 }
1122
1123 /* --- IS-IS Segment Routing Management function ---------------------------- */
1124
1125 /**
1126 * Thread function to re-attempt connection to the Label Manager and thus be
1127 * able to start Segment Routing.
1128 *
1129 * @param start Thread structure that contains area as argument
1130 *
1131 * @return 1 on success
1132 */
1133 static void sr_start_label_manager(struct event *start)
1134 {
1135 struct isis_area *area;
1136
1137 area = EVENT_ARG(start);
1138
1139 /* re-attempt to start SR & Label Manager connection */
1140 isis_sr_start(area);
1141 }
1142
1143 /**
1144 * Enable SR on the given IS-IS area.
1145 *
1146 * @param area IS-IS area
1147 *
1148 * @return 0 on success, -1 otherwise
1149 */
1150 int isis_sr_start(struct isis_area *area)
1151 {
1152 struct isis_sr_db *srdb = &area->srdb;
1153 struct isis_adjacency *adj;
1154 struct listnode *node;
1155
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,
1161 &srdb->t_start_lm);
1162 return -1;
1163 }
1164
1165 /* Label Manager is ready, initialize the SRLB */
1166 if (sr_local_block_init(area) < 0)
1167 return -1;
1168
1169 /*
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.
1173 */
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)
1179 < 0) {
1180 srdb->srgb_active = false;
1181 return -1;
1182 } else
1183 srdb->srgb_active = true;
1184 }
1185
1186 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
1187 area->area_tag);
1188
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);
1195 }
1196
1197 area->srdb.enabled = true;
1198
1199 /* Regenerate LSPs to advertise Segment Routing capabilities. */
1200 lsp_regenerate_schedule(area, area->is_type, 0);
1201
1202 return 0;
1203 }
1204
1205 /**
1206 * Disable SR on the given IS-IS area.
1207 *
1208 * @param area IS-IS area
1209 */
1210 void isis_sr_stop(struct isis_area *area)
1211 {
1212 struct isis_sr_db *srdb = &area->srdb;
1213 struct sr_adjacency *sra;
1214 struct listnode *node, *nnode;
1215
1216 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
1217 area->area_tag);
1218
1219 /* Disable any re-attempt to connect to Label Manager */
1220 EVENT_OFF(srdb->t_start_lm);
1221
1222 /* Uninstall all local Adjacency-SIDs. */
1223 for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
1224 sr_adj_sid_del(sra);
1225
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;
1231 }
1232
1233 /* Delete SRLB */
1234 sr_local_block_delete(area);
1235
1236 area->srdb.enabled = false;
1237
1238 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
1239 lsp_regenerate_schedule(area, area->is_type, 0);
1240 }
1241
1242 /**
1243 * IS-IS Segment Routing initialization for given area.
1244 *
1245 * @param area IS-IS area
1246 */
1247 void isis_sr_area_init(struct isis_area *area)
1248 {
1249 struct isis_sr_db *srdb = &area->srdb;
1250
1251 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
1252 area->area_tag);
1253
1254 /* Initialize Segment Routing Data Base */
1255 memset(srdb, 0, sizeof(*srdb));
1256 srdb->adj_sids = list_new();
1257
1258 /* Pull defaults from the YANG module. */
1259 #ifndef FABRICD
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);
1269 #else
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;
1275 #endif
1276 srdb->config.msd = 0;
1277 srdb_prefix_cfg_init(&srdb->config.prefix_sids);
1278 }
1279
1280 /**
1281 * Terminate IS-IS Segment Routing for the given area.
1282 *
1283 * @param area IS-IS area
1284 */
1285 void isis_sr_area_term(struct isis_area *area)
1286 {
1287 struct isis_sr_db *srdb = &area->srdb;
1288
1289 /* Stop Segment Routing */
1290 if (area->srdb.enabled)
1291 isis_sr_stop(area);
1292
1293 /* Free Adjacency SID list */
1294 list_delete(&srdb->adj_sids);
1295
1296 /* Clear Prefix-SID configuration. */
1297 while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) {
1298 struct sr_prefix_cfg *pcfg;
1299
1300 pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids);
1301 isis_sr_cfg_prefix_del(pcfg);
1302 }
1303 }
1304
1305 /**
1306 * IS-IS Segment Routing global initialization.
1307 */
1308 void isis_sr_init(void)
1309 {
1310 install_element(VIEW_NODE, &show_sr_node_cmd);
1311
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);
1317 }
1318
1319 /**
1320 * IS-IS Segment Routing global terminate.
1321 */
1322 void isis_sr_term(void)
1323 {
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);
1329 }