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