]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
26f6acaf | 2 | /* |
f2333421 | 3 | * This is an implementation of Segment Routing for IS-IS as per RFC 8667 |
26f6acaf | 4 | * |
f2333421 | 5 | * Copyright (C) 2019 Orange http://www.orange.com |
26f6acaf RW |
6 | * |
7 | * Author: Olivier Dugeon <olivier.dugeon@orange.com> | |
8 | * Contributor: Renato Westphal <renato@opensourcerouting.org> for NetDEF | |
26f6acaf RW |
9 | */ |
10 | ||
11 | #include <zebra.h> | |
12 | ||
13 | #include "if.h" | |
14 | #include "linklist.h" | |
15 | #include "log.h" | |
16 | #include "command.h" | |
17 | #include "termtable.h" | |
18 | #include "memory.h" | |
19 | #include "prefix.h" | |
20 | #include "table.h" | |
d47d6089 | 21 | #include "srcdest_table.h" |
26f6acaf RW |
22 | #include "vty.h" |
23 | #include "zclient.h" | |
24 | #include "lib/lib_errors.h" | |
25 | ||
26 | #include "isisd/isisd.h" | |
27 | #include "isisd/isis_spf.h" | |
28 | #include "isisd/isis_spf_private.h" | |
29 | #include "isisd/isis_adjacency.h" | |
30 | #include "isisd/isis_route.h" | |
31 | #include "isisd/isis_mt.h" | |
32 | #include "isisd/isis_sr.h" | |
33 | #include "isisd/isis_tlvs.h" | |
34 | #include "isisd/isis_misc.h" | |
35 | #include "isisd/isis_zebra.h" | |
36 | #include "isisd/isis_errors.h" | |
37 | ||
f2333421 | 38 | /* Local variables and functions */ |
bf8d3d6a | 39 | DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information"); |
26f6acaf | 40 | |
d8391312 OD |
41 | static void sr_local_block_delete(struct isis_area *area); |
42 | static int sr_local_block_init(struct isis_area *area); | |
43 | static void sr_adj_sid_update(struct sr_adjacency *sra, | |
44 | struct sr_local_block *srlb); | |
054fda12 | 45 | static void sr_adj_sid_del(struct sr_adjacency *sra); |
26f6acaf | 46 | |
f2333421 | 47 | /* --- RB-Tree Management functions ----------------------------------------- */ |
26f6acaf | 48 | |
f2333421 | 49 | /** |
d47d6089 | 50 | * Configured SR Prefix comparison for RB-Tree. |
f2333421 OD |
51 | * |
52 | * @param a First SR prefix | |
53 | * @param b Second SR prefix | |
54 | * | |
55 | * @return -1 (a < b), 0 (a == b) or +1 (a > b) | |
56 | */ | |
d47d6089 RW |
57 | static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a, |
58 | const struct sr_prefix_cfg *b) | |
26f6acaf RW |
59 | { |
60 | return prefix_cmp(&a->prefix, &b->prefix); | |
61 | } | |
d47d6089 | 62 | DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry, |
960b9a53 | 63 | sr_prefix_sid_cfg_compare); |
26f6acaf | 64 | |
f2333421 | 65 | /** |
d47d6089 | 66 | * Find SRGB associated to a System ID. |
f2333421 | 67 | * |
d47d6089 RW |
68 | * @param area IS-IS LSP database |
69 | * @param sysid System ID to lookup | |
f2333421 | 70 | * |
d47d6089 | 71 | * @return Pointer to SRGB if found, NULL otherwise |
f2333421 | 72 | */ |
d47d6089 RW |
73 | struct isis_sr_block *isis_sr_find_srgb(struct lspdb_head *lspdb, |
74 | const uint8_t *sysid) | |
26f6acaf | 75 | { |
d47d6089 RW |
76 | struct isis_lsp *lsp; |
77 | ||
78 | lsp = isis_root_system_lsp(lspdb, sysid); | |
79 | if (!lsp) | |
80 | return NULL; | |
81 | ||
82 | if (!lsp->tlvs->router_cap | |
83 | || lsp->tlvs->router_cap->srgb.range_size == 0) | |
84 | return NULL; | |
85 | ||
86 | return &lsp->tlvs->router_cap->srgb; | |
26f6acaf | 87 | } |
26f6acaf | 88 | |
f2333421 | 89 | /** |
d47d6089 | 90 | * Compute input label for the given Prefix-SID. |
f2333421 | 91 | * |
d47d6089 RW |
92 | * @param area IS-IS area |
93 | * @param psid IS-IS Prefix-SID Sub-TLV | |
94 | * @param local Indicates whether the Prefix-SID is local or not | |
f2333421 | 95 | * |
d47d6089 | 96 | * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow |
f2333421 | 97 | */ |
d47d6089 RW |
98 | mpls_label_t sr_prefix_in_label(struct isis_area *area, |
99 | struct isis_prefix_sid *psid, bool local) | |
26f6acaf | 100 | { |
d47d6089 RW |
101 | /* |
102 | * No need to assign a label for local Prefix-SIDs unless the no-PHP | |
103 | * flag is set. | |
104 | */ | |
105 | if (local | |
106 | && (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP) | |
107 | || CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL))) | |
108 | return MPLS_INVALID_LABEL; | |
109 | ||
110 | /* Return SID value as MPLS label if it is an Absolute SID */ | |
111 | if (CHECK_FLAG(psid->flags, | |
112 | ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) | |
113 | return psid->value; | |
114 | ||
115 | /* Check that SID index falls inside the SRGB */ | |
116 | if (psid->value >= (area->srdb.config.srgb_upper_bound | |
117 | - area->srdb.config.srgb_lower_bound + 1)) { | |
118 | flog_warn(EC_ISIS_SID_OVERFLOW, | |
119 | "%s: SID index %u falls outside local SRGB range", | |
120 | __func__, psid->value); | |
121 | return MPLS_INVALID_LABEL; | |
122 | } | |
123 | ||
124 | /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ | |
125 | return (area->srdb.config.srgb_lower_bound + psid->value); | |
126 | } | |
127 | ||
128 | /** | |
129 | * Compute output label for the given Prefix-SID. | |
130 | * | |
131 | * @param lspdb IS-IS LSP database | |
132 | * @param family Prefix-SID address family | |
133 | * @param psid Prefix-SID Sub-TLV | |
134 | * @param nh_sysid System ID of the nexthop node | |
135 | * @param last_hop Indicates whether the nexthop node is the last hop | |
136 | * | |
137 | * @return MPLS label or MPLS_INVALID_LABEL in case of error | |
138 | */ | |
139 | mpls_label_t sr_prefix_out_label(struct lspdb_head *lspdb, int family, | |
140 | struct isis_prefix_sid *psid, | |
141 | const uint8_t *nh_sysid, bool last_hop) | |
142 | { | |
143 | struct isis_sr_block *nh_srgb; | |
144 | ||
145 | if (last_hop) { | |
146 | if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP)) | |
147 | return MPLS_LABEL_IMPLICIT_NULL; | |
148 | ||
149 | if (CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) { | |
150 | if (family == AF_INET) | |
151 | return MPLS_LABEL_IPV4_EXPLICIT_NULL; | |
152 | else | |
153 | return MPLS_LABEL_IPV6_EXPLICIT_NULL; | |
154 | } | |
155 | /* Fallthrough */ | |
156 | } | |
157 | ||
158 | /* Return SID value as MPLS label if it is an Absolute SID */ | |
159 | if (CHECK_FLAG(psid->flags, | |
160 | ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) { | |
161 | /* | |
162 | * V/L SIDs have local significance, so only adjacent routers | |
163 | * can use them (RFC8667 section #2.1.1.1) | |
164 | */ | |
165 | if (!last_hop) | |
166 | return MPLS_INVALID_LABEL; | |
167 | return psid->value; | |
168 | } | |
169 | ||
170 | /* Check that SID index falls inside the SRGB */ | |
171 | nh_srgb = isis_sr_find_srgb(lspdb, nh_sysid); | |
172 | if (!nh_srgb) | |
173 | return MPLS_INVALID_LABEL; | |
174 | ||
175 | /* | |
176 | * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or | |
177 | * IPv6 packets. | |
178 | */ | |
179 | if ((family == AF_INET && !IS_SR_IPV4(nh_srgb)) | |
180 | || (family == AF_INET6 && !IS_SR_IPV6(nh_srgb))) | |
181 | return MPLS_INVALID_LABEL; | |
182 | ||
183 | if (psid->value >= nh_srgb->range_size) { | |
184 | flog_warn(EC_ISIS_SID_OVERFLOW, | |
185 | "%s: SID index %u falls outside remote SRGB range", | |
186 | __func__, psid->value); | |
187 | return MPLS_INVALID_LABEL; | |
188 | } | |
189 | ||
190 | /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ | |
191 | return (nh_srgb->lower_bound + psid->value); | |
26f6acaf | 192 | } |
26f6acaf | 193 | |
f2333421 | 194 | /* --- Functions used for Yang model and CLI to configure Segment Routing --- */ |
26f6acaf | 195 | |
f2333421 OD |
196 | /** |
197 | * Check if prefix correspond to a Node SID. | |
198 | * | |
199 | * @param ifp Interface | |
200 | * @param prefix Prefix to be checked | |
201 | * | |
202 | * @return True if the interface/address pair corresponds to a Node-SID | |
203 | */ | |
b1d80d43 OD |
204 | static bool sr_prefix_is_node_sid(const struct interface *ifp, |
205 | const struct prefix *prefix) | |
26f6acaf RW |
206 | { |
207 | return (if_is_loopback(ifp) && is_host_route(prefix)); | |
208 | } | |
209 | ||
f2333421 OD |
210 | /** |
211 | * Update local SRGB configuration. SRGB is reserved though Label Manager. | |
212 | * This function trigger the update of local Prefix-SID installation. | |
213 | * | |
214 | * @param area IS-IS area | |
215 | * @param lower_bound Lower bound of SRGB | |
216 | * @param upper_bound Upper bound of SRGB | |
217 | * | |
218 | * @return 0 on success, -1 otherwise | |
219 | */ | |
26f6acaf RW |
220 | int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound, |
221 | uint32_t upper_bound) | |
222 | { | |
223 | struct isis_sr_db *srdb = &area->srdb; | |
224 | ||
d8391312 OD |
225 | sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]", |
226 | area->area_tag, lower_bound, upper_bound); | |
b407c77a | 227 | |
58fbcdf2 OD |
228 | /* Just store new SRGB values if Label Manager is not available. |
229 | * SRGB will be configured later when SR start */ | |
230 | if (!isis_zebra_label_manager_ready()) { | |
231 | srdb->config.srgb_lower_bound = lower_bound; | |
232 | srdb->config.srgb_upper_bound = upper_bound; | |
233 | return 0; | |
234 | } | |
235 | ||
236 | /* Label Manager is ready, start by releasing the old SRGB. */ | |
237 | if (srdb->srgb_active) { | |
238 | isis_zebra_release_label_range(srdb->config.srgb_lower_bound, | |
26f6acaf | 239 | srdb->config.srgb_upper_bound); |
58fbcdf2 OD |
240 | srdb->srgb_active = false; |
241 | } | |
26f6acaf RW |
242 | |
243 | srdb->config.srgb_lower_bound = lower_bound; | |
244 | srdb->config.srgb_upper_bound = upper_bound; | |
245 | ||
246 | if (srdb->enabled) { | |
d8391312 | 247 | /* then request new SRGB if SR is enabled. */ |
26f6acaf RW |
248 | if (isis_zebra_request_label_range( |
249 | srdb->config.srgb_lower_bound, | |
250 | srdb->config.srgb_upper_bound | |
58fbcdf2 OD |
251 | - srdb->config.srgb_lower_bound + 1) < 0) { |
252 | srdb->srgb_active = false; | |
26f6acaf | 253 | return -1; |
58fbcdf2 OD |
254 | } else |
255 | srdb->srgb_active = true; | |
256 | ||
26f6acaf | 257 | |
d8391312 | 258 | sr_debug(" |- Got new SRGB [%u/%u]", |
b407c77a OD |
259 | srdb->config.srgb_lower_bound, |
260 | srdb->config.srgb_upper_bound); | |
261 | ||
26f6acaf RW |
262 | lsp_regenerate_schedule(area, area->is_type, 0); |
263 | } else if (srdb->config.enabled) { | |
264 | /* Try to enable SR again using the new SRGB. */ | |
58fbcdf2 | 265 | isis_sr_start(area); |
26f6acaf RW |
266 | } |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
d8391312 OD |
271 | /** |
272 | * Update Segment Routing Local Block range which is reserved though the | |
273 | * Label Manager. This function trigger the update of local Adjacency-SID | |
274 | * installation. | |
275 | * | |
276 | * @param area IS-IS area | |
277 | * @param lower_bound Lower bound of SRLB | |
278 | * @param upper_bound Upper bound of SRLB | |
279 | * | |
280 | * @return 0 on success, -1 otherwise | |
281 | */ | |
282 | int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, | |
283 | uint32_t upper_bound) | |
284 | { | |
285 | struct isis_sr_db *srdb = &area->srdb; | |
36944791 | 286 | struct listnode *node; |
d8391312 | 287 | struct sr_adjacency *sra; |
d8391312 OD |
288 | |
289 | sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]", | |
290 | area->area_tag, lower_bound, upper_bound); | |
291 | ||
58fbcdf2 OD |
292 | /* Just store new SRLB values if Label Manager is not available. |
293 | * SRLB will be configured later when SR start */ | |
294 | if (!isis_zebra_label_manager_ready()) { | |
295 | srdb->config.srlb_lower_bound = lower_bound; | |
296 | srdb->config.srlb_upper_bound = upper_bound; | |
297 | return 0; | |
298 | } | |
299 | ||
300 | /* LM is ready, start by deleting the old SRLB */ | |
d8391312 OD |
301 | sr_local_block_delete(area); |
302 | ||
303 | srdb->config.srlb_lower_bound = lower_bound; | |
304 | srdb->config.srlb_upper_bound = upper_bound; | |
305 | ||
58fbcdf2 | 306 | if (srdb->enabled) { |
d47d6089 RW |
307 | /* Initialize new SRLB */ |
308 | if (sr_local_block_init(area) != 0) | |
309 | return -1; | |
26f6acaf | 310 | |
d47d6089 RW |
311 | /* Reinstall local Adjacency-SIDs with new labels. */ |
312 | for (ALL_LIST_ELEMENTS_RO(area->srdb.adj_sids, node, sra)) | |
313 | sr_adj_sid_update(sra, &srdb->srlb); | |
26f6acaf | 314 | |
d47d6089 RW |
315 | /* Update and Flood LSP */ |
316 | lsp_regenerate_schedule(area, area->is_type, 0); | |
317 | } else if (srdb->config.enabled) { | |
318 | /* Try to enable SR again using the new SRLB. */ | |
319 | isis_sr_start(area); | |
26f6acaf | 320 | } |
d47d6089 RW |
321 | |
322 | return 0; | |
26f6acaf RW |
323 | } |
324 | ||
f2333421 | 325 | /** |
d47d6089 | 326 | * Add new Prefix-SID configuration to the SRDB. |
f2333421 | 327 | * |
d47d6089 RW |
328 | * @param area IS-IS area |
329 | * @param prefix Prefix to be added | |
330 | * | |
331 | * @return Newly added Prefix-SID configuration structure | |
f2333421 | 332 | */ |
d47d6089 RW |
333 | struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, |
334 | const struct prefix *prefix) | |
26f6acaf | 335 | { |
d47d6089 RW |
336 | struct sr_prefix_cfg *pcfg; |
337 | struct interface *ifp; | |
26f6acaf | 338 | |
d47d6089 | 339 | sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix); |
26f6acaf | 340 | |
d47d6089 RW |
341 | pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg)); |
342 | pcfg->prefix = *prefix; | |
343 | pcfg->area = area; | |
26f6acaf | 344 | |
d47d6089 RW |
345 | /* Pull defaults from the YANG module. */ |
346 | pcfg->sid_type = yang_get_default_enum( | |
347 | "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR); | |
348 | pcfg->last_hop_behavior = yang_get_default_enum( | |
349 | "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR); | |
26f6acaf | 350 | |
249c4457 | 351 | /* Mark as node Sid if the prefix is host and configured in loopback */ |
d47d6089 | 352 | ifp = if_lookup_prefix(prefix, VRF_DEFAULT); |
249c4457 | 353 | if (ifp && sr_prefix_is_node_sid(ifp, prefix)) |
d47d6089 | 354 | pcfg->node_sid = true; |
26f6acaf | 355 | |
d47d6089 RW |
356 | /* Save prefix-sid configuration. */ |
357 | srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg); | |
26f6acaf | 358 | |
d47d6089 | 359 | return pcfg; |
26f6acaf RW |
360 | } |
361 | ||
f2333421 | 362 | /** |
d47d6089 | 363 | * Removal of locally configured Prefix-SID. |
f2333421 | 364 | * |
d47d6089 | 365 | * @param pcfg Configured Prefix-SID |
f2333421 | 366 | */ |
d47d6089 | 367 | void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg) |
26f6acaf | 368 | { |
d47d6089 | 369 | struct isis_area *area = pcfg->area; |
26f6acaf | 370 | |
d47d6089 RW |
371 | sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u", |
372 | area->area_tag, &pcfg->prefix, | |
373 | pcfg->sid_type == SR_SID_VALUE_TYPE_INDEX ? "index" : "label", | |
374 | pcfg->sid); | |
375 | ||
376 | srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg); | |
377 | XFREE(MTYPE_ISIS_SR_INFO, pcfg); | |
26f6acaf RW |
378 | } |
379 | ||
f2333421 | 380 | /** |
d47d6089 | 381 | * Lookup for Prefix-SID in the local configuration. |
f2333421 | 382 | * |
d47d6089 RW |
383 | * @param area IS-IS area |
384 | * @param prefix Prefix to lookup | |
385 | * | |
386 | * @return Configured Prefix-SID structure if found, NULL otherwise | |
f2333421 | 387 | */ |
d47d6089 RW |
388 | struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area, |
389 | union prefixconstptr prefix) | |
26f6acaf | 390 | { |
d47d6089 | 391 | struct sr_prefix_cfg pcfg = {}; |
26f6acaf | 392 | |
d47d6089 RW |
393 | prefix_copy(&pcfg.prefix, prefix.p); |
394 | return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg); | |
26f6acaf RW |
395 | } |
396 | ||
f2333421 | 397 | /** |
d47d6089 | 398 | * Fill in Prefix-SID Sub-TLV according to the corresponding configuration. |
f2333421 | 399 | * |
d47d6089 RW |
400 | * @param pcfg Prefix-SID configuration |
401 | * @param external False if prefix is locally configured, true otherwise | |
402 | * @param psid Prefix-SID sub-TLV to be updated | |
f2333421 | 403 | */ |
d47d6089 RW |
404 | void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, |
405 | struct isis_prefix_sid *psid) | |
26f6acaf | 406 | { |
d47d6089 RW |
407 | /* Set SID algorithm. */ |
408 | psid->algorithm = SR_ALGORITHM_SPF; | |
26f6acaf | 409 | |
d47d6089 RW |
410 | /* Set SID flags. */ |
411 | psid->flags = 0; | |
412 | switch (pcfg->last_hop_behavior) { | |
413 | case SR_LAST_HOP_BEHAVIOR_EXP_NULL: | |
414 | SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP); | |
415 | SET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL); | |
cab10e86 | 416 | break; |
d47d6089 RW |
417 | case SR_LAST_HOP_BEHAVIOR_NO_PHP: |
418 | SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP); | |
419 | UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL); | |
420 | break; | |
421 | case SR_LAST_HOP_BEHAVIOR_PHP: | |
422 | UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP); | |
423 | UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL); | |
cab10e86 | 424 | break; |
26f6acaf | 425 | } |
d47d6089 RW |
426 | if (external) |
427 | SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED); | |
8f6a0d64 | 428 | if (pcfg->node_sid && !pcfg->n_flag_clear) |
d47d6089 | 429 | SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE); |
26f6acaf | 430 | |
d47d6089 RW |
431 | /* Set SID value. */ |
432 | psid->value = pcfg->sid; | |
433 | if (pcfg->sid_type == SR_SID_VALUE_TYPE_ABSOLUTE) { | |
434 | SET_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE); | |
435 | SET_FLAG(psid->flags, ISIS_PREFIX_SID_LOCAL); | |
436 | } | |
26f6acaf RW |
437 | } |
438 | ||
054fda12 RW |
439 | /** |
440 | * Delete all backup Adj-SIDs. | |
441 | * | |
442 | * @param area IS-IS area | |
443 | * @param level IS-IS level | |
444 | */ | |
445 | void isis_area_delete_backup_adj_sids(struct isis_area *area, int level) | |
446 | { | |
447 | struct sr_adjacency *sra; | |
448 | struct listnode *node, *nnode; | |
449 | ||
450 | for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) | |
451 | if (sra->type == ISIS_SR_LAN_BACKUP | |
452 | && (sra->adj->level & level)) | |
453 | sr_adj_sid_del(sra); | |
454 | } | |
455 | ||
d8391312 OD |
456 | /* --- Segment Routing Local Block management functions --------------------- */ |
457 | ||
458 | /** | |
459 | * Initialize Segment Routing Local Block from SRDB configuration and reserve | |
460 | * block of bits to manage label allocation. | |
461 | * | |
462 | * @param area IS-IS area | |
463 | */ | |
58fbcdf2 | 464 | static int sr_local_block_init(struct isis_area *area) |
d8391312 OD |
465 | { |
466 | struct isis_sr_db *srdb = &area->srdb; | |
467 | struct sr_local_block *srlb = &srdb->srlb; | |
468 | ||
58fbcdf2 OD |
469 | /* Check if SRLB is not already configured */ |
470 | if (srlb->active) | |
471 | return 0; | |
472 | ||
d8391312 OD |
473 | /* |
474 | * Request SRLB to the label manager. If the allocation fails, return | |
475 | * an error to disable SR until a new SRLB is successfully allocated. | |
476 | */ | |
477 | if (isis_zebra_request_label_range( | |
478 | srdb->config.srlb_lower_bound, | |
479 | srdb->config.srlb_upper_bound | |
58fbcdf2 OD |
480 | - srdb->config.srlb_lower_bound + 1)) { |
481 | srlb->active = false; | |
482 | return -1; | |
483 | } | |
d8391312 OD |
484 | |
485 | sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area->area_tag, | |
486 | srdb->config.srlb_lower_bound, srdb->config.srlb_upper_bound); | |
487 | ||
488 | /* Initialize the SRLB */ | |
489 | srlb->start = srdb->config.srlb_lower_bound; | |
490 | srlb->end = srdb->config.srlb_upper_bound; | |
491 | srlb->current = 0; | |
492 | /* Compute the needed Used Mark number and allocate them */ | |
493 | srlb->max_block = (srlb->end - srlb->start + 1) / SRLB_BLOCK_SIZE; | |
494 | if (((srlb->end - srlb->start + 1) % SRLB_BLOCK_SIZE) != 0) | |
495 | srlb->max_block++; | |
496 | srlb->used_mark = XCALLOC(MTYPE_ISIS_SR_INFO, | |
497 | srlb->max_block * SRLB_BLOCK_SIZE); | |
58fbcdf2 OD |
498 | srlb->active = true; |
499 | ||
500 | return 0; | |
d8391312 OD |
501 | } |
502 | ||
503 | /** | |
504 | * Remove Segment Routing Local Block. | |
505 | * | |
506 | * @param area IS-IS area | |
507 | */ | |
508 | static void sr_local_block_delete(struct isis_area *area) | |
509 | { | |
510 | struct isis_sr_db *srdb = &area->srdb; | |
511 | struct sr_local_block *srlb = &srdb->srlb; | |
512 | ||
58fbcdf2 OD |
513 | /* Check if SRLB is not already delete */ |
514 | if (!srlb->active) | |
515 | return; | |
516 | ||
d8391312 OD |
517 | sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area->area_tag, |
518 | srlb->start, srlb->end); | |
519 | ||
520 | /* First release the label block */ | |
521 | isis_zebra_release_label_range(srdb->config.srlb_lower_bound, | |
522 | srdb->config.srlb_upper_bound); | |
523 | ||
524 | /* Then reset SRLB structure */ | |
525 | if (srlb->used_mark != NULL) | |
526 | XFREE(MTYPE_ISIS_SR_INFO, srlb->used_mark); | |
58fbcdf2 | 527 | srlb->active = false; |
d8391312 OD |
528 | } |
529 | ||
530 | /** | |
531 | * Request a label from the Segment Routing Local Block. | |
532 | * | |
533 | * @param srlb Segment Routing Local Block | |
534 | * | |
535 | * @return First available label on success or MPLS_INVALID_LABEL if the | |
536 | * block of labels is full | |
537 | */ | |
538 | static mpls_label_t sr_local_block_request_label(struct sr_local_block *srlb) | |
539 | { | |
d8391312 OD |
540 | mpls_label_t label; |
541 | uint32_t index; | |
542 | uint32_t pos; | |
e90c0383 | 543 | uint32_t size = srlb->end - srlb->start + 1; |
d8391312 OD |
544 | |
545 | /* Check if we ran out of available labels */ | |
e90c0383 | 546 | if (srlb->current >= size) |
d8391312 OD |
547 | return MPLS_INVALID_LABEL; |
548 | ||
549 | /* Get first available label and mark it used */ | |
550 | label = srlb->current + srlb->start; | |
551 | index = srlb->current / SRLB_BLOCK_SIZE; | |
552 | pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE); | |
553 | srlb->used_mark[index] |= pos; | |
554 | ||
555 | /* Jump to the next free position */ | |
556 | srlb->current++; | |
557 | pos = srlb->current % SRLB_BLOCK_SIZE; | |
e90c0383 | 558 | while (srlb->current < size) { |
d8391312 OD |
559 | if (pos == 0) |
560 | index++; | |
561 | if (!((1ULL << pos) & srlb->used_mark[index])) | |
562 | break; | |
563 | else { | |
564 | srlb->current++; | |
565 | pos = srlb->current % SRLB_BLOCK_SIZE; | |
566 | } | |
567 | } | |
568 | ||
e90c0383 FR |
569 | if (srlb->current == size) |
570 | zlog_warn( | |
571 | "SR: Warning, SRLB is depleted and next label request will fail"); | |
572 | ||
d8391312 OD |
573 | return label; |
574 | } | |
575 | ||
576 | /** | |
577 | * Release label in the Segment Routing Local Block. | |
578 | * | |
579 | * @param srlb Segment Routing Local Block | |
580 | * @param label Label to be release | |
581 | * | |
582 | * @return 0 on success or -1 if label falls outside SRLB | |
583 | */ | |
584 | static int sr_local_block_release_label(struct sr_local_block *srlb, | |
585 | mpls_label_t label) | |
586 | { | |
587 | uint32_t index; | |
588 | uint32_t pos; | |
589 | ||
590 | /* Check that label falls inside the SRLB */ | |
591 | if ((label < srlb->start) || (label > srlb->end)) { | |
592 | flog_warn(EC_ISIS_SID_OVERFLOW, | |
593 | "%s: Returning label %u is outside SRLB [%u/%u]", | |
594 | __func__, label, srlb->start, srlb->end); | |
595 | return -1; | |
596 | } | |
597 | ||
598 | index = (label - srlb->start) / SRLB_BLOCK_SIZE; | |
599 | pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE); | |
600 | srlb->used_mark[index] &= ~pos; | |
601 | /* Reset current to the first available position */ | |
602 | for (index = 0; index < srlb->max_block; index++) { | |
603 | if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) { | |
604 | for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++) | |
605 | if (!((1ULL << pos) & srlb->used_mark[index])) { | |
606 | srlb->current = | |
607 | index * SRLB_BLOCK_SIZE + pos; | |
608 | break; | |
609 | } | |
610 | break; | |
611 | } | |
612 | } | |
613 | ||
614 | return 0; | |
615 | } | |
616 | ||
f2333421 | 617 | /* --- Segment Routing Adjacency-SID management functions ------------------- */ |
26f6acaf | 618 | |
f2333421 OD |
619 | /** |
620 | * Add new local Adjacency-SID. | |
621 | * | |
054fda12 RW |
622 | * @param adj IS-IS Adjacency |
623 | * @param family Inet Family (IPv4 or IPv6) | |
624 | * @param backup True to initialize backup Adjacency SID | |
625 | * @param nexthops List of backup nexthops (for backup Adj-SIDs only) | |
f2333421 | 626 | */ |
054fda12 RW |
627 | void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, bool backup, |
628 | struct list *nexthops) | |
26f6acaf RW |
629 | { |
630 | struct isis_circuit *circuit = adj->circuit; | |
631 | struct isis_area *area = circuit->area; | |
632 | struct sr_adjacency *sra; | |
633 | struct isis_adj_sid *adj_sid; | |
634 | struct isis_lan_adj_sid *ladj_sid; | |
635 | union g_addr nexthop = {}; | |
636 | uint8_t flags; | |
b1d80d43 | 637 | mpls_label_t input_label; |
26f6acaf | 638 | |
b407c77a OD |
639 | sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag, |
640 | backup ? "Backup" : "Primary"); | |
641 | ||
f2333421 | 642 | /* Determine nexthop IP address */ |
26f6acaf RW |
643 | switch (family) { |
644 | case AF_INET: | |
0af5e414 | 645 | if (!circuit->ip_router || !adj->ipv4_address_count) |
26f6acaf RW |
646 | return; |
647 | ||
648 | nexthop.ipv4 = adj->ipv4_addresses[0]; | |
649 | break; | |
650 | case AF_INET6: | |
173f8887 | 651 | if (!circuit->ipv6_router || !adj->ll_ipv6_count) |
26f6acaf RW |
652 | return; |
653 | ||
173f8887 | 654 | nexthop.ipv6 = adj->ll_ipv6_addrs[0]; |
26f6acaf RW |
655 | break; |
656 | default: | |
657 | flog_err(EC_LIB_DEVELOPMENT, | |
658 | "%s: unexpected address-family: %u", __func__, family); | |
659 | exit(1); | |
660 | } | |
661 | ||
f2333421 | 662 | /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */ |
26f6acaf RW |
663 | flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG; |
664 | if (family == AF_INET6) | |
665 | SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG); | |
666 | if (backup) | |
667 | SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG); | |
668 | ||
d8391312 OD |
669 | /* Get a label from the SRLB for this Adjacency */ |
670 | input_label = sr_local_block_request_label(&area->srdb.srlb); | |
671 | if (input_label == MPLS_INVALID_LABEL) | |
672 | return; | |
673 | ||
26f6acaf RW |
674 | if (circuit->ext == NULL) |
675 | circuit->ext = isis_alloc_ext_subtlvs(); | |
676 | ||
26f6acaf RW |
677 | sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra)); |
678 | sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL; | |
054fda12 | 679 | sra->input_label = input_label; |
26f6acaf RW |
680 | sra->nexthop.family = family; |
681 | sra->nexthop.address = nexthop; | |
054fda12 RW |
682 | |
683 | if (backup && nexthops) { | |
684 | struct isis_vertex_adj *vadj; | |
685 | struct listnode *node; | |
686 | ||
687 | sra->backup_nexthops = list_new(); | |
688 | for (ALL_LIST_ELEMENTS_RO(nexthops, node, vadj)) { | |
689 | struct isis_adjacency *adj = vadj->sadj->adj; | |
690 | struct mpls_label_stack *label_stack; | |
691 | ||
692 | label_stack = vadj->label_stack; | |
d47d6089 | 693 | adjinfo2nexthop(family, sra->backup_nexthops, adj, NULL, |
054fda12 RW |
694 | label_stack); |
695 | } | |
696 | } | |
697 | ||
26f6acaf | 698 | switch (circuit->circ_type) { |
f2333421 | 699 | /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */ |
26f6acaf RW |
700 | case CIRCUIT_T_BROADCAST: |
701 | ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid)); | |
702 | ladj_sid->family = family; | |
703 | ladj_sid->flags = flags; | |
704 | ladj_sid->weight = 0; | |
705 | memcpy(ladj_sid->neighbor_id, adj->sysid, | |
706 | sizeof(ladj_sid->neighbor_id)); | |
b1d80d43 | 707 | ladj_sid->sid = input_label; |
26f6acaf RW |
708 | isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid); |
709 | sra->u.ladj_sid = ladj_sid; | |
710 | break; | |
f2333421 | 711 | /* Adjacency-SID for Point to Point interface section #2.2.1 */ |
26f6acaf RW |
712 | case CIRCUIT_T_P2P: |
713 | adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid)); | |
714 | adj_sid->family = family; | |
715 | adj_sid->flags = flags; | |
716 | adj_sid->weight = 0; | |
b1d80d43 | 717 | adj_sid->sid = input_label; |
26f6acaf RW |
718 | isis_tlvs_add_adj_sid(circuit->ext, adj_sid); |
719 | sra->u.adj_sid = adj_sid; | |
720 | break; | |
721 | default: | |
722 | flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", | |
723 | __func__, circuit->circ_type); | |
724 | exit(1); | |
725 | } | |
f2333421 OD |
726 | |
727 | /* Add Adjacency-SID in SRDB */ | |
26f6acaf RW |
728 | sra->adj = adj; |
729 | listnode_add(area->srdb.adj_sids, sra); | |
730 | listnode_add(adj->adj_sids, sra); | |
731 | ||
c3f7b406 | 732 | isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra); |
26f6acaf RW |
733 | } |
734 | ||
f2333421 OD |
735 | /** |
736 | * Add Primary and Backup local Adjacency SID. | |
737 | * | |
738 | * @param adj IS-IS Adjacency | |
739 | * @param family Inet Family (IPv4 or IPv6) | |
740 | */ | |
b1d80d43 | 741 | static void sr_adj_sid_add(struct isis_adjacency *adj, int family) |
26f6acaf | 742 | { |
054fda12 | 743 | sr_adj_sid_add_single(adj, family, false, NULL); |
26f6acaf RW |
744 | } |
745 | ||
d8391312 OD |
746 | static void sr_adj_sid_update(struct sr_adjacency *sra, |
747 | struct sr_local_block *srlb) | |
748 | { | |
749 | struct isis_circuit *circuit = sra->adj->circuit; | |
750 | ||
751 | /* First remove the old MPLS Label */ | |
752 | isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra); | |
753 | ||
754 | /* Got new label in the new SRLB */ | |
054fda12 RW |
755 | sra->input_label = sr_local_block_request_label(srlb); |
756 | if (sra->input_label == MPLS_INVALID_LABEL) | |
d8391312 OD |
757 | return; |
758 | ||
759 | switch (circuit->circ_type) { | |
760 | case CIRCUIT_T_BROADCAST: | |
054fda12 | 761 | sra->u.ladj_sid->sid = sra->input_label; |
d8391312 OD |
762 | break; |
763 | case CIRCUIT_T_P2P: | |
054fda12 | 764 | sra->u.adj_sid->sid = sra->input_label; |
d8391312 OD |
765 | break; |
766 | default: | |
767 | flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", | |
768 | __func__, circuit->circ_type); | |
769 | break; | |
770 | } | |
771 | ||
772 | /* Finally configure the new MPLS Label */ | |
773 | isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra); | |
774 | } | |
775 | ||
f2333421 OD |
776 | /** |
777 | * Delete local Adj-SID. | |
778 | * | |
779 | * @param sra Segment Routing Adjacency | |
780 | */ | |
b1d80d43 | 781 | static void sr_adj_sid_del(struct sr_adjacency *sra) |
26f6acaf RW |
782 | { |
783 | struct isis_circuit *circuit = sra->adj->circuit; | |
784 | struct isis_area *area = circuit->area; | |
785 | ||
b407c77a | 786 | sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag); |
26f6acaf | 787 | |
c3f7b406 | 788 | isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra); |
26f6acaf | 789 | |
f2333421 | 790 | /* Release dynamic label and remove subTLVs */ |
26f6acaf RW |
791 | switch (circuit->circ_type) { |
792 | case CIRCUIT_T_BROADCAST: | |
d8391312 OD |
793 | sr_local_block_release_label(&area->srdb.srlb, |
794 | sra->u.ladj_sid->sid); | |
26f6acaf RW |
795 | isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid); |
796 | break; | |
797 | case CIRCUIT_T_P2P: | |
d8391312 OD |
798 | sr_local_block_release_label(&area->srdb.srlb, |
799 | sra->u.adj_sid->sid); | |
26f6acaf RW |
800 | isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid); |
801 | break; | |
802 | default: | |
803 | flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", | |
804 | __func__, circuit->circ_type); | |
805 | exit(1); | |
806 | } | |
807 | ||
054fda12 RW |
808 | if (sra->type == ISIS_SR_LAN_BACKUP && sra->backup_nexthops) { |
809 | sra->backup_nexthops->del = | |
810 | (void (*)(void *))isis_nexthop_delete; | |
811 | list_delete(&sra->backup_nexthops); | |
812 | } | |
813 | ||
f2333421 | 814 | /* Remove Adjacency-SID from the SRDB */ |
26f6acaf RW |
815 | listnode_delete(area->srdb.adj_sids, sra); |
816 | listnode_delete(sra->adj->adj_sids, sra); | |
817 | XFREE(MTYPE_ISIS_SR_INFO, sra); | |
818 | } | |
819 | ||
054fda12 RW |
820 | /** |
821 | * Lookup Segment Routing Adj-SID by family and type. | |
822 | * | |
823 | * @param adj IS-IS Adjacency | |
824 | * @param family Inet Family (IPv4 or IPv6) | |
825 | * @param type Adjacency SID type | |
826 | */ | |
827 | struct sr_adjacency *isis_sr_adj_sid_find(struct isis_adjacency *adj, | |
828 | int family, enum sr_adj_type type) | |
829 | { | |
830 | struct sr_adjacency *sra; | |
831 | struct listnode *node; | |
832 | ||
833 | for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, node, sra)) | |
834 | if (sra->nexthop.family == family && sra->type == type) | |
835 | return sra; | |
836 | ||
837 | return NULL; | |
838 | } | |
839 | ||
f2333421 OD |
840 | /** |
841 | * Remove all Adjacency-SIDs associated to an adjacency that is going down. | |
842 | * | |
843 | * @param adj IS-IS Adjacency | |
844 | * | |
845 | * @return 0 | |
846 | */ | |
b1d80d43 | 847 | static int sr_adj_state_change(struct isis_adjacency *adj) |
26f6acaf RW |
848 | { |
849 | struct sr_adjacency *sra; | |
850 | struct listnode *node, *nnode; | |
851 | ||
852 | if (!adj->circuit->area->srdb.enabled) | |
853 | return 0; | |
854 | ||
855 | if (adj->adj_state == ISIS_ADJ_UP) | |
856 | return 0; | |
857 | ||
858 | for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra)) | |
b1d80d43 | 859 | sr_adj_sid_del(sra); |
26f6acaf RW |
860 | |
861 | return 0; | |
862 | } | |
863 | ||
f2333421 OD |
864 | /** |
865 | * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or | |
866 | * IPv6 address to corresponding Adjacency-SID accordingly. | |
867 | * | |
868 | * @param adj IS-IS Adjacency | |
869 | * @param family Inet Family (IPv4 or IPv6) | |
173f8887 | 870 | * @param global Indicate if it concerns the Local or Global IPv6 addresses |
f2333421 OD |
871 | * |
872 | * @return 0 | |
26f6acaf | 873 | */ |
173f8887 OD |
874 | static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family, |
875 | bool global) | |
26f6acaf | 876 | { |
173f8887 | 877 | if (!adj->circuit->area->srdb.enabled || global) |
26f6acaf RW |
878 | return 0; |
879 | ||
b1d80d43 | 880 | sr_adj_sid_add(adj, family); |
26f6acaf RW |
881 | |
882 | return 0; | |
883 | } | |
884 | ||
f2333421 OD |
885 | /** |
886 | * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore, | |
887 | * delete the corresponding Adjacency-SID(s) accordingly. | |
888 | * | |
889 | * @param adj IS-IS Adjacency | |
890 | * @param family Inet Family (IPv4 or IPv6) | |
173f8887 | 891 | * @param global Indicate if it concerns the Local or Global IPv6 addresses |
f2333421 OD |
892 | * |
893 | * @return 0 | |
26f6acaf | 894 | */ |
173f8887 OD |
895 | static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family, |
896 | bool global) | |
26f6acaf RW |
897 | { |
898 | struct sr_adjacency *sra; | |
899 | struct listnode *node, *nnode; | |
900 | ||
173f8887 | 901 | if (!adj->circuit->area->srdb.enabled || global) |
26f6acaf RW |
902 | return 0; |
903 | ||
904 | for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra)) | |
905 | if (sra->nexthop.family == family) | |
b1d80d43 | 906 | sr_adj_sid_del(sra); |
26f6acaf RW |
907 | |
908 | return 0; | |
909 | } | |
910 | ||
f2333421 OD |
911 | /** |
912 | * Activate local Prefix-SID when loopback interface goes up for IS-IS. | |
913 | * | |
914 | * @param ifp Loopback Interface | |
915 | * | |
916 | * @return 0 | |
917 | */ | |
b1d80d43 | 918 | static int sr_if_new_hook(struct interface *ifp) |
26f6acaf RW |
919 | { |
920 | struct isis_circuit *circuit; | |
921 | struct isis_area *area; | |
922 | struct connected *connected; | |
923 | struct listnode *node; | |
924 | ||
f2333421 | 925 | /* Get corresponding circuit */ |
26f6acaf RW |
926 | circuit = circuit_scan_by_ifp(ifp); |
927 | if (!circuit) | |
928 | return 0; | |
929 | ||
930 | area = circuit->area; | |
931 | if (!area) | |
932 | return 0; | |
933 | ||
934 | /* | |
935 | * Update the Node-SID flag of the configured Prefix-SID mappings if | |
936 | * necessary. This needs to be done here since isisd reads the startup | |
937 | * configuration before receiving interface information from zebra. | |
938 | */ | |
939 | FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) { | |
940 | struct sr_prefix_cfg *pcfg; | |
941 | ||
942 | pcfg = isis_sr_cfg_prefix_find(area, connected->address); | |
943 | if (!pcfg) | |
944 | continue; | |
945 | ||
249c4457 | 946 | if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)) { |
26f6acaf RW |
947 | pcfg->node_sid = true; |
948 | lsp_regenerate_schedule(area, area->is_type, 0); | |
949 | } | |
950 | } | |
951 | ||
952 | return 0; | |
953 | } | |
954 | ||
c0083e53 OD |
955 | /** |
956 | * Show LFIB operation in human readable format. | |
957 | * | |
c951ee6e RW |
958 | * @param buf Buffer to store string output. Must be pre-allocate |
959 | * @param size Size of the buffer | |
960 | * @param label_in Input Label | |
961 | * @param label_out Output Label | |
c0083e53 OD |
962 | * |
963 | * @return String containing LFIB operation in human readable format | |
964 | */ | |
d47d6089 RW |
965 | char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, |
966 | mpls_label_t label_out) | |
c0083e53 OD |
967 | { |
968 | if (size < 24) | |
969 | return NULL; | |
970 | ||
971 | if (label_in == MPLS_INVALID_LABEL) { | |
972 | snprintf(buf, size, "no-op."); | |
973 | return buf; | |
974 | } | |
975 | ||
976 | switch (label_out) { | |
977 | case MPLS_LABEL_IMPLICIT_NULL: | |
978 | snprintf(buf, size, "Pop(%u)", label_in); | |
979 | break; | |
980 | case MPLS_LABEL_IPV4_EXPLICIT_NULL: | |
981 | case MPLS_LABEL_IPV6_EXPLICIT_NULL: | |
982 | snprintf(buf, size, "Swap(%u, null)", label_in); | |
983 | break; | |
984 | case MPLS_INVALID_LABEL: | |
985 | snprintf(buf, size, "no-op."); | |
986 | break; | |
987 | default: | |
988 | snprintf(buf, size, "Swap(%u, %u)", label_in, label_out); | |
989 | break; | |
990 | } | |
991 | return buf; | |
992 | } | |
993 | ||
c0083e53 OD |
994 | /** |
995 | * Show Segment Routing Node. | |
996 | * | |
997 | * @param vty VTY output | |
998 | * @param area IS-IS area | |
999 | * @param level IS-IS level | |
1000 | */ | |
1001 | static void show_node(struct vty *vty, struct isis_area *area, int level) | |
1002 | { | |
d47d6089 | 1003 | struct isis_lsp *lsp; |
c0083e53 OD |
1004 | struct ttable *tt; |
1005 | ||
d47d6089 | 1006 | vty_out(vty, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level)); |
c0083e53 OD |
1007 | |
1008 | /* Prepare table. */ | |
1009 | tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); | |
d8391312 | 1010 | ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD"); |
c0083e53 OD |
1011 | tt->style.cell.rpad = 2; |
1012 | tt->style.corner = '+'; | |
1013 | ttable_restyle(tt); | |
1014 | ttable_rowseps(tt, 0, BOTTOM, true, '-'); | |
1015 | ||
d47d6089 RW |
1016 | frr_each (lspdb, &area->lspdb[level - 1], lsp) { |
1017 | struct isis_router_cap *cap; | |
1018 | ||
1019 | if (!lsp->tlvs) | |
1020 | continue; | |
1021 | cap = lsp->tlvs->router_cap; | |
1022 | if (!cap) | |
1023 | continue; | |
1024 | ||
d8391312 OD |
1025 | ttable_add_row( |
1026 | tt, "%s|%u - %u|%u - %u|%s|%u", | |
d47d6089 RW |
1027 | sysid_print(lsp->hdr.lsp_id), cap->srgb.lower_bound, |
1028 | cap->srgb.lower_bound + cap->srgb.range_size - 1, | |
1029 | cap->srlb.lower_bound, | |
1030 | cap->srlb.lower_bound + cap->srlb.range_size - 1, | |
1031 | cap->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF", | |
1032 | cap->msd); | |
c0083e53 OD |
1033 | } |
1034 | ||
1035 | /* Dump the generated table. */ | |
1036 | if (tt->nrows > 1) { | |
1037 | char *table; | |
1038 | ||
1039 | table = ttable_dump(tt, "\n"); | |
1040 | vty_out(vty, "%s\n", table); | |
1041 | XFREE(MTYPE_TMP, table); | |
1042 | } | |
1043 | ttable_del(tt); | |
1044 | } | |
1045 | ||
1046 | DEFUN(show_sr_node, show_sr_node_cmd, | |
903ad92c DL |
1047 | "show " PROTO_NAME " segment-routing node", |
1048 | SHOW_STR | |
1049 | PROTO_HELP | |
c0083e53 OD |
1050 | "Segment-Routing\n" |
1051 | "Segment-Routing node\n") | |
1052 | { | |
36944791 | 1053 | struct listnode *node, *inode; |
c0083e53 | 1054 | struct isis_area *area; |
36944791 | 1055 | struct isis *isis; |
c0083e53 | 1056 | |
36944791 | 1057 | for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { |
eab88f36 K |
1058 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { |
1059 | vty_out(vty, "Area %s:\n", | |
1060 | area->area_tag ? area->area_tag : "null"); | |
7700a88a OD |
1061 | if (!area->srdb.enabled) { |
1062 | vty_out(vty, " Segment Routing is disabled\n"); | |
1063 | continue; | |
1064 | } | |
eab88f36 K |
1065 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; |
1066 | level++) | |
1067 | show_node(vty, area, level); | |
1068 | } | |
c0083e53 OD |
1069 | } |
1070 | ||
1071 | return CMD_SUCCESS; | |
1072 | } | |
1073 | ||
f2333421 | 1074 | /* --- IS-IS Segment Routing Management function ---------------------------- */ |
26f6acaf | 1075 | |
58fbcdf2 OD |
1076 | /** |
1077 | * Thread function to re-attempt connection to the Label Manager and thus be | |
1078 | * able to start Segment Routing. | |
1079 | * | |
1080 | * @param start Thread structure that contains area as argument | |
1081 | * | |
1082 | * @return 1 on success | |
1083 | */ | |
cc9f21da | 1084 | static void sr_start_label_manager(struct thread *start) |
58fbcdf2 OD |
1085 | { |
1086 | struct isis_area *area; | |
1087 | ||
1088 | area = THREAD_ARG(start); | |
1089 | ||
1090 | /* re-attempt to start SR & Label Manager connection */ | |
1091 | isis_sr_start(area); | |
58fbcdf2 OD |
1092 | } |
1093 | ||
f2333421 OD |
1094 | /** |
1095 | * Enable SR on the given IS-IS area. | |
1096 | * | |
1097 | * @param area IS-IS area | |
1098 | * | |
1099 | * @return 0 on success, -1 otherwise | |
1100 | */ | |
26f6acaf RW |
1101 | int isis_sr_start(struct isis_area *area) |
1102 | { | |
1103 | struct isis_sr_db *srdb = &area->srdb; | |
75eddbc3 | 1104 | struct isis_adjacency *adj; |
26f6acaf RW |
1105 | struct listnode *node; |
1106 | ||
58fbcdf2 OD |
1107 | /* First start Label Manager if not ready */ |
1108 | if (!isis_zebra_label_manager_ready()) | |
1109 | if (isis_zebra_label_manager_connect() < 0) { | |
1110 | /* Re-attempt to connect to Label Manager in 1 sec. */ | |
1111 | thread_add_timer(master, sr_start_label_manager, area, | |
1112 | 1, &srdb->t_start_lm); | |
1113 | return -1; | |
1114 | } | |
1115 | ||
1116 | /* Label Manager is ready, initialize the SRLB */ | |
1117 | if (sr_local_block_init(area) < 0) | |
d8391312 OD |
1118 | return -1; |
1119 | ||
26f6acaf | 1120 | /* |
58fbcdf2 OD |
1121 | * Request SGRB to the label manager if not already active. If the |
1122 | * allocation fails, return an error to disable SR until a new SRGB | |
1123 | * is successfully allocated. | |
26f6acaf | 1124 | */ |
58fbcdf2 OD |
1125 | if (!srdb->srgb_active) { |
1126 | if (isis_zebra_request_label_range( | |
1127 | srdb->config.srgb_lower_bound, | |
1128 | srdb->config.srgb_upper_bound | |
1129 | - srdb->config.srgb_lower_bound + 1) | |
1130 | < 0) { | |
1131 | srdb->srgb_active = false; | |
1132 | return -1; | |
1133 | } else | |
1134 | srdb->srgb_active = true; | |
1135 | } | |
26f6acaf | 1136 | |
b407c77a OD |
1137 | sr_debug("ISIS-Sr: Starting Segment Routing for area %s", |
1138 | area->area_tag); | |
26f6acaf | 1139 | |
f2333421 | 1140 | /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */ |
75eddbc3 RW |
1141 | for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) { |
1142 | if (adj->ipv4_address_count > 0) | |
1143 | sr_adj_sid_add(adj, AF_INET); | |
173f8887 | 1144 | if (adj->ll_ipv6_count > 0) |
75eddbc3 | 1145 | sr_adj_sid_add(adj, AF_INET6); |
26f6acaf RW |
1146 | } |
1147 | ||
58fbcdf2 OD |
1148 | area->srdb.enabled = true; |
1149 | ||
f2333421 | 1150 | /* Regenerate LSPs to advertise Segment Routing capabilities. */ |
26f6acaf RW |
1151 | lsp_regenerate_schedule(area, area->is_type, 0); |
1152 | ||
1153 | return 0; | |
1154 | } | |
1155 | ||
f2333421 OD |
1156 | /** |
1157 | * Disable SR on the given IS-IS area. | |
1158 | * | |
1159 | * @param area IS-IS area | |
1160 | */ | |
26f6acaf RW |
1161 | void isis_sr_stop(struct isis_area *area) |
1162 | { | |
1163 | struct isis_sr_db *srdb = &area->srdb; | |
1164 | struct sr_adjacency *sra; | |
1165 | struct listnode *node, *nnode; | |
1166 | ||
b407c77a OD |
1167 | sr_debug("ISIS-Sr: Stopping Segment Routing for area %s", |
1168 | area->area_tag); | |
26f6acaf | 1169 | |
58fbcdf2 | 1170 | /* Disable any re-attempt to connect to Label Manager */ |
fa935aa7 | 1171 | THREAD_OFF(srdb->t_start_lm); |
58fbcdf2 | 1172 | |
f2333421 | 1173 | /* Uninstall all local Adjacency-SIDs. */ |
26f6acaf | 1174 | for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) |
b1d80d43 | 1175 | sr_adj_sid_del(sra); |
26f6acaf | 1176 | |
58fbcdf2 OD |
1177 | /* Release SRGB if active. */ |
1178 | if (srdb->srgb_active) { | |
1179 | isis_zebra_release_label_range(srdb->config.srgb_lower_bound, | |
1180 | srdb->config.srgb_upper_bound); | |
1181 | srdb->srgb_active = false; | |
1182 | } | |
26f6acaf | 1183 | |
d8391312 OD |
1184 | /* Delete SRLB */ |
1185 | sr_local_block_delete(area); | |
1186 | ||
58fbcdf2 OD |
1187 | area->srdb.enabled = false; |
1188 | ||
f2333421 | 1189 | /* Regenerate LSPs to advertise that the Node is no more SR enable. */ |
26f6acaf RW |
1190 | lsp_regenerate_schedule(area, area->is_type, 0); |
1191 | } | |
1192 | ||
f2333421 OD |
1193 | /** |
1194 | * IS-IS Segment Routing initialization for given area. | |
1195 | * | |
1196 | * @param area IS-IS area | |
1197 | */ | |
26f6acaf RW |
1198 | void isis_sr_area_init(struct isis_area *area) |
1199 | { | |
1200 | struct isis_sr_db *srdb = &area->srdb; | |
1201 | ||
b407c77a OD |
1202 | sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB", |
1203 | area->area_tag); | |
1204 | ||
f2333421 | 1205 | /* Initialize Segment Routing Data Base */ |
26f6acaf | 1206 | memset(srdb, 0, sizeof(*srdb)); |
26f6acaf RW |
1207 | srdb->adj_sids = list_new(); |
1208 | ||
26f6acaf RW |
1209 | /* Pull defaults from the YANG module. */ |
1210 | #ifndef FABRICD | |
1211 | srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR); | |
01d43141 EDP |
1212 | srdb->config.srgb_lower_bound = yang_get_default_uint32( |
1213 | "%s/label-blocks/srgb/lower-bound", ISIS_SR); | |
1214 | srdb->config.srgb_upper_bound = yang_get_default_uint32( | |
1215 | "%s/label-blocks/srgb/upper-bound", ISIS_SR); | |
1216 | srdb->config.srlb_lower_bound = yang_get_default_uint32( | |
1217 | "%s/label-blocks/srlb/lower-bound", ISIS_SR); | |
1218 | srdb->config.srlb_upper_bound = yang_get_default_uint32( | |
1219 | "%s/label-blocks/srlb/upper-bound", ISIS_SR); | |
26f6acaf RW |
1220 | #else |
1221 | srdb->config.enabled = false; | |
1222 | srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND; | |
1223 | srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND; | |
d8391312 OD |
1224 | srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND; |
1225 | srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND; | |
26f6acaf RW |
1226 | #endif |
1227 | srdb->config.msd = 0; | |
cab10e86 | 1228 | srdb_prefix_cfg_init(&srdb->config.prefix_sids); |
26f6acaf RW |
1229 | } |
1230 | ||
f2333421 OD |
1231 | /** |
1232 | * Terminate IS-IS Segment Routing for the given area. | |
1233 | * | |
1234 | * @param area IS-IS area | |
1235 | */ | |
26f6acaf RW |
1236 | void isis_sr_area_term(struct isis_area *area) |
1237 | { | |
1238 | struct isis_sr_db *srdb = &area->srdb; | |
1239 | ||
1240 | /* Stop Segment Routing */ | |
1241 | if (area->srdb.enabled) | |
1242 | isis_sr_stop(area); | |
1243 | ||
1fa63850 OD |
1244 | /* Free Adjacency SID list */ |
1245 | list_delete(&srdb->adj_sids); | |
1246 | ||
26f6acaf | 1247 | /* Clear Prefix-SID configuration. */ |
cab10e86 | 1248 | while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) { |
26f6acaf RW |
1249 | struct sr_prefix_cfg *pcfg; |
1250 | ||
cab10e86 | 1251 | pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids); |
26f6acaf RW |
1252 | isis_sr_cfg_prefix_del(pcfg); |
1253 | } | |
1254 | } | |
1255 | ||
f2333421 OD |
1256 | /** |
1257 | * IS-IS Segment Routing global initialization. | |
1258 | */ | |
26f6acaf RW |
1259 | void isis_sr_init(void) |
1260 | { | |
c0083e53 | 1261 | install_element(VIEW_NODE, &show_sr_node_cmd); |
26f6acaf RW |
1262 | |
1263 | /* Register hooks. */ | |
b1d80d43 OD |
1264 | hook_register(isis_adj_state_change_hook, sr_adj_state_change); |
1265 | hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); | |
1266 | hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); | |
b1d80d43 | 1267 | hook_register(isis_if_new_hook, sr_if_new_hook); |
26f6acaf RW |
1268 | } |
1269 | ||
f2333421 OD |
1270 | /** |
1271 | * IS-IS Segment Routing global terminate. | |
1272 | */ | |
26f6acaf RW |
1273 | void isis_sr_term(void) |
1274 | { | |
1275 | /* Unregister hooks. */ | |
b1d80d43 OD |
1276 | hook_unregister(isis_adj_state_change_hook, sr_adj_state_change); |
1277 | hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); | |
1278 | hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); | |
b1d80d43 | 1279 | hook_unregister(isis_if_new_hook, sr_if_new_hook); |
26f6acaf | 1280 | } |