]>
Commit | Line | Data |
---|---|---|
26f6acaf | 1 | /* |
f2333421 | 2 | * This is an implementation of Segment Routing for IS-IS as per RFC 8667 |
26f6acaf | 3 | * |
f2333421 | 4 | * Copyright (C) 2019 Orange http://www.orange.com |
26f6acaf RW |
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 | ||
f2333421 | 50 | /* Local variables and functions */ |
26f6acaf RW |
51 | DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information") |
52 | ||
b1d80d43 | 53 | static void sr_prefix_uninstall(struct sr_prefix *srp); |
c3f7b406 | 54 | static void sr_prefix_reinstall(struct sr_prefix *srp, bool make_before_break); |
d8391312 OD |
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); | |
26f6acaf | 59 | |
f2333421 | 60 | /* --- RB-Tree Management functions ----------------------------------------- */ |
26f6acaf | 61 | |
f2333421 OD |
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 | */ | |
26f6acaf RW |
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 | } | |
cab10e86 | 75 | DECLARE_RBTREE_UNIQ(srdb_node_prefix, struct sr_prefix, node_entry, |
26f6acaf | 76 | sr_prefix_sid_compare) |
cab10e86 | 77 | DECLARE_RBTREE_UNIQ(srdb_area_prefix, struct sr_prefix, area_entry, |
26f6acaf RW |
78 | sr_prefix_sid_compare) |
79 | ||
f2333421 OD |
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 | */ | |
26f6acaf RW |
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 | } | |
cab10e86 | 93 | DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry, |
26f6acaf RW |
94 | sr_prefix_sid_cfg_compare) |
95 | ||
f2333421 OD |
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 | */ | |
26f6acaf RW |
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 | } | |
cab10e86 | 109 | DECLARE_RBTREE_UNIQ(srdb_node, struct sr_node, entry, sr_node_compare) |
26f6acaf | 110 | |
f2333421 | 111 | /* --- Functions used for Yang model and CLI to configure Segment Routing --- */ |
26f6acaf | 112 | |
f2333421 OD |
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 | */ | |
b1d80d43 OD |
121 | static bool sr_prefix_is_node_sid(const struct interface *ifp, |
122 | const struct prefix *prefix) | |
26f6acaf RW |
123 | { |
124 | return (if_is_loopback(ifp) && is_host_route(prefix)); | |
125 | } | |
126 | ||
f2333421 OD |
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 | */ | |
26f6acaf RW |
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 | ||
d8391312 OD |
142 | sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]", |
143 | area->area_tag, lower_bound, upper_bound); | |
b407c77a | 144 | |
58fbcdf2 OD |
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, | |
26f6acaf | 156 | srdb->config.srgb_upper_bound); |
58fbcdf2 OD |
157 | srdb->srgb_active = false; |
158 | } | |
26f6acaf RW |
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 | ||
d8391312 | 166 | /* then request new SRGB if SR is enabled. */ |
26f6acaf RW |
167 | if (isis_zebra_request_label_range( |
168 | srdb->config.srgb_lower_bound, | |
169 | srdb->config.srgb_upper_bound | |
58fbcdf2 OD |
170 | - srdb->config.srgb_lower_bound + 1) < 0) { |
171 | srdb->srgb_active = false; | |
26f6acaf | 172 | return -1; |
58fbcdf2 OD |
173 | } else |
174 | srdb->srgb_active = true; | |
175 | ||
26f6acaf | 176 | |
d8391312 | 177 | sr_debug(" |- Got new SRGB [%u/%u]", |
b407c77a OD |
178 | srdb->config.srgb_lower_bound, |
179 | srdb->config.srgb_upper_bound); | |
180 | ||
26f6acaf RW |
181 | /* Reinstall local Prefix-SIDs to update their input labels. */ |
182 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { | |
cab10e86 | 183 | frr_each (srdb_area_prefix, |
26f6acaf | 184 | &area->srdb.prefix_sids[level - 1], srp) { |
b1d80d43 | 185 | sr_prefix_reinstall(srp, false); |
26f6acaf RW |
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. */ | |
58fbcdf2 | 192 | isis_sr_start(area); |
26f6acaf RW |
193 | } |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
d8391312 OD |
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 | */ | |
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, *nnode; | |
214 | struct sr_adjacency *sra; | |
d8391312 OD |
215 | |
216 | sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]", | |
217 | area->area_tag, lower_bound, upper_bound); | |
218 | ||
58fbcdf2 OD |
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 */ | |
d8391312 OD |
228 | sr_local_block_delete(area); |
229 | ||
230 | srdb->config.srlb_lower_bound = lower_bound; | |
231 | srdb->config.srlb_upper_bound = upper_bound; | |
232 | ||
58fbcdf2 OD |
233 | if (srdb->enabled) { |
234 | /* Initialize new SRLB */ | |
235 | if (sr_local_block_init(area) != 0) | |
236 | return -1; | |
d8391312 | 237 | |
58fbcdf2 OD |
238 | /* Reinstall local Adjacency-SIDs with new labels. */ |
239 | for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) | |
240 | sr_adj_sid_update(sra, &srdb->srlb); | |
d8391312 | 241 | |
58fbcdf2 OD |
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 | } | |
d8391312 OD |
248 | |
249 | return 0; | |
250 | } | |
251 | ||
f2333421 OD |
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 | */ | |
26f6acaf RW |
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 | ||
b407c77a OD |
266 | sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix); |
267 | ||
26f6acaf RW |
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); | |
b1d80d43 | 280 | if (ifp && sr_prefix_is_node_sid(ifp, prefix)) |
26f6acaf RW |
281 | pcfg->node_sid = true; |
282 | ||
283 | /* Save prefix-sid configuration. */ | |
cab10e86 | 284 | srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg); |
26f6acaf RW |
285 | |
286 | return pcfg; | |
287 | } | |
288 | ||
f2333421 OD |
289 | /** |
290 | * Removal of locally configured Prefix-SID. | |
291 | * | |
292 | * @param pcfg Configured Prefix-SID | |
293 | */ | |
26f6acaf RW |
294 | void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg) |
295 | { | |
b407c77a OD |
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); | |
26f6acaf | 302 | |
cab10e86 | 303 | srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg); |
26f6acaf RW |
304 | XFREE(MTYPE_ISIS_SR_INFO, pcfg); |
305 | } | |
306 | ||
f2333421 OD |
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 | */ | |
26f6acaf RW |
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); | |
cab10e86 | 321 | return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg); |
26f6acaf RW |
322 | } |
323 | ||
f2333421 OD |
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 | */ | |
26f6acaf RW |
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 | ||
f2333421 | 366 | /* --- Segment Routing Prefix Management functions -------------------------- */ |
26f6acaf | 367 | |
f2333421 OD |
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 | */ | |
b1d80d43 OD |
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) | |
26f6acaf RW |
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; | |
b1d80d43 | 389 | srp->input_label = MPLS_INVALID_LABEL; |
26f6acaf RW |
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; | |
cab10e86 | 398 | srdb_node_prefix_add(&srn->prefix_sids, srp); |
26f6acaf | 399 | /* TODO: this might fail if we have Anycast SIDs in the IS-IS area. */ |
cab10e86 | 400 | srdb_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp); |
26f6acaf | 401 | |
b407c77a OD |
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 | ||
26f6acaf RW |
406 | return srp; |
407 | } | |
408 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
417 | static void sr_prefix_del(struct isis_area *area, struct sr_node *srn, |
418 | struct sr_prefix *srp) | |
26f6acaf | 419 | { |
b407c77a OD |
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 | ||
b1d80d43 | 424 | sr_prefix_uninstall(srp); |
cab10e86 OD |
425 | srdb_node_prefix_del(&srn->prefix_sids, srp); |
426 | srdb_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp); | |
26f6acaf RW |
427 | XFREE(MTYPE_ISIS_SR_INFO, srp); |
428 | } | |
429 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
439 | static struct sr_prefix *sr_prefix_find_by_area(struct isis_area *area, |
440 | int level, | |
441 | union prefixconstptr prefix) | |
26f6acaf RW |
442 | { |
443 | struct sr_prefix srp = {}; | |
444 | ||
445 | prefix_copy(&srp.prefix, prefix.p); | |
f2333421 | 446 | return srdb_area_prefix_find(&area->srdb.prefix_sids[level - 1], &srp); |
26f6acaf RW |
447 | } |
448 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
457 | static struct sr_prefix *sr_prefix_find_by_node(struct sr_node *srn, |
458 | union prefixconstptr prefix) | |
26f6acaf RW |
459 | { |
460 | struct sr_prefix srp = {}; | |
461 | ||
462 | prefix_copy(&srp.prefix, prefix.p); | |
cab10e86 | 463 | return srdb_node_prefix_find(&srn->prefix_sids, &srp); |
26f6acaf RW |
464 | } |
465 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 478 | static struct sr_node *sr_node_add(struct isis_area *area, int level, |
d8391312 | 479 | const uint8_t *sysid) |
26f6acaf RW |
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); | |
26f6acaf | 486 | srn->area = area; |
cab10e86 OD |
487 | srdb_node_prefix_init(&srn->prefix_sids); |
488 | srdb_node_add(&area->srdb.sr_nodes[level - 1], srn); | |
26f6acaf | 489 | |
b407c77a OD |
490 | sr_debug(" |- Added new SR Node %s", sysid_print(srn->sysid)); |
491 | ||
26f6acaf RW |
492 | return srn; |
493 | } | |
494 | ||
b1d80d43 | 495 | static void sr_node_del(struct isis_area *area, int level, struct sr_node *srn) |
f2333421 OD |
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 | */ | |
26f6acaf | 504 | { |
b407c77a OD |
505 | |
506 | sr_debug(" |- Delete SR Node %s", sysid_print(srn->sysid)); | |
507 | ||
26f6acaf | 508 | /* Remove and uninstall Prefix-SIDs. */ |
cab10e86 | 509 | while (srdb_node_prefix_count(&srn->prefix_sids) > 0) { |
26f6acaf RW |
510 | struct sr_prefix *srp; |
511 | ||
cab10e86 | 512 | srp = srdb_node_prefix_first(&srn->prefix_sids); |
b1d80d43 | 513 | sr_prefix_del(area, srn, srp); |
26f6acaf RW |
514 | } |
515 | ||
cab10e86 | 516 | srdb_node_del(&area->srdb.sr_nodes[level - 1], srn); |
26f6acaf RW |
517 | XFREE(MTYPE_ISIS_SR_INFO, srn); |
518 | } | |
519 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
529 | static struct sr_node *sr_node_find(struct isis_area *area, int level, |
530 | const uint8_t *sysid) | |
26f6acaf RW |
531 | { |
532 | struct sr_node srn = {}; | |
533 | ||
534 | memcpy(srn.sysid, sysid, ISIS_SYS_ID_LEN); | |
cab10e86 | 535 | return srdb_node_find(&area->srdb.sr_nodes[level - 1], &srn); |
26f6acaf RW |
536 | } |
537 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
546 | static void sr_node_srgb_update(struct isis_area *area, int level, |
547 | uint8_t *sysid) | |
26f6acaf RW |
548 | { |
549 | struct sr_prefix *srp; | |
550 | ||
b407c77a OD |
551 | sr_debug("ISIS-Sr (%s): Update neighbors SR Node with new SRGB", |
552 | area->area_tag); | |
553 | ||
b1d80d43 | 554 | frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) { |
26f6acaf RW |
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 | /* | |
c3f7b406 OD |
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). | |
26f6acaf | 574 | */ |
b1d80d43 | 575 | sr_prefix_reinstall(srp, true); |
26f6acaf RW |
576 | break; |
577 | } | |
578 | } | |
579 | } | |
580 | ||
f2333421 OD |
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 | */ | |
26f6acaf RW |
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 | ||
f2333421 OD |
596 | /** |
597 | * Reset Segment Routing Nexthop. | |
598 | * | |
599 | * @param srnh Segment Routing Nexthop | |
600 | */ | |
26f6acaf RW |
601 | void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh) |
602 | { | |
603 | srnh->label = MPLS_INVALID_LABEL; | |
604 | srnh->uptime = 0; | |
605 | } | |
606 | ||
f2333421 | 607 | /* --- Segment Routing Prefix-SID Management functions to configure LFIB ---- */ |
26f6acaf | 608 | |
f2333421 OD |
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 | */ | |
b1d80d43 OD |
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) | |
26f6acaf RW |
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 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 643 | static mpls_label_t sr_prefix_in_label(const struct sr_prefix *srp) |
26f6acaf RW |
644 | { |
645 | const struct sr_node *srn = srp->srn; | |
646 | struct isis_area *area = srn->area; | |
647 | ||
f2333421 | 648 | /* Return SID value as MPLS label if it is an Absolute SID */ |
26f6acaf RW |
649 | if (CHECK_FLAG(srp->sid.flags, |
650 | ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) | |
651 | return srp->sid.value; | |
652 | ||
f2333421 | 653 | /* Check that SID index falls inside the SRGB */ |
26f6acaf RW |
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 | ||
f2333421 | 662 | /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ |
26f6acaf RW |
663 | return (area->srdb.config.srgb_lower_bound + srp->sid.value); |
664 | } | |
665 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
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) | |
26f6acaf RW |
678 | { |
679 | const struct sr_node *srn = srp->srn; | |
680 | ||
f2333421 | 681 | /* Check if the nexthop SR Node is the last hop? */ |
26f6acaf | 682 | if (memcmp(sysid, srn->sysid, ISIS_SYS_ID_LEN) == 0) { |
f2333421 | 683 | /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */ |
26f6acaf RW |
684 | if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP)) |
685 | return MPLS_LABEL_IMPLICIT_NULL; | |
686 | ||
f2333421 | 687 | /* SR-Node requests Implicit NULL Label */ |
26f6acaf RW |
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 | ||
f2333421 | 697 | /* Return SID value as MPLS label if it is an Absolute SID */ |
26f6acaf RW |
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 | |
f2333421 | 702 | * can use them (RFC8667 section #2.1.1.1) |
26f6acaf RW |
703 | */ |
704 | if (srp->srn != srn_nexthop) | |
705 | return MPLS_INVALID_LABEL; | |
706 | return srp->sid.value; | |
707 | } | |
708 | ||
f2333421 | 709 | /* Check that SID index falls inside the SRGB */ |
26f6acaf RW |
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 | ||
f2333421 | 717 | /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ |
26f6acaf RW |
718 | return (srn_nexthop->cap.srgb.lower_bound + srp->sid.value); |
719 | } | |
720 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 729 | static int sr_prefix_install_local(struct sr_prefix *srp) |
26f6acaf | 730 | { |
b1d80d43 | 731 | mpls_label_t input_label; |
b407c77a | 732 | const struct sr_node *srn = srp->srn; |
26f6acaf RW |
733 | |
734 | /* | |
f2333421 | 735 | * No need to install Label for local Prefix-SID unless the |
26f6acaf RW |
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 | ||
b407c77a OD |
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)); | |
26f6acaf | 745 | |
f2333421 | 746 | /* Compute input label and check that is valid. */ |
b1d80d43 OD |
747 | input_label = sr_prefix_in_label(srp); |
748 | if (input_label == MPLS_INVALID_LABEL) | |
26f6acaf RW |
749 | return -1; |
750 | ||
751 | /* Update internal state. */ | |
b1d80d43 | 752 | srp->input_label = input_label; |
26f6acaf RW |
753 | isis_sr_nexthop_update(&srp->u.local.info, MPLS_LABEL_IMPLICIT_NULL); |
754 | ||
755 | /* Install Prefix-SID in the forwarding plane. */ | |
c3f7b406 | 756 | isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp); |
26f6acaf RW |
757 | |
758 | return 0; | |
759 | } | |
760 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 769 | static int sr_prefix_install_remote(struct sr_prefix *srp) |
26f6acaf RW |
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; | |
b1d80d43 | 776 | mpls_label_t input_label; |
26f6acaf RW |
777 | size_t nexthop_num = 0; |
778 | ||
f2333421 | 779 | /* Lookup to associated IS-IS route. */ |
26f6acaf | 780 | tree_id = (srp->prefix.family == AF_INET) ? SPFTREE_IPV4 : SPFTREE_IPV6; |
b1d80d43 | 781 | srp->u.remote.rinfo = sr_prefix_lookup_route(area, tree_id, srp); |
26f6acaf RW |
782 | if (!srp->u.remote.rinfo) |
783 | /* SPF hasn't converged for this route yet. */ | |
784 | return -1; | |
785 | ||
f2333421 | 786 | /* Compute input label and check that is valid. */ |
b1d80d43 OD |
787 | input_label = sr_prefix_in_label(srp); |
788 | if (input_label == MPLS_INVALID_LABEL) | |
26f6acaf RW |
789 | return -1; |
790 | ||
b407c77a OD |
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 | ||
f2333421 | 795 | /* Process all SPF nexthops */ |
26f6acaf RW |
796 | for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node, |
797 | nexthop)) { | |
798 | struct sr_node *srn_nexthop; | |
b1d80d43 | 799 | mpls_label_t output_label; |
26f6acaf RW |
800 | |
801 | /* Check if the nexthop advertised a SRGB. */ | |
b1d80d43 | 802 | srn_nexthop = sr_node_find(area, srn->level, nexthop->sysid); |
26f6acaf RW |
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 | ||
f2333421 | 816 | /* Compute output label and check if it is valid */ |
b407c77a OD |
817 | output_label = |
818 | sr_prefix_out_label(srp, srn_nexthop, nexthop->sysid); | |
b1d80d43 | 819 | if (output_label == MPLS_INVALID_LABEL) |
26f6acaf RW |
820 | goto next; |
821 | ||
e740f9c1 | 822 | if (IS_DEBUG_SR) { |
26f6acaf RW |
823 | static char buf[INET6_ADDRSTRLEN]; |
824 | ||
825 | inet_ntop(nexthop->family, &nexthop->ip, buf, | |
826 | sizeof(buf)); | |
b407c77a OD |
827 | zlog_debug(" |- nexthop %s label %u", buf, |
828 | output_label); | |
26f6acaf RW |
829 | } |
830 | ||
b1d80d43 | 831 | isis_sr_nexthop_update(&nexthop->sr, output_label); |
26f6acaf RW |
832 | nexthop_num++; |
833 | continue; | |
834 | next: | |
835 | isis_sr_nexthop_reset(&nexthop->sr); | |
836 | } | |
f2333421 OD |
837 | |
838 | /* Check that we found at least one valid nexthop */ | |
26f6acaf | 839 | if (nexthop_num == 0) { |
b407c77a | 840 | sr_debug(" |- no valid nexthops"); |
26f6acaf RW |
841 | return -1; |
842 | } | |
843 | ||
844 | /* Update internal state. */ | |
b1d80d43 | 845 | srp->input_label = input_label; |
26f6acaf RW |
846 | |
847 | /* Install Prefix-SID in the forwarding plane. */ | |
c3f7b406 | 848 | isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp); |
26f6acaf RW |
849 | |
850 | return 0; | |
851 | } | |
852 | ||
f2333421 OD |
853 | /** |
854 | * Process local or remote Prefix-SID and install it if possible. | |
855 | * | |
856 | * @param srp Segment Routing Prefix | |
857 | */ | |
b1d80d43 | 858 | static void sr_prefix_install(struct sr_prefix *srp) |
26f6acaf RW |
859 | { |
860 | const struct sr_node *srn = srp->srn; | |
861 | struct isis_area *area = srn->area; | |
862 | int ret; | |
863 | ||
b407c77a OD |
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 | ||
26f6acaf RW |
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: | |
b1d80d43 OD |
874 | srp_l2 = sr_prefix_find_by_area(area, ISIS_LEVEL2, |
875 | &srp->prefix); | |
26f6acaf | 876 | if (srp_l2) |
b1d80d43 | 877 | sr_prefix_uninstall(srp_l2); |
26f6acaf RW |
878 | break; |
879 | case ISIS_LEVEL2: | |
b1d80d43 OD |
880 | srp_l1 = sr_prefix_find_by_area(area, ISIS_LEVEL1, |
881 | &srp->prefix); | |
26f6acaf RW |
882 | if (srp_l1) |
883 | return; | |
884 | break; | |
885 | default: | |
886 | break; | |
887 | } | |
888 | } | |
889 | ||
f2333421 | 890 | /* Install corresponding LFIB entry */ |
26f6acaf | 891 | if (srp->type == ISIS_SR_PREFIX_LOCAL) |
b1d80d43 | 892 | ret = sr_prefix_install_local(srp); |
26f6acaf | 893 | else |
b1d80d43 | 894 | ret = sr_prefix_install_remote(srp); |
26f6acaf | 895 | if (ret != 0) |
b1d80d43 | 896 | sr_prefix_uninstall(srp); |
26f6acaf RW |
897 | } |
898 | ||
f2333421 OD |
899 | /** |
900 | * Uninstall local or remote Prefix-SID. | |
901 | * | |
902 | * @param srp Segment Routing Prefix | |
903 | */ | |
b1d80d43 | 904 | static void sr_prefix_uninstall(struct sr_prefix *srp) |
26f6acaf | 905 | { |
26f6acaf RW |
906 | struct listnode *node; |
907 | struct isis_nexthop *nexthop; | |
908 | ||
f2333421 | 909 | /* Check that Input Label is valid */ |
b1d80d43 | 910 | if (srp->input_label == MPLS_INVALID_LABEL) |
26f6acaf RW |
911 | return; |
912 | ||
b407c77a OD |
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); | |
26f6acaf RW |
916 | |
917 | /* Uninstall Prefix-SID from the forwarding plane. */ | |
c3f7b406 | 918 | isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_DELETE, srp); |
26f6acaf RW |
919 | |
920 | /* Reset internal state. */ | |
b1d80d43 | 921 | srp->input_label = MPLS_INVALID_LABEL; |
26f6acaf RW |
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 | ||
f2333421 OD |
936 | /** |
937 | * Reinstall local or remote Prefix-SID. | |
938 | * | |
939 | * @param srp Segment Routing Prefix | |
940 | */ | |
c3f7b406 OD |
941 | static inline void sr_prefix_reinstall(struct sr_prefix *srp, |
942 | bool make_before_break) | |
26f6acaf RW |
943 | { |
944 | /* | |
c3f7b406 | 945 | * Make Before Break can be used only when we know for sure that |
26f6acaf RW |
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 | */ | |
c3f7b406 | 950 | if (!make_before_break) |
b1d80d43 | 951 | sr_prefix_uninstall(srp); |
26f6acaf | 952 | |
f2333421 | 953 | /* New input label is computed in sr_prefix_install() function */ |
b1d80d43 | 954 | sr_prefix_install(srp); |
26f6acaf RW |
955 | } |
956 | ||
f2333421 | 957 | /* --- IS-IS LSP Parse functions -------------------------------------------- */ |
26f6acaf | 958 | |
d8391312 OD |
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 | */ | |
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 | ||
f2333421 OD |
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 | */ | |
26f6acaf | 989 | static struct sr_node * |
b1d80d43 OD |
990 | parse_router_cap_tlv(struct isis_area *area, int level, const uint8_t *sysid, |
991 | const struct isis_router_cap *router_cap) | |
26f6acaf RW |
992 | { |
993 | struct sr_node *srn; | |
994 | ||
995 | if (!router_cap || router_cap->srgb.range_size == 0) | |
996 | return NULL; | |
997 | ||
b407c77a OD |
998 | sr_debug("ISIS-Sr (%s): Parse Router Capability TLV", area->area_tag); |
999 | ||
b1d80d43 | 1000 | srn = sr_node_find(area, level, sysid); |
26f6acaf | 1001 | if (srn) { |
d8391312 | 1002 | if (router_cap_cmp(&srn->cap, router_cap) != 0) { |
cab10e86 | 1003 | srn->state = SRDB_STATE_MODIFIED; |
26f6acaf | 1004 | } else |
cab10e86 | 1005 | srn->state = SRDB_STATE_UNCHANGED; |
b407c77a OD |
1006 | sr_debug(" |- Found %s SR Node %s", |
1007 | srn->state == SRDB_STATE_MODIFIED ? "Modified" | |
1008 | : "Unchanged", | |
1009 | sysid_print(srn->sysid)); | |
26f6acaf | 1010 | } else { |
d8391312 | 1011 | srn = sr_node_add(area, level, sysid); |
cab10e86 | 1012 | srn->state = SRDB_STATE_NEW; |
26f6acaf RW |
1013 | } |
1014 | ||
d8391312 OD |
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 | ||
26f6acaf RW |
1021 | return srn; |
1022 | } | |
1023 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
1032 | static void parse_prefix_sid_subtlvs(struct sr_node *srn, |
1033 | union prefixconstptr prefix, bool local, | |
1034 | struct isis_item_list *prefix_sids) | |
26f6acaf RW |
1035 | { |
1036 | struct isis_area *area = srn->area; | |
1037 | struct isis_item *i; | |
1038 | ||
b407c77a OD |
1039 | sr_debug("ISIS-Sr (%s): Parse Prefix SID TLV", area->area_tag); |
1040 | ||
f2333421 | 1041 | /* Parse list of Prefix SID subTLVs */ |
26f6acaf RW |
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 | ||
f2333421 | 1046 | /* Only SPF algorithm is supported right now */ |
26f6acaf RW |
1047 | if (psid->algorithm != SR_ALGORITHM_SPF) |
1048 | continue; | |
1049 | ||
f2333421 | 1050 | /* Compute corresponding Segment Routing Prefix */ |
b1d80d43 | 1051 | srp = sr_prefix_find_by_node(srn, prefix); |
26f6acaf RW |
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; | |
cab10e86 | 1057 | srp->state = SRDB_STATE_MODIFIED; |
0a5b7077 | 1058 | } else if (srp->state == SRDB_STATE_VALIDATED) |
cab10e86 | 1059 | srp->state = SRDB_STATE_UNCHANGED; |
b407c77a OD |
1060 | sr_debug(" |- Found %s Prefix-SID %pFX", |
1061 | srp->state == SRDB_STATE_MODIFIED | |
1062 | ? "Modified" | |
1063 | : "Unchanged", | |
1064 | &srp->prefix); | |
1065 | ||
26f6acaf | 1066 | } else { |
b1d80d43 | 1067 | srp = sr_prefix_add(area, srn, prefix, local, psid); |
cab10e86 | 1068 | srp->state = SRDB_STATE_NEW; |
26f6acaf RW |
1069 | } |
1070 | /* | |
1071 | * Stop the Prefix-SID iteration since we only support the SPF | |
1072 | * algorithm for now. | |
1073 | */ | |
1074 | break; | |
1075 | } | |
1076 | } | |
1077 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
1086 | static void parse_lsp(struct isis_area *area, int level, struct sr_node **srn, |
1087 | struct isis_lsp *lsp) | |
26f6acaf RW |
1088 | { |
1089 | struct isis_item_list *items; | |
1090 | struct isis_item *i; | |
1091 | bool local = lsp->own_lsp; | |
1092 | ||
f2333421 | 1093 | /* Check LSP sequence number */ |
26f6acaf RW |
1094 | if (lsp->hdr.seqno == 0) { |
1095 | zlog_warn("%s: lsp with 0 seq_num - ignore", __func__); | |
1096 | return; | |
1097 | } | |
1098 | ||
b407c77a OD |
1099 | sr_debug("ISIS-Sr (%s): Parse LSP from node %s", area->area_tag, |
1100 | sysid_print(lsp->hdr.lsp_id)); | |
1101 | ||
26f6acaf RW |
1102 | /* Parse the Router Capability TLV. */ |
1103 | if (*srn == NULL) { | |
b407c77a OD |
1104 | *srn = parse_router_cap_tlv(area, level, lsp->hdr.lsp_id, |
1105 | lsp->tlvs->router_cap); | |
26f6acaf RW |
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 | ||
b1d80d43 OD |
1119 | parse_prefix_sid_subtlvs(*srn, &ir->prefix, local, |
1120 | &ir->subtlvs->prefix_sids); | |
26f6acaf RW |
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 | ||
b1d80d43 OD |
1133 | parse_prefix_sid_subtlvs(*srn, &ir->prefix, local, |
1134 | &ir->subtlvs->prefix_sids); | |
26f6acaf RW |
1135 | } |
1136 | } | |
1137 | ||
f2333421 OD |
1138 | /** |
1139 | * Parse all SR-related information from the entire LSPDB. | |
1140 | * | |
1141 | * @param area IS-IS area | |
1142 | */ | |
b1d80d43 | 1143 | static void parse_lspdb(struct isis_area *area) |
26f6acaf RW |
1144 | { |
1145 | struct isis_lsp *lsp; | |
1146 | ||
b407c77a OD |
1147 | sr_debug("ISIS-Sr (%s): Parse LSP Data Base", area->area_tag); |
1148 | ||
f2333421 | 1149 | /* Process all LSP from Level 1 & 2 */ |
26f6acaf RW |
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 | ||
f2333421 | 1156 | /* Skip Pseudo ID LSP and LSP without TLVs */ |
26f6acaf RW |
1157 | if (LSP_PSEUDO_ID(lsp->hdr.lsp_id)) |
1158 | continue; | |
1159 | if (!lsp->tlvs) | |
1160 | continue; | |
1161 | ||
f2333421 | 1162 | /* Parse LSP, then fragment */ |
b1d80d43 | 1163 | parse_lsp(area, level, &srn, lsp); |
26f6acaf | 1164 | for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) |
b1d80d43 | 1165 | parse_lsp(area, level, &srn, frag); |
26f6acaf RW |
1166 | } |
1167 | } | |
1168 | } | |
1169 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 1176 | static void process_prefix_changes(struct sr_node *srn, struct sr_prefix *srp) |
26f6acaf RW |
1177 | { |
1178 | struct isis_area *area = srn->area; | |
1179 | ||
26f6acaf | 1180 | /* Install/reinstall/uninstall Prefix-SID if necessary. */ |
cab10e86 OD |
1181 | switch (srp->state) { |
1182 | case SRDB_STATE_NEW: | |
b407c77a OD |
1183 | sr_debug("ISIS-Sr (%s): Created Prefix-SID %pFX for SR node %s", |
1184 | area->area_tag, &srp->prefix, sysid_print(srn->sysid)); | |
b1d80d43 | 1185 | sr_prefix_install(srp); |
cab10e86 OD |
1186 | break; |
1187 | case SRDB_STATE_MODIFIED: | |
b407c77a OD |
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)); | |
b1d80d43 | 1191 | sr_prefix_reinstall(srp, false); |
cab10e86 OD |
1192 | break; |
1193 | case SRDB_STATE_UNCHANGED: | |
1194 | break; | |
1195 | default: | |
b407c77a OD |
1196 | sr_debug("ISIS-Sr (%s): Removed Prefix-SID %pFX for SR node %s", |
1197 | area->area_tag, &srp->prefix, sysid_print(srn->sysid)); | |
b1d80d43 | 1198 | sr_prefix_del(area, srn, srp); |
26f6acaf RW |
1199 | return; |
1200 | } | |
1201 | ||
f2333421 | 1202 | /* Validate SRDB State for next LSPDB parsing */ |
cab10e86 | 1203 | srp->state = SRDB_STATE_VALIDATED; |
26f6acaf RW |
1204 | } |
1205 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
1213 | static void process_node_changes(struct isis_area *area, int level, |
1214 | struct sr_node *srn) | |
26f6acaf RW |
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 | ||
26f6acaf | 1222 | /* |
f2333421 OD |
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. | |
26f6acaf RW |
1225 | */ |
1226 | adjacent = isis_adj_exists(area, level, sysid); | |
cab10e86 OD |
1227 | switch (srn->state) { |
1228 | case SRDB_STATE_NEW: | |
1229 | case SRDB_STATE_MODIFIED: | |
b407c77a OD |
1230 | sr_debug("ISIS-Sr (%s): Create/Update SR node %s", |
1231 | area->area_tag, sysid_print(srn->sysid)); | |
26f6acaf | 1232 | if (adjacent) |
b407c77a | 1233 | sr_node_srgb_update(area, level, sysid); |
cab10e86 OD |
1234 | break; |
1235 | case SRDB_STATE_UNCHANGED: | |
1236 | break; | |
1237 | default: | |
f2333421 | 1238 | /* SR capabilities have been removed. Delete SR-Node */ |
b407c77a OD |
1239 | sr_debug("ISIS-Sr (%s): Remove SR node %s", area->area_tag, |
1240 | sysid_print(srn->sysid)); | |
26f6acaf | 1241 | |
f2333421 OD |
1242 | sr_node_del(area, level, srn); |
1243 | /* and Update remaining Prefix-SID from all remaining SR Node */ | |
26f6acaf | 1244 | if (adjacent) |
b1d80d43 | 1245 | sr_node_srgb_update(area, level, sysid); |
26f6acaf RW |
1246 | return; |
1247 | } | |
1248 | ||
f2333421 | 1249 | /* Validate SRDB State for next LSPDB parsing */ |
cab10e86 | 1250 | srn->state = SRDB_STATE_VALIDATED; |
26f6acaf | 1251 | |
f2333421 | 1252 | /* Finally, process all Prefix-SID of this SR Node */ |
cab10e86 | 1253 | frr_each_safe (srdb_node_prefix, &srn->prefix_sids, srp) |
b1d80d43 | 1254 | process_prefix_changes(srn, srp); |
26f6acaf RW |
1255 | } |
1256 | ||
f2333421 OD |
1257 | /** |
1258 | * Parse and process all SR-related Sub-TLVs after running the SPF algorithm. | |
1259 | * | |
1260 | * @param area IS-IS area | |
1261 | */ | |
26f6acaf RW |
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. */ | |
b1d80d43 | 1270 | parse_lspdb(area); |
26f6acaf RW |
1271 | |
1272 | /* Process possible SR-related changes in the LDPSB. */ | |
1273 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { | |
b1d80d43 OD |
1274 | frr_each_safe (srdb_node, &area->srdb.sr_nodes[level - 1], srn) |
1275 | process_node_changes(area, level, srn); | |
26f6acaf RW |
1276 | } |
1277 | } | |
1278 | ||
f2333421 | 1279 | /** |
26f6acaf RW |
1280 | * Once a route is updated in the SPT, reinstall or uninstall its corresponding |
1281 | * Prefix-SID (if any). | |
f2333421 OD |
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 | |
26f6acaf | 1288 | */ |
b1d80d43 OD |
1289 | static int sr_route_update(struct isis_area *area, struct prefix *prefix, |
1290 | struct isis_route_info *route_info) | |
26f6acaf RW |
1291 | { |
1292 | struct sr_prefix *srp; | |
1293 | ||
1294 | if (!area->srdb.enabled) | |
1295 | return 0; | |
1296 | ||
b407c77a OD |
1297 | sr_debug("ISIS-Sr (%s): Update route for prefix %pFX", area->area_tag, |
1298 | prefix); | |
1299 | ||
f2333421 | 1300 | /* Lookup to Segment Routing Prefix for this prefix */ |
26f6acaf RW |
1301 | switch (area->is_type) { |
1302 | case IS_LEVEL_1: | |
b1d80d43 | 1303 | srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix); |
26f6acaf RW |
1304 | break; |
1305 | case IS_LEVEL_2: | |
b1d80d43 | 1306 | srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix); |
26f6acaf RW |
1307 | break; |
1308 | case IS_LEVEL_1_AND_2: | |
b1d80d43 | 1309 | srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix); |
26f6acaf | 1310 | if (!srp) |
b1d80d43 | 1311 | srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix); |
26f6acaf RW |
1312 | break; |
1313 | default: | |
1314 | flog_err(EC_LIB_DEVELOPMENT, "%s: unknown area level", | |
1315 | __func__); | |
1316 | exit(1); | |
1317 | } | |
1318 | ||
f2333421 | 1319 | /* Skip NULL or local Segment Routing Prefix */ |
26f6acaf RW |
1320 | if (!srp || srp->type == ISIS_SR_PREFIX_LOCAL) |
1321 | return 0; | |
1322 | ||
f2333421 | 1323 | /* Install or unintall Prefix-SID if route is Active or not */ |
26f6acaf | 1324 | if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) { |
c3f7b406 OD |
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 | */ | |
b1d80d43 | 1330 | sr_prefix_reinstall(srp, true); |
26f6acaf RW |
1331 | srp->u.remote.rinfo = route_info; |
1332 | } else { | |
b1d80d43 | 1333 | sr_prefix_uninstall(srp); |
26f6acaf RW |
1334 | srp->u.remote.rinfo = NULL; |
1335 | } | |
1336 | ||
1337 | return 0; | |
1338 | } | |
1339 | ||
d8391312 OD |
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 | */ | |
58fbcdf2 | 1348 | static int sr_local_block_init(struct isis_area *area) |
d8391312 OD |
1349 | { |
1350 | struct isis_sr_db *srdb = &area->srdb; | |
1351 | struct sr_local_block *srlb = &srdb->srlb; | |
1352 | ||
58fbcdf2 OD |
1353 | /* Check if SRLB is not already configured */ |
1354 | if (srlb->active) | |
1355 | return 0; | |
1356 | ||
d8391312 OD |
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 | |
58fbcdf2 OD |
1364 | - srdb->config.srlb_lower_bound + 1)) { |
1365 | srlb->active = false; | |
1366 | return -1; | |
1367 | } | |
d8391312 OD |
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); | |
58fbcdf2 OD |
1382 | srlb->active = true; |
1383 | ||
1384 | return 0; | |
d8391312 OD |
1385 | } |
1386 | ||
1387 | /** | |
1388 | * Remove Segment Routing Local Block. | |
1389 | * | |
1390 | * @param area IS-IS area | |
1391 | */ | |
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 | ||
58fbcdf2 OD |
1397 | /* Check if SRLB is not already delete */ |
1398 | if (!srlb->active) | |
1399 | return; | |
1400 | ||
d8391312 OD |
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); | |
58fbcdf2 | 1411 | srlb->active = false; |
d8391312 OD |
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 | */ | |
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 | */ | |
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 | ||
f2333421 | 1497 | /* --- Segment Routing Adjacency-SID management functions ------------------- */ |
26f6acaf | 1498 | |
f2333421 OD |
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 | */ | |
b1d80d43 OD |
1506 | static void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, |
1507 | bool backup) | |
26f6acaf RW |
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; | |
b1d80d43 | 1516 | mpls_label_t input_label; |
26f6acaf | 1517 | |
b407c77a OD |
1518 | sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag, |
1519 | backup ? "Backup" : "Primary"); | |
1520 | ||
f2333421 | 1521 | /* Determine nexthop IP address */ |
26f6acaf RW |
1522 | switch (family) { |
1523 | case AF_INET: | |
1524 | if (!circuit->ip_router) | |
1525 | return; | |
1526 | ||
1527 | nexthop.ipv4 = adj->ipv4_addresses[0]; | |
1528 | break; | |
1529 | case AF_INET6: | |
1530 | if (!circuit->ipv6_router) | |
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 | ||
f2333421 | 1541 | /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */ |
26f6acaf RW |
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 | ||
d8391312 OD |
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 | ||
26f6acaf RW |
1553 | if (circuit->ext == NULL) |
1554 | circuit->ext = isis_alloc_ext_subtlvs(); | |
1555 | ||
26f6acaf RW |
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; | |
b1d80d43 | 1560 | sra->nexthop.label = input_label; |
26f6acaf | 1561 | switch (circuit->circ_type) { |
f2333421 | 1562 | /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */ |
26f6acaf RW |
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)); | |
b1d80d43 | 1570 | ladj_sid->sid = input_label; |
26f6acaf RW |
1571 | isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid); |
1572 | sra->u.ladj_sid = ladj_sid; | |
1573 | break; | |
f2333421 | 1574 | /* Adjacency-SID for Point to Point interface section #2.2.1 */ |
26f6acaf RW |
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; | |
b1d80d43 | 1580 | adj_sid->sid = input_label; |
26f6acaf RW |
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 | } | |
f2333421 OD |
1589 | |
1590 | /* Add Adjacency-SID in SRDB */ | |
26f6acaf RW |
1591 | sra->adj = adj; |
1592 | listnode_add(area->srdb.adj_sids, sra); | |
1593 | listnode_add(adj->adj_sids, sra); | |
1594 | ||
c3f7b406 | 1595 | isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra); |
26f6acaf RW |
1596 | } |
1597 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 1604 | static void sr_adj_sid_add(struct isis_adjacency *adj, int family) |
26f6acaf | 1605 | { |
b1d80d43 OD |
1606 | sr_adj_sid_add_single(adj, family, false); |
1607 | sr_adj_sid_add_single(adj, family, true); | |
26f6acaf RW |
1608 | } |
1609 | ||
d8391312 OD |
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 | ||
f2333421 OD |
1640 | /** |
1641 | * Delete local Adj-SID. | |
1642 | * | |
1643 | * @param sra Segment Routing Adjacency | |
1644 | */ | |
b1d80d43 | 1645 | static void sr_adj_sid_del(struct sr_adjacency *sra) |
26f6acaf RW |
1646 | { |
1647 | struct isis_circuit *circuit = sra->adj->circuit; | |
1648 | struct isis_area *area = circuit->area; | |
1649 | ||
b407c77a | 1650 | sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag); |
26f6acaf | 1651 | |
c3f7b406 | 1652 | isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra); |
26f6acaf | 1653 | |
f2333421 | 1654 | /* Release dynamic label and remove subTLVs */ |
26f6acaf RW |
1655 | switch (circuit->circ_type) { |
1656 | case CIRCUIT_T_BROADCAST: | |
d8391312 OD |
1657 | sr_local_block_release_label(&area->srdb.srlb, |
1658 | sra->u.ladj_sid->sid); | |
26f6acaf RW |
1659 | isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid); |
1660 | break; | |
1661 | case CIRCUIT_T_P2P: | |
d8391312 OD |
1662 | sr_local_block_release_label(&area->srdb.srlb, |
1663 | sra->u.adj_sid->sid); | |
26f6acaf RW |
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 | ||
f2333421 | 1672 | /* Remove Adjacency-SID from the SRDB */ |
26f6acaf RW |
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 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 1685 | static int sr_adj_state_change(struct isis_adjacency *adj) |
26f6acaf RW |
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)) | |
b1d80d43 | 1697 | sr_adj_sid_del(sra); |
26f6acaf RW |
1698 | |
1699 | return 0; | |
1700 | } | |
1701 | ||
f2333421 OD |
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 | |
26f6acaf | 1710 | */ |
b1d80d43 | 1711 | static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family) |
26f6acaf RW |
1712 | { |
1713 | if (!adj->circuit->area->srdb.enabled) | |
1714 | return 0; | |
1715 | ||
b1d80d43 | 1716 | sr_adj_sid_add(adj, family); |
26f6acaf RW |
1717 | |
1718 | return 0; | |
1719 | } | |
1720 | ||
f2333421 OD |
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 | |
26f6acaf | 1729 | */ |
b1d80d43 | 1730 | static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family) |
26f6acaf RW |
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) | |
b1d80d43 | 1740 | sr_adj_sid_del(sra); |
26f6acaf RW |
1741 | |
1742 | return 0; | |
1743 | } | |
1744 | ||
f2333421 OD |
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 | */ | |
b1d80d43 | 1752 | static int sr_if_new_hook(struct interface *ifp) |
26f6acaf RW |
1753 | { |
1754 | struct isis_circuit *circuit; | |
1755 | struct isis_area *area; | |
1756 | struct connected *connected; | |
1757 | struct listnode *node; | |
1758 | ||
f2333421 | 1759 | /* Get corresponding circuit */ |
26f6acaf RW |
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 | ||
b1d80d43 | 1780 | if (sr_prefix_is_node_sid(ifp, &pcfg->prefix) |
26f6acaf RW |
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 | ||
f2333421 | 1790 | /* --- Segment Routing Show information functions --------------------------- */ |
26f6acaf | 1791 | |
c0083e53 OD |
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 | */ | |
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 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
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) | |
26f6acaf RW |
1842 | { |
1843 | const struct sr_nexthop_info *srnh = &srp->u.local.info; | |
1844 | char buf_prefix[BUFSIZ]; | |
c0083e53 OD |
1845 | char buf_oper[BUFSIZ]; |
1846 | char buf_iface[BUFSIZ]; | |
26f6acaf RW |
1847 | char buf_uptime[BUFSIZ]; |
1848 | ||
26f6acaf | 1849 | if (srnh->label != MPLS_INVALID_LABEL) { |
c0083e53 OD |
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), "-"); | |
26f6acaf RW |
1856 | log_uptime(srnh->uptime, buf_uptime, sizeof(buf_uptime)); |
1857 | } else { | |
c0083e53 | 1858 | snprintf(buf_iface, sizeof(buf_iface), "-"); |
26f6acaf RW |
1859 | snprintf(buf_uptime, sizeof(buf_uptime), "-"); |
1860 | } | |
c0083e53 OD |
1861 | sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label, |
1862 | MPLS_LABEL_IMPLICIT_NULL); | |
26f6acaf | 1863 | |
c0083e53 | 1864 | ttable_add_row(tt, "%s|%u|%s|-|%s|%s", |
26f6acaf | 1865 | prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)), |
c0083e53 | 1866 | srp->sid.value, buf_oper, buf_iface, buf_uptime); |
26f6acaf RW |
1867 | } |
1868 | ||
f2333421 OD |
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 | */ | |
b1d80d43 OD |
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) | |
26f6acaf RW |
1880 | { |
1881 | struct isis_nexthop *nexthop; | |
1882 | struct listnode *node; | |
1883 | char buf_prefix[BUFSIZ]; | |
c0083e53 | 1884 | char buf_oper[BUFSIZ]; |
26f6acaf RW |
1885 | char buf_nhop[BUFSIZ]; |
1886 | char buf_iface[BUFSIZ]; | |
26f6acaf RW |
1887 | char buf_uptime[BUFSIZ]; |
1888 | bool first = true; | |
1889 | ||
1890 | (void)prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)); | |
26f6acaf RW |
1891 | |
1892 | if (!srp->u.remote.rinfo) { | |
1893 | ttable_add_row(tt, "%s|%u|%s|-|-|-", buf_prefix, srp->sid.value, | |
c0083e53 OD |
1894 | sr_op2str(buf_oper, sizeof(buf_oper), |
1895 | srp->input_label, | |
1896 | MPLS_LABEL_IMPLICIT_NULL)); | |
26f6acaf RW |
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); | |
c0083e53 | 1912 | if (nexthop->sr.label == MPLS_INVALID_LABEL) |
26f6acaf | 1913 | snprintf(buf_uptime, sizeof(buf_uptime), "-"); |
c0083e53 | 1914 | else |
26f6acaf RW |
1915 | log_uptime(nexthop->sr.uptime, buf_uptime, |
1916 | sizeof(buf_uptime)); | |
c0083e53 OD |
1917 | sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label, |
1918 | nexthop->sr.label); | |
26f6acaf RW |
1919 | |
1920 | if (first) | |
c0083e53 OD |
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); | |
26f6acaf | 1924 | else |
c0083e53 OD |
1925 | ttable_add_row(tt, "|||%s|%s|%s|%s", buf_oper, buf_nhop, |
1926 | buf_iface, buf_uptime); | |
26f6acaf RW |
1927 | first = false; |
1928 | } | |
1929 | } | |
1930 | ||
f2333421 OD |
1931 | /** |
1932 | * Show Prefix-SIDs. | |
1933 | * | |
1934 | * @param vty VTY output | |
1935 | * @param area IS-IS area | |
1936 | * @param level IS-IS level | |
1937 | */ | |
b1d80d43 | 1938 | static void show_prefix_sids(struct vty *vty, struct isis_area *area, int level) |
26f6acaf RW |
1939 | { |
1940 | struct sr_prefix *srp; | |
1941 | struct ttable *tt; | |
1942 | ||
cab10e86 | 1943 | if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0) |
26f6acaf RW |
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]); | |
c0083e53 | 1950 | ttable_add_row(tt, "Prefix|SID|Label Op.|Nexthop|Interface|Uptime"); |
26f6acaf RW |
1951 | tt->style.cell.rpad = 2; |
1952 | tt->style.corner = '+'; | |
1953 | ttable_restyle(tt); | |
1954 | ttable_rowseps(tt, 0, BOTTOM, true, '-'); | |
1955 | ||
f2333421 | 1956 | /* Process all Prefix-SID from the SRDB */ |
b1d80d43 | 1957 | frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) { |
26f6acaf RW |
1958 | switch (srp->type) { |
1959 | case ISIS_SR_PREFIX_LOCAL: | |
b1d80d43 | 1960 | show_prefix_sid_local(vty, tt, area, srp); |
26f6acaf RW |
1961 | break; |
1962 | case ISIS_SR_PREFIX_REMOTE: | |
b1d80d43 | 1963 | show_prefix_sid_remote(vty, tt, area, srp); |
26f6acaf RW |
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 | ||
f2333421 | 1979 | /** |
c0083e53 | 1980 | * Declaration of new show commands. |
f2333421 | 1981 | */ |
26f6acaf RW |
1982 | DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd, |
1983 | "show isis segment-routing prefix-sids", | |
1984 | SHOW_STR PROTO_HELP | |
1985 | "Segment-Routing\n" | |
1986 | "Segment-Routing Prefix-SIDs\n") | |
1987 | { | |
1988 | struct listnode *node; | |
1989 | struct isis_area *area; | |
1990 | ||
1991 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { | |
1992 | vty_out(vty, "Area %s:\n", | |
1993 | area->area_tag ? area->area_tag : "null"); | |
1994 | ||
1995 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) | |
b1d80d43 | 1996 | show_prefix_sids(vty, area, level); |
26f6acaf RW |
1997 | } |
1998 | ||
1999 | return CMD_SUCCESS; | |
2000 | } | |
2001 | ||
c0083e53 OD |
2002 | /** |
2003 | * Show Segment Routing Node. | |
2004 | * | |
2005 | * @param vty VTY output | |
2006 | * @param area IS-IS area | |
2007 | * @param level IS-IS level | |
2008 | */ | |
2009 | static void show_node(struct vty *vty, struct isis_area *area, int level) | |
2010 | { | |
2011 | struct sr_node *srn; | |
2012 | struct ttable *tt; | |
2013 | ||
2014 | if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0) | |
2015 | return; | |
2016 | ||
2017 | vty_out(vty, " IS-IS %s SR-Node:\n\n", circuit_t2string(level)); | |
2018 | ||
2019 | /* Prepare table. */ | |
2020 | tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); | |
d8391312 | 2021 | ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD"); |
c0083e53 OD |
2022 | tt->style.cell.rpad = 2; |
2023 | tt->style.corner = '+'; | |
2024 | ttable_restyle(tt); | |
2025 | ttable_rowseps(tt, 0, BOTTOM, true, '-'); | |
2026 | ||
2027 | /* Process all SR-Node from the SRDB */ | |
2028 | frr_each (srdb_node, &area->srdb.sr_nodes[level - 1], srn) { | |
d8391312 OD |
2029 | ttable_add_row( |
2030 | tt, "%s|%u - %u|%u - %u|%s|%u", | |
2031 | sysid_print(srn->sysid), | |
2032 | srn->cap.srgb.lower_bound, | |
2033 | srn->cap.srgb.lower_bound + srn->cap.srgb.range_size | |
2034 | - 1, | |
2035 | srn->cap.srlb.lower_bound, | |
2036 | srn->cap.srlb.lower_bound + srn->cap.srlb.range_size | |
2037 | - 1, | |
2038 | srn->cap.algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF", | |
2039 | srn->cap.msd); | |
c0083e53 OD |
2040 | } |
2041 | ||
2042 | /* Dump the generated table. */ | |
2043 | if (tt->nrows > 1) { | |
2044 | char *table; | |
2045 | ||
2046 | table = ttable_dump(tt, "\n"); | |
2047 | vty_out(vty, "%s\n", table); | |
2048 | XFREE(MTYPE_TMP, table); | |
2049 | } | |
2050 | ttable_del(tt); | |
2051 | } | |
2052 | ||
2053 | DEFUN(show_sr_node, show_sr_node_cmd, | |
2054 | "show isis segment-routing node", | |
2055 | SHOW_STR PROTO_HELP | |
2056 | "Segment-Routing\n" | |
2057 | "Segment-Routing node\n") | |
2058 | { | |
2059 | struct listnode *node; | |
2060 | struct isis_area *area; | |
2061 | ||
2062 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { | |
2063 | vty_out(vty, "Area %s:\n", | |
2064 | area->area_tag ? area->area_tag : "null"); | |
2065 | ||
2066 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) | |
2067 | show_node(vty, area, level); | |
2068 | } | |
2069 | ||
2070 | return CMD_SUCCESS; | |
2071 | } | |
2072 | ||
2073 | ||
f2333421 | 2074 | /* --- IS-IS Segment Routing Management function ---------------------------- */ |
26f6acaf | 2075 | |
58fbcdf2 OD |
2076 | /** |
2077 | * Thread function to re-attempt connection to the Label Manager and thus be | |
2078 | * able to start Segment Routing. | |
2079 | * | |
2080 | * @param start Thread structure that contains area as argument | |
2081 | * | |
2082 | * @return 1 on success | |
2083 | */ | |
2084 | static int sr_start_label_manager(struct thread *start) | |
2085 | { | |
2086 | struct isis_area *area; | |
2087 | ||
2088 | area = THREAD_ARG(start); | |
2089 | ||
2090 | /* re-attempt to start SR & Label Manager connection */ | |
2091 | isis_sr_start(area); | |
2092 | ||
2093 | return 1; | |
2094 | } | |
2095 | ||
f2333421 OD |
2096 | /** |
2097 | * Enable SR on the given IS-IS area. | |
2098 | * | |
2099 | * @param area IS-IS area | |
2100 | * | |
2101 | * @return 0 on success, -1 otherwise | |
2102 | */ | |
26f6acaf RW |
2103 | int isis_sr_start(struct isis_area *area) |
2104 | { | |
2105 | struct isis_sr_db *srdb = &area->srdb; | |
2106 | struct isis_circuit *circuit; | |
2107 | struct listnode *node; | |
2108 | ||
58fbcdf2 OD |
2109 | /* First start Label Manager if not ready */ |
2110 | if (!isis_zebra_label_manager_ready()) | |
2111 | if (isis_zebra_label_manager_connect() < 0) { | |
2112 | /* Re-attempt to connect to Label Manager in 1 sec. */ | |
2113 | thread_add_timer(master, sr_start_label_manager, area, | |
2114 | 1, &srdb->t_start_lm); | |
2115 | return -1; | |
2116 | } | |
2117 | ||
2118 | /* Label Manager is ready, initialize the SRLB */ | |
2119 | if (sr_local_block_init(area) < 0) | |
d8391312 OD |
2120 | return -1; |
2121 | ||
26f6acaf | 2122 | /* |
58fbcdf2 OD |
2123 | * Request SGRB to the label manager if not already active. If the |
2124 | * allocation fails, return an error to disable SR until a new SRGB | |
2125 | * is successfully allocated. | |
26f6acaf | 2126 | */ |
58fbcdf2 OD |
2127 | if (!srdb->srgb_active) { |
2128 | if (isis_zebra_request_label_range( | |
2129 | srdb->config.srgb_lower_bound, | |
2130 | srdb->config.srgb_upper_bound | |
2131 | - srdb->config.srgb_lower_bound + 1) | |
2132 | < 0) { | |
2133 | srdb->srgb_active = false; | |
2134 | return -1; | |
2135 | } else | |
2136 | srdb->srgb_active = true; | |
2137 | } | |
26f6acaf | 2138 | |
b407c77a OD |
2139 | sr_debug("ISIS-Sr: Starting Segment Routing for area %s", |
2140 | area->area_tag); | |
26f6acaf | 2141 | |
f2333421 | 2142 | /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */ |
26f6acaf RW |
2143 | for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { |
2144 | struct isis_adjacency *adj; | |
2145 | struct listnode *anode; | |
2146 | ||
2147 | switch (circuit->circ_type) { | |
2148 | case CIRCUIT_T_BROADCAST: | |
2149 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; | |
2150 | level++) { | |
2151 | for (ALL_LIST_ELEMENTS_RO( | |
2152 | circuit->u.bc.adjdb[level - 1], | |
2153 | anode, adj)) { | |
2154 | if (adj->ipv4_address_count > 0) | |
b1d80d43 | 2155 | sr_adj_sid_add(adj, AF_INET); |
26f6acaf | 2156 | if (adj->ipv6_address_count > 0) |
b1d80d43 | 2157 | sr_adj_sid_add(adj, AF_INET6); |
26f6acaf RW |
2158 | } |
2159 | } | |
2160 | break; | |
2161 | case CIRCUIT_T_P2P: | |
2162 | adj = circuit->u.p2p.neighbor; | |
2163 | if (adj && adj->ipv4_address_count > 0) | |
b1d80d43 | 2164 | sr_adj_sid_add(adj, AF_INET); |
26f6acaf | 2165 | if (adj && adj->ipv6_address_count > 0) |
b1d80d43 | 2166 | sr_adj_sid_add(adj, AF_INET6); |
26f6acaf RW |
2167 | break; |
2168 | default: | |
2169 | break; | |
2170 | } | |
2171 | } | |
2172 | ||
58fbcdf2 OD |
2173 | area->srdb.enabled = true; |
2174 | ||
f2333421 | 2175 | /* Regenerate LSPs to advertise Segment Routing capabilities. */ |
26f6acaf RW |
2176 | lsp_regenerate_schedule(area, area->is_type, 0); |
2177 | ||
2178 | return 0; | |
2179 | } | |
2180 | ||
f2333421 OD |
2181 | /** |
2182 | * Disable SR on the given IS-IS area. | |
2183 | * | |
2184 | * @param area IS-IS area | |
2185 | */ | |
26f6acaf RW |
2186 | void isis_sr_stop(struct isis_area *area) |
2187 | { | |
2188 | struct isis_sr_db *srdb = &area->srdb; | |
2189 | struct sr_adjacency *sra; | |
2190 | struct listnode *node, *nnode; | |
2191 | ||
b407c77a OD |
2192 | sr_debug("ISIS-Sr: Stopping Segment Routing for area %s", |
2193 | area->area_tag); | |
26f6acaf | 2194 | |
58fbcdf2 OD |
2195 | /* Disable any re-attempt to connect to Label Manager */ |
2196 | THREAD_TIMER_OFF(srdb->t_start_lm); | |
2197 | ||
f2333421 | 2198 | /* Uninstall all local Adjacency-SIDs. */ |
26f6acaf | 2199 | for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) |
b1d80d43 | 2200 | sr_adj_sid_del(sra); |
26f6acaf | 2201 | |
f2333421 | 2202 | /* Uninstall all Prefix-SIDs from all SR Node. */ |
26f6acaf | 2203 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { |
cab10e86 | 2204 | while (srdb_node_count(&srdb->sr_nodes[level - 1]) > 0) { |
26f6acaf RW |
2205 | struct sr_node *srn; |
2206 | ||
cab10e86 | 2207 | srn = srdb_node_first(&srdb->sr_nodes[level - 1]); |
b1d80d43 | 2208 | sr_node_del(area, level, srn); |
26f6acaf RW |
2209 | } |
2210 | } | |
2211 | ||
58fbcdf2 OD |
2212 | /* Release SRGB if active. */ |
2213 | if (srdb->srgb_active) { | |
2214 | isis_zebra_release_label_range(srdb->config.srgb_lower_bound, | |
2215 | srdb->config.srgb_upper_bound); | |
2216 | srdb->srgb_active = false; | |
2217 | } | |
26f6acaf | 2218 | |
d8391312 OD |
2219 | /* Delete SRLB */ |
2220 | sr_local_block_delete(area); | |
2221 | ||
58fbcdf2 OD |
2222 | area->srdb.enabled = false; |
2223 | ||
f2333421 | 2224 | /* Regenerate LSPs to advertise that the Node is no more SR enable. */ |
26f6acaf RW |
2225 | lsp_regenerate_schedule(area, area->is_type, 0); |
2226 | } | |
2227 | ||
f2333421 OD |
2228 | /** |
2229 | * IS-IS Segment Routing initialization for given area. | |
2230 | * | |
2231 | * @param area IS-IS area | |
2232 | */ | |
26f6acaf RW |
2233 | void isis_sr_area_init(struct isis_area *area) |
2234 | { | |
2235 | struct isis_sr_db *srdb = &area->srdb; | |
2236 | ||
b407c77a OD |
2237 | sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB", |
2238 | area->area_tag); | |
2239 | ||
f2333421 | 2240 | /* Initialize Segment Routing Data Base */ |
26f6acaf | 2241 | memset(srdb, 0, sizeof(*srdb)); |
26f6acaf RW |
2242 | srdb->adj_sids = list_new(); |
2243 | ||
2244 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { | |
cab10e86 OD |
2245 | srdb_node_init(&srdb->sr_nodes[level - 1]); |
2246 | srdb_area_prefix_init(&srdb->prefix_sids[level - 1]); | |
26f6acaf RW |
2247 | } |
2248 | ||
2249 | /* Pull defaults from the YANG module. */ | |
2250 | #ifndef FABRICD | |
2251 | srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR); | |
2252 | srdb->config.srgb_lower_bound = | |
2253 | yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR); | |
2254 | srdb->config.srgb_upper_bound = | |
2255 | yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR); | |
d8391312 OD |
2256 | srdb->config.srlb_lower_bound = |
2257 | yang_get_default_uint32("%s/srlb/lower-bound", ISIS_SR); | |
2258 | srdb->config.srlb_upper_bound = | |
2259 | yang_get_default_uint32("%s/srlb/upper-bound", ISIS_SR); | |
26f6acaf RW |
2260 | #else |
2261 | srdb->config.enabled = false; | |
2262 | srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND; | |
2263 | srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND; | |
d8391312 OD |
2264 | srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND; |
2265 | srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND; | |
26f6acaf RW |
2266 | #endif |
2267 | srdb->config.msd = 0; | |
cab10e86 | 2268 | srdb_prefix_cfg_init(&srdb->config.prefix_sids); |
26f6acaf RW |
2269 | } |
2270 | ||
f2333421 OD |
2271 | /** |
2272 | * Terminate IS-IS Segment Routing for the given area. | |
2273 | * | |
2274 | * @param area IS-IS area | |
2275 | */ | |
26f6acaf RW |
2276 | void isis_sr_area_term(struct isis_area *area) |
2277 | { | |
2278 | struct isis_sr_db *srdb = &area->srdb; | |
2279 | ||
2280 | /* Stop Segment Routing */ | |
2281 | if (area->srdb.enabled) | |
2282 | isis_sr_stop(area); | |
2283 | ||
2284 | /* Clear Prefix-SID configuration. */ | |
cab10e86 | 2285 | while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) { |
26f6acaf RW |
2286 | struct sr_prefix_cfg *pcfg; |
2287 | ||
cab10e86 | 2288 | pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids); |
26f6acaf RW |
2289 | isis_sr_cfg_prefix_del(pcfg); |
2290 | } | |
2291 | } | |
2292 | ||
f2333421 OD |
2293 | /** |
2294 | * IS-IS Segment Routing global initialization. | |
2295 | */ | |
26f6acaf RW |
2296 | void isis_sr_init(void) |
2297 | { | |
2298 | install_element(VIEW_NODE, &show_sr_prefix_sids_cmd); | |
c0083e53 | 2299 | install_element(VIEW_NODE, &show_sr_node_cmd); |
26f6acaf RW |
2300 | |
2301 | /* Register hooks. */ | |
b1d80d43 OD |
2302 | hook_register(isis_adj_state_change_hook, sr_adj_state_change); |
2303 | hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); | |
2304 | hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); | |
2305 | hook_register(isis_route_update_hook, sr_route_update); | |
2306 | hook_register(isis_if_new_hook, sr_if_new_hook); | |
26f6acaf RW |
2307 | } |
2308 | ||
f2333421 OD |
2309 | /** |
2310 | * IS-IS Segment Routing global terminate. | |
2311 | */ | |
26f6acaf RW |
2312 | void isis_sr_term(void) |
2313 | { | |
2314 | /* Unregister hooks. */ | |
b1d80d43 OD |
2315 | hook_unregister(isis_adj_state_change_hook, sr_adj_state_change); |
2316 | hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); | |
2317 | hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); | |
2318 | hook_unregister(isis_route_update_hook, sr_route_update); | |
2319 | hook_unregister(isis_if_new_hook, sr_if_new_hook); | |
26f6acaf | 2320 | } |