1 /*
2 * This is an implementation of Segment Routing for IS-IS as per RFC 8667
3 *
4 * Copyright (C) 2019 Orange http://www.orange.com
5 *
6 * Author: Olivier Dugeon <olivier.dugeon@orange.com>
7 * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; see the file COPYING; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include <zebra.h>
25
26 #include "if.h"
27 #include "linklist.h"
28 #include "log.h"
29 #include "command.h"
30 #include "termtable.h"
31 #include "memory.h"
32 #include "prefix.h"
33 #include "table.h"
34 #include "vty.h"
35 #include "zclient.h"
36 #include "lib/lib_errors.h"
37
38 #include "isisd/isisd.h"
39 #include "isisd/isis_spf.h"
40 #include "isisd/isis_spf_private.h"
41 #include "isisd/isis_adjacency.h"
42 #include "isisd/isis_route.h"
43 #include "isisd/isis_mt.h"
44 #include "isisd/isis_sr.h"
45 #include "isisd/isis_tlvs.h"
46 #include "isisd/isis_misc.h"
47 #include "isisd/isis_zebra.h"
48 #include "isisd/isis_errors.h"
49
50 /* Local variables and functions */
51 DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information")
52
53 static void sr_prefix_uninstall(struct sr_prefix *srp);
54 static void sr_prefix_reinstall(struct sr_prefix *srp, bool make_before_break);
55 static void sr_local_block_delete(struct isis_area *area);
56 static int sr_local_block_init(struct isis_area *area);
57 static void sr_adj_sid_update(struct sr_adjacency *sra,
58 struct sr_local_block *srlb);
59
60 /* --- RB-Tree Management functions ----------------------------------------- */
61
62 /**
63 * SR Prefix comparison for RB-Tree.
64 *
65 * @param a First SR prefix
66 * @param b Second SR prefix
67 *
68 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
69 */
sr_prefix_sid_compare(const struct sr_prefix * a,const struct sr_prefix * b)70 static inline int sr_prefix_sid_compare(const struct sr_prefix *a,
71 const struct sr_prefix *b)
72 {
73 return prefix_cmp(&a->prefix, &b->prefix);
74 }
DECLARE_RBTREE_UNIQ(srdb_node_prefix,struct sr_prefix,node_entry,sr_prefix_sid_compare)75 DECLARE_RBTREE_UNIQ(srdb_node_prefix, struct sr_prefix, node_entry,
76 sr_prefix_sid_compare)
77 DECLARE_RBTREE_UNIQ(srdb_area_prefix, struct sr_prefix, area_entry,
78 sr_prefix_sid_compare)
79
80 /**
81 * Configured SR Prefix comparison for RB-Tree.
82 *
83 * @param a First SR prefix
84 * @param b Second SR prefix
85 *
86 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
87 */
88 static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a,
89 const struct sr_prefix_cfg *b)
90 {
91 return prefix_cmp(&a->prefix, &b->prefix);
92 }
DECLARE_RBTREE_UNIQ(srdb_prefix_cfg,struct sr_prefix_cfg,entry,sr_prefix_sid_cfg_compare)93 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry,
94 sr_prefix_sid_cfg_compare)
95
96 /**
97 * SR Node comparison for RB-Tree.
98 *
99 * @param a First SR node
100 * @param b Second SR node
101 *
102 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
103 */
104 static inline int sr_node_compare(const struct sr_node *a,
105 const struct sr_node *b)
106 {
107 return memcmp(a->sysid, b->sysid, ISIS_SYS_ID_LEN);
108 }
DECLARE_RBTREE_UNIQ(srdb_node,struct sr_node,entry,sr_node_compare)109 DECLARE_RBTREE_UNIQ(srdb_node, struct sr_node, entry, sr_node_compare)
110
111 /* --- Functions used for Yang model and CLI to configure Segment Routing --- */
112
113 /**
114 * Check if prefix correspond to a Node SID.
115 *
116 * @param ifp Interface
117 * @param prefix Prefix to be checked
118 *
119 * @return True if the interface/address pair corresponds to a Node-SID
120 */
121 static bool sr_prefix_is_node_sid(const struct interface *ifp,
122 const struct prefix *prefix)
123 {
124 return (if_is_loopback(ifp) && is_host_route(prefix));
125 }
126
127 /**
128 * Update local SRGB configuration. SRGB is reserved though Label Manager.
129 * This function trigger the update of local Prefix-SID installation.
130 *
131 * @param area IS-IS area
132 * @param lower_bound Lower bound of SRGB
133 * @param upper_bound Upper bound of SRGB
134 *
135 * @return 0 on success, -1 otherwise
136 */
isis_sr_cfg_srgb_update(struct isis_area * area,uint32_t lower_bound,uint32_t upper_bound)137 int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
138 uint32_t upper_bound)
139 {
140 struct isis_sr_db *srdb = &area->srdb;
141
142 sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]",
143 area->area_tag, lower_bound, upper_bound);
144
145 /* Just store new SRGB values if Label Manager is not available.
146 * SRGB will be configured later when SR start */
147 if (!isis_zebra_label_manager_ready()) {
148 srdb->config.srgb_lower_bound = lower_bound;
149 srdb->config.srgb_upper_bound = upper_bound;
150 return 0;
151 }
152
153 /* Label Manager is ready, start by releasing the old SRGB. */
154 if (srdb->srgb_active) {
155 isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
156 srdb->config.srgb_upper_bound);
157 srdb->srgb_active = false;
158 }
159
160 srdb->config.srgb_lower_bound = lower_bound;
161 srdb->config.srgb_upper_bound = upper_bound;
162
163 if (srdb->enabled) {
164 struct sr_prefix *srp;
165
166 /* then request new SRGB if SR is enabled. */
167 if (isis_zebra_request_label_range(
168 srdb->config.srgb_lower_bound,
169 srdb->config.srgb_upper_bound
170 - srdb->config.srgb_lower_bound + 1) < 0) {
171 srdb->srgb_active = false;
172 return -1;
173 } else
174 srdb->srgb_active = true;
175
176
177 sr_debug(" |- Got new SRGB [%u/%u]",
178 srdb->config.srgb_lower_bound,
179 srdb->config.srgb_upper_bound);
180
181 /* Reinstall local Prefix-SIDs to update their input labels. */
182 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
183 frr_each (srdb_area_prefix,
184 &area->srdb.prefix_sids[level - 1], srp) {
185 sr_prefix_reinstall(srp, false);
186 }
187 }
188
189 lsp_regenerate_schedule(area, area->is_type, 0);
190 } else if (srdb->config.enabled) {
191 /* Try to enable SR again using the new SRGB. */
192 isis_sr_start(area);
193 }
194
195 return 0;
196 }
197
198 /**
199 * Update Segment Routing Local Block range which is reserved though the
200 * Label Manager. This function trigger the update of local Adjacency-SID
201 * installation.
202 *
203 * @param area IS-IS area
204 * @param lower_bound Lower bound of SRLB
205 * @param upper_bound Upper bound of SRLB
206 *
207 * @return 0 on success, -1 otherwise
208 */
isis_sr_cfg_srlb_update(struct isis_area * area,uint32_t lower_bound,uint32_t upper_bound)209 int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound,
210 uint32_t upper_bound)
211 {
212 struct isis_sr_db *srdb = &area->srdb;
213 struct listnode *node;
214 struct sr_adjacency *sra;
215
216 sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]",
217 area->area_tag, lower_bound, upper_bound);
218
219 /* Just store new SRLB values if Label Manager is not available.
220 * SRLB will be configured later when SR start */
221 if (!isis_zebra_label_manager_ready()) {
222 srdb->config.srlb_lower_bound = lower_bound;
223 srdb->config.srlb_upper_bound = upper_bound;
224 return 0;
225 }
226
227 /* LM is ready, start by deleting the old SRLB */
228 sr_local_block_delete(area);
229
230 srdb->config.srlb_lower_bound = lower_bound;
231 srdb->config.srlb_upper_bound = upper_bound;
232
233 if (srdb->enabled) {
234 /* Initialize new SRLB */
235 if (sr_local_block_init(area) != 0)
236 return -1;
237
238 /* Reinstall local Adjacency-SIDs with new labels. */
239 for (ALL_LIST_ELEMENTS_RO(area->srdb.adj_sids, node, sra))
240 sr_adj_sid_update(sra, &srdb->srlb);
241
242 /* Update and Flood LSP */
243 lsp_regenerate_schedule(area, area->is_type, 0);
244 } else if (srdb->config.enabled) {
245 /* Try to enable SR again using the new SRLB. */
246 isis_sr_start(area);
247 }
248
249 return 0;
250 }
251
252 /**
253 * Add new Prefix-SID configuration to the SRDB.
254 *
255 * @param area IS-IS area
256 * @param prefix Prefix to be added
257 *
258 * @return Newly added Prefix-SID configuration structure
259 */
isis_sr_cfg_prefix_add(struct isis_area * area,const struct prefix * prefix)260 struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
261 const struct prefix *prefix)
262 {
263 struct sr_prefix_cfg *pcfg;
264 struct interface *ifp;
265
266 sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix);
267
268 pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg));
269 pcfg->prefix = *prefix;
270 pcfg->area = area;
271
272 /* Pull defaults from the YANG module. */
273 pcfg->sid_type = yang_get_default_enum(
274 "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR);
275 pcfg->last_hop_behavior = yang_get_default_enum(
276 "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR);
277
278 /* Set the N-flag when appropriate. */
279 ifp = if_lookup_prefix(prefix, VRF_DEFAULT);
280 if (ifp && sr_prefix_is_node_sid(ifp, prefix))
281 pcfg->node_sid = true;
282
283 /* Save prefix-sid configuration. */
284 srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg);
285
286 return pcfg;
287 }
288
289 /**
290 * Removal of locally configured Prefix-SID.
291 *
292 * @param pcfg Configured Prefix-SID
293 */
isis_sr_cfg_prefix_del(struct sr_prefix_cfg * pcfg)294 void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg)
295 {
296 struct isis_area *area = pcfg->area;
297
298 sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u",
299 area->area_tag, &pcfg->prefix,
300 pcfg->sid_type == SR_SID_VALUE_TYPE_INDEX ? "index" : "label",
301 pcfg->sid);
302
303 srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg);
304 XFREE(MTYPE_ISIS_SR_INFO, pcfg);
305 }
306
307 /**
308 * Lookup for Prefix-SID in the local configuration.
309 *
310 * @param area IS-IS area
311 * @param prefix Prefix to lookup
312 *
313 * @return Configured Prefix-SID structure if found, NULL otherwise
314 */
isis_sr_cfg_prefix_find(struct isis_area * area,union prefixconstptr prefix)315 struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area,
316 union prefixconstptr prefix)
317 {
318 struct sr_prefix_cfg pcfg = {};
319
320 prefix_copy(&pcfg.prefix, prefix.p);
321 return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg);
322 }
323
324 /**
325 * Fill in Prefix-SID Sub-TLV according to the corresponding configuration.
326 *
327 * @param pcfg Prefix-SID configuration
328 * @param external False if prefix is locally configured, true otherwise
329 * @param psid Prefix-SID sub-TLV to be updated
330 */
isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg * pcfg,bool external,struct isis_prefix_sid * psid)331 void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external,
332 struct isis_prefix_sid *psid)
333 {
334 /* Set SID algorithm. */
335 psid->algorithm = SR_ALGORITHM_SPF;
336
337 /* Set SID flags. */
338 psid->flags = 0;
339 switch (pcfg->last_hop_behavior) {
340 case SR_LAST_HOP_BEHAVIOR_EXP_NULL:
341 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
342 SET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
343 break;
344 case SR_LAST_HOP_BEHAVIOR_NO_PHP:
345 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
346 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
347 break;
348 case SR_LAST_HOP_BEHAVIOR_PHP:
349 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP);
350 UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL);
351 break;
352 }
353 if (external)
354 SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED);
355 if (pcfg->node_sid)
356 SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE);
357
358 /* Set SID value. */
359 psid->value = pcfg->sid;
360 if (pcfg->sid_type == SR_SID_VALUE_TYPE_ABSOLUTE) {
361 SET_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE);
362 SET_FLAG(psid->flags, ISIS_PREFIX_SID_LOCAL);
363 }
364 }
365
366 /* --- Segment Routing Prefix Management functions -------------------------- */
367
368 /**
369 * Add Segment Routing Prefix to a given Segment Routing Node.
370 *
371 * @param area IS-IS area
372 * @param srn Segment Routing Node
373 * @param prefix Prefix to be added
374 * @param local True if prefix is locally configured, false otherwise
375 * @param psid Prefix-SID sub-TLVs
376 *
377 * @return New Segment Routing Prefix structure
378 */
sr_prefix_add(struct isis_area * area,struct sr_node * srn,union prefixconstptr prefix,bool local,const struct isis_prefix_sid * psid)379 static struct sr_prefix *sr_prefix_add(struct isis_area *area,
380 struct sr_node *srn,
381 union prefixconstptr prefix, bool local,
382 const struct isis_prefix_sid *psid)
383 {
384 struct sr_prefix *srp;
385
386 srp = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srp));
387 prefix_copy(&srp->prefix, prefix.p);
388 srp->sid = *psid;
389 srp->input_label = MPLS_INVALID_LABEL;
390 if (local) {
391 srp->type = ISIS_SR_PREFIX_LOCAL;
392 isis_sr_nexthop_reset(&srp->u.local.info);
393 } else {
394 srp->type = ISIS_SR_PREFIX_REMOTE;
395 srp->u.remote.rinfo = NULL;
396 }
397 srp->srn = srn;
398 srdb_node_prefix_add(&srn->prefix_sids, srp);
399 /* TODO: this might fail if we have Anycast SIDs in the IS-IS area. */
400 srdb_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp);
401
402 sr_debug(" |- Added new SR Prefix-SID %pFX %s %u to SR Node %s",
403 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
404 srp->sid.value, sysid_print(srn->sysid));
405
406 return srp;
407 }
408
409 /**
410 * Remove given Segment Prefix from given Segment Routing Node.
411 * Prefix-SID is un-installed first.
412 *
413 * @param area IS-IS area
414 * @param srn Segment Routing Node
415 * @param srp Segment Routing Prefix
416 */
sr_prefix_del(struct isis_area * area,struct sr_node * srn,struct sr_prefix * srp)417 static void sr_prefix_del(struct isis_area *area, struct sr_node *srn,
418 struct sr_prefix *srp)
419 {
420 sr_debug(" |- Delete SR Prefix-SID %pFX %s %u to SR Node %s",
421 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
422 srp->sid.value, sysid_print(srn->sysid));
423
424 sr_prefix_uninstall(srp);
425 srdb_node_prefix_del(&srn->prefix_sids, srp);
426 srdb_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp);
427 XFREE(MTYPE_ISIS_SR_INFO, srp);
428 }
429
430 /**
431 * Find Segment Routing Prefix by Area.
432 *
433 * @param area IS-IS area
434 * @param level IS-IS level
435 * @param prefix Prefix to lookup
436 *
437 * @return Segment Routing Prefix structure if found, NULL otherwise
438 */
sr_prefix_find_by_area(struct isis_area * area,int level,union prefixconstptr prefix)439 static struct sr_prefix *sr_prefix_find_by_area(struct isis_area *area,
440 int level,
441 union prefixconstptr prefix)
442 {
443 struct sr_prefix srp = {};
444
445 prefix_copy(&srp.prefix, prefix.p);
446 return srdb_area_prefix_find(&area->srdb.prefix_sids[level - 1], &srp);
447 }
448
449 /**
450 * Find Segment Routing Prefix by Segment Routing Node.
451 *
452 * @param srn Segment Routing Node
453 * @param prefix Prefix to lookup
454 *
455 * @return Segment Routing Prefix structure if found, NULL otherwise
456 */
sr_prefix_find_by_node(struct sr_node * srn,union prefixconstptr prefix)457 static struct sr_prefix *sr_prefix_find_by_node(struct sr_node *srn,
458 union prefixconstptr prefix)
459 {
460 struct sr_prefix srp = {};
461
462 prefix_copy(&srp.prefix, prefix.p);
463 return srdb_node_prefix_find(&srn->prefix_sids, &srp);
464 }
465
466 /* --- Segment Routing Node Management functions ---------------------------- */
467
468 /**
469 * Add Segment Routing Node to the Segment Routing Data Base.
470 *
471 * @param area IS-IS area
472 * @param level IS-IS level
473 * @param sysid Node System ID
474 * @param cap Segment Routing Capability sub-TLVs
475 *
476 * @return New Segment Routing Node structure
477 */
sr_node_add(struct isis_area * area,int level,const uint8_t * sysid)478 static struct sr_node *sr_node_add(struct isis_area *area, int level,
479 const uint8_t *sysid)
480 {
481 struct sr_node *srn;
482
483 srn = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srn));
484 srn->level = level;
485 memcpy(srn->sysid, sysid, ISIS_SYS_ID_LEN);
486 srn->area = area;
487 srdb_node_prefix_init(&srn->prefix_sids);
488 srdb_node_add(&area->srdb.sr_nodes[level - 1], srn);
489
490 sr_debug(" |- Added new SR Node %s", sysid_print(srn->sysid));
491
492 return srn;
493 }
494
sr_node_del(struct isis_area * area,int level,struct sr_node * srn)495 static void sr_node_del(struct isis_area *area, int level, struct sr_node *srn)
496 /**
497 * Remove Segment Routing Node from the Segment Routing Data Base.
498 * All Prefix-SID attached to this Segment Routing Node are removed first.
499 *
500 * @param area IS-IS area
501 * @param level IS-IS level
502 * @param srn Segment Routing Node to be deleted
503 */
504 {
505
506 sr_debug(" |- Delete SR Node %s", sysid_print(srn->sysid));
507
508 /* Remove and uninstall Prefix-SIDs. */
509 while (srdb_node_prefix_count(&srn->prefix_sids) > 0) {
510 struct sr_prefix *srp;
511
512 srp = srdb_node_prefix_first(&srn->prefix_sids);
513 sr_prefix_del(area, srn, srp);
514 }
515
516 srdb_node_del(&area->srdb.sr_nodes[level - 1], srn);
517 XFREE(MTYPE_ISIS_SR_INFO, srn);
518 }
519
520 /**
521 * Find Segment Routing Node in the Segment Routing Data Base per system ID.
522 *
523 * @param area IS-IS area
524 * @param level IS-IS level
525 * @param sysid Node System ID to lookup
526 *
527 * @return Segment Routing Node structure if found, NULL otherwise
528 */
sr_node_find(struct isis_area * area,int level,const uint8_t * sysid)529 static struct sr_node *sr_node_find(struct isis_area *area, int level,
530 const uint8_t *sysid)
531 {
532 struct sr_node srn = {};
533
534 memcpy(srn.sysid, sysid, ISIS_SYS_ID_LEN);
535 return srdb_node_find(&area->srdb.sr_nodes[level - 1], &srn);
536 }
537
538 /**
539 * Update Segment Routing Node following an SRGB update. This function
540 * is called when a neighbor SR Node has updated its SRGB.
541 *
542 * @param area IS-IS area
543 * @param level IS-IS level
544 * @param sysid Segment Routing Node system ID
545 */
sr_node_srgb_update(struct isis_area * area,int level,uint8_t * sysid)546 static void sr_node_srgb_update(struct isis_area *area, int level,
547 uint8_t *sysid)
548 {
549 struct sr_prefix *srp;
550
551 sr_debug("ISIS-Sr (%s): Update neighbors SR Node with new SRGB",
552 area->area_tag);
553
554 frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) {
555 struct listnode *node;
556 struct isis_nexthop *nh;
557
558 if (srp->type == ISIS_SR_PREFIX_LOCAL)
559 continue;
560
561 if (srp->u.remote.rinfo == NULL)
562 continue;
563
564 for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
565 nh)) {
566 if (memcmp(nh->sysid, sysid, ISIS_SYS_ID_LEN) != 0)
567 continue;
568
569 /*
570 * The Prefix-SID input label hasn't changed. We could
571 * re-install all Prefix-SID with "Make Before Break"
572 * option. Zebra layer will update output label(s) by
573 * adding new entry before removing the old one(s).
574 */
575 sr_prefix_reinstall(srp, true);
576 break;
577 }
578 }
579 }
580
581 /* --- Segment Routing Nexthop information Management functions ------------- */
582
583 /**
584 * Update Segment Routing Nexthop.
585 *
586 * @param srnh Segment Routing next hop
587 * @param label Output MPLS label
588 */
isis_sr_nexthop_update(struct sr_nexthop_info * srnh,mpls_label_t label)589 void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, mpls_label_t label)
590 {
591 srnh->label = label;
592 if (srnh->uptime == 0)
593 srnh->uptime = time(NULL);
594 }
595
596 /**
597 * Reset Segment Routing Nexthop.
598 *
599 * @param srnh Segment Routing Nexthop
600 */
isis_sr_nexthop_reset(struct sr_nexthop_info * srnh)601 void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh)
602 {
603 srnh->label = MPLS_INVALID_LABEL;
604 srnh->uptime = 0;
605 }
606
607 /* --- Segment Routing Prefix-SID Management functions to configure LFIB ---- */
608
609 /**
610 * Lookup IS-IS route in the Shortest Path Tree.
611 *
612 * @param area IS-IS area
613 * @param tree_id Shortest Path Tree identifier
614 * @param srp Segment Routing Prefix to lookup
615 *
616 * @return Route Information for this prefix if found, NULL otherwise
617 */
sr_prefix_lookup_route(struct isis_area * area,enum spf_tree_id tree_id,struct sr_prefix * srp)618 static struct isis_route_info *sr_prefix_lookup_route(struct isis_area *area,
619 enum spf_tree_id tree_id,
620 struct sr_prefix *srp)
621 {
622 struct route_node *rn;
623 int level = srp->srn->level;
624
625 rn = route_node_lookup(area->spftree[tree_id][level - 1]->route_table,
626 &srp->prefix);
627 if (rn) {
628 route_unlock_node(rn);
629 if (rn->info)
630 return rn->info;
631 }
632
633 return NULL;
634 }
635
636 /**
637 * Compute input label for the given Prefix-SID.
638 *
639 * @param srp Segment Routing Prefix
640 *
641 * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow
642 */
sr_prefix_in_label(const struct sr_prefix * srp)643 static mpls_label_t sr_prefix_in_label(const struct sr_prefix *srp)
644 {
645 const struct sr_node *srn = srp->srn;
646 struct isis_area *area = srn->area;
647
648 /* Return SID value as MPLS label if it is an Absolute SID */
649 if (CHECK_FLAG(srp->sid.flags,
650 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL))
651 return srp->sid.value;
652
653 /* Check that SID index falls inside the SRGB */
654 if (srp->sid.value >= (area->srdb.config.srgb_upper_bound
655 - area->srdb.config.srgb_lower_bound + 1)) {
656 flog_warn(EC_ISIS_SID_OVERFLOW,
657 "%s: SID index %u falls outside local SRGB range",
658 __func__, srp->sid.value);
659 return MPLS_INVALID_LABEL;
660 }
661
662 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
663 return (area->srdb.config.srgb_lower_bound + srp->sid.value);
664 }
665
666 /**
667 * Compute output label for the given Prefix-SID.
668 *
669 * @param srp Segment Routing Prefix
670 * @param srn_nexthop Segment Routing nexthop node
671 * @param sysid System ID of the SR node which advertised the Prefix-SID
672 *
673 * @return MPLS label or MPLS_INVALID_LABEL in case of error
674 */
sr_prefix_out_label(const struct sr_prefix * srp,const struct sr_node * srn_nexthop,const uint8_t * sysid)675 static mpls_label_t sr_prefix_out_label(const struct sr_prefix *srp,
676 const struct sr_node *srn_nexthop,
677 const uint8_t *sysid)
678 {
679 const struct sr_node *srn = srp->srn;
680
681 /* Check if the nexthop SR Node is the last hop? */
682 if (memcmp(sysid, srn->sysid, ISIS_SYS_ID_LEN) == 0) {
683 /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */
684 if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP))
685 return MPLS_LABEL_IMPLICIT_NULL;
686
687 /* SR-Node requests Implicit NULL Label */
688 if (CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) {
689 if (srp->prefix.family == AF_INET)
690 return MPLS_LABEL_IPV4_EXPLICIT_NULL;
691 else
692 return MPLS_LABEL_IPV6_EXPLICIT_NULL;
693 }
694 /* Fallthrough */
695 }
696
697 /* Return SID value as MPLS label if it is an Absolute SID */
698 if (CHECK_FLAG(srp->sid.flags,
699 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) {
700 /*
701 * V/L SIDs have local significance, so only adjacent routers
702 * can use them (RFC8667 section #2.1.1.1)
703 */
704 if (srp->srn != srn_nexthop)
705 return MPLS_INVALID_LABEL;
706 return srp->sid.value;
707 }
708
709 /* Check that SID index falls inside the SRGB */
710 if (srp->sid.value >= srn_nexthop->cap.srgb.range_size) {
711 flog_warn(EC_ISIS_SID_OVERFLOW,
712 "%s: SID index %u falls outside remote SRGB range",
713 __func__, srp->sid.value);
714 return MPLS_INVALID_LABEL;
715 }
716
717 /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */
718 return (srn_nexthop->cap.srgb.lower_bound + srp->sid.value);
719 }
720
721 /**
722 * Process local Prefix-SID and install it if possible. Input label is
723 * computed before installing it in LFIB.
724 *
725 * @param srp Segment Routing Prefix
726 *
727 * @return 0 on success, -1 otherwise
728 */
sr_prefix_install_local(struct sr_prefix * srp)729 static int sr_prefix_install_local(struct sr_prefix *srp)
730 {
731 mpls_label_t input_label;
732 const struct sr_node *srn = srp->srn;
733
734 /*
735 * No need to install Label for local Prefix-SID unless the
736 * no-PHP option is configured.
737 */
738 if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP)
739 || CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL))
740 return -1;
741
742 sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s) with nexthop self",
743 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
744 srp->sid.value, circuit_t2string(srn->level));
745
746 /* Compute input label and check that is valid. */
747 input_label = sr_prefix_in_label(srp);
748 if (input_label == MPLS_INVALID_LABEL)
749 return -1;
750
751 /* Update internal state. */
752 srp->input_label = input_label;
753 isis_sr_nexthop_update(&srp->u.local.info, MPLS_LABEL_IMPLICIT_NULL);
754
755 /* Install Prefix-SID in the forwarding plane. */
756 isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp);
757
758 return 0;
759 }
760
761 /**
762 * Process remote Prefix-SID and install it if possible. Input and Output
763 * labels are computed before installing them in LFIB.
764 *
765 * @param srp Segment Routing Prefix
766 *
767 * @return 0 on success, -1 otherwise
768 */
sr_prefix_install_remote(struct sr_prefix * srp)769 static int sr_prefix_install_remote(struct sr_prefix *srp)
770 {
771 const struct sr_node *srn = srp->srn;
772 struct isis_area *area = srn->area;
773 enum spf_tree_id tree_id;
774 struct listnode *node;
775 struct isis_nexthop *nexthop;
776 mpls_label_t input_label;
777 size_t nexthop_num = 0;
778
779 /* Lookup to associated IS-IS route. */
780 tree_id = (srp->prefix.family == AF_INET) ? SPFTREE_IPV4 : SPFTREE_IPV6;
781 srp->u.remote.rinfo = sr_prefix_lookup_route(area, tree_id, srp);
782 if (!srp->u.remote.rinfo)
783 /* SPF hasn't converged for this route yet. */
784 return -1;
785
786 /* Compute input label and check that is valid. */
787 input_label = sr_prefix_in_label(srp);
788 if (input_label == MPLS_INVALID_LABEL)
789 return -1;
790
791 sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s)", &srp->prefix,
792 IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
793 srp->sid.value, circuit_t2string(srn->level));
794
795 /* Process all SPF nexthops */
796 for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
797 nexthop)) {
798 struct sr_node *srn_nexthop;
799 mpls_label_t output_label;
800
801 /* Check if the nexthop advertised a SRGB. */
802 srn_nexthop = sr_node_find(area, srn->level, nexthop->sysid);
803 if (!srn_nexthop)
804 goto next;
805
806 /*
807 * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or
808 * IPv6 packets.
809 */
810 if ((nexthop->family == AF_INET
811 && !IS_SR_IPV4(srn_nexthop->cap.srgb))
812 || (nexthop->family == AF_INET6
813 && !IS_SR_IPV6(srn_nexthop->cap.srgb)))
814 goto next;
815
816 /* Compute output label and check if it is valid */
817 output_label =
818 sr_prefix_out_label(srp, srn_nexthop, nexthop->sysid);
819 if (output_label == MPLS_INVALID_LABEL)
820 goto next;
821
822 if (IS_DEBUG_SR) {
823 static char buf[INET6_ADDRSTRLEN];
824
825 inet_ntop(nexthop->family, &nexthop->ip, buf,
826 sizeof(buf));
827 zlog_debug(" |- nexthop %s label %u", buf,
828 output_label);
829 }
830
831 isis_sr_nexthop_update(&nexthop->sr, output_label);
832 nexthop_num++;
833 continue;
834 next:
835 isis_sr_nexthop_reset(&nexthop->sr);
836 }
837
838 /* Check that we found at least one valid nexthop */
839 if (nexthop_num == 0) {
840 sr_debug(" |- no valid nexthops");
841 return -1;
842 }
843
844 /* Update internal state. */
845 srp->input_label = input_label;
846
847 /* Install Prefix-SID in the forwarding plane. */
848 isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp);
849
850 return 0;
851 }
852
853 /**
854 * Process local or remote Prefix-SID and install it if possible.
855 *
856 * @param srp Segment Routing Prefix
857 */
sr_prefix_install(struct sr_prefix * srp)858 static void sr_prefix_install(struct sr_prefix *srp)
859 {
860 const struct sr_node *srn = srp->srn;
861 struct isis_area *area = srn->area;
862 int ret;
863
864 sr_debug("ISIS-Sr (%s): Install Prefix-SID %pFX %s %u", area->area_tag,
865 &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
866 srp->sid.value);
867
868 /* L1 routes are preferred over the L2 ones. */
869 if (area->is_type == IS_LEVEL_1_AND_2) {
870 struct sr_prefix *srp_l1, *srp_l2;
871
872 switch (srn->level) {
873 case ISIS_LEVEL1:
874 srp_l2 = sr_prefix_find_by_area(area, ISIS_LEVEL2,
875 &srp->prefix);
876 if (srp_l2)
877 sr_prefix_uninstall(srp_l2);
878 break;
879 case ISIS_LEVEL2:
880 srp_l1 = sr_prefix_find_by_area(area, ISIS_LEVEL1,
881 &srp->prefix);
882 if (srp_l1)
883 return;
884 break;
885 default:
886 break;
887 }
888 }
889
890 /* Install corresponding LFIB entry */
891 if (srp->type == ISIS_SR_PREFIX_LOCAL)
892 ret = sr_prefix_install_local(srp);
893 else
894 ret = sr_prefix_install_remote(srp);
895 if (ret != 0)
896 sr_prefix_uninstall(srp);
897 }
898
899 /**
900 * Uninstall local or remote Prefix-SID.
901 *
902 * @param srp Segment Routing Prefix
903 */
sr_prefix_uninstall(struct sr_prefix * srp)904 static void sr_prefix_uninstall(struct sr_prefix *srp)
905 {
906 struct listnode *node;
907 struct isis_nexthop *nexthop;
908
909 /* Check that Input Label is valid */
910 if (srp->input_label == MPLS_INVALID_LABEL)
911 return;
912
913 sr_debug("ISIS-Sr: Un-install Prefix-SID %pFX %s %u", &srp->prefix,
914 IS_SID_VALUE(srp->sid.flags) ? "label" : "index",
915 srp->sid.value);
916
917 /* Uninstall Prefix-SID from the forwarding plane. */
918 isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_DELETE, srp);
919
920 /* Reset internal state. */
921 srp->input_label = MPLS_INVALID_LABEL;
922 switch (srp->type) {
923 case ISIS_SR_PREFIX_LOCAL:
924 isis_sr_nexthop_reset(&srp->u.local.info);
925 break;
926 case ISIS_SR_PREFIX_REMOTE:
927 if (srp->u.remote.rinfo) {
928 for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops,
929 node, nexthop))
930 isis_sr_nexthop_reset(&nexthop->sr);
931 }
932 break;
933 }
934 }
935
936 /**
937 * Reinstall local or remote Prefix-SID.
938 *
939 * @param srp Segment Routing Prefix
940 */
sr_prefix_reinstall(struct sr_prefix * srp,bool make_before_break)941 static inline void sr_prefix_reinstall(struct sr_prefix *srp,
942 bool make_before_break)
943 {
944 /*
945 * Make Before Break can be used only when we know for sure that
946 * the Prefix-SID input label hasn't changed. Otherwise we need to
947 * uninstall the Prefix-SID first using the old input label before
948 * reinstalling it.
949 */
950 if (!make_before_break)
951 sr_prefix_uninstall(srp);
952
953 /* New input label is computed in sr_prefix_install() function */
954 sr_prefix_install(srp);
955 }
956
957 /* --- IS-IS LSP Parse functions -------------------------------------------- */
958
959 /**
960 * Compare Router Capabilities. Only Flags, SRGB and Algorithm are used for the
961 * comparison. MSD and SRLB modification must not trigger and SR-Prefix update.
962 *
963 * @param r1 First Router Capabilities to compare
964 * @param r2 Second Router Capabilities to compare
965 * @return 0 if r1 and r2 are equal or -1 otherwise
966 */
router_cap_cmp(const struct isis_router_cap * r1,const struct isis_router_cap * r2)967 static int router_cap_cmp(const struct isis_router_cap *r1,
968 const struct isis_router_cap *r2)
969 {
970 if (r1->flags == r2->flags
971 && r1->srgb.lower_bound == r2->srgb.lower_bound
972 && r1->srgb.range_size == r2->srgb.range_size
973 && r1->algo[0] == r2->algo[0])
974 return 0;
975 else
976 return -1;
977 }
978
979 /**
980 * Parse all SR-related information from the given Router Capabilities TLV.
981 *
982 * @param area IS-IS area
983 * @param level IS-IS level
984 * @param sysid System ID of the LSP
985 * @param router_cap Router Capability subTLVs
986 *
987 * @return Segment Routing Node structure for this System ID
988 */
989 static struct sr_node *
parse_router_cap_tlv(struct isis_area * area,int level,const uint8_t * sysid,const struct isis_router_cap * router_cap)990 parse_router_cap_tlv(struct isis_area *area, int level, const uint8_t *sysid,
991 const struct isis_router_cap *router_cap)
992 {
993 struct sr_node *srn;
994
995 if (!router_cap || router_cap->srgb.range_size == 0)
996 return NULL;
997
998 sr_debug("ISIS-Sr (%s): Parse Router Capability TLV", area->area_tag);
999
1000 srn = sr_node_find(area, level, sysid);
1001 if (srn) {
1002 if (router_cap_cmp(&srn->cap, router_cap) != 0) {
1003 srn->state = SRDB_STATE_MODIFIED;
1004 } else
1005 srn->state = SRDB_STATE_UNCHANGED;
1006 sr_debug(" |- Found %s SR Node %s",
1007 srn->state == SRDB_STATE_MODIFIED ? "Modified"
1008 : "Unchanged",
1009 sysid_print(srn->sysid));
1010 } else {
1011 srn = sr_node_add(area, level, sysid);
1012 srn->state = SRDB_STATE_NEW;
1013 }
1014
1015 /*
1016 * Update Router Capabilities in any case as SRLB or MSD
1017 * modification are not take into account for comparison.
1018 */
1019 srn->cap = *router_cap;
1020
1021 return srn;
1022 }
1023
1024 /**
1025 * Parse list of Prefix-SID Sub-TLVs.
1026 *
1027 * @param srn Segment Routing Node
1028 * @param prefix Prefix to be parsed
1029 * @param local True if prefix comes from own LSP, false otherwise
1030 * @param prefix_sids Prefix SID subTLVs
1031 */
parse_prefix_sid_subtlvs(struct sr_node * srn,union prefixconstptr prefix,bool local,struct isis_item_list * prefix_sids)1032 static void parse_prefix_sid_subtlvs(struct sr_node *srn,
1033 union prefixconstptr prefix, bool local,
1034 struct isis_item_list *prefix_sids)
1035 {
1036 struct isis_area *area = srn->area;
1037 struct isis_item *i;
1038
1039 sr_debug("ISIS-Sr (%s): Parse Prefix SID TLV", area->area_tag);
1040
1041 /* Parse list of Prefix SID subTLVs */
1042 for (i = prefix_sids->head; i; i = i->next) {
1043 struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i;
1044 struct sr_prefix *srp;
1045
1046 /* Only SPF algorithm is supported right now */
1047 if (psid->algorithm != SR_ALGORITHM_SPF)
1048 continue;
1049
1050 /* Compute corresponding Segment Routing Prefix */
1051 srp = sr_prefix_find_by_node(srn, prefix);
1052 if (srp) {
1053 if (srp->sid.flags != psid->flags
1054 || srp->sid.algorithm != psid->algorithm
1055 || srp->sid.value != psid->value) {
1056 srp->sid = *psid;
1057 srp->state = SRDB_STATE_MODIFIED;
1058 } else if (srp->state == SRDB_STATE_VALIDATED)
1059 srp->state = SRDB_STATE_UNCHANGED;
1060 sr_debug(" |- Found %s Prefix-SID %pFX",
1061 srp->state == SRDB_STATE_MODIFIED
1062 ? "Modified"
1063 : "Unchanged",
1064 &srp->prefix);
1065
1066 } else {
1067 srp = sr_prefix_add(area, srn, prefix, local, psid);
1068 srp->state = SRDB_STATE_NEW;
1069 }
1070 /*
1071 * Stop the Prefix-SID iteration since we only support the SPF
1072 * algorithm for now.
1073 */
1074 break;
1075 }
1076 }
1077
1078 /**
1079 * Parse all SR-related information from the given LSP.
1080 *
1081 * @param area IS-IS area
1082 * @param level IS-IS level
1083 * @param srn Segment Routing Node
1084 * @param lsp IS-IS LSP
1085 */
parse_lsp(struct isis_area * area,int level,struct sr_node ** srn,struct isis_lsp * lsp)1086 static void parse_lsp(struct isis_area *area, int level, struct sr_node **srn,
1087 struct isis_lsp *lsp)
1088 {
1089 struct isis_item_list *items;
1090 struct isis_item *i;
1091 bool local = lsp->own_lsp;
1092
1093 /* Check LSP sequence number */
1094 if (lsp->hdr.seqno == 0) {
1095 zlog_warn("%s: lsp with 0 seq_num - ignore", __func__);
1096 return;
1097 }
1098
1099 sr_debug("ISIS-Sr (%s): Parse LSP from node %s", area->area_tag,
1100 sysid_print(lsp->hdr.lsp_id));
1101
1102 /* Parse the Router Capability TLV. */
1103 if (*srn == NULL) {
1104 *srn = parse_router_cap_tlv(area, level, lsp->hdr.lsp_id,
1105 lsp->tlvs->router_cap);
1106 if (!*srn)
1107 return;
1108 }
1109
1110 /* Parse the Extended IP Reachability TLV. */
1111 items = &lsp->tlvs->extended_ip_reach;
1112 for (i = items->head; i; i = i->next) {
1113 struct isis_extended_ip_reach *ir;
1114
1115 ir = (struct isis_extended_ip_reach *)i;
1116 if (!ir->subtlvs)
1117 continue;
1118
1119 parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
1120 &ir->subtlvs->prefix_sids);
1121 }
1122
1123 /* Parse Multi Topology Reachable IPv6 Prefixes TLV. */
1124 items = isis_lookup_mt_items(&lsp->tlvs->mt_ipv6_reach,
1125 ISIS_MT_IPV6_UNICAST);
1126 for (i = items ? items->head : NULL; i; i = i->next) {
1127 struct isis_ipv6_reach *ir;
1128
1129 ir = (struct isis_ipv6_reach *)i;
1130 if (!ir->subtlvs)
1131 continue;
1132
1133 parse_prefix_sid_subtlvs(*srn, &ir->prefix, local,
1134 &ir->subtlvs->prefix_sids);
1135 }
1136 }
1137
1138 /**
1139 * Parse all SR-related information from the entire LSPDB.
1140 *
1141 * @param area IS-IS area
1142 */
parse_lspdb(struct isis_area * area)1143 static void parse_lspdb(struct isis_area *area)
1144 {
1145 struct isis_lsp *lsp;
1146
1147 sr_debug("ISIS-Sr (%s): Parse LSP Data Base", area->area_tag);
1148
1149 /* Process all LSP from Level 1 & 2 */
1150 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
1151 frr_each (lspdb, &area->lspdb[level - 1], lsp) {
1152 struct isis_lsp *frag;
1153 struct listnode *node;
1154 struct sr_node *srn = NULL;
1155
1156 /* Skip Pseudo ID LSP and LSP without TLVs */
1157 if (LSP_PSEUDO_ID(lsp->hdr.lsp_id))
1158 continue;
1159 if (!lsp->tlvs)
1160 continue;
1161
1162 /* Parse LSP, then fragment */
1163 parse_lsp(area, level, &srn, lsp);
1164 for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag))
1165 parse_lsp(area, level, &srn, frag);
1166 }
1167 }
1168 }
1169
1170 /**
1171 * Process any new/deleted/modified Prefix-SID in the LSPDB.
1172 *
1173 * @param srn Segment Routing Node
1174 * @param srp Segment Routing Prefix
1175 */
process_prefix_changes(struct sr_node * srn,struct sr_prefix * srp)1176 static void process_prefix_changes(struct sr_node *srn, struct sr_prefix *srp)
1177 {
1178 struct isis_area *area = srn->area;
1179
1180 /* Install/reinstall/uninstall Prefix-SID if necessary. */
1181 switch (srp->state) {
1182 case SRDB_STATE_NEW:
1183 sr_debug("ISIS-Sr (%s): Created Prefix-SID %pFX for SR node %s",
1184 area->area_tag, &srp->prefix, sysid_print(srn->sysid));
1185 sr_prefix_install(srp);
1186 break;
1187 case SRDB_STATE_MODIFIED:
1188 sr_debug(
1189 "ISIS-Sr (%s): Modified Prefix-SID %pFX for SR node %s",
1190 area->area_tag, &srp->prefix, sysid_print(srn->sysid));
1191 sr_prefix_reinstall(srp, false);
1192 break;
1193 case SRDB_STATE_UNCHANGED:
1194 break;
1195 default:
1196 sr_debug("ISIS-Sr (%s): Removed Prefix-SID %pFX for SR node %s",
1197 area->area_tag, &srp->prefix, sysid_print(srn->sysid));
1198 sr_prefix_del(area, srn, srp);
1199 return;
1200 }
1201
1202 /* Validate SRDB State for next LSPDB parsing */
1203 srp->state = SRDB_STATE_VALIDATED;
1204 }
1205
1206 /**
1207 * Process any new/deleted/modified SRGB in the LSPDB.
1208 *
1209 * @param area IS-IS area
1210 * @param level IS-IS level
1211 * @param srn Segment Routing Node
1212 */
process_node_changes(struct isis_area * area,int level,struct sr_node * srn)1213 static void process_node_changes(struct isis_area *area, int level,
1214 struct sr_node *srn)
1215 {
1216 struct sr_prefix *srp;
1217 uint8_t sysid[ISIS_SYS_ID_LEN];
1218 bool adjacent;
1219
1220 memcpy(sysid, srn->sysid, sizeof(sysid));
1221
1222 /*
1223 * If an neighbor router's SRGB was changed or created, then reinstall
1224 * all Prefix-SIDs from all nodes that use this neighbor as nexthop.
1225 */
1226 adjacent = !!isis_adj_find(area, level, sysid);
1227 switch (srn->state) {
1228 case SRDB_STATE_NEW:
1229 case SRDB_STATE_MODIFIED:
1230 sr_debug("ISIS-Sr (%s): Create/Update SR node %s",
1231 area->area_tag, sysid_print(srn->sysid));
1232 if (adjacent)
1233 sr_node_srgb_update(area, level, sysid);
1234 break;
1235 case SRDB_STATE_UNCHANGED:
1236 break;
1237 default:
1238 /* SR capabilities have been removed. Delete SR-Node */
1239 sr_debug("ISIS-Sr (%s): Remove SR node %s", area->area_tag,
1240 sysid_print(srn->sysid));
1241
1242 sr_node_del(area, level, srn);
1243 /* and Update remaining Prefix-SID from all remaining SR Node */
1244 if (adjacent)
1245 sr_node_srgb_update(area, level, sysid);
1246 return;
1247 }
1248
1249 /* Validate SRDB State for next LSPDB parsing */
1250 srn->state = SRDB_STATE_VALIDATED;
1251
1252 /* Finally, process all Prefix-SID of this SR Node */
1253 frr_each_safe (srdb_node_prefix, &srn->prefix_sids, srp)
1254 process_prefix_changes(srn, srp);
1255 }
1256
1257 /**
1258 * Parse and process all SR-related Sub-TLVs after running the SPF algorithm.
1259 *
1260 * @param area IS-IS area
1261 */
isis_area_verify_sr(struct isis_area * area)1262 void isis_area_verify_sr(struct isis_area *area)
1263 {
1264 struct sr_node *srn;
1265
1266 if (!area->srdb.enabled)
1267 return;
1268
1269 /* Parse LSPDB to detect new/deleted/modified SR (sub-)TLVs. */
1270 parse_lspdb(area);
1271
1272 /* Process possible SR-related changes in the LDPSB. */
1273 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
1274 frr_each_safe (srdb_node, &area->srdb.sr_nodes[level - 1], srn)
1275 process_node_changes(area, level, srn);
1276 }
1277 }
1278
1279 /**
1280 * Once a route is updated in the SPT, reinstall or uninstall its corresponding
1281 * Prefix-SID (if any).
1282 *
1283 * @param area IS-IS area
1284 * @param prefix Prefix to be updated
1285 * @param route_info New Route Information
1286 *
1287 * @return 0
1288 */
sr_route_update(struct isis_area * area,struct prefix * prefix,struct isis_route_info * route_info)1289 static int sr_route_update(struct isis_area *area, struct prefix *prefix,
1290 struct isis_route_info *route_info)
1291 {
1292 struct sr_prefix *srp;
1293
1294 if (!area->srdb.enabled)
1295 return 0;
1296
1297 sr_debug("ISIS-Sr (%s): Update route for prefix %pFX", area->area_tag,
1298 prefix);
1299
1300 /* Lookup to Segment Routing Prefix for this prefix */
1301 switch (area->is_type) {
1302 case IS_LEVEL_1:
1303 srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix);
1304 break;
1305 case IS_LEVEL_2:
1306 srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix);
1307 break;
1308 case IS_LEVEL_1_AND_2:
1309 srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix);
1310 if (!srp)
1311 srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix);
1312 break;
1313 default:
1314 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown area level",
1315 __func__);
1316 exit(1);
1317 }
1318
1319 /* Skip NULL or local Segment Routing Prefix */
1320 if (!srp || srp->type == ISIS_SR_PREFIX_LOCAL)
1321 return 0;
1322
1323 /* Install or unintall Prefix-SID if route is Active or not */
1324 if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) {
1325 /*
1326 * The Prefix-SID input label hasn't changed. We could use the
1327 * "Make Before Break" option. Zebra layer will update output
1328 * label by adding new label(s) before removing old one(s).
1329 */
1330 sr_prefix_reinstall(srp, true);
1331 srp->u.remote.rinfo = route_info;
1332 } else {
1333 sr_prefix_uninstall(srp);
1334 srp->u.remote.rinfo = NULL;
1335 }
1336
1337 return 0;
1338 }
1339
1340 /* --- Segment Routing Local Block management functions --------------------- */
1341
1342 /**
1343 * Initialize Segment Routing Local Block from SRDB configuration and reserve
1344 * block of bits to manage label allocation.
1345 *
1346 * @param area IS-IS area
1347 */
sr_local_block_init(struct isis_area * area)1348 static int sr_local_block_init(struct isis_area *area)
1349 {
1350 struct isis_sr_db *srdb = &area->srdb;
1351 struct sr_local_block *srlb = &srdb->srlb;
1352
1353 /* Check if SRLB is not already configured */
1354 if (srlb->active)
1355 return 0;
1356
1357 /*
1358 * Request SRLB to the label manager. If the allocation fails, return
1359 * an error to disable SR until a new SRLB is successfully allocated.
1360 */
1361 if (isis_zebra_request_label_range(
1362 srdb->config.srlb_lower_bound,
1363 srdb->config.srlb_upper_bound
1364 - srdb->config.srlb_lower_bound + 1)) {
1365 srlb->active = false;
1366 return -1;
1367 }
1368
1369 sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area->area_tag,
1370 srdb->config.srlb_lower_bound, srdb->config.srlb_upper_bound);
1371
1372 /* Initialize the SRLB */
1373 srlb->start = srdb->config.srlb_lower_bound;
1374 srlb->end = srdb->config.srlb_upper_bound;
1375 srlb->current = 0;
1376 /* Compute the needed Used Mark number and allocate them */
1377 srlb->max_block = (srlb->end - srlb->start + 1) / SRLB_BLOCK_SIZE;
1378 if (((srlb->end - srlb->start + 1) % SRLB_BLOCK_SIZE) != 0)
1379 srlb->max_block++;
1380 srlb->used_mark = XCALLOC(MTYPE_ISIS_SR_INFO,
1381 srlb->max_block * SRLB_BLOCK_SIZE);
1382 srlb->active = true;
1383
1384 return 0;
1385 }
1386
1387 /**
1388 * Remove Segment Routing Local Block.
1389 *
1390 * @param area IS-IS area
1391 */
sr_local_block_delete(struct isis_area * area)1392 static void sr_local_block_delete(struct isis_area *area)
1393 {
1394 struct isis_sr_db *srdb = &area->srdb;
1395 struct sr_local_block *srlb = &srdb->srlb;
1396
1397 /* Check if SRLB is not already delete */
1398 if (!srlb->active)
1399 return;
1400
1401 sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area->area_tag,
1402 srlb->start, srlb->end);
1403
1404 /* First release the label block */
1405 isis_zebra_release_label_range(srdb->config.srlb_lower_bound,
1406 srdb->config.srlb_upper_bound);
1407
1408 /* Then reset SRLB structure */
1409 if (srlb->used_mark != NULL)
1410 XFREE(MTYPE_ISIS_SR_INFO, srlb->used_mark);
1411 srlb->active = false;
1412 }
1413
1414 /**
1415 * Request a label from the Segment Routing Local Block.
1416 *
1417 * @param srlb Segment Routing Local Block
1418 *
1419 * @return First available label on success or MPLS_INVALID_LABEL if the
1420 * block of labels is full
1421 */
sr_local_block_request_label(struct sr_local_block * srlb)1422 static mpls_label_t sr_local_block_request_label(struct sr_local_block *srlb)
1423 {
1424
1425 mpls_label_t label;
1426 uint32_t index;
1427 uint32_t pos;
1428
1429 /* Check if we ran out of available labels */
1430 if (srlb->current >= srlb->end)
1431 return MPLS_INVALID_LABEL;
1432
1433 /* Get first available label and mark it used */
1434 label = srlb->current + srlb->start;
1435 index = srlb->current / SRLB_BLOCK_SIZE;
1436 pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE);
1437 srlb->used_mark[index] |= pos;
1438
1439 /* Jump to the next free position */
1440 srlb->current++;
1441 pos = srlb->current % SRLB_BLOCK_SIZE;
1442 while (srlb->current < srlb->end) {
1443 if (pos == 0)
1444 index++;
1445 if (!((1ULL << pos) & srlb->used_mark[index]))
1446 break;
1447 else {
1448 srlb->current++;
1449 pos = srlb->current % SRLB_BLOCK_SIZE;
1450 }
1451 }
1452
1453 return label;
1454 }
1455
1456 /**
1457 * Release label in the Segment Routing Local Block.
1458 *
1459 * @param srlb Segment Routing Local Block
1460 * @param label Label to be release
1461 *
1462 * @return 0 on success or -1 if label falls outside SRLB
1463 */
sr_local_block_release_label(struct sr_local_block * srlb,mpls_label_t label)1464 static int sr_local_block_release_label(struct sr_local_block *srlb,
1465 mpls_label_t label)
1466 {
1467 uint32_t index;
1468 uint32_t pos;
1469
1470 /* Check that label falls inside the SRLB */
1471 if ((label < srlb->start) || (label > srlb->end)) {
1472 flog_warn(EC_ISIS_SID_OVERFLOW,
1473 "%s: Returning label %u is outside SRLB [%u/%u]",
1474 __func__, label, srlb->start, srlb->end);
1475 return -1;
1476 }
1477
1478 index = (label - srlb->start) / SRLB_BLOCK_SIZE;
1479 pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE);
1480 srlb->used_mark[index] &= ~pos;
1481 /* Reset current to the first available position */
1482 for (index = 0; index < srlb->max_block; index++) {
1483 if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) {
1484 for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++)
1485 if (!((1ULL << pos) & srlb->used_mark[index])) {
1486 srlb->current =
1487 index * SRLB_BLOCK_SIZE + pos;
1488 break;
1489 }
1490 break;
1491 }
1492 }
1493
1494 return 0;
1495 }
1496
1497 /* --- Segment Routing Adjacency-SID management functions ------------------- */
1498
1499 /**
1500 * Add new local Adjacency-SID.
1501 *
1502 * @param adj IS-IS Adjacency
1503 * @param family Inet Family (IPv4 or IPv6)
1504 * @param backup True to initialize backup Adjacency SID
1505 */
sr_adj_sid_add_single(struct isis_adjacency * adj,int family,bool backup)1506 static void sr_adj_sid_add_single(struct isis_adjacency *adj, int family,
1507 bool backup)
1508 {
1509 struct isis_circuit *circuit = adj->circuit;
1510 struct isis_area *area = circuit->area;
1511 struct sr_adjacency *sra;
1512 struct isis_adj_sid *adj_sid;
1513 struct isis_lan_adj_sid *ladj_sid;
1514 union g_addr nexthop = {};
1515 uint8_t flags;
1516 mpls_label_t input_label;
1517
1518 sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag,
1519 backup ? "Backup" : "Primary");
1520
1521 /* Determine nexthop IP address */
1522 switch (family) {
1523 case AF_INET:
1524 if (!circuit->ip_router || !adj->ipv4_address_count)
1525 return;
1526
1527 nexthop.ipv4 = adj->ipv4_addresses[0];
1528 break;
1529 case AF_INET6:
1530 if (!circuit->ipv6_router || !adj->ipv6_address_count)
1531 return;
1532
1533 nexthop.ipv6 = adj->ipv6_addresses[0];
1534 break;
1535 default:
1536 flog_err(EC_LIB_DEVELOPMENT,
1537 "%s: unexpected address-family: %u", __func__, family);
1538 exit(1);
1539 }
1540
1541 /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */
1542 flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG;
1543 if (family == AF_INET6)
1544 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG);
1545 if (backup)
1546 SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG);
1547
1548 /* Get a label from the SRLB for this Adjacency */
1549 input_label = sr_local_block_request_label(&area->srdb.srlb);
1550 if (input_label == MPLS_INVALID_LABEL)
1551 return;
1552
1553 if (circuit->ext == NULL)
1554 circuit->ext = isis_alloc_ext_subtlvs();
1555
1556 sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra));
1557 sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL;
1558 sra->nexthop.family = family;
1559 sra->nexthop.address = nexthop;
1560 sra->nexthop.label = input_label;
1561 switch (circuit->circ_type) {
1562 /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */
1563 case CIRCUIT_T_BROADCAST:
1564 ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid));
1565 ladj_sid->family = family;
1566 ladj_sid->flags = flags;
1567 ladj_sid->weight = 0;
1568 memcpy(ladj_sid->neighbor_id, adj->sysid,
1569 sizeof(ladj_sid->neighbor_id));
1570 ladj_sid->sid = input_label;
1571 isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid);
1572 sra->u.ladj_sid = ladj_sid;
1573 break;
1574 /* Adjacency-SID for Point to Point interface section #2.2.1 */
1575 case CIRCUIT_T_P2P:
1576 adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid));
1577 adj_sid->family = family;
1578 adj_sid->flags = flags;
1579 adj_sid->weight = 0;
1580 adj_sid->sid = input_label;
1581 isis_tlvs_add_adj_sid(circuit->ext, adj_sid);
1582 sra->u.adj_sid = adj_sid;
1583 break;
1584 default:
1585 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
1586 __func__, circuit->circ_type);
1587 exit(1);
1588 }
1589
1590 /* Add Adjacency-SID in SRDB */
1591 sra->adj = adj;
1592 listnode_add(area->srdb.adj_sids, sra);
1593 listnode_add(adj->adj_sids, sra);
1594
1595 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
1596 }
1597
1598 /**
1599 * Add Primary and Backup local Adjacency SID.
1600 *
1601 * @param adj IS-IS Adjacency
1602 * @param family Inet Family (IPv4 or IPv6)
1603 */
sr_adj_sid_add(struct isis_adjacency * adj,int family)1604 static void sr_adj_sid_add(struct isis_adjacency *adj, int family)
1605 {
1606 sr_adj_sid_add_single(adj, family, false);
1607 sr_adj_sid_add_single(adj, family, true);
1608 }
1609
sr_adj_sid_update(struct sr_adjacency * sra,struct sr_local_block * srlb)1610 static void sr_adj_sid_update(struct sr_adjacency *sra,
1611 struct sr_local_block *srlb)
1612 {
1613 struct isis_circuit *circuit = sra->adj->circuit;
1614
1615 /* First remove the old MPLS Label */
1616 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
1617
1618 /* Got new label in the new SRLB */
1619 sra->nexthop.label = sr_local_block_request_label(srlb);
1620 if (sra->nexthop.label == MPLS_INVALID_LABEL)
1621 return;
1622
1623 switch (circuit->circ_type) {
1624 case CIRCUIT_T_BROADCAST:
1625 sra->u.ladj_sid->sid = sra->nexthop.label;
1626 break;
1627 case CIRCUIT_T_P2P:
1628 sra->u.adj_sid->sid = sra->nexthop.label;
1629 break;
1630 default:
1631 flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
1632 __func__, circuit->circ_type);
1633 break;
1634 }
1635
1636 /* Finally configure the new MPLS Label */
1637 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra);
1638 }
1639
1640 /**
1641 * Delete local Adj-SID.
1642 *
1643 * @param sra Segment Routing Adjacency
1644 */
sr_adj_sid_del(struct sr_adjacency * sra)1645 static void sr_adj_sid_del(struct sr_adjacency *sra)
1646 {
1647 struct isis_circuit *circuit = sra->adj->circuit;
1648 struct isis_area *area = circuit->area;
1649
1650 sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag);
1651
1652 isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra);
1653
1654 /* Release dynamic label and remove subTLVs */
1655 switch (circuit->circ_type) {
1656 case CIRCUIT_T_BROADCAST:
1657 sr_local_block_release_label(&area->srdb.srlb,
1658 sra->u.ladj_sid->sid);
1659 isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid);
1660 break;
1661 case CIRCUIT_T_P2P:
1662 sr_local_block_release_label(&area->srdb.srlb,
1663 sra->u.adj_sid->sid);
1664 isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid);
1665 break;
1666 default:
1667 flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u",
1668 __func__, circuit->circ_type);
1669 exit(1);
1670 }
1671
1672 /* Remove Adjacency-SID from the SRDB */
1673 listnode_delete(area->srdb.adj_sids, sra);
1674 listnode_delete(sra->adj->adj_sids, sra);
1675 XFREE(MTYPE_ISIS_SR_INFO, sra);
1676 }
1677
1678 /**
1679 * Remove all Adjacency-SIDs associated to an adjacency that is going down.
1680 *
1681 * @param adj IS-IS Adjacency
1682 *
1683 * @return 0
1684 */
sr_adj_state_change(struct isis_adjacency * adj)1685 static int sr_adj_state_change(struct isis_adjacency *adj)
1686 {
1687 struct sr_adjacency *sra;
1688 struct listnode *node, *nnode;
1689
1690 if (!adj->circuit->area->srdb.enabled)
1691 return 0;
1692
1693 if (adj->adj_state == ISIS_ADJ_UP)
1694 return 0;
1695
1696 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
1697 sr_adj_sid_del(sra);
1698
1699 return 0;
1700 }
1701
1702 /**
1703 * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or
1704 * IPv6 address to corresponding Adjacency-SID accordingly.
1705 *
1706 * @param adj IS-IS Adjacency
1707 * @param family Inet Family (IPv4 or IPv6)
1708 *
1709 * @return 0
1710 */
sr_adj_ip_enabled(struct isis_adjacency * adj,int family)1711 static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family)
1712 {
1713 if (!adj->circuit->area->srdb.enabled)
1714 return 0;
1715
1716 sr_adj_sid_add(adj, family);
1717
1718 return 0;
1719 }
1720
1721 /**
1722 * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore,
1723 * delete the corresponding Adjacency-SID(s) accordingly.
1724 *
1725 * @param adj IS-IS Adjacency
1726 * @param family Inet Family (IPv4 or IPv6)
1727 *
1728 * @return 0
1729 */
sr_adj_ip_disabled(struct isis_adjacency * adj,int family)1730 static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family)
1731 {
1732 struct sr_adjacency *sra;
1733 struct listnode *node, *nnode;
1734
1735 if (!adj->circuit->area->srdb.enabled)
1736 return 0;
1737
1738 for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra))
1739 if (sra->nexthop.family == family)
1740 sr_adj_sid_del(sra);
1741
1742 return 0;
1743 }
1744
1745 /**
1746 * Activate local Prefix-SID when loopback interface goes up for IS-IS.
1747 *
1748 * @param ifp Loopback Interface
1749 *
1750 * @return 0
1751 */
sr_if_new_hook(struct interface * ifp)1752 static int sr_if_new_hook(struct interface *ifp)
1753 {
1754 struct isis_circuit *circuit;
1755 struct isis_area *area;
1756 struct connected *connected;
1757 struct listnode *node;
1758
1759 /* Get corresponding circuit */
1760 circuit = circuit_scan_by_ifp(ifp);
1761 if (!circuit)
1762 return 0;
1763
1764 area = circuit->area;
1765 if (!area)
1766 return 0;
1767
1768 /*
1769 * Update the Node-SID flag of the configured Prefix-SID mappings if
1770 * necessary. This needs to be done here since isisd reads the startup
1771 * configuration before receiving interface information from zebra.
1772 */
1773 FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
1774 struct sr_prefix_cfg *pcfg;
1775
1776 pcfg = isis_sr_cfg_prefix_find(area, connected->address);
1777 if (!pcfg)
1778 continue;
1779
1780 if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)
1781 && !pcfg->node_sid) {
1782 pcfg->node_sid = true;
1783 lsp_regenerate_schedule(area, area->is_type, 0);
1784 }
1785 }
1786
1787 return 0;
1788 }
1789
1790 /* --- Segment Routing Show information functions --------------------------- */
1791
1792 /**
1793 * Show LFIB operation in human readable format.
1794 *
1795 * @param buf Buffer to store string output. Must be pre-allocate
1796 * @param size Size of the buffer
1797 * @param label_in Input Label
1798 * @param label_out Output Label
1799 *
1800 * @return String containing LFIB operation in human readable format
1801 */
sr_op2str(char * buf,size_t size,mpls_label_t label_in,mpls_label_t label_out)1802 static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in,
1803 mpls_label_t label_out)
1804 {
1805 if (size < 24)
1806 return NULL;
1807
1808 if (label_in == MPLS_INVALID_LABEL) {
1809 snprintf(buf, size, "no-op.");
1810 return buf;
1811 }
1812
1813 switch (label_out) {
1814 case MPLS_LABEL_IMPLICIT_NULL:
1815 snprintf(buf, size, "Pop(%u)", label_in);
1816 break;
1817 case MPLS_LABEL_IPV4_EXPLICIT_NULL:
1818 case MPLS_LABEL_IPV6_EXPLICIT_NULL:
1819 snprintf(buf, size, "Swap(%u, null)", label_in);
1820 break;
1821 case MPLS_INVALID_LABEL:
1822 snprintf(buf, size, "no-op.");
1823 break;
1824 default:
1825 snprintf(buf, size, "Swap(%u, %u)", label_in, label_out);
1826 break;
1827 }
1828 return buf;
1829 }
1830
1831 /**
1832 * Show Local Prefix-SID.
1833 *
1834 * @param vty VTY output
1835 * @param tt Table format
1836 * @param area IS-IS area
1837 * @param srp Segment Routing Prefix
1838 */
show_prefix_sid_local(struct vty * vty,struct ttable * tt,const struct isis_area * area,const struct sr_prefix * srp)1839 static void show_prefix_sid_local(struct vty *vty, struct ttable *tt,
1840 const struct isis_area *area,
1841 const struct sr_prefix *srp)
1842 {
1843 const struct sr_nexthop_info *srnh = &srp->u.local.info;
1844 char buf_prefix[BUFSIZ];
1845 char buf_oper[BUFSIZ];
1846 char buf_iface[BUFSIZ];
1847 char buf_uptime[BUFSIZ];
1848
1849 if (srnh->label != MPLS_INVALID_LABEL) {
1850 struct interface *ifp;
1851 ifp = if_lookup_prefix(&srp->prefix, VRF_DEFAULT);
1852 if (ifp)
1853 strlcpy(buf_iface, ifp->name, sizeof(buf_iface));
1854 else
1855 snprintf(buf_iface, sizeof(buf_iface), "-");
1856 log_uptime(srnh->uptime, buf_uptime, sizeof(buf_uptime));
1857 } else {
1858 snprintf(buf_iface, sizeof(buf_iface), "-");
1859 snprintf(buf_uptime, sizeof(buf_uptime), "-");
1860 }
1861 sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label,
1862 MPLS_LABEL_IMPLICIT_NULL);
1863
1864 ttable_add_row(tt, "%s|%u|%s|-|%s|%s",
1865 prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)),
1866 srp->sid.value, buf_oper, buf_iface, buf_uptime);
1867 }
1868
1869 /**
1870 * Show Remote Prefix-SID.
1871 *
1872 * @param vty VTY output
1873 * @param tt Table format
1874 * @param area IS-IS area
1875 * @param srp Segment Routing Prefix
1876 */
show_prefix_sid_remote(struct vty * vty,struct ttable * tt,const struct isis_area * area,const struct sr_prefix * srp)1877 static void show_prefix_sid_remote(struct vty *vty, struct ttable *tt,
1878 const struct isis_area *area,
1879 const struct sr_prefix *srp)
1880 {
1881 struct isis_nexthop *nexthop;
1882 struct listnode *node;
1883 char buf_prefix[BUFSIZ];
1884 char buf_oper[BUFSIZ];
1885 char buf_nhop[BUFSIZ];
1886 char buf_iface[BUFSIZ];
1887 char buf_uptime[BUFSIZ];
1888 bool first = true;
1889
1890 (void)prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix));
1891
1892 if (!srp->u.remote.rinfo) {
1893 ttable_add_row(tt, "%s|%u|%s|-|-|-", buf_prefix, srp->sid.value,
1894 sr_op2str(buf_oper, sizeof(buf_oper),
1895 srp->input_label,
1896 MPLS_LABEL_IMPLICIT_NULL));
1897 return;
1898 }
1899
1900 for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node,
1901 nexthop)) {
1902 struct interface *ifp;
1903
1904 inet_ntop(nexthop->family, &nexthop->ip, buf_nhop,
1905 sizeof(buf_nhop));
1906 ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT);
1907 if (ifp)
1908 strlcpy(buf_iface, ifp->name, sizeof(buf_iface));
1909 else
1910 snprintf(buf_iface, sizeof(buf_iface), "ifindex %u",
1911 nexthop->ifindex);
1912 if (nexthop->sr.label == MPLS_INVALID_LABEL)
1913 snprintf(buf_uptime, sizeof(buf_uptime), "-");
1914 else
1915 log_uptime(nexthop->sr.uptime, buf_uptime,
1916 sizeof(buf_uptime));
1917 sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label,
1918 nexthop->sr.label);
1919
1920 if (first)
1921 ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", buf_prefix,
1922 srp->sid.value, buf_oper, buf_nhop,
1923 buf_iface, buf_uptime);
1924 else
1925 ttable_add_row(tt, "|||%s|%s|%s|%s", buf_oper, buf_nhop,
1926 buf_iface, buf_uptime);
1927 first = false;
1928 }
1929 }
1930
1931 /**
1932 * Show Prefix-SIDs.
1933 *
1934 * @param vty VTY output
1935 * @param area IS-IS area
1936 * @param level IS-IS level
1937 */
show_prefix_sids(struct vty * vty,struct isis_area * area,int level)1938 static void show_prefix_sids(struct vty *vty, struct isis_area *area, int level)
1939 {
1940 struct sr_prefix *srp;
1941 struct ttable *tt;
1942
1943 if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0)
1944 return;
1945
1946 vty_out(vty, " IS-IS %s Prefix-SIDs:\n\n", circuit_t2string(level));
1947
1948 /* Prepare table. */
1949 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1950 ttable_add_row(tt, "Prefix|SID|Label Op.|Nexthop|Interface|Uptime");
1951 tt->style.cell.rpad = 2;
1952 tt->style.corner = '+';
1953 ttable_restyle(tt);
1954 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1955
1956 /* Process all Prefix-SID from the SRDB */
1957 frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) {
1958 switch (srp->type) {
1959 case ISIS_SR_PREFIX_LOCAL:
1960 show_prefix_sid_local(vty, tt, area, srp);
1961 break;
1962 case ISIS_SR_PREFIX_REMOTE:
1963 show_prefix_sid_remote(vty, tt, area, srp);
1964 break;
1965 }
1966 }
1967
1968 /* Dump the generated table. */
1969 if (tt->nrows > 1) {
1970 char *table;
1971
1972 table = ttable_dump(tt, "\n");
1973 vty_out(vty, "%s\n", table);
1974 XFREE(MTYPE_TMP, table);
1975 }
1976 ttable_del(tt);
1977 }
1978
1979 /**
1980 * Declaration of new show commands.
1981 */
1982 DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd,
1983 "show isis [vrf <NAME|all>] segment-routing prefix-sids",
1984 SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
1985 "All VRFs\n"
1986 "Segment-Routing\n"
1987 "Segment-Routing Prefix-SIDs\n")
1988 {
1989 struct listnode *node, *inode;
1990 struct isis_area *area;
1991 struct isis *isis = NULL;
1992 const char *vrf_name = VRF_DEFAULT_NAME;
1993 bool all_vrf = false;
1994 int idx_vrf = 0;
1995
1996 ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
1997 if (vrf_name) {
1998 if (all_vrf) {
1999 for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
2000 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node,
2001 area)) {
2002 vty_out(vty, "Area %s:\n",
2003 area->area_tag ? area->area_tag
2004 : "null");
2005 for (int level = ISIS_LEVEL1;
2006 level <= ISIS_LEVELS; level++)
2007 show_prefix_sids(vty, area,
2008 level);
2009 }
2010 }
2011 return 0;
2012 }
2013 isis = isis_lookup_by_vrfname(vrf_name);
2014 if (isis != NULL) {
2015 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node,
2016 area)) {
2017 vty_out(vty, "Area %s:\n",
2018 area->area_tag ? area->area_tag
2019 : "null");
2020 for (int level = ISIS_LEVEL1;
2021 level <= ISIS_LEVELS; level++)
2022 show_prefix_sids(vty, area, level);
2023 }
2024 }
2025 }
2026
2027 return CMD_SUCCESS;
2028 }
2029
2030 /**
2031 * Show Segment Routing Node.
2032 *
2033 * @param vty VTY output
2034 * @param area IS-IS area
2035 * @param level IS-IS level
2036 */
show_node(struct vty * vty,struct isis_area * area,int level)2037 static void show_node(struct vty *vty, struct isis_area *area, int level)
2038 {
2039 struct sr_node *srn;
2040 struct ttable *tt;
2041
2042 if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0)
2043 return;
2044
2045 vty_out(vty, " IS-IS %s SR-Node:\n\n", circuit_t2string(level));
2046
2047 /* Prepare table. */
2048 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
2049 ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD");
2050 tt->style.cell.rpad = 2;
2051 tt->style.corner = '+';
2052 ttable_restyle(tt);
2053 ttable_rowseps(tt, 0, BOTTOM, true, '-');
2054
2055 /* Process all SR-Node from the SRDB */
2056 frr_each (srdb_node, &area->srdb.sr_nodes[level - 1], srn) {
2057 ttable_add_row(
2058 tt, "%s|%u - %u|%u - %u|%s|%u",
2059 sysid_print(srn->sysid),
2060 srn->cap.srgb.lower_bound,
2061 srn->cap.srgb.lower_bound + srn->cap.srgb.range_size
2062 - 1,
2063 srn->cap.srlb.lower_bound,
2064 srn->cap.srlb.lower_bound + srn->cap.srlb.range_size
2065 - 1,
2066 srn->cap.algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF",
2067 srn->cap.msd);
2068 }
2069
2070 /* Dump the generated table. */
2071 if (tt->nrows > 1) {
2072 char *table;
2073
2074 table = ttable_dump(tt, "\n");
2075 vty_out(vty, "%s\n", table);
2076 XFREE(MTYPE_TMP, table);
2077 }
2078 ttable_del(tt);
2079 }
2080
2081 DEFUN(show_sr_node, show_sr_node_cmd,
2082 "show isis segment-routing node",
2083 SHOW_STR PROTO_HELP
2084 "Segment-Routing\n"
2085 "Segment-Routing node\n")
2086 {
2087 struct listnode *node, *inode;
2088 struct isis_area *area;
2089 struct isis *isis;
2090
2091 for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
2092 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
2093 vty_out(vty, "Area %s:\n",
2094 area->area_tag ? area->area_tag : "null");
2095
2096 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
2097 level++)
2098 show_node(vty, area, level);
2099 }
2100 }
2101
2102 return CMD_SUCCESS;
2103 }
2104
2105
2106 /* --- IS-IS Segment Routing Management function ---------------------------- */
2107
2108 /**
2109 * Thread function to re-attempt connection to the Label Manager and thus be
2110 * able to start Segment Routing.
2111 *
2112 * @param start Thread structure that contains area as argument
2113 *
2114 * @return 1 on success
2115 */
sr_start_label_manager(struct thread * start)2116 static int sr_start_label_manager(struct thread *start)
2117 {
2118 struct isis_area *area;
2119
2120 area = THREAD_ARG(start);
2121
2122 /* re-attempt to start SR & Label Manager connection */
2123 isis_sr_start(area);
2124
2125 return 1;
2126 }
2127
2128 /**
2129 * Enable SR on the given IS-IS area.
2130 *
2131 * @param area IS-IS area
2132 *
2133 * @return 0 on success, -1 otherwise
2134 */
isis_sr_start(struct isis_area * area)2135 int isis_sr_start(struct isis_area *area)
2136 {
2137 struct isis_sr_db *srdb = &area->srdb;
2138 struct isis_adjacency *adj;
2139 struct listnode *node;
2140
2141 /* First start Label Manager if not ready */
2142 if (!isis_zebra_label_manager_ready())
2143 if (isis_zebra_label_manager_connect() < 0) {
2144 /* Re-attempt to connect to Label Manager in 1 sec. */
2145 thread_add_timer(master, sr_start_label_manager, area,
2146 1, &srdb->t_start_lm);
2147 return -1;
2148 }
2149
2150 /* Label Manager is ready, initialize the SRLB */
2151 if (sr_local_block_init(area) < 0)
2152 return -1;
2153
2154 /*
2155 * Request SGRB to the label manager if not already active. If the
2156 * allocation fails, return an error to disable SR until a new SRGB
2157 * is successfully allocated.
2158 */
2159 if (!srdb->srgb_active) {
2160 if (isis_zebra_request_label_range(
2161 srdb->config.srgb_lower_bound,
2162 srdb->config.srgb_upper_bound
2163 - srdb->config.srgb_lower_bound + 1)
2164 < 0) {
2165 srdb->srgb_active = false;
2166 return -1;
2167 } else
2168 srdb->srgb_active = true;
2169 }
2170
2171 sr_debug("ISIS-Sr: Starting Segment Routing for area %s",
2172 area->area_tag);
2173
2174 /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */
2175 for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
2176 if (adj->ipv4_address_count > 0)
2177 sr_adj_sid_add(adj, AF_INET);
2178 if (adj->ipv6_address_count > 0)
2179 sr_adj_sid_add(adj, AF_INET6);
2180 }
2181
2182 area->srdb.enabled = true;
2183
2184 /* Regenerate LSPs to advertise Segment Routing capabilities. */
2185 lsp_regenerate_schedule(area, area->is_type, 0);
2186
2187 return 0;
2188 }
2189
2190 /**
2191 * Disable SR on the given IS-IS area.
2192 *
2193 * @param area IS-IS area
2194 */
isis_sr_stop(struct isis_area * area)2195 void isis_sr_stop(struct isis_area *area)
2196 {
2197 struct isis_sr_db *srdb = &area->srdb;
2198 struct sr_adjacency *sra;
2199 struct listnode *node, *nnode;
2200
2201 sr_debug("ISIS-Sr: Stopping Segment Routing for area %s",
2202 area->area_tag);
2203
2204 /* Disable any re-attempt to connect to Label Manager */
2205 THREAD_TIMER_OFF(srdb->t_start_lm);
2206
2207 /* Uninstall all local Adjacency-SIDs. */
2208 for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
2209 sr_adj_sid_del(sra);
2210
2211 /* Uninstall all Prefix-SIDs from all SR Node. */
2212 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
2213 while (srdb_node_count(&srdb->sr_nodes[level - 1]) > 0) {
2214 struct sr_node *srn;
2215
2216 srn = srdb_node_first(&srdb->sr_nodes[level - 1]);
2217 sr_node_del(area, level, srn);
2218 }
2219 }
2220
2221 /* Release SRGB if active. */
2222 if (srdb->srgb_active) {
2223 isis_zebra_release_label_range(srdb->config.srgb_lower_bound,
2224 srdb->config.srgb_upper_bound);
2225 srdb->srgb_active = false;
2226 }
2227
2228 /* Delete SRLB */
2229 sr_local_block_delete(area);
2230
2231 area->srdb.enabled = false;
2232
2233 /* Regenerate LSPs to advertise that the Node is no more SR enable. */
2234 lsp_regenerate_schedule(area, area->is_type, 0);
2235 }
2236
2237 /**
2238 * IS-IS Segment Routing initialization for given area.
2239 *
2240 * @param area IS-IS area
2241 */
isis_sr_area_init(struct isis_area * area)2242 void isis_sr_area_init(struct isis_area *area)
2243 {
2244 struct isis_sr_db *srdb = &area->srdb;
2245
2246 sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB",
2247 area->area_tag);
2248
2249 /* Initialize Segment Routing Data Base */
2250 memset(srdb, 0, sizeof(*srdb));
2251 srdb->adj_sids = list_new();
2252
2253 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
2254 srdb_node_init(&srdb->sr_nodes[level - 1]);
2255 srdb_area_prefix_init(&srdb->prefix_sids[level - 1]);
2256 }
2257
2258 /* Pull defaults from the YANG module. */
2259 #ifndef FABRICD
2260 srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR);
2261 srdb->config.srgb_lower_bound =
2262 yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR);
2263 srdb->config.srgb_upper_bound =
2264 yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR);
2265 srdb->config.srlb_lower_bound =
2266 yang_get_default_uint32("%s/srlb/lower-bound", ISIS_SR);
2267 srdb->config.srlb_upper_bound =
2268 yang_get_default_uint32("%s/srlb/upper-bound", ISIS_SR);
2269 #else
2270 srdb->config.enabled = false;
2271 srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND;
2272 srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND;
2273 srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND;
2274 srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND;
2275 #endif
2276 srdb->config.msd = 0;
2277 srdb_prefix_cfg_init(&srdb->config.prefix_sids);
2278 }
2279
2280 /**
2281 * Terminate IS-IS Segment Routing for the given area.
2282 *
2283 * @param area IS-IS area
2284 */
isis_sr_area_term(struct isis_area * area)2285 void isis_sr_area_term(struct isis_area *area)
2286 {
2287 struct isis_sr_db *srdb = &area->srdb;
2288
2289 /* Stop Segment Routing */
2290 if (area->srdb.enabled)
2291 isis_sr_stop(area);
2292
2293 /* Clear Prefix-SID configuration. */
2294 while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) {
2295 struct sr_prefix_cfg *pcfg;
2296
2297 pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids);
2298 isis_sr_cfg_prefix_del(pcfg);
2299 }
2300 }
2301
2302 /**
2303 * IS-IS Segment Routing global initialization.
2304 */
isis_sr_init(void)2305 void isis_sr_init(void)
2306 {
2307 install_element(VIEW_NODE, &show_sr_prefix_sids_cmd);
2308 install_element(VIEW_NODE, &show_sr_node_cmd);
2309
2310 /* Register hooks. */
2311 hook_register(isis_adj_state_change_hook, sr_adj_state_change);
2312 hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
2313 hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
2314 hook_register(isis_route_update_hook, sr_route_update);
2315 hook_register(isis_if_new_hook, sr_if_new_hook);
2316 }
2317
2318 /**
2319 * IS-IS Segment Routing global terminate.
2320 */
isis_sr_term(void)2321 void isis_sr_term(void)
2322 {
2323 /* Unregister hooks. */
2324 hook_unregister(isis_adj_state_change_hook, sr_adj_state_change);
2325 hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled);
2326 hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled);
2327 hook_unregister(isis_route_update_hook, sr_route_update);
2328 hook_unregister(isis_if_new_hook, sr_if_new_hook);
2329 }
2330