]> git.proxmox.com Git - mirror_frr.git/blame - ospfd/ospf_ti_lfa.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / ospfd / ospf_ti_lfa.c
CommitLineData
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
24DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item,
960b9a53 25 p_spaces_compare_func);
7fd0729f 26DECLARE_RBTREE_UNIQ(q_spaces, struct q_space, q_spaces_item,
960b9a53 27 q_spaces_compare_func);
7fd0729f 28
bdcfd34a
G
29static void
30ospf_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
34void 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
58static enum ospf_ti_lfa_p_q_space_adjacency
59ospf_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
99static 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
151static 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
167static struct mpls_label_stack *
168ospf_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
190static struct list *
191ospf_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
208static 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
235static 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
374static 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
585static struct list *
586ospf_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
618static 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
734static 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
769static void
770ospf_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
817void 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
927static 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
958void 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
1057void 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
1087void 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}