]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
7fd0729f G |
2 | /* |
3 | * OSPF TI-LFA | |
4 | * Copyright (C) 2020 NetDEF, Inc. | |
5 | * Sascha Kattelmann | |
7fd0729f G |
6 | */ |
7 | ||
8 | #include <zebra.h> | |
9 | ||
10 | #include "prefix.h" | |
11 | #include "table.h" | |
385a1e07 | 12 | #include "printfrr.h" |
7fd0729f G |
13 | |
14 | #include "ospfd/ospfd.h" | |
15 | #include "ospfd/ospf_asbr.h" | |
16 | #include "ospfd/ospf_lsa.h" | |
17 | #include "ospfd/ospf_spf.h" | |
18 | #include "ospfd/ospf_sr.h" | |
19 | #include "ospfd/ospf_route.h" | |
20 | #include "ospfd/ospf_ti_lfa.h" | |
a4553b5b | 21 | #include "ospfd/ospf_dump.h" |
7fd0729f G |
22 | |
23 | ||
24 | DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item, | |
960b9a53 | 25 | p_spaces_compare_func); |
7fd0729f | 26 | DECLARE_RBTREE_UNIQ(q_spaces, struct q_space, q_spaces_item, |
960b9a53 | 27 | q_spaces_compare_func); |
7fd0729f | 28 | |
bdcfd34a G |
29 | static void |
30 | ospf_ti_lfa_generate_p_space(struct ospf_area *area, struct vertex *child, | |
31 | struct protected_resource *protected_resource, | |
32 | bool recursive, struct list *pc_path); | |
33 | ||
385a1e07 G |
34 | void ospf_print_protected_resource( |
35 | struct protected_resource *protected_resource, char *buf) | |
36 | { | |
37 | struct router_lsa_link *link; | |
38 | ||
39 | switch (protected_resource->type) { | |
40 | case OSPF_TI_LFA_LINK_PROTECTION: | |
41 | link = protected_resource->link; | |
42 | snprintfrr(buf, PROTECTED_RESOURCE_STRLEN, | |
43 | "protected link: %pI4 %pI4", &link->link_id, | |
44 | &link->link_data); | |
45 | break; | |
46 | case OSPF_TI_LFA_NODE_PROTECTION: | |
47 | snprintfrr(buf, PROTECTED_RESOURCE_STRLEN, | |
48 | "protected node: %pI4", | |
49 | &protected_resource->router_id); | |
50 | break; | |
51 | case OSPF_TI_LFA_UNDEFINED_PROTECTION: | |
52 | snprintfrr(buf, PROTECTED_RESOURCE_STRLEN, | |
53 | "undefined protected resource"); | |
54 | break; | |
55 | } | |
56 | } | |
7fd0729f | 57 | |
bdcfd34a G |
58 | static enum ospf_ti_lfa_p_q_space_adjacency |
59 | ospf_ti_lfa_find_p_node(struct vertex *pc_node, struct p_space *p_space, | |
60 | struct q_space *q_space) | |
7fd0729f | 61 | { |
9d3444f8 G |
62 | struct listnode *curr_node; |
63 | struct vertex *p_node = NULL, *pc_node_parent, *p_node_pc_parent; | |
7fd0729f G |
64 | struct vertex_parent *pc_vertex_parent; |
65 | ||
9d3444f8 G |
66 | curr_node = listnode_lookup(q_space->pc_path, pc_node); |
67 | pc_node_parent = listgetdata(curr_node->next); | |
7fd0729f | 68 | |
bdcfd34a | 69 | q_space->p_node_info->type = OSPF_TI_LFA_UNDEFINED_NODE; |
7fd0729f | 70 | |
9d3444f8 G |
71 | p_node = ospf_spf_vertex_find(pc_node_parent->id, p_space->vertex_list); |
72 | ||
73 | if (p_node) { | |
bdcfd34a G |
74 | q_space->p_node_info->node = p_node; |
75 | q_space->p_node_info->type = OSPF_TI_LFA_P_NODE; | |
9d3444f8 G |
76 | |
77 | if (curr_node->next->next) { | |
78 | p_node_pc_parent = listgetdata(curr_node->next->next); | |
79 | pc_vertex_parent = ospf_spf_vertex_parent_find( | |
80 | p_node_pc_parent->id, pc_node_parent); | |
bdcfd34a G |
81 | q_space->p_node_info->nexthop = |
82 | pc_vertex_parent->nexthop->router; | |
9d3444f8 G |
83 | } else { |
84 | /* | |
85 | * It can happen that the P node is the root node itself | |
86 | * (hence there can be no parents). In this case we | |
87 | * don't need to set a nexthop. | |
88 | */ | |
bdcfd34a | 89 | q_space->p_node_info->nexthop.s_addr = INADDR_ANY; |
9d3444f8 | 90 | } |
bdcfd34a G |
91 | |
92 | return OSPF_TI_LFA_P_Q_SPACE_ADJACENT; | |
7fd0729f | 93 | } |
bdcfd34a G |
94 | |
95 | ospf_ti_lfa_find_p_node(pc_node_parent, p_space, q_space); | |
96 | return OSPF_TI_LFA_P_Q_SPACE_NON_ADJACENT; | |
7fd0729f G |
97 | } |
98 | ||
99 | static void ospf_ti_lfa_find_q_node(struct vertex *pc_node, | |
100 | struct p_space *p_space, | |
bdcfd34a | 101 | struct q_space *q_space) |
7fd0729f | 102 | { |
9d3444f8 G |
103 | struct listnode *curr_node, *next_node; |
104 | struct vertex *p_node, *q_node, *q_space_parent = NULL, *pc_node_parent; | |
7fd0729f G |
105 | struct vertex_parent *pc_vertex_parent; |
106 | ||
9d3444f8 G |
107 | curr_node = listnode_lookup(q_space->pc_path, pc_node); |
108 | next_node = curr_node->next; | |
109 | pc_node_parent = listgetdata(next_node); | |
110 | pc_vertex_parent = | |
111 | ospf_spf_vertex_parent_find(pc_node_parent->id, pc_node); | |
112 | ||
7fd0729f G |
113 | p_node = ospf_spf_vertex_find(pc_node->id, p_space->vertex_list); |
114 | q_node = ospf_spf_vertex_find(pc_node->id, q_space->vertex_list); | |
115 | ||
7815c834 G |
116 | /* The Q node is always present. */ |
117 | assert(q_node); | |
118 | ||
bdcfd34a | 119 | q_space->q_node_info->type = OSPF_TI_LFA_UNDEFINED_NODE; |
7fd0729f G |
120 | |
121 | if (p_node && q_node) { | |
bdcfd34a G |
122 | q_space->q_node_info->node = pc_node; |
123 | q_space->q_node_info->type = OSPF_TI_LFA_PQ_NODE; | |
124 | q_space->q_node_info->nexthop = | |
125 | pc_vertex_parent->nexthop->router; | |
7fd0729f G |
126 | return; |
127 | } | |
128 | ||
9d3444f8 G |
129 | /* |
130 | * Note that the Q space has the 'reverse' direction of the PC | |
131 | * SPF. Hence compare PC SPF parent to Q space children. | |
132 | */ | |
133 | q_space_parent = | |
134 | ospf_spf_vertex_find(pc_node_parent->id, q_node->children); | |
7fd0729f G |
135 | |
136 | /* | |
137 | * If the Q space parent doesn't exist we 'hit' the border to the P | |
138 | * space and hence got our Q node. | |
139 | */ | |
140 | if (!q_space_parent) { | |
bdcfd34a G |
141 | q_space->q_node_info->node = pc_node; |
142 | q_space->q_node_info->type = OSPF_TI_LFA_Q_NODE; | |
143 | q_space->q_node_info->nexthop = | |
144 | pc_vertex_parent->nexthop->router; | |
7fd0729f G |
145 | return; |
146 | } | |
147 | ||
bdcfd34a | 148 | return ospf_ti_lfa_find_q_node(pc_node_parent, p_space, q_space); |
7fd0729f G |
149 | } |
150 | ||
bdcfd34a G |
151 | static void ospf_ti_lfa_append_label_stack(struct mpls_label_stack *label_stack, |
152 | mpls_label_t labels[], | |
153 | uint32_t num_labels) | |
154 | { | |
155 | int i, offset, limit; | |
156 | ||
157 | limit = label_stack->num_labels + num_labels; | |
158 | offset = label_stack->num_labels; | |
159 | ||
160 | for (i = label_stack->num_labels; i < limit; i++) { | |
161 | label_stack->label[i] = labels[i - offset]; | |
162 | label_stack->num_labels++; | |
163 | } | |
164 | } | |
165 | ||
166 | ||
7fd0729f G |
167 | static struct mpls_label_stack * |
168 | ospf_ti_lfa_create_label_stack(mpls_label_t labels[], uint32_t num_labels) | |
169 | { | |
170 | struct mpls_label_stack *label_stack; | |
171 | uint32_t i; | |
172 | ||
173 | /* Sanity check */ | |
174 | for (i = 0; i < num_labels; i++) { | |
175 | if (labels[i] == MPLS_INVALID_LABEL) | |
176 | return NULL; | |
177 | } | |
178 | ||
179 | label_stack = XCALLOC(MTYPE_OSPF_Q_SPACE, | |
180 | sizeof(struct mpls_label_stack) | |
bdcfd34a | 181 | + MPLS_MAX_LABELS * sizeof(mpls_label_t)); |
7fd0729f G |
182 | label_stack->num_labels = num_labels; |
183 | ||
184 | for (i = 0; i < num_labels; i++) | |
185 | label_stack->label[i] = labels[i]; | |
186 | ||
187 | return label_stack; | |
188 | } | |
189 | ||
bdcfd34a G |
190 | static struct list * |
191 | ospf_ti_lfa_map_path_to_pc_vertices(struct list *path, | |
192 | struct list *pc_vertex_list) | |
193 | { | |
194 | struct listnode *node; | |
195 | struct vertex *vertex, *pc_vertex; | |
196 | struct list *pc_path; | |
197 | ||
198 | pc_path = list_new(); | |
199 | ||
200 | for (ALL_LIST_ELEMENTS_RO(path, node, vertex)) { | |
201 | pc_vertex = ospf_spf_vertex_find(vertex->id, pc_vertex_list); | |
202 | listnode_add(pc_path, pc_vertex); | |
203 | } | |
204 | ||
205 | return pc_path; | |
206 | } | |
207 | ||
208 | static struct list *ospf_ti_lfa_cut_out_pc_path(struct list *pc_vertex_list, | |
209 | struct list *pc_path, | |
210 | struct vertex *p_node, | |
211 | struct vertex *q_node) | |
212 | { | |
213 | struct list *inner_pc_path; | |
214 | struct vertex *current_vertex; | |
215 | struct listnode *current_listnode; | |
216 | ||
217 | inner_pc_path = list_new(); | |
218 | current_vertex = ospf_spf_vertex_find(q_node->id, pc_vertex_list); | |
219 | current_listnode = listnode_lookup(pc_path, current_vertex); | |
220 | ||
221 | /* Note that the post-convergence paths are reversed. */ | |
222 | for (;;) { | |
223 | current_vertex = listgetdata(current_listnode); | |
224 | listnode_add(inner_pc_path, current_vertex); | |
225 | ||
226 | if (current_vertex->id.s_addr == p_node->id.s_addr) | |
227 | break; | |
228 | ||
229 | current_listnode = current_listnode->next; | |
230 | } | |
231 | ||
232 | return inner_pc_path; | |
233 | } | |
234 | ||
235 | static void ospf_ti_lfa_generate_inner_label_stack( | |
236 | struct ospf_area *area, struct p_space *p_space, | |
237 | struct q_space *q_space, | |
238 | struct ospf_ti_lfa_inner_backup_path_info *inner_backup_path_info) | |
239 | { | |
240 | struct route_table *new_table, *new_rtrs; | |
241 | struct vertex *q_node; | |
242 | struct vertex *start_vertex, *end_vertex; | |
243 | struct vertex_parent *vertex_parent; | |
244 | struct listnode *pc_p_node, *pc_q_node; | |
245 | struct vertex *spf_orig; | |
246 | struct list *vertex_list_orig; | |
247 | struct p_spaces_head *p_spaces_orig; | |
248 | struct p_space *inner_p_space; | |
249 | struct q_space *inner_q_space; | |
250 | struct ospf_ti_lfa_node_info *p_node_info, *q_node_info; | |
251 | struct protected_resource *protected_resource; | |
252 | struct list *inner_pc_path; | |
253 | mpls_label_t start_label, end_label; | |
254 | ||
255 | p_node_info = q_space->p_node_info; | |
256 | q_node_info = q_space->q_node_info; | |
257 | protected_resource = p_space->protected_resource; | |
258 | ||
259 | start_vertex = p_node_info->node; | |
260 | end_vertex = q_node_info->node; | |
261 | ||
262 | /* | |
263 | * It can happen that the P node and/or the Q node are the root or | |
264 | * the destination, therefore we need to force one step forward (resp. | |
265 | * backward) using an Adjacency-SID. | |
266 | */ | |
267 | start_label = MPLS_INVALID_LABEL; | |
268 | end_label = MPLS_INVALID_LABEL; | |
269 | if (p_node_info->node->id.s_addr == p_space->root->id.s_addr) { | |
270 | pc_p_node = listnode_lookup(q_space->pc_path, p_space->pc_spf); | |
271 | start_vertex = listgetdata(pc_p_node->prev); | |
272 | start_label = ospf_sr_get_adj_sid_by_id(&p_node_info->node->id, | |
273 | &start_vertex->id); | |
274 | } | |
275 | if (q_node_info->node->id.s_addr == q_space->root->id.s_addr) { | |
276 | pc_q_node = listnode_lookup(q_space->pc_path, | |
277 | listnode_head(q_space->pc_path)); | |
278 | end_vertex = listgetdata(pc_q_node->next); | |
279 | end_label = ospf_sr_get_adj_sid_by_id(&end_vertex->id, | |
280 | &q_node_info->node->id); | |
281 | } | |
282 | ||
283 | /* Corner case: inner path is just one node */ | |
284 | if (start_vertex->id.s_addr == end_vertex->id.s_addr) { | |
285 | inner_backup_path_info->label_stack = | |
286 | ospf_ti_lfa_create_label_stack(&start_label, 1); | |
287 | inner_backup_path_info->q_node_info.node = end_vertex; | |
288 | inner_backup_path_info->q_node_info.type = OSPF_TI_LFA_PQ_NODE; | |
289 | inner_backup_path_info->p_node_info.type = | |
290 | OSPF_TI_LFA_UNDEFINED_NODE; | |
291 | vertex_parent = ospf_spf_vertex_parent_find(p_space->root->id, | |
292 | end_vertex); | |
293 | inner_backup_path_info->p_node_info.nexthop = | |
294 | vertex_parent->nexthop->router; | |
295 | return; | |
296 | } | |
297 | ||
298 | inner_pc_path = ospf_ti_lfa_cut_out_pc_path(p_space->pc_vertex_list, | |
299 | q_space->pc_path, | |
300 | start_vertex, end_vertex); | |
301 | ||
302 | new_table = route_table_init(); | |
303 | new_rtrs = route_table_init(); | |
304 | ||
305 | /* Copy the current state ... */ | |
306 | spf_orig = area->spf; | |
307 | vertex_list_orig = area->spf_vertex_list; | |
308 | p_spaces_orig = area->p_spaces; | |
309 | ||
310 | area->p_spaces = | |
311 | XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head)); | |
312 | ||
313 | /* dry run true, root node false */ | |
b538baf3 CH |
314 | ospf_spf_calculate(area, start_vertex->lsa_p, new_table, NULL, new_rtrs, |
315 | true, false); | |
bdcfd34a G |
316 | |
317 | q_node = ospf_spf_vertex_find(end_vertex->id, area->spf_vertex_list); | |
318 | ||
319 | ospf_ti_lfa_generate_p_space(area, q_node, protected_resource, false, | |
320 | inner_pc_path); | |
321 | ||
322 | /* There's just one P and Q space */ | |
323 | inner_p_space = p_spaces_pop(area->p_spaces); | |
324 | inner_q_space = q_spaces_pop(inner_p_space->q_spaces); | |
325 | ||
326 | /* Copy over inner backup path information from the inner q_space */ | |
327 | ||
328 | /* In case the outer P node is also the root of the P space */ | |
329 | if (start_label != MPLS_INVALID_LABEL) { | |
330 | inner_backup_path_info->label_stack = | |
331 | ospf_ti_lfa_create_label_stack(&start_label, 1); | |
332 | ospf_ti_lfa_append_label_stack( | |
333 | inner_backup_path_info->label_stack, | |
334 | inner_q_space->label_stack->label, | |
335 | inner_q_space->label_stack->num_labels); | |
336 | inner_backup_path_info->p_node_info.node = start_vertex; | |
337 | inner_backup_path_info->p_node_info.type = OSPF_TI_LFA_P_NODE; | |
338 | vertex_parent = ospf_spf_vertex_parent_find(p_space->root->id, | |
339 | start_vertex); | |
340 | inner_backup_path_info->p_node_info.nexthop = | |
341 | vertex_parent->nexthop->router; | |
342 | } else { | |
343 | memcpy(inner_backup_path_info->label_stack, | |
344 | inner_q_space->label_stack, | |
345 | sizeof(struct mpls_label_stack) | |
346 | + sizeof(mpls_label_t) | |
347 | * inner_q_space->label_stack | |
348 | ->num_labels); | |
349 | memcpy(&inner_backup_path_info->p_node_info, | |
350 | inner_q_space->p_node_info, | |
351 | sizeof(struct ospf_ti_lfa_node_info)); | |
352 | } | |
353 | ||
354 | /* In case the outer Q node is also the root of the Q space */ | |
355 | if (end_label != MPLS_INVALID_LABEL) { | |
356 | inner_backup_path_info->q_node_info.node = end_vertex; | |
357 | inner_backup_path_info->q_node_info.type = OSPF_TI_LFA_Q_NODE; | |
358 | } else { | |
359 | memcpy(&inner_backup_path_info->q_node_info, | |
360 | inner_q_space->q_node_info, | |
361 | sizeof(struct ospf_ti_lfa_node_info)); | |
362 | } | |
363 | ||
364 | /* Cleanup */ | |
365 | ospf_ti_lfa_free_p_spaces(area); | |
366 | ospf_spf_cleanup(area->spf, area->spf_vertex_list); | |
367 | ||
368 | /* ... and copy the current state back. */ | |
369 | area->spf = spf_orig; | |
370 | area->spf_vertex_list = vertex_list_orig; | |
371 | area->p_spaces = p_spaces_orig; | |
372 | } | |
373 | ||
374 | static void ospf_ti_lfa_generate_label_stack(struct ospf_area *area, | |
375 | struct p_space *p_space, | |
7fd0729f G |
376 | struct q_space *q_space) |
377 | { | |
bdcfd34a G |
378 | enum ospf_ti_lfa_p_q_space_adjacency adjacency_result; |
379 | mpls_label_t labels[MPLS_MAX_LABELS]; | |
7fd0729f | 380 | struct vertex *pc_node; |
bdcfd34a | 381 | struct ospf_ti_lfa_inner_backup_path_info inner_backup_path_info; |
7fd0729f | 382 | |
a4553b5b G |
383 | if (IS_DEBUG_OSPF_TI_LFA) |
384 | zlog_debug( | |
385 | "%s: Generating Label stack for src %pI4 and dest %pI4.", | |
386 | __func__, &p_space->root->id, &q_space->root->id); | |
7fd0729f | 387 | |
9d3444f8 | 388 | pc_node = listnode_head(q_space->pc_path); |
cc1725bd | 389 | |
7fd0729f | 390 | if (!pc_node) { |
a4553b5b G |
391 | if (IS_DEBUG_OSPF_TI_LFA) |
392 | zlog_debug( | |
393 | "%s: There seems to be no post convergence path (yet).", | |
394 | __func__); | |
7fd0729f G |
395 | return; |
396 | } | |
397 | ||
bdcfd34a G |
398 | ospf_ti_lfa_find_q_node(pc_node, p_space, q_space); |
399 | if (q_space->q_node_info->type == OSPF_TI_LFA_UNDEFINED_NODE) { | |
a4553b5b G |
400 | if (IS_DEBUG_OSPF_TI_LFA) |
401 | zlog_debug("%s: Q node not found!", __func__); | |
7fd0729f G |
402 | return; |
403 | } | |
404 | ||
405 | /* Found a PQ node? Then we are done here. */ | |
bdcfd34a | 406 | if (q_space->q_node_info->type == OSPF_TI_LFA_PQ_NODE) { |
cc1725bd G |
407 | /* |
408 | * If the PQ node is a child of the root, then we can use an | |
409 | * adjacency SID instead of a prefix SID for the backup path. | |
410 | */ | |
411 | if (ospf_spf_vertex_parent_find(p_space->root->id, | |
bdcfd34a | 412 | q_space->q_node_info->node)) |
cc1725bd | 413 | labels[0] = ospf_sr_get_adj_sid_by_id( |
bdcfd34a G |
414 | &p_space->root->id, |
415 | &q_space->q_node_info->node->id); | |
cc1725bd G |
416 | else |
417 | labels[0] = ospf_sr_get_prefix_sid_by_id( | |
bdcfd34a | 418 | &q_space->q_node_info->node->id); |
cc1725bd | 419 | |
7fd0729f G |
420 | q_space->label_stack = |
421 | ospf_ti_lfa_create_label_stack(labels, 1); | |
bdcfd34a | 422 | q_space->nexthop = q_space->q_node_info->nexthop; |
cc1725bd | 423 | |
7fd0729f G |
424 | return; |
425 | } | |
426 | ||
bdcfd34a G |
427 | /* Otherwise find a (hopefully adjacent) P node. */ |
428 | pc_node = ospf_spf_vertex_find(q_space->q_node_info->node->id, | |
7fd0729f | 429 | p_space->pc_vertex_list); |
bdcfd34a G |
430 | adjacency_result = ospf_ti_lfa_find_p_node(pc_node, p_space, q_space); |
431 | ||
432 | if (q_space->p_node_info->type == OSPF_TI_LFA_UNDEFINED_NODE) { | |
a4553b5b G |
433 | if (IS_DEBUG_OSPF_TI_LFA) |
434 | zlog_debug("%s: P node not found!", __func__); | |
7fd0729f G |
435 | return; |
436 | } | |
437 | ||
438 | /* | |
bdcfd34a G |
439 | * This should be the regular case: P and Q space are adjacent or even |
440 | * overlapping. This is guaranteed for link protection when used with | |
441 | * symmetric weights. | |
7fd0729f | 442 | */ |
bdcfd34a G |
443 | if (adjacency_result == OSPF_TI_LFA_P_Q_SPACE_ADJACENT) { |
444 | /* | |
445 | * It can happen that the P node is the root itself, therefore | |
446 | * we don't need a label for it. So just one adjacency SID for | |
447 | * the Q node. | |
448 | */ | |
449 | if (q_space->p_node_info->node->id.s_addr | |
450 | == p_space->root->id.s_addr) { | |
451 | labels[0] = ospf_sr_get_adj_sid_by_id( | |
452 | &p_space->root->id, | |
453 | &q_space->q_node_info->node->id); | |
454 | q_space->label_stack = | |
455 | ospf_ti_lfa_create_label_stack(labels, 1); | |
456 | q_space->nexthop = q_space->q_node_info->nexthop; | |
457 | return; | |
458 | } | |
459 | ||
460 | /* | |
461 | * Otherwise we have a P and also a Q node (which are adjacent). | |
462 | * | |
463 | * It can happen that the P node is a child of the root, | |
464 | * therefore we might just need the adjacency SID for the P node | |
465 | * instead of the prefix SID. For the Q node always take the | |
466 | * adjacency SID. | |
467 | */ | |
468 | if (ospf_spf_vertex_parent_find(p_space->root->id, | |
469 | q_space->p_node_info->node)) | |
470 | labels[0] = ospf_sr_get_adj_sid_by_id( | |
471 | &p_space->root->id, | |
472 | &q_space->p_node_info->node->id); | |
473 | else | |
474 | labels[0] = ospf_sr_get_prefix_sid_by_id( | |
475 | &q_space->p_node_info->node->id); | |
476 | ||
477 | labels[1] = ospf_sr_get_adj_sid_by_id( | |
478 | &q_space->p_node_info->node->id, | |
479 | &q_space->q_node_info->node->id); | |
480 | ||
7fd0729f | 481 | q_space->label_stack = |
bdcfd34a G |
482 | ospf_ti_lfa_create_label_stack(labels, 2); |
483 | q_space->nexthop = q_space->p_node_info->nexthop; | |
7fd0729f | 484 | |
bdcfd34a G |
485 | } else { |
486 | /* | |
487 | * It can happen that the P and Q space are not adjacent when | |
488 | * e.g. node protection or asymmetric weights are used. In this | |
489 | * case the found P and Q nodes are used as a reference for | |
490 | * another run of the algorithm! | |
491 | * | |
492 | * After having found the inner label stack it is stitched | |
493 | * together with the outer labels. | |
494 | */ | |
495 | inner_backup_path_info.label_stack = XCALLOC( | |
496 | MTYPE_OSPF_PATH, | |
497 | sizeof(struct mpls_label_stack) | |
498 | + sizeof(mpls_label_t) * MPLS_MAX_LABELS); | |
499 | ospf_ti_lfa_generate_inner_label_stack(area, p_space, q_space, | |
500 | &inner_backup_path_info); | |
cc1725bd | 501 | |
bdcfd34a G |
502 | /* |
503 | * First stitch together the outer P node label with the inner | |
504 | * label stack. | |
505 | */ | |
506 | if (q_space->p_node_info->node->id.s_addr | |
507 | == p_space->root->id.s_addr) { | |
508 | /* | |
509 | * It can happen that the P node is the root itself, | |
510 | * therefore we don't need a label for it. Just take | |
511 | * the inner label stack first. | |
512 | */ | |
513 | q_space->label_stack = ospf_ti_lfa_create_label_stack( | |
514 | inner_backup_path_info.label_stack->label, | |
515 | inner_backup_path_info.label_stack->num_labels); | |
516 | ||
517 | /* Use the inner P or Q node for the nexthop */ | |
518 | if (inner_backup_path_info.p_node_info.type | |
519 | != OSPF_TI_LFA_UNDEFINED_NODE) | |
520 | q_space->nexthop = inner_backup_path_info | |
521 | .p_node_info.nexthop; | |
522 | else | |
523 | q_space->nexthop = inner_backup_path_info | |
524 | .q_node_info.nexthop; | |
525 | ||
526 | } else if (ospf_spf_vertex_parent_find( | |
527 | p_space->root->id, | |
528 | q_space->p_node_info->node)) { | |
529 | /* | |
530 | * It can happen that the outer P node is a child of | |
531 | * the root, therefore we might just need the | |
532 | * adjacency SID for the outer P node instead of the | |
533 | * prefix SID. Then just append the inner label stack. | |
534 | */ | |
535 | labels[0] = ospf_sr_get_adj_sid_by_id( | |
536 | &p_space->root->id, | |
537 | &q_space->p_node_info->node->id); | |
538 | q_space->label_stack = | |
539 | ospf_ti_lfa_create_label_stack(labels, 1); | |
540 | ospf_ti_lfa_append_label_stack( | |
541 | q_space->label_stack, | |
542 | inner_backup_path_info.label_stack->label, | |
543 | inner_backup_path_info.label_stack->num_labels); | |
544 | q_space->nexthop = q_space->p_node_info->nexthop; | |
545 | } else { | |
546 | /* The outer P node needs a Prefix-SID here */ | |
547 | labels[0] = ospf_sr_get_prefix_sid_by_id( | |
548 | &q_space->p_node_info->node->id); | |
549 | q_space->label_stack = | |
550 | ospf_ti_lfa_create_label_stack(labels, 1); | |
551 | ospf_ti_lfa_append_label_stack( | |
552 | q_space->label_stack, | |
553 | inner_backup_path_info.label_stack->label, | |
554 | inner_backup_path_info.label_stack->num_labels); | |
555 | q_space->nexthop = q_space->p_node_info->nexthop; | |
556 | } | |
cc1725bd | 557 | |
bdcfd34a G |
558 | /* Now the outer Q node needs to be considered */ |
559 | if (ospf_spf_vertex_parent_find( | |
560 | inner_backup_path_info.q_node_info.node->id, | |
561 | q_space->q_node_info->node)) { | |
562 | /* | |
563 | * The outer Q node can be a child of the inner Q node, | |
564 | * hence just add an Adjacency-SID. | |
565 | */ | |
566 | labels[0] = ospf_sr_get_adj_sid_by_id( | |
567 | &inner_backup_path_info.q_node_info.node->id, | |
568 | &q_space->q_node_info->node->id); | |
569 | ospf_ti_lfa_append_label_stack(q_space->label_stack, | |
570 | labels, 1); | |
571 | } else { | |
572 | /* Otherwise a Prefix-SID is needed */ | |
573 | labels[0] = ospf_sr_get_prefix_sid_by_id( | |
574 | &q_space->q_node_info->node->id); | |
575 | ospf_ti_lfa_append_label_stack(q_space->label_stack, | |
576 | labels, 1); | |
577 | } | |
578 | /* | |
579 | * Note that there's also the case where the inner and outer Q | |
580 | * node are the same, but then there's nothing to do! | |
581 | */ | |
582 | } | |
7fd0729f G |
583 | } |
584 | ||
9d3444f8 G |
585 | static struct list * |
586 | ospf_ti_lfa_generate_post_convergence_path(struct list *pc_vertex_list, | |
587 | struct vertex *dest) | |
588 | { | |
589 | struct list *pc_path; | |
590 | struct vertex *current_vertex; | |
591 | struct vertex_parent *parent; | |
592 | ||
9d3444f8 G |
593 | current_vertex = ospf_spf_vertex_find(dest->id, pc_vertex_list); |
594 | if (!current_vertex) { | |
a4553b5b G |
595 | if (IS_DEBUG_OSPF_TI_LFA) |
596 | zlog_debug( | |
597 | "%s: There seems to be no post convergence path (yet).", | |
598 | __func__); | |
bdcfd34a | 599 | return NULL; |
9d3444f8 G |
600 | } |
601 | ||
bdcfd34a | 602 | pc_path = list_new(); |
9d3444f8 G |
603 | listnode_add(pc_path, current_vertex); |
604 | ||
605 | /* Generate a backup path in reverse order */ | |
606 | for (;;) { | |
607 | parent = listnode_head(current_vertex->parents); | |
608 | if (!parent) | |
609 | break; | |
610 | ||
611 | listnode_add(pc_path, parent->parent); | |
612 | current_vertex = parent->parent; | |
613 | } | |
614 | ||
615 | return pc_path; | |
616 | } | |
617 | ||
7fd0729f G |
618 | static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area, |
619 | struct p_space *p_space, | |
bdcfd34a G |
620 | struct vertex *dest, bool recursive, |
621 | struct list *pc_path) | |
7fd0729f G |
622 | { |
623 | struct listnode *node; | |
624 | struct vertex *child; | |
625 | struct route_table *new_table, *new_rtrs; | |
626 | struct q_space *q_space, q_space_search; | |
385a1e07 G |
627 | char label_buf[MPLS_LABEL_STRLEN]; |
628 | char res_buf[PROTECTED_RESOURCE_STRLEN]; | |
bdcfd34a | 629 | bool node_protected; |
385a1e07 G |
630 | |
631 | ospf_print_protected_resource(p_space->protected_resource, res_buf); | |
bdcfd34a G |
632 | node_protected = |
633 | p_space->protected_resource->type == OSPF_TI_LFA_NODE_PROTECTION | |
634 | && dest->id.s_addr | |
635 | == p_space->protected_resource->router_id.s_addr; | |
385a1e07 G |
636 | |
637 | /* | |
638 | * If node protection is used, don't build a Q space for the protected | |
639 | * node of that particular P space. Move on with children instead. | |
640 | */ | |
bdcfd34a G |
641 | if (node_protected) { |
642 | if (recursive) { | |
643 | /* Recursively generate Q spaces for all children */ | |
644 | for (ALL_LIST_ELEMENTS_RO(dest->children, node, child)) | |
645 | ospf_ti_lfa_generate_q_spaces(area, p_space, | |
646 | child, recursive, | |
647 | pc_path); | |
648 | } | |
385a1e07 G |
649 | return; |
650 | } | |
7fd0729f G |
651 | |
652 | /* Check if we already have a Q space for this destination */ | |
653 | q_space_search.root = dest; | |
654 | if (q_spaces_find(p_space->q_spaces, &q_space_search)) | |
655 | return; | |
656 | ||
657 | q_space = XCALLOC(MTYPE_OSPF_Q_SPACE, sizeof(struct q_space)); | |
bdcfd34a G |
658 | q_space->p_node_info = XCALLOC(MTYPE_OSPF_Q_SPACE, |
659 | sizeof(struct ospf_ti_lfa_node_info)); | |
660 | q_space->q_node_info = XCALLOC(MTYPE_OSPF_Q_SPACE, | |
661 | sizeof(struct ospf_ti_lfa_node_info)); | |
7fd0729f G |
662 | |
663 | new_table = route_table_init(); | |
b538baf3 | 664 | /* XXX do these get freed?? */ |
7fd0729f G |
665 | new_rtrs = route_table_init(); |
666 | ||
667 | /* | |
133e59cf | 668 | * Generate a new (reversed!) SPF tree for this vertex, |
7fd0729f G |
669 | * dry run true, root node false |
670 | */ | |
133e59cf | 671 | area->spf_reversed = true; |
b538baf3 CH |
672 | ospf_spf_calculate(area, dest->lsa_p, new_table, NULL, new_rtrs, true, |
673 | false); | |
7fd0729f | 674 | |
133e59cf G |
675 | /* Reset the flag for reverse SPF */ |
676 | area->spf_reversed = false; | |
677 | ||
7fd0729f G |
678 | q_space->root = area->spf; |
679 | q_space->vertex_list = area->spf_vertex_list; | |
680 | q_space->label_stack = NULL; | |
bdcfd34a G |
681 | |
682 | if (pc_path) | |
683 | q_space->pc_path = ospf_ti_lfa_map_path_to_pc_vertices( | |
684 | pc_path, p_space->pc_vertex_list); | |
685 | else | |
686 | q_space->pc_path = ospf_ti_lfa_generate_post_convergence_path( | |
687 | p_space->pc_vertex_list, q_space->root); | |
688 | ||
689 | /* If there's no backup path available then we are done here. */ | |
690 | if (!q_space->pc_path) { | |
691 | zlog_info( | |
692 | "%s: NO backup path found for root %pI4 and destination %pI4 for %s, aborting ...", | |
693 | __func__, &p_space->root->id, &q_space->root->id, | |
694 | res_buf); | |
695 | return; | |
696 | } | |
7fd0729f | 697 | |
385a1e07 G |
698 | /* 'Cut' the protected resource out of the new SPF tree */ |
699 | ospf_spf_remove_resource(q_space->root, q_space->vertex_list, | |
700 | p_space->protected_resource); | |
7fd0729f G |
701 | |
702 | /* | |
703 | * Generate the smallest possible label stack from the root of the P | |
704 | * space to the root of the Q space. | |
705 | */ | |
bdcfd34a | 706 | ospf_ti_lfa_generate_label_stack(area, p_space, q_space); |
385a1e07 | 707 | |
7fd0729f G |
708 | if (q_space->label_stack) { |
709 | mpls_label2str(q_space->label_stack->num_labels, | |
385a1e07 | 710 | q_space->label_stack->label, label_buf, |
4645cb6b | 711 | MPLS_LABEL_STRLEN, 0, true); |
7fd0729f | 712 | zlog_info( |
385a1e07 G |
713 | "%s: Generated label stack %s for root %pI4 and destination %pI4 for %s", |
714 | __func__, label_buf, &p_space->root->id, | |
715 | &q_space->root->id, res_buf); | |
7fd0729f G |
716 | } else { |
717 | zlog_info( | |
385a1e07 | 718 | "%s: NO label stack generated for root %pI4 and destination %pI4 for %s", |
7fd0729f | 719 | __func__, &p_space->root->id, &q_space->root->id, |
385a1e07 | 720 | res_buf); |
7fd0729f G |
721 | } |
722 | ||
723 | /* We are finished, store the new Q space in the P space struct */ | |
724 | q_spaces_add(p_space->q_spaces, q_space); | |
725 | ||
726 | /* Recursively generate Q spaces for all children */ | |
bdcfd34a G |
727 | if (recursive) { |
728 | for (ALL_LIST_ELEMENTS_RO(dest->children, node, child)) | |
729 | ospf_ti_lfa_generate_q_spaces(area, p_space, child, | |
730 | recursive, pc_path); | |
731 | } | |
7fd0729f G |
732 | } |
733 | ||
734 | static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area, | |
735 | struct p_space *p_space) | |
736 | { | |
737 | struct route_table *new_table, *new_rtrs; | |
738 | ||
739 | new_table = route_table_init(); | |
b538baf3 | 740 | /* XXX do these get freed?? */ |
7fd0729f G |
741 | new_rtrs = route_table_init(); |
742 | ||
385a1e07 | 743 | area->spf_protected_resource = p_space->protected_resource; |
7fd0729f G |
744 | |
745 | /* | |
746 | * The 'post convergence' SPF tree is generated here | |
747 | * dry run true, root node false | |
748 | * | |
749 | * So how does this work? During the SPF calculation the algorithm | |
385a1e07 G |
750 | * checks if a link belongs to a protected resource and then just |
751 | * ignores it. | |
752 | * This is actually _NOT_ a good way to calculate the post | |
7fd0729f | 753 | * convergence SPF tree. The preferred way would be to delete the |
385a1e07 G |
754 | * relevant links (and nodes) from a copy of the LSDB and then just run |
755 | * the SPF algorithm on that as usual. | |
756 | * However, removing links from router LSAs appears to be its own | |
757 | * endeavour (because LSAs are stored as a 'raw' stream), so we go with | |
758 | * this rather hacky way for now. | |
7fd0729f | 759 | */ |
b538baf3 CH |
760 | ospf_spf_calculate(area, area->router_lsa_self, new_table, NULL, |
761 | new_rtrs, true, false); | |
7fd0729f G |
762 | |
763 | p_space->pc_spf = area->spf; | |
764 | p_space->pc_vertex_list = area->spf_vertex_list; | |
765 | ||
385a1e07 | 766 | area->spf_protected_resource = NULL; |
7fd0729f G |
767 | } |
768 | ||
385a1e07 G |
769 | static void |
770 | ospf_ti_lfa_generate_p_space(struct ospf_area *area, struct vertex *child, | |
bdcfd34a G |
771 | struct protected_resource *protected_resource, |
772 | bool recursive, struct list *pc_path) | |
7fd0729f G |
773 | { |
774 | struct vertex *spf_orig; | |
775 | struct list *vertex_list, *vertex_list_orig; | |
776 | struct p_space *p_space; | |
777 | ||
778 | p_space = XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_space)); | |
779 | vertex_list = list_new(); | |
780 | ||
781 | /* The P-space will get its own SPF tree, so copy the old one */ | |
782 | ospf_spf_copy(area->spf, vertex_list); | |
783 | p_space->root = listnode_head(vertex_list); | |
784 | p_space->vertex_list = vertex_list; | |
385a1e07 | 785 | p_space->protected_resource = protected_resource; |
7fd0729f | 786 | |
385a1e07 | 787 | /* Initialize the Q spaces for this P space and protected resource */ |
7fd0729f G |
788 | p_space->q_spaces = |
789 | XCALLOC(MTYPE_OSPF_Q_SPACE, sizeof(struct q_spaces_head)); | |
790 | q_spaces_init(p_space->q_spaces); | |
791 | ||
385a1e07 G |
792 | /* 'Cut' the protected resource out of the new SPF tree */ |
793 | ospf_spf_remove_resource(p_space->root, p_space->vertex_list, | |
794 | p_space->protected_resource); | |
7fd0729f G |
795 | |
796 | /* | |
797 | * Since we are going to calculate more SPF trees for Q spaces, keep the | |
798 | * 'original' one here temporarily | |
799 | */ | |
800 | spf_orig = area->spf; | |
801 | vertex_list_orig = area->spf_vertex_list; | |
802 | ||
803 | /* Generate the post convergence SPF as a blueprint for backup paths */ | |
804 | ospf_ti_lfa_generate_post_convergence_spf(area, p_space); | |
805 | ||
806 | /* Generate the relevant Q spaces for this particular P space */ | |
bdcfd34a | 807 | ospf_ti_lfa_generate_q_spaces(area, p_space, child, recursive, pc_path); |
7fd0729f G |
808 | |
809 | /* Put the 'original' SPF tree back in place */ | |
810 | area->spf = spf_orig; | |
811 | area->spf_vertex_list = vertex_list_orig; | |
812 | ||
813 | /* We are finished, store the new P space */ | |
814 | p_spaces_add(area->p_spaces, p_space); | |
815 | } | |
816 | ||
385a1e07 G |
817 | void ospf_ti_lfa_generate_p_spaces(struct ospf_area *area, |
818 | enum protection_type protection_type) | |
7fd0729f G |
819 | { |
820 | struct listnode *node, *inner_node; | |
821 | struct vertex *root, *child; | |
822 | struct vertex_parent *vertex_parent; | |
823 | uint8_t *p, *lim; | |
824 | struct router_lsa_link *l = NULL; | |
825 | struct prefix stub_prefix, child_prefix; | |
385a1e07 | 826 | struct protected_resource *protected_resource; |
7fd0729f G |
827 | |
828 | area->p_spaces = | |
829 | XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head)); | |
830 | p_spaces_init(area->p_spaces); | |
831 | ||
832 | root = area->spf; | |
833 | ||
834 | /* Root or its router LSA was not created yet? */ | |
835 | if (!root || !root->lsa) | |
836 | return; | |
837 | ||
838 | stub_prefix.family = AF_INET; | |
839 | child_prefix.family = AF_INET; | |
936fbaef | 840 | child_prefix.prefixlen = IPV4_MAX_BITLEN; |
7fd0729f G |
841 | |
842 | p = ((uint8_t *)root->lsa) + OSPF_LSA_HEADER_SIZE + 4; | |
843 | lim = ((uint8_t *)root->lsa) + ntohs(root->lsa->length); | |
844 | ||
845 | zlog_info("%s: Generating P spaces for area %pI4", __func__, | |
846 | &area->area_id); | |
847 | ||
848 | /* | |
849 | * Iterate over all stub networks which target other OSPF neighbors. | |
850 | * Check the nexthop of the child vertex if a stub network is relevant. | |
851 | */ | |
852 | while (p < lim) { | |
853 | l = (struct router_lsa_link *)p; | |
854 | p += (OSPF_ROUTER_LSA_LINK_SIZE | |
855 | + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); | |
856 | ||
385a1e07 G |
857 | /* First comes node protection */ |
858 | if (protection_type == OSPF_TI_LFA_NODE_PROTECTION) { | |
859 | if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { | |
860 | protected_resource = XCALLOC( | |
861 | MTYPE_OSPF_P_SPACE, | |
862 | sizeof(struct protected_resource)); | |
863 | protected_resource->type = protection_type; | |
864 | protected_resource->router_id = l->link_id; | |
865 | child = ospf_spf_vertex_find( | |
866 | protected_resource->router_id, | |
867 | root->children); | |
868 | if (child) | |
869 | ospf_ti_lfa_generate_p_space( | |
bdcfd34a G |
870 | area, child, protected_resource, |
871 | true, NULL); | |
385a1e07 G |
872 | } |
873 | ||
874 | continue; | |
875 | } | |
876 | ||
877 | /* The rest is about link protection */ | |
878 | if (protection_type != OSPF_TI_LFA_LINK_PROTECTION) | |
879 | continue; | |
880 | ||
7fd0729f G |
881 | if (l->m[0].type != LSA_LINK_TYPE_STUB) |
882 | continue; | |
883 | ||
884 | stub_prefix.prefixlen = ip_masklen(l->link_data); | |
885 | stub_prefix.u.prefix4 = l->link_id; | |
886 | ||
887 | for (ALL_LIST_ELEMENTS_RO(root->children, node, child)) { | |
888 | ||
889 | if (child->type != OSPF_VERTEX_ROUTER) | |
890 | continue; | |
891 | ||
892 | for (ALL_LIST_ELEMENTS_RO(child->parents, inner_node, | |
893 | vertex_parent)) { | |
894 | ||
895 | child_prefix.u.prefix4 = | |
896 | vertex_parent->nexthop->router; | |
897 | ||
898 | /* | |
899 | * If there's a link for that stub network then | |
900 | * we will protect it. Hence generate a P space | |
901 | * for that particular link including the | |
902 | * Q spaces so we can later on generate a | |
903 | * backup path for the link. | |
904 | */ | |
905 | if (prefix_match(&stub_prefix, &child_prefix)) { | |
906 | zlog_info( | |
907 | "%s: Generating P space for %pI4", | |
908 | __func__, &l->link_id); | |
385a1e07 G |
909 | |
910 | protected_resource = XCALLOC( | |
911 | MTYPE_OSPF_P_SPACE, | |
912 | sizeof(struct | |
913 | protected_resource)); | |
914 | protected_resource->type = | |
915 | protection_type; | |
916 | protected_resource->link = l; | |
917 | ||
918 | ospf_ti_lfa_generate_p_space( | |
bdcfd34a G |
919 | area, child, protected_resource, |
920 | true, NULL); | |
7fd0729f G |
921 | } |
922 | } | |
923 | } | |
924 | } | |
925 | } | |
926 | ||
385a1e07 G |
927 | static struct p_space *ospf_ti_lfa_get_p_space_by_path(struct ospf_area *area, |
928 | struct ospf_path *path) | |
7fd0729f G |
929 | { |
930 | struct p_space *p_space; | |
931 | struct router_lsa_link *link; | |
385a1e07 G |
932 | struct vertex *child; |
933 | int type; | |
7fd0729f G |
934 | |
935 | frr_each(p_spaces, area->p_spaces, p_space) { | |
385a1e07 G |
936 | type = p_space->protected_resource->type; |
937 | ||
938 | if (type == OSPF_TI_LFA_LINK_PROTECTION) { | |
939 | link = p_space->protected_resource->link; | |
940 | if ((path->nexthop.s_addr & link->link_data.s_addr) | |
941 | == (link->link_id.s_addr & link->link_data.s_addr)) | |
942 | return p_space; | |
943 | } | |
944 | ||
945 | if (type == OSPF_TI_LFA_NODE_PROTECTION) { | |
946 | child = ospf_spf_vertex_by_nexthop(area->spf, | |
947 | &path->nexthop); | |
948 | if (child | |
949 | && p_space->protected_resource->router_id.s_addr | |
950 | == child->id.s_addr) | |
951 | return p_space; | |
952 | } | |
7fd0729f G |
953 | } |
954 | ||
955 | return NULL; | |
956 | } | |
957 | ||
958 | void ospf_ti_lfa_insert_backup_paths(struct ospf_area *area, | |
959 | struct route_table *new_table) | |
960 | { | |
961 | struct route_node *rn; | |
962 | struct ospf_route *or; | |
963 | struct ospf_path *path; | |
964 | struct listnode *node; | |
965 | struct p_space *p_space; | |
966 | struct q_space *q_space, q_space_search; | |
967 | struct vertex root_search; | |
385a1e07 | 968 | char label_buf[MPLS_LABEL_STRLEN]; |
7fd0729f G |
969 | |
970 | for (rn = route_top(new_table); rn; rn = route_next(rn)) { | |
971 | or = rn->info; | |
972 | if (or == NULL) | |
973 | continue; | |
974 | ||
975 | /* Insert a backup path for all OSPF paths */ | |
976 | for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) { | |
385a1e07 G |
977 | |
978 | if (path->adv_router.s_addr == INADDR_ANY | |
979 | || path->nexthop.s_addr == INADDR_ANY) | |
980 | continue; | |
981 | ||
a4553b5b G |
982 | if (IS_DEBUG_OSPF_TI_LFA) |
983 | zlog_debug( | |
984 | "%s: attempting to insert backup path for prefix %pFX, router id %pI4 and nexthop %pI4.", | |
985 | __func__, &rn->p, &path->adv_router, | |
986 | &path->nexthop); | |
385a1e07 G |
987 | |
988 | p_space = ospf_ti_lfa_get_p_space_by_path(area, path); | |
7fd0729f | 989 | if (!p_space) { |
a4553b5b G |
990 | if (IS_DEBUG_OSPF_TI_LFA) |
991 | zlog_debug( | |
992 | "%s: P space not found for router id %pI4 and nexthop %pI4.", | |
993 | __func__, &path->adv_router, | |
994 | &path->nexthop); | |
7fd0729f G |
995 | continue; |
996 | } | |
997 | ||
998 | root_search.id = path->adv_router; | |
999 | q_space_search.root = &root_search; | |
1000 | q_space = q_spaces_find(p_space->q_spaces, | |
1001 | &q_space_search); | |
1002 | if (!q_space) { | |
a4553b5b G |
1003 | if (IS_DEBUG_OSPF_TI_LFA) |
1004 | zlog_debug( | |
1005 | "%s: Q space not found for advertising router %pI4.", | |
1006 | __func__, &path->adv_router); | |
7fd0729f G |
1007 | continue; |
1008 | } | |
1009 | ||
669247b8 G |
1010 | /* If there's a backup label stack, insert it*/ |
1011 | if (q_space->label_stack) { | |
1012 | /* Init the backup path data in path */ | |
1013 | path->srni.backup_label_stack = XCALLOC( | |
1014 | MTYPE_OSPF_PATH, | |
1015 | sizeof(struct mpls_label_stack) | |
1016 | + sizeof(mpls_label_t) | |
1017 | * q_space->label_stack | |
1018 | ->num_labels); | |
1019 | ||
1020 | /* Copy over the label stack */ | |
1021 | path->srni.backup_label_stack->num_labels = | |
1022 | q_space->label_stack->num_labels; | |
1023 | memcpy(path->srni.backup_label_stack->label, | |
1024 | q_space->label_stack->label, | |
1025 | sizeof(mpls_label_t) | |
1026 | * q_space->label_stack | |
1027 | ->num_labels); | |
1028 | ||
1029 | /* Set the backup nexthop too */ | |
1030 | path->srni.backup_nexthop = q_space->nexthop; | |
1031 | } | |
385a1e07 G |
1032 | |
1033 | if (path->srni.backup_label_stack) { | |
7815c834 G |
1034 | mpls_label2str( |
1035 | path->srni.backup_label_stack | |
1036 | ->num_labels, | |
1037 | path->srni.backup_label_stack->label, | |
4645cb6b | 1038 | label_buf, MPLS_LABEL_STRLEN, 0, true); |
a4553b5b G |
1039 | if (IS_DEBUG_OSPF_TI_LFA) |
1040 | zlog_debug( | |
1041 | "%s: inserted backup path %s for prefix %pFX, router id %pI4 and nexthop %pI4.", | |
1042 | __func__, label_buf, &rn->p, | |
1043 | &path->adv_router, | |
1044 | &path->nexthop); | |
385a1e07 | 1045 | } else { |
a4553b5b G |
1046 | if (IS_DEBUG_OSPF_TI_LFA) |
1047 | zlog_debug( | |
1048 | "%s: inserted NO backup path for prefix %pFX, router id %pI4 and nexthop %pI4.", | |
1049 | __func__, &rn->p, | |
1050 | &path->adv_router, | |
1051 | &path->nexthop); | |
385a1e07 | 1052 | } |
7fd0729f G |
1053 | } |
1054 | } | |
1055 | } | |
1056 | ||
1057 | void ospf_ti_lfa_free_p_spaces(struct ospf_area *area) | |
1058 | { | |
1059 | struct p_space *p_space; | |
1060 | struct q_space *q_space; | |
1061 | ||
1062 | while ((p_space = p_spaces_pop(area->p_spaces))) { | |
1063 | while ((q_space = q_spaces_pop(p_space->q_spaces))) { | |
1064 | ospf_spf_cleanup(q_space->root, q_space->vertex_list); | |
1065 | ||
bdcfd34a G |
1066 | if (q_space->pc_path) |
1067 | list_delete(&q_space->pc_path); | |
9d3444f8 | 1068 | |
bdcfd34a G |
1069 | XFREE(MTYPE_OSPF_Q_SPACE, q_space->p_node_info); |
1070 | XFREE(MTYPE_OSPF_Q_SPACE, q_space->q_node_info); | |
669247b8 | 1071 | XFREE(MTYPE_OSPF_Q_SPACE, q_space->label_stack); |
7fd0729f G |
1072 | XFREE(MTYPE_OSPF_Q_SPACE, q_space); |
1073 | } | |
669247b8 | 1074 | |
7fd0729f G |
1075 | ospf_spf_cleanup(p_space->root, p_space->vertex_list); |
1076 | ospf_spf_cleanup(p_space->pc_spf, p_space->pc_vertex_list); | |
385a1e07 | 1077 | XFREE(MTYPE_OSPF_P_SPACE, p_space->protected_resource); |
7fd0729f G |
1078 | |
1079 | q_spaces_fini(p_space->q_spaces); | |
1080 | XFREE(MTYPE_OSPF_Q_SPACE, p_space->q_spaces); | |
1081 | } | |
1082 | ||
1083 | p_spaces_fini(area->p_spaces); | |
1084 | XFREE(MTYPE_OSPF_P_SPACE, area->p_spaces); | |
1085 | } | |
1086 | ||
385a1e07 G |
1087 | void ospf_ti_lfa_compute(struct ospf_area *area, struct route_table *new_table, |
1088 | enum protection_type protection_type) | |
7fd0729f G |
1089 | { |
1090 | /* | |
385a1e07 G |
1091 | * Generate P spaces per protected link/node and their respective Q |
1092 | * spaces, generate backup paths (MPLS label stacks) by finding P/Q | |
1093 | * nodes. | |
7fd0729f | 1094 | */ |
385a1e07 | 1095 | ospf_ti_lfa_generate_p_spaces(area, protection_type); |
7fd0729f G |
1096 | |
1097 | /* Insert the generated backup paths into the routing table. */ | |
1098 | ospf_ti_lfa_insert_backup_paths(area, new_table); | |
1099 | ||
1100 | /* Cleanup P spaces and related datastructures including Q spaces. */ | |
1101 | ospf_ti_lfa_free_p_spaces(area); | |
1102 | } |