]>
Commit | Line | Data |
---|---|---|
c951ee6e RW |
1 | /* |
2 | * Copyright (C) 2020 NetDEF, Inc. | |
3 | * Renato Westphal | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the Free | |
7 | * Software Foundation; either version 2 of the License, or (at your option) | |
8 | * any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; see the file COPYING; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
19 | ||
20 | #include <zebra.h> | |
21 | ||
22 | #include "linklist.h" | |
23 | #include "log.h" | |
24 | #include "memory.h" | |
25 | #include "vrf.h" | |
26 | #include "table.h" | |
27 | #include "srcdest_table.h" | |
28 | ||
29 | #include "isis_common.h" | |
30 | #include "isisd.h" | |
31 | #include "isis_misc.h" | |
32 | #include "isis_adjacency.h" | |
33 | #include "isis_circuit.h" | |
34 | #include "isis_lsp.h" | |
35 | #include "isis_spf.h" | |
36 | #include "isis_route.h" | |
37 | #include "isis_mt.h" | |
38 | #include "isis_tlvs.h" | |
39 | #include "isis_spf_private.h" | |
40 | #include "isisd/isis_errors.h" | |
41 | ||
42 | DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_NODE, "ISIS SPF Node"); | |
43 | ||
44 | static inline int isis_spf_node_compare(const struct isis_spf_node *a, | |
45 | const struct isis_spf_node *b) | |
46 | { | |
47 | return memcmp(a->sysid, b->sysid, sizeof(a->sysid)); | |
48 | } | |
49 | RB_GENERATE(isis_spf_nodes, isis_spf_node, entry, isis_spf_node_compare) | |
50 | ||
51 | /** | |
52 | * Initialize list of SPF nodes. | |
53 | * | |
54 | * @param nodes List of SPF nodes | |
55 | */ | |
56 | void isis_spf_node_list_init(struct isis_spf_nodes *nodes) | |
57 | { | |
58 | RB_INIT(isis_spf_nodes, nodes); | |
59 | } | |
60 | ||
61 | /** | |
62 | * Clear list of SPF nodes, releasing all allocated memory. | |
63 | * | |
64 | * @param nodes List of SPF nodes | |
65 | */ | |
66 | void isis_spf_node_list_clear(struct isis_spf_nodes *nodes) | |
67 | { | |
68 | while (!RB_EMPTY(isis_spf_nodes, nodes)) { | |
69 | struct isis_spf_node *node = RB_ROOT(isis_spf_nodes, nodes); | |
70 | ||
71 | if (node->adjacencies) | |
72 | list_delete(&node->adjacencies); | |
73 | if (node->lfa.spftree) | |
74 | isis_spftree_del(node->lfa.spftree); | |
75 | if (node->lfa.spftree_reverse) | |
76 | isis_spftree_del(node->lfa.spftree_reverse); | |
77 | isis_spf_node_list_clear(&node->lfa.p_space); | |
78 | RB_REMOVE(isis_spf_nodes, nodes, node); | |
79 | XFREE(MTYPE_ISIS_SPF_NODE, node); | |
80 | } | |
81 | } | |
82 | ||
83 | /** | |
84 | * Add new node to list of SPF nodes. | |
85 | * | |
86 | * @param nodes List of SPF nodes | |
87 | * @param sysid Node System ID | |
88 | * | |
89 | * @return Pointer to new IS-IS SPF node structure. | |
90 | */ | |
91 | struct isis_spf_node *isis_spf_node_new(struct isis_spf_nodes *nodes, | |
92 | const uint8_t *sysid) | |
93 | { | |
94 | struct isis_spf_node *node; | |
95 | ||
96 | node = XCALLOC(MTYPE_ISIS_SPF_NODE, sizeof(*node)); | |
97 | memcpy(node->sysid, sysid, sizeof(node->sysid)); | |
98 | node->adjacencies = list_new(); | |
99 | isis_spf_node_list_init(&node->lfa.p_space); | |
100 | RB_INSERT(isis_spf_nodes, nodes, node); | |
101 | ||
102 | return node; | |
103 | } | |
104 | ||
105 | /** | |
106 | * Lookup SPF node by its System ID on the given list. | |
107 | * | |
108 | * @param nodes List of SPF nodes | |
109 | * @param sysid Node System ID | |
110 | * | |
111 | * @return Pointer to SPF node if found, NULL otherwise | |
112 | */ | |
113 | struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes, | |
114 | const uint8_t *sysid) | |
115 | { | |
116 | struct isis_spf_node node = {}; | |
117 | ||
118 | memcpy(node.sysid, sysid, sizeof(node.sysid)); | |
119 | return RB_FIND(isis_spf_nodes, nodes, &node); | |
120 | } | |
121 | ||
122 | /** | |
123 | * Check if a given IS-IS adjacency needs to be excised when computing the SPF | |
124 | * post-convergence tree. | |
125 | * | |
126 | * @param spftree IS-IS SPF tree | |
127 | * @param id Adjacency System ID (or LAN ID of the designated router | |
128 | * for broadcast interfaces) | |
129 | * | |
130 | * @return true if the adjacency needs to be excised, false | |
131 | * otherwise | |
132 | */ | |
133 | bool isis_lfa_excise_adj_check(const struct isis_spftree *spftree, | |
134 | const uint8_t *id) | |
135 | { | |
136 | const struct lfa_protected_resource *resource; | |
137 | ||
138 | if (spftree->type != SPF_TYPE_TI_LFA) | |
139 | return false; | |
140 | ||
141 | /* | |
142 | * Adjacencies formed over the failed interface should be excised both | |
143 | * when using link and node protection. | |
144 | */ | |
145 | resource = &spftree->lfa.protected_resource; | |
146 | if (!memcmp(resource->adjacency, id, ISIS_SYS_ID_LEN + 1)) | |
147 | return true; | |
148 | ||
149 | return false; | |
150 | } | |
151 | ||
152 | /** | |
153 | * Check if a given IS-IS node needs to be excised when computing the SPF | |
154 | * post-convergence tree. | |
155 | * | |
156 | * @param spftree IS-IS SPF tree | |
157 | * @param id Node System ID | |
158 | * | |
159 | * @return true if the node needs to be excised, false otherwise | |
160 | */ | |
161 | bool isis_lfa_excise_node_check(const struct isis_spftree *spftree, | |
162 | const uint8_t *id) | |
163 | { | |
164 | const struct lfa_protected_resource *resource; | |
165 | ||
166 | if (spftree->type != SPF_TYPE_TI_LFA) | |
167 | return false; | |
168 | ||
169 | /* | |
170 | * When using node protection, nodes reachable over the failed interface | |
171 | * must be excised. | |
172 | */ | |
173 | resource = &spftree->lfa.protected_resource; | |
174 | if (resource->type == LFA_LINK_PROTECTION) | |
175 | return false; | |
176 | ||
177 | if (isis_spf_node_find(&resource->nodes, id)) | |
178 | return true; | |
179 | ||
180 | return false; | |
181 | } | |
182 | ||
c951ee6e RW |
183 | struct tilfa_find_pnode_prefix_sid_args { |
184 | uint32_t sid_index; | |
185 | }; | |
186 | ||
187 | static int tilfa_find_pnode_prefix_sid_cb(const struct prefix *prefix, | |
188 | uint32_t metric, bool external, | |
189 | struct isis_subtlvs *subtlvs, | |
190 | void *arg) | |
191 | { | |
192 | struct tilfa_find_pnode_prefix_sid_args *args = arg; | |
193 | struct isis_prefix_sid *psid; | |
194 | ||
195 | if (!subtlvs || subtlvs->prefix_sids.count == 0) | |
196 | return LSP_ITER_CONTINUE; | |
197 | ||
198 | psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head; | |
199 | ||
200 | /* Require the node flag to be set. */ | |
201 | if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NODE)) | |
202 | return LSP_ITER_CONTINUE; | |
203 | ||
204 | args->sid_index = psid->value; | |
205 | ||
206 | return LSP_ITER_STOP; | |
207 | } | |
208 | ||
209 | /* Find Prefix-SID associated to a System ID. */ | |
210 | static uint32_t tilfa_find_pnode_prefix_sid(struct isis_spftree *spftree, | |
211 | const uint8_t *sysid) | |
212 | { | |
213 | struct isis_lsp *lsp; | |
214 | struct tilfa_find_pnode_prefix_sid_args args; | |
215 | ||
216 | lsp = isis_root_system_lsp(spftree->lspdb, sysid); | |
217 | if (!lsp) | |
218 | return UINT32_MAX; | |
219 | ||
220 | args.sid_index = UINT32_MAX; | |
221 | isis_lsp_iterate_ip_reach(lsp, spftree->family, spftree->mtid, | |
222 | tilfa_find_pnode_prefix_sid_cb, &args); | |
223 | ||
224 | return args.sid_index; | |
225 | } | |
226 | ||
227 | struct tilfa_find_qnode_adj_sid_args { | |
228 | const uint8_t *qnode_sysid; | |
229 | mpls_label_t label; | |
230 | }; | |
231 | ||
232 | static int tilfa_find_qnode_adj_sid_cb(const uint8_t *id, uint32_t metric, | |
233 | bool oldmetric, | |
234 | struct isis_ext_subtlvs *subtlvs, | |
235 | void *arg) | |
236 | { | |
237 | struct tilfa_find_qnode_adj_sid_args *args = arg; | |
238 | struct isis_adj_sid *adj_sid; | |
239 | ||
240 | if (memcmp(id, args->qnode_sysid, ISIS_SYS_ID_LEN)) | |
241 | return LSP_ITER_CONTINUE; | |
242 | if (!subtlvs || subtlvs->adj_sid.count == 0) | |
243 | return LSP_ITER_CONTINUE; | |
244 | ||
245 | adj_sid = (struct isis_adj_sid *)subtlvs->adj_sid.head; | |
246 | args->label = adj_sid->sid; | |
247 | ||
248 | return LSP_ITER_STOP; | |
249 | } | |
250 | ||
251 | /* Find Adj-SID associated to a pair of System IDs. */ | |
252 | static mpls_label_t tilfa_find_qnode_adj_sid(struct isis_spftree *spftree, | |
253 | const uint8_t *source_sysid, | |
254 | const uint8_t *qnode_sysid) | |
255 | { | |
256 | struct isis_lsp *lsp; | |
257 | struct tilfa_find_qnode_adj_sid_args args; | |
258 | ||
259 | lsp = isis_root_system_lsp(spftree->lspdb, source_sysid); | |
260 | if (!lsp) | |
261 | return MPLS_INVALID_LABEL; | |
262 | ||
263 | args.qnode_sysid = qnode_sysid; | |
264 | args.label = MPLS_INVALID_LABEL; | |
265 | isis_lsp_iterate_is_reach(lsp, spftree->mtid, | |
266 | tilfa_find_qnode_adj_sid_cb, &args); | |
267 | ||
268 | return args.label; | |
269 | } | |
270 | ||
271 | /* | |
272 | * Compute the MPLS label stack associated to a TI-LFA repair list. This | |
273 | * needs to be computed separately for each adjacency since different | |
274 | * neighbors can have different SRGBs. | |
275 | */ | |
276 | static struct mpls_label_stack * | |
277 | tilfa_compute_label_stack(struct lspdb_head *lspdb, | |
278 | const struct isis_spf_adj *sadj, | |
279 | const struct list *repair_list) | |
280 | { | |
281 | struct mpls_label_stack *label_stack; | |
282 | struct isis_tilfa_sid *sid; | |
283 | struct listnode *node; | |
284 | size_t i = 0; | |
285 | ||
286 | /* Allocate label stack. */ | |
287 | label_stack = XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS, | |
288 | sizeof(struct mpls_label_stack) | |
289 | + listcount(repair_list) | |
290 | * sizeof(mpls_label_t)); | |
291 | label_stack->num_labels = listcount(repair_list); | |
292 | ||
293 | for (ALL_LIST_ELEMENTS_RO(repair_list, node, sid)) { | |
4c75f7c7 | 294 | const uint8_t *target_node; |
c951ee6e RW |
295 | struct isis_sr_block *srgb; |
296 | mpls_label_t label; | |
297 | ||
298 | switch (sid->type) { | |
299 | case TILFA_SID_PREFIX: | |
4c75f7c7 RW |
300 | if (sid->value.index.remote) |
301 | target_node = sid->value.index.remote_sysid; | |
302 | else | |
303 | target_node = sadj->id; | |
304 | srgb = isis_sr_find_srgb(lspdb, target_node); | |
c951ee6e RW |
305 | if (!srgb) { |
306 | zlog_warn("%s: SRGB not found for node %s", | |
307 | __func__, | |
4c75f7c7 | 308 | print_sys_hostname(target_node)); |
c951ee6e RW |
309 | goto error; |
310 | } | |
311 | ||
312 | /* Check if the SID index falls inside the SRGB. */ | |
4c75f7c7 | 313 | if (sid->value.index.value >= srgb->range_size) { |
c951ee6e RW |
314 | flog_warn( |
315 | EC_ISIS_SID_OVERFLOW, | |
316 | "%s: SID index %u falls outside remote SRGB range", | |
4c75f7c7 | 317 | __func__, sid->value.index.value); |
c951ee6e RW |
318 | goto error; |
319 | } | |
320 | ||
321 | /* | |
322 | * Prefix-SID: map SID index to label value within the | |
323 | * SRGB. | |
324 | */ | |
4c75f7c7 | 325 | label = srgb->lower_bound + sid->value.index.value; |
c951ee6e RW |
326 | break; |
327 | case TILFA_SID_ADJ: | |
328 | /* Adj-SID: absolute label value can be used directly */ | |
329 | label = sid->value.label; | |
330 | break; | |
331 | default: | |
332 | flog_err(EC_LIB_DEVELOPMENT, | |
333 | "%s: unknown TI-LFA SID type [%u]", __func__, | |
334 | sid->type); | |
335 | exit(1); | |
336 | } | |
337 | label_stack->label[i++] = label; | |
338 | } | |
339 | ||
340 | return label_stack; | |
341 | ||
342 | error: | |
343 | XFREE(MTYPE_ISIS_NEXTHOP_LABELS, label_stack); | |
344 | return NULL; | |
345 | } | |
346 | ||
347 | static int tilfa_repair_list_apply(struct isis_spftree *spftree, | |
348 | struct isis_vertex *vertex_dest, | |
349 | const struct isis_vertex *vertex_pnode, | |
350 | const struct list *repair_list) | |
351 | { | |
352 | struct isis_vertex_adj *vadj; | |
353 | struct listnode *node; | |
354 | ||
355 | for (ALL_LIST_ELEMENTS_RO(vertex_dest->Adj_N, node, vadj)) { | |
356 | struct isis_spf_adj *sadj = vadj->sadj; | |
357 | struct mpls_label_stack *label_stack; | |
358 | ||
359 | if (!isis_vertex_adj_exists(spftree, vertex_pnode, sadj)) | |
360 | continue; | |
361 | ||
362 | assert(!vadj->label_stack); | |
363 | label_stack = tilfa_compute_label_stack(spftree->lspdb, sadj, | |
364 | repair_list); | |
365 | if (!label_stack) { | |
366 | char buf[VID2STR_BUFFER]; | |
367 | ||
368 | vid2string(vertex_dest, buf, sizeof(buf)); | |
369 | zlog_warn( | |
370 | "%s: %s %s adjacency %s: failed to compute label stack", | |
371 | __func__, vtype2string(vertex_dest->type), buf, | |
372 | print_sys_hostname(sadj->id)); | |
373 | return -1; | |
374 | } | |
375 | ||
376 | vadj->label_stack = label_stack; | |
377 | } | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
382 | /* | |
383 | * Check if a node belongs to the extended P-space corresponding to a given | |
384 | * destination. | |
385 | */ | |
386 | static bool lfa_ext_p_space_check(const struct isis_spftree *spftree_pc, | |
387 | const struct isis_vertex *vertex_dest, | |
388 | const struct isis_vertex *vertex) | |
389 | { | |
390 | struct isis_spftree *spftree_old = spftree_pc->lfa.old.spftree; | |
391 | struct isis_vertex_adj *vadj; | |
392 | struct listnode *node; | |
393 | ||
394 | /* Check the local P-space first. */ | |
395 | if (isis_spf_node_find(&spftree_pc->lfa.p_space, vertex->N.id)) | |
396 | return true; | |
397 | ||
398 | /* | |
399 | * Check the P-space of the adjacent routers used to reach the | |
400 | * destination. | |
401 | */ | |
402 | for (ALL_LIST_ELEMENTS_RO(vertex_dest->Adj_N, node, vadj)) { | |
403 | struct isis_spf_adj *sadj = vadj->sadj; | |
404 | struct isis_spf_node *adj_node; | |
405 | ||
406 | adj_node = | |
407 | isis_spf_node_find(&spftree_old->adj_nodes, sadj->id); | |
408 | if (!adj_node) | |
409 | continue; | |
410 | ||
411 | if (isis_spf_node_find(&adj_node->lfa.p_space, vertex->N.id)) | |
412 | return true; | |
413 | } | |
414 | ||
415 | return false; | |
416 | } | |
417 | ||
418 | /* Check if a node belongs to the Q-space. */ | |
419 | static bool lfa_q_space_check(const struct isis_spftree *spftree_pc, | |
420 | const struct isis_vertex *vertex) | |
421 | { | |
422 | return isis_spf_node_find(&spftree_pc->lfa.q_space, vertex->N.id); | |
423 | } | |
424 | ||
425 | /* This is a recursive function. */ | |
426 | static int tilfa_build_repair_list(struct isis_spftree *spftree_pc, | |
427 | struct isis_vertex *vertex_dest, | |
428 | const struct isis_vertex *vertex, | |
429 | const struct isis_vertex *vertex_child, | |
430 | struct isis_spf_nodes *used_pnodes, | |
431 | struct list *repair_list) | |
432 | { | |
433 | struct isis_vertex *pvertex; | |
434 | struct listnode *node; | |
435 | bool is_pnode, is_qnode; | |
436 | char buf[VID2STR_BUFFER]; | |
4c75f7c7 RW |
437 | struct isis_tilfa_sid sid_dest = {}, sid_qnode = {}, sid_pnode = {}; |
438 | uint32_t sid_index; | |
c951ee6e RW |
439 | mpls_label_t label_qnode; |
440 | ||
2866b119 | 441 | if (IS_DEBUG_LFA) { |
c951ee6e | 442 | vid2string(vertex, buf, sizeof(buf)); |
2866b119 RW |
443 | zlog_debug("ISIS-LFA: vertex %s %s", vtype2string(vertex->type), |
444 | buf); | |
c951ee6e RW |
445 | } |
446 | ||
4c75f7c7 RW |
447 | /* Push original Prefix-SID label when necessary. */ |
448 | if (VTYPE_IP(vertex->type) && vertex->N.ip.sr.present) { | |
449 | pvertex = listnode_head(vertex->parents); | |
450 | assert(pvertex); | |
451 | ||
452 | sid_index = vertex->N.ip.sr.sid.value; | |
2866b119 | 453 | if (IS_DEBUG_LFA) |
4c75f7c7 | 454 | zlog_debug( |
2866b119 | 455 | "ISIS-LFA: pushing Prefix-SID to %pFX (index %u)", |
4c75f7c7 RW |
456 | &vertex->N.ip.p.dest, sid_index); |
457 | sid_dest.type = TILFA_SID_PREFIX; | |
458 | sid_dest.value.index.value = sid_index; | |
459 | sid_dest.value.index.remote = true; | |
460 | memcpy(sid_dest.value.index.remote_sysid, pvertex->N.id, | |
461 | sizeof(sid_dest.value.index.remote_sysid)); | |
462 | listnode_add_head(repair_list, &sid_dest); | |
463 | } | |
464 | ||
c951ee6e RW |
465 | if (!vertex_child) |
466 | goto parents; | |
467 | if (vertex->type != VTYPE_NONPSEUDO_IS | |
468 | && vertex->type != VTYPE_NONPSEUDO_TE_IS) | |
469 | goto parents; | |
470 | if (!VTYPE_IS(vertex_child->type)) | |
471 | vertex_child = NULL; | |
472 | ||
473 | /* Check if node is part of the extended P-space and/or Q-space. */ | |
474 | is_pnode = lfa_ext_p_space_check(spftree_pc, vertex_dest, vertex); | |
475 | is_qnode = lfa_q_space_check(spftree_pc, vertex); | |
476 | ||
477 | /* Push Adj-SID label when necessary. */ | |
478 | if ((!is_qnode | |
479 | || spftree_pc->lfa.protected_resource.type == LFA_NODE_PROTECTION) | |
480 | && vertex_child) { | |
481 | label_qnode = tilfa_find_qnode_adj_sid(spftree_pc, vertex->N.id, | |
482 | vertex_child->N.id); | |
483 | if (label_qnode == MPLS_INVALID_LABEL) { | |
2866b119 | 484 | zlog_warn("ISIS-LFA: failed to find %s->%s Adj-SID", |
c951ee6e RW |
485 | print_sys_hostname(vertex->N.id), |
486 | print_sys_hostname(vertex_child->N.id)); | |
487 | return -1; | |
488 | } | |
2866b119 | 489 | if (IS_DEBUG_LFA) |
c951ee6e | 490 | zlog_debug( |
2866b119 | 491 | "ISIS-LFA: pushing %s->%s Adj-SID (label %u)", |
c951ee6e RW |
492 | print_sys_hostname(vertex->N.id), |
493 | print_sys_hostname(vertex_child->N.id), | |
494 | label_qnode); | |
495 | sid_qnode.type = TILFA_SID_ADJ; | |
496 | sid_qnode.value.label = label_qnode; | |
497 | listnode_add_head(repair_list, &sid_qnode); | |
498 | } | |
499 | ||
500 | /* Push Prefix-SID label when necessary. */ | |
501 | if (is_pnode) { | |
c951ee6e RW |
502 | /* The same P-node can't be used more than once. */ |
503 | if (isis_spf_node_find(used_pnodes, vertex->N.id)) { | |
2866b119 | 504 | if (IS_DEBUG_LFA) |
c951ee6e | 505 | zlog_debug( |
2866b119 | 506 | "ISIS-LFA: skipping already used P-node"); |
c951ee6e RW |
507 | return 0; |
508 | } | |
509 | isis_spf_node_new(used_pnodes, vertex->N.id); | |
510 | ||
511 | if (!vertex_child) { | |
2866b119 | 512 | if (IS_DEBUG_LFA) |
c951ee6e | 513 | zlog_debug( |
2866b119 | 514 | "ISIS-LFA: destination is within Ext-P-Space"); |
c951ee6e RW |
515 | return 0; |
516 | } | |
517 | ||
518 | sid_index = | |
519 | tilfa_find_pnode_prefix_sid(spftree_pc, vertex->N.id); | |
520 | if (sid_index == UINT32_MAX) { | |
521 | zlog_warn( | |
2866b119 | 522 | "ISIS-LFA: failed to find Prefix-SID corresponding to PQ-node %s", |
c951ee6e RW |
523 | print_sys_hostname(vertex->N.id)); |
524 | return -1; | |
525 | } | |
526 | ||
2866b119 | 527 | if (IS_DEBUG_LFA) |
c951ee6e | 528 | zlog_debug( |
2866b119 | 529 | "ISIS-LFA: pushing Node-SID to %s (index %u)", |
c951ee6e RW |
530 | print_sys_hostname(vertex->N.id), sid_index); |
531 | sid_pnode.type = TILFA_SID_PREFIX; | |
4c75f7c7 | 532 | sid_pnode.value.index.value = sid_index; |
c951ee6e RW |
533 | listnode_add_head(repair_list, &sid_pnode); |
534 | ||
535 | /* Apply repair list. */ | |
6dfb7f59 RW |
536 | if (listcount(repair_list) |
537 | > spftree_pc->area->srdb.config.msd) { | |
538 | zlog_warn( | |
2866b119 | 539 | "ISIS-LFA: list of repair segments exceeds locally configured MSD (%u > %u)", |
6dfb7f59 RW |
540 | listcount(repair_list), |
541 | spftree_pc->area->srdb.config.msd); | |
542 | return -1; | |
543 | } | |
c951ee6e RW |
544 | if (tilfa_repair_list_apply(spftree_pc, vertex_dest, vertex, |
545 | repair_list) | |
546 | != 0) | |
547 | return -1; | |
548 | return 0; | |
549 | } | |
550 | ||
551 | parents: | |
552 | for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, pvertex)) { | |
553 | struct list *repair_list_parent; | |
554 | bool ecmp; | |
555 | int ret; | |
556 | ||
557 | ecmp = (listcount(vertex->parents) > 1) ? true : false; | |
558 | repair_list_parent = ecmp ? list_dup(repair_list) : repair_list; | |
559 | ret = tilfa_build_repair_list(spftree_pc, vertex_dest, pvertex, | |
560 | vertex, used_pnodes, | |
561 | repair_list_parent); | |
562 | if (ecmp) | |
563 | list_delete(&repair_list_parent); | |
564 | if (ret != 0) | |
565 | return ret; | |
566 | } | |
567 | ||
568 | return 0; | |
569 | } | |
570 | ||
571 | static const char *lfa_protection_type2str(enum lfa_protection_type type) | |
572 | { | |
573 | switch (type) { | |
574 | case LFA_LINK_PROTECTION: | |
575 | return "link protection"; | |
576 | case LFA_NODE_PROTECTION: | |
577 | return "node protection"; | |
578 | default: | |
579 | return "unknown protection type"; | |
580 | } | |
581 | } | |
582 | ||
583 | static const char * | |
584 | lfa_protected_resource2str(const struct lfa_protected_resource *resource) | |
585 | { | |
586 | const uint8_t *fail_id; | |
587 | static char buffer[128]; | |
588 | ||
589 | fail_id = resource->adjacency; | |
590 | snprintf(buffer, sizeof(buffer), "%s.%u's failure (%s)", | |
591 | print_sys_hostname(fail_id), LSP_PSEUDO_ID(fail_id), | |
592 | lfa_protection_type2str(resource->type)); | |
593 | ||
594 | return buffer; | |
595 | } | |
596 | ||
597 | static bool | |
598 | spf_adj_check_is_affected(const struct isis_spf_adj *sadj, | |
599 | const struct lfa_protected_resource *resource, | |
600 | const uint8_t *root_sysid, bool reverse) | |
601 | { | |
602 | if (!!CHECK_FLAG(sadj->flags, F_ISIS_SPF_ADJ_BROADCAST) | |
603 | != !!LSP_PSEUDO_ID(resource->adjacency)) | |
604 | return false; | |
605 | ||
606 | if (CHECK_FLAG(sadj->flags, F_ISIS_SPF_ADJ_BROADCAST)) { | |
607 | if (!memcmp(sadj->lan.desig_is_id, resource->adjacency, | |
608 | ISIS_SYS_ID_LEN + 1)) | |
609 | return true; | |
610 | } else { | |
611 | if (!reverse | |
612 | && !memcmp(sadj->id, resource->adjacency, ISIS_SYS_ID_LEN)) | |
613 | return true; | |
614 | if (reverse && !memcmp(sadj->id, root_sysid, ISIS_SYS_ID_LEN)) | |
615 | return true; | |
616 | } | |
617 | ||
618 | return false; | |
619 | } | |
620 | ||
621 | /* Check if the given SPF vertex needs LFA protection. */ | |
622 | static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc, | |
623 | const struct isis_vertex *vertex) | |
624 | { | |
625 | struct isis_vertex *vertex_old; | |
626 | struct listnode *node; | |
627 | size_t affected_nhs = 0; | |
628 | struct isis_vertex_adj *vadj; | |
629 | ||
d4fcd8bd RW |
630 | /* Local routes don't need protection. */ |
631 | if (VTYPE_IP(vertex->type) && vertex->depth == 1) | |
632 | return false; | |
633 | ||
c951ee6e RW |
634 | /* Only local adjacencies need Adj-SID protection. */ |
635 | if (VTYPE_IS(vertex->type) | |
636 | && !isis_adj_find(spftree_pc->area, spftree_pc->level, | |
637 | vertex->N.id)) | |
638 | return false; | |
639 | ||
640 | vertex_old = isis_find_vertex(&spftree_pc->lfa.old.spftree->paths, | |
641 | &vertex->N, vertex->type); | |
642 | if (!vertex_old) | |
643 | return false; | |
644 | ||
645 | for (ALL_LIST_ELEMENTS_RO(vertex_old->Adj_N, node, vadj)) { | |
646 | struct isis_spf_adj *sadj = vadj->sadj; | |
647 | ||
648 | if (spf_adj_check_is_affected( | |
649 | sadj, &spftree_pc->lfa.protected_resource, | |
650 | spftree_pc->sysid, false)) | |
651 | affected_nhs++; | |
652 | } | |
653 | ||
654 | /* | |
655 | * No need to compute backup paths for ECMP routes, except if all | |
656 | * primary nexthops share the same broadcast interface. | |
657 | */ | |
658 | if (listcount(vertex_old->Adj_N) == affected_nhs) | |
659 | return true; | |
660 | ||
661 | return false; | |
662 | } | |
663 | ||
664 | /** | |
665 | * Check if the given SPF vertex needs protection and, if so, compute and | |
666 | * install the corresponding repair paths. | |
667 | * | |
668 | * @param spftree_pc The post-convergence SPF tree | |
669 | * @param vertex IS-IS SPF vertex to check | |
670 | * | |
671 | * @return 0 if the vertex needs to be protected, -1 otherwise | |
672 | */ | |
673 | int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex) | |
674 | { | |
675 | struct isis_spf_nodes used_pnodes; | |
676 | char buf[VID2STR_BUFFER]; | |
677 | struct list *repair_list; | |
678 | int ret; | |
679 | ||
680 | if (!spftree_pc->area->srdb.enabled) | |
681 | return -1; | |
682 | ||
2866b119 | 683 | if (IS_DEBUG_LFA) |
c951ee6e RW |
684 | vid2string(vertex, buf, sizeof(buf)); |
685 | ||
686 | if (!lfa_check_needs_protection(spftree_pc, vertex)) { | |
2866b119 | 687 | if (IS_DEBUG_LFA) |
c951ee6e | 688 | zlog_debug( |
2866b119 | 689 | "ISIS-LFA: %s %s unaffected by %s", |
c951ee6e RW |
690 | vtype2string(vertex->type), buf, |
691 | lfa_protected_resource2str( | |
692 | &spftree_pc->lfa.protected_resource)); | |
693 | ||
694 | return -1; | |
695 | } | |
696 | ||
697 | /* | |
054fda12 | 698 | * Check if the route/adjacency was already covered by node protection. |
c951ee6e | 699 | */ |
054fda12 RW |
700 | if (VTYPE_IS(vertex->type)) { |
701 | struct isis_adjacency *adj; | |
702 | ||
703 | adj = isis_adj_find(spftree_pc->area, spftree_pc->level, | |
704 | vertex->N.id); | |
705 | if (adj | |
706 | && isis_sr_adj_sid_find(adj, spftree_pc->family, | |
707 | ISIS_SR_LAN_BACKUP)) { | |
2866b119 | 708 | if (IS_DEBUG_LFA) |
054fda12 | 709 | zlog_debug( |
2866b119 | 710 | "ISIS-LFA: %s %s already covered by node protection", |
054fda12 RW |
711 | vtype2string(vertex->type), buf); |
712 | ||
713 | return -1; | |
714 | } | |
715 | } | |
c951ee6e RW |
716 | if (VTYPE_IP(vertex->type)) { |
717 | struct route_table *route_table; | |
718 | ||
719 | route_table = spftree_pc->lfa.old.spftree->route_table_backup; | |
d47d6089 | 720 | if (route_node_lookup(route_table, &vertex->N.ip.p.dest)) { |
2866b119 | 721 | if (IS_DEBUG_LFA) |
c951ee6e | 722 | zlog_debug( |
2866b119 | 723 | "ISIS-LFA: %s %s already covered by node protection", |
c951ee6e RW |
724 | vtype2string(vertex->type), buf); |
725 | ||
726 | return -1; | |
727 | } | |
728 | } | |
729 | ||
2866b119 | 730 | if (IS_DEBUG_LFA) |
c951ee6e | 731 | zlog_debug( |
2866b119 | 732 | "ISIS-LFA: computing repair path(s) of %s %s w.r.t %s", |
c951ee6e RW |
733 | vtype2string(vertex->type), buf, |
734 | lfa_protected_resource2str( | |
735 | &spftree_pc->lfa.protected_resource)); | |
736 | ||
737 | /* Create base repair list. */ | |
738 | repair_list = list_new(); | |
739 | ||
740 | isis_spf_node_list_init(&used_pnodes); | |
741 | ret = tilfa_build_repair_list(spftree_pc, vertex, vertex, NULL, | |
742 | &used_pnodes, repair_list); | |
743 | isis_spf_node_list_clear(&used_pnodes); | |
744 | list_delete(&repair_list); | |
745 | if (ret != 0) | |
6dfb7f59 | 746 | zlog_warn( |
2866b119 | 747 | "ISIS-LFA: failed to compute repair path(s) of %s %s w.r.t %s", |
6dfb7f59 RW |
748 | vtype2string(vertex->type), buf, |
749 | lfa_protected_resource2str( | |
750 | &spftree_pc->lfa.protected_resource)); | |
c951ee6e RW |
751 | |
752 | return ret; | |
753 | } | |
754 | ||
755 | static bool | |
756 | spf_adj_node_is_affected(struct isis_spf_node *adj_node, | |
757 | const struct lfa_protected_resource *resource, | |
758 | const uint8_t *root_sysid) | |
759 | { | |
760 | struct isis_spf_adj *sadj; | |
761 | struct listnode *node; | |
762 | ||
763 | for (ALL_LIST_ELEMENTS_RO(adj_node->adjacencies, node, sadj)) { | |
764 | if (sadj->metric != adj_node->best_metric) | |
765 | continue; | |
766 | if (spf_adj_check_is_affected(sadj, resource, root_sysid, | |
767 | false)) | |
768 | return true; | |
769 | } | |
770 | ||
771 | return false; | |
772 | } | |
773 | ||
774 | static bool vertex_is_affected(struct isis_spftree *spftree_root, | |
775 | const struct isis_spf_nodes *adj_nodes, | |
776 | bool p_space, const struct isis_vertex *vertex, | |
777 | const struct lfa_protected_resource *resource) | |
778 | { | |
779 | struct isis_vertex *pvertex; | |
780 | struct listnode *node, *vnode; | |
781 | ||
782 | for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, pvertex)) { | |
783 | struct isis_spftree *spftree_parent; | |
784 | struct isis_vertex *vertex_child; | |
785 | struct isis_vertex_adj *vadj; | |
786 | bool reverse = false; | |
787 | char buf1[VID2STR_BUFFER]; | |
788 | char buf2[VID2STR_BUFFER]; | |
789 | ||
2866b119 RW |
790 | if (IS_DEBUG_LFA) |
791 | zlog_debug("ISIS-LFA: vertex %s parent %s", | |
c951ee6e RW |
792 | vid2string(vertex, buf1, sizeof(buf1)), |
793 | vid2string(pvertex, buf2, sizeof(buf2))); | |
794 | ||
795 | if (p_space && resource->type == LFA_NODE_PROTECTION) { | |
796 | if (isis_spf_node_find(&resource->nodes, vertex->N.id)) | |
797 | return true; | |
798 | goto parents; | |
799 | } | |
800 | ||
801 | /* Check if either the vertex or its parent is the root node. */ | |
802 | if (memcmp(vertex->N.id, spftree_root->sysid, ISIS_SYS_ID_LEN) | |
803 | && memcmp(pvertex->N.id, spftree_root->sysid, | |
804 | ISIS_SYS_ID_LEN)) | |
805 | goto parents; | |
806 | ||
807 | /* Get SPT of the parent vertex. */ | |
808 | if (!memcmp(pvertex->N.id, spftree_root->sysid, | |
809 | ISIS_SYS_ID_LEN)) | |
810 | spftree_parent = spftree_root; | |
811 | else { | |
812 | struct isis_spf_node *adj_node; | |
813 | ||
814 | adj_node = isis_spf_node_find(adj_nodes, pvertex->N.id); | |
815 | assert(adj_node); | |
816 | spftree_parent = adj_node->lfa.spftree; | |
817 | assert(spftree_parent); | |
818 | reverse = true; | |
819 | } | |
820 | ||
821 | /* Get paths pvertex uses to reach vertex. */ | |
822 | vertex_child = isis_find_vertex(&spftree_parent->paths, | |
823 | &vertex->N, vertex->type); | |
824 | if (!vertex_child) | |
825 | goto parents; | |
826 | ||
827 | /* Check if any of these paths use the protected resource. */ | |
828 | for (ALL_LIST_ELEMENTS_RO(vertex_child->Adj_N, vnode, vadj)) | |
829 | if (spf_adj_check_is_affected(vadj->sadj, resource, | |
830 | spftree_root->sysid, | |
831 | reverse)) | |
832 | return true; | |
833 | ||
834 | parents: | |
835 | if (vertex_is_affected(spftree_root, adj_nodes, p_space, | |
836 | pvertex, resource)) | |
837 | return true; | |
838 | } | |
839 | ||
840 | return false; | |
841 | } | |
842 | ||
843 | /* Calculate set of nodes reachable without using the protected interface. */ | |
844 | static void lfa_calc_reach_nodes(struct isis_spftree *spftree, | |
845 | struct isis_spftree *spftree_root, | |
846 | const struct isis_spf_nodes *adj_nodes, | |
847 | bool p_space, | |
848 | const struct lfa_protected_resource *resource, | |
849 | struct isis_spf_nodes *nodes) | |
850 | { | |
851 | struct isis_vertex *vertex; | |
852 | struct listnode *node; | |
853 | ||
854 | for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, node, vertex)) { | |
855 | char buf[VID2STR_BUFFER]; | |
856 | ||
857 | if (!VTYPE_IS(vertex->type)) | |
858 | continue; | |
859 | ||
860 | /* Skip root node. */ | |
861 | if (!memcmp(vertex->N.id, spftree_root->sysid, ISIS_SYS_ID_LEN)) | |
862 | continue; | |
863 | ||
864 | /* Don't add the same node twice. */ | |
865 | if (isis_spf_node_find(nodes, vertex->N.id)) | |
866 | continue; | |
867 | ||
2866b119 RW |
868 | if (IS_DEBUG_LFA) |
869 | zlog_debug("ISIS-LFA: checking %s", | |
c951ee6e RW |
870 | vid2string(vertex, buf, sizeof(buf))); |
871 | ||
872 | if (!vertex_is_affected(spftree_root, adj_nodes, p_space, | |
873 | vertex, resource)) { | |
2866b119 | 874 | if (IS_DEBUG_LFA) |
c951ee6e | 875 | zlog_debug( |
2866b119 | 876 | "ISIS-LFA: adding %s", |
c951ee6e RW |
877 | vid2string(vertex, buf, sizeof(buf))); |
878 | ||
879 | isis_spf_node_new(nodes, vertex->N.id); | |
880 | } | |
881 | } | |
882 | } | |
883 | ||
884 | /** | |
885 | * Helper function used to create an SPF tree structure and run reverse SPF on | |
886 | * it. | |
887 | * | |
888 | * @param spftree IS-IS SPF tree | |
889 | * | |
2866b119 | 890 | * @return Pointer to new SPF tree structure. |
c951ee6e RW |
891 | */ |
892 | struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree) | |
893 | { | |
894 | struct isis_spftree *spftree_reverse; | |
895 | ||
896 | spftree_reverse = isis_spftree_new( | |
897 | spftree->area, spftree->lspdb, spftree->sysid, spftree->level, | |
898 | spftree->tree_id, SPF_TYPE_REVERSE, | |
899 | F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES); | |
900 | isis_run_spf(spftree_reverse); | |
901 | ||
902 | return spftree_reverse; | |
903 | } | |
904 | ||
905 | /* | |
906 | * Calculate the Extended P-space and Q-space associated to a given link | |
907 | * failure. | |
908 | */ | |
909 | static void lfa_calc_pq_spaces(struct isis_spftree *spftree_pc, | |
910 | const struct lfa_protected_resource *resource) | |
911 | { | |
912 | struct isis_spftree *spftree; | |
913 | struct isis_spftree *spftree_reverse; | |
914 | struct isis_spf_nodes *adj_nodes; | |
915 | struct isis_spf_node *adj_node; | |
916 | ||
917 | /* Obtain pre-failure SPTs and list of adjacent nodes. */ | |
918 | spftree = spftree_pc->lfa.old.spftree; | |
919 | spftree_reverse = spftree_pc->lfa.old.spftree_reverse; | |
920 | adj_nodes = &spftree->adj_nodes; | |
921 | ||
2866b119 RW |
922 | if (IS_DEBUG_LFA) |
923 | zlog_debug("ISIS-LFA: computing P-space (self)"); | |
c951ee6e RW |
924 | lfa_calc_reach_nodes(spftree, spftree, adj_nodes, true, resource, |
925 | &spftree_pc->lfa.p_space); | |
926 | ||
927 | RB_FOREACH (adj_node, isis_spf_nodes, adj_nodes) { | |
928 | if (spf_adj_node_is_affected(adj_node, resource, | |
929 | spftree->sysid)) { | |
2866b119 RW |
930 | if (IS_DEBUG_LFA) |
931 | zlog_debug("ISIS-LFA: computing Q-space (%s)", | |
932 | print_sys_hostname(adj_node->sysid)); | |
c951ee6e RW |
933 | |
934 | /* | |
935 | * Compute the reverse SPF in the behalf of the node | |
936 | * adjacent to the failure. | |
937 | */ | |
938 | adj_node->lfa.spftree_reverse = | |
939 | isis_spf_reverse_run(adj_node->lfa.spftree); | |
940 | ||
941 | lfa_calc_reach_nodes(adj_node->lfa.spftree_reverse, | |
942 | spftree_reverse, adj_nodes, false, | |
943 | resource, | |
944 | &spftree_pc->lfa.q_space); | |
945 | } else { | |
2866b119 RW |
946 | if (IS_DEBUG_LFA) |
947 | zlog_debug("ISIS-LFA: computing P-space (%s)", | |
948 | print_sys_hostname(adj_node->sysid)); | |
c951ee6e RW |
949 | lfa_calc_reach_nodes(adj_node->lfa.spftree, spftree, |
950 | adj_nodes, true, resource, | |
951 | &adj_node->lfa.p_space); | |
952 | } | |
953 | } | |
954 | } | |
955 | ||
956 | /** | |
957 | * Compute the TI-LFA backup paths for a given protected interface. | |
958 | * | |
959 | * @param area IS-IS area | |
960 | * @param spftree IS-IS SPF tree | |
961 | * @param spftree_reverse IS-IS Reverse SPF tree | |
962 | * @param resource Protected resource | |
963 | * | |
964 | * @return Pointer to the post-convergence SPF tree | |
965 | */ | |
966 | struct isis_spftree *isis_tilfa_compute(struct isis_area *area, | |
967 | struct isis_spftree *spftree, | |
968 | struct isis_spftree *spftree_reverse, | |
969 | struct lfa_protected_resource *resource) | |
970 | { | |
971 | struct isis_spftree *spftree_pc; | |
972 | struct isis_spf_node *adj_node; | |
973 | ||
2866b119 RW |
974 | if (IS_DEBUG_LFA) |
975 | zlog_debug("ISIS-LFA: computing the P/Q spaces w.r.t. %s", | |
c951ee6e RW |
976 | lfa_protected_resource2str(resource)); |
977 | ||
978 | /* Populate list of nodes affected by link failure. */ | |
979 | if (resource->type == LFA_NODE_PROTECTION) { | |
980 | isis_spf_node_list_init(&resource->nodes); | |
981 | RB_FOREACH (adj_node, isis_spf_nodes, &spftree->adj_nodes) { | |
982 | if (spf_adj_node_is_affected(adj_node, resource, | |
983 | spftree->sysid)) | |
984 | isis_spf_node_new(&resource->nodes, | |
985 | adj_node->sysid); | |
986 | } | |
987 | } | |
988 | ||
989 | /* Create post-convergence SPF tree. */ | |
990 | spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid, | |
991 | spftree->level, spftree->tree_id, | |
992 | SPF_TYPE_TI_LFA, spftree->flags); | |
993 | spftree_pc->lfa.old.spftree = spftree; | |
994 | spftree_pc->lfa.old.spftree_reverse = spftree_reverse; | |
995 | spftree_pc->lfa.protected_resource = *resource; | |
996 | ||
997 | /* Compute the extended P-space and Q-space. */ | |
998 | lfa_calc_pq_spaces(spftree_pc, resource); | |
999 | ||
2866b119 | 1000 | if (IS_DEBUG_LFA) |
c951ee6e | 1001 | zlog_debug( |
2866b119 | 1002 | "ISIS-LFA: computing the post convergence SPT w.r.t. %s", |
c951ee6e RW |
1003 | lfa_protected_resource2str(resource)); |
1004 | ||
1005 | /* Re-run SPF in the local node to find the post-convergence paths. */ | |
1006 | isis_run_spf(spftree_pc); | |
1007 | ||
1008 | /* Clear list of nodes affeted by link failure. */ | |
1009 | if (resource->type == LFA_NODE_PROTECTION) | |
1010 | isis_spf_node_list_clear(&resource->nodes); | |
1011 | ||
1012 | return spftree_pc; | |
1013 | } | |
1014 | ||
1015 | /** | |
1016 | * Run forward SPF on all adjacent routers. | |
1017 | * | |
1018 | * @param spftree IS-IS SPF tree | |
1019 | * | |
1020 | * @return 0 on success, -1 otherwise | |
1021 | */ | |
1022 | int isis_spf_run_neighbors(struct isis_spftree *spftree) | |
1023 | { | |
1024 | struct isis_lsp *lsp; | |
1025 | struct isis_spf_node *adj_node; | |
1026 | ||
1027 | lsp = isis_root_system_lsp(spftree->lspdb, spftree->sysid); | |
1028 | if (!lsp) | |
1029 | return -1; | |
1030 | ||
1031 | RB_FOREACH (adj_node, isis_spf_nodes, &spftree->adj_nodes) { | |
2866b119 RW |
1032 | if (IS_DEBUG_LFA) |
1033 | zlog_debug("ISIS-LFA: running SPF on neighbor %s", | |
c951ee6e RW |
1034 | print_sys_hostname(adj_node->sysid)); |
1035 | ||
1036 | /* Compute the SPT on behalf of the neighbor. */ | |
1037 | adj_node->lfa.spftree = isis_spftree_new( | |
1038 | spftree->area, spftree->lspdb, adj_node->sysid, | |
1039 | spftree->level, spftree->tree_id, SPF_TYPE_FORWARD, | |
1040 | F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES); | |
1041 | isis_run_spf(adj_node->lfa.spftree); | |
1042 | } | |
1043 | ||
1044 | return 0; | |
1045 | } | |
1046 | ||
1047 | /** | |
1048 | * Run the TI-LFA algorithm for all proctected interfaces. | |
1049 | * | |
1050 | * @param area IS-IS area | |
1051 | * @param spftree IS-IS SPF tree | |
1052 | */ | |
1053 | void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree) | |
1054 | { | |
1055 | struct isis_spftree *spftree_reverse; | |
1056 | struct isis_circuit *circuit; | |
1057 | struct listnode *node; | |
1058 | ||
1059 | /* Run reverse SPF locally. */ | |
1060 | spftree_reverse = isis_spf_reverse_run(spftree); | |
1061 | ||
1062 | /* Run forward SPF on all adjacent routers. */ | |
1063 | isis_spf_run_neighbors(spftree); | |
1064 | ||
1065 | /* Check which interfaces are protected. */ | |
1066 | for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { | |
1067 | struct lfa_protected_resource resource = {}; | |
1068 | struct isis_adjacency *adj; | |
1069 | struct isis_spftree *spftree_pc_link; | |
1070 | struct isis_spftree *spftree_pc_node; | |
1071 | static uint8_t null_sysid[ISIS_SYS_ID_LEN + 1]; | |
1072 | ||
1073 | if (!(circuit->is_type & spftree->level)) | |
1074 | continue; | |
1075 | ||
1076 | if (!circuit->tilfa_protection[spftree->level - 1]) | |
1077 | continue; | |
1078 | ||
1079 | /* Fill in the protected resource. */ | |
1080 | switch (circuit->circ_type) { | |
1081 | case CIRCUIT_T_BROADCAST: | |
1082 | if (spftree->level == 1) | |
1083 | memcpy(resource.adjacency, | |
1084 | circuit->u.bc.l1_desig_is, | |
1085 | ISIS_SYS_ID_LEN + 1); | |
1086 | else | |
1087 | memcpy(resource.adjacency, | |
1088 | circuit->u.bc.l2_desig_is, | |
1089 | ISIS_SYS_ID_LEN + 1); | |
1090 | /* Do nothing if no DR was elected yet. */ | |
1091 | if (!memcmp(resource.adjacency, null_sysid, | |
1092 | ISIS_SYS_ID_LEN + 1)) | |
1093 | continue; | |
1094 | break; | |
1095 | case CIRCUIT_T_P2P: | |
1096 | adj = circuit->u.p2p.neighbor; | |
1097 | if (!adj) | |
1098 | continue; | |
1099 | memcpy(resource.adjacency, adj->sysid, ISIS_SYS_ID_LEN); | |
1100 | LSP_PSEUDO_ID(resource.adjacency) = 0; | |
1101 | break; | |
1102 | default: | |
1103 | continue; | |
1104 | } | |
1105 | ||
1106 | /* Compute node protecting repair paths first (if necessary). */ | |
1107 | if (circuit->tilfa_node_protection[spftree->level - 1]) { | |
1108 | resource.type = LFA_NODE_PROTECTION; | |
1109 | spftree_pc_node = isis_tilfa_compute( | |
1110 | area, spftree, spftree_reverse, &resource); | |
1111 | isis_spftree_del(spftree_pc_node); | |
1112 | } | |
1113 | ||
1114 | /* Compute link protecting repair paths. */ | |
1115 | resource.type = LFA_LINK_PROTECTION; | |
1116 | spftree_pc_link = isis_tilfa_compute( | |
1117 | area, spftree, spftree_reverse, &resource); | |
1118 | isis_spftree_del(spftree_pc_link); | |
1119 | } | |
1120 | ||
1121 | isis_spftree_del(spftree_reverse); | |
1122 | } |