1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2020 NetDEF, Inc.
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"
21 #include "ospfd/ospf_dump.h"
24 DECLARE_RBTREE_UNIQ(p_spaces
, struct p_space
, p_spaces_item
,
25 p_spaces_compare_func
);
26 DECLARE_RBTREE_UNIQ(q_spaces
, struct q_space
, q_spaces_item
,
27 q_spaces_compare_func
);
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
);
34 void ospf_print_protected_resource(
35 struct protected_resource
*protected_resource
, char *buf
)
37 struct router_lsa_link
*link
;
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
,
46 case OSPF_TI_LFA_NODE_PROTECTION
:
47 snprintfrr(buf
, PROTECTED_RESOURCE_STRLEN
,
48 "protected node: %pI4",
49 &protected_resource
->router_id
);
51 case OSPF_TI_LFA_UNDEFINED_PROTECTION
:
52 snprintfrr(buf
, PROTECTED_RESOURCE_STRLEN
,
53 "undefined protected resource");
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
)
62 struct listnode
*curr_node
;
63 struct vertex
*p_node
= NULL
, *pc_node_parent
, *p_node_pc_parent
;
64 struct vertex_parent
*pc_vertex_parent
;
66 curr_node
= listnode_lookup(q_space
->pc_path
, pc_node
);
67 pc_node_parent
= listgetdata(curr_node
->next
);
69 q_space
->p_node_info
->type
= OSPF_TI_LFA_UNDEFINED_NODE
;
71 p_node
= ospf_spf_vertex_find(pc_node_parent
->id
, p_space
->vertex_list
);
74 q_space
->p_node_info
->node
= p_node
;
75 q_space
->p_node_info
->type
= OSPF_TI_LFA_P_NODE
;
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
);
81 q_space
->p_node_info
->nexthop
=
82 pc_vertex_parent
->nexthop
->router
;
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.
89 q_space
->p_node_info
->nexthop
.s_addr
= INADDR_ANY
;
92 return OSPF_TI_LFA_P_Q_SPACE_ADJACENT
;
95 ospf_ti_lfa_find_p_node(pc_node_parent
, p_space
, q_space
);
96 return OSPF_TI_LFA_P_Q_SPACE_NON_ADJACENT
;
99 static void ospf_ti_lfa_find_q_node(struct vertex
*pc_node
,
100 struct p_space
*p_space
,
101 struct q_space
*q_space
)
103 struct listnode
*curr_node
, *next_node
;
104 struct vertex
*p_node
, *q_node
, *q_space_parent
= NULL
, *pc_node_parent
;
105 struct vertex_parent
*pc_vertex_parent
;
107 curr_node
= listnode_lookup(q_space
->pc_path
, pc_node
);
108 next_node
= curr_node
->next
;
109 pc_node_parent
= listgetdata(next_node
);
111 ospf_spf_vertex_parent_find(pc_node_parent
->id
, pc_node
);
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
);
116 /* The Q node is always present. */
119 q_space
->q_node_info
->type
= OSPF_TI_LFA_UNDEFINED_NODE
;
121 if (p_node
&& q_node
) {
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
;
130 * Note that the Q space has the 'reverse' direction of the PC
131 * SPF. Hence compare PC SPF parent to Q space children.
134 ospf_spf_vertex_find(pc_node_parent
->id
, q_node
->children
);
137 * If the Q space parent doesn't exist we 'hit' the border to the P
138 * space and hence got our Q node.
140 if (!q_space_parent
) {
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
;
148 return ospf_ti_lfa_find_q_node(pc_node_parent
, p_space
, q_space
);
151 static void ospf_ti_lfa_append_label_stack(struct mpls_label_stack
*label_stack
,
152 mpls_label_t labels
[],
155 int i
, offset
, limit
;
157 limit
= label_stack
->num_labels
+ num_labels
;
158 offset
= label_stack
->num_labels
;
160 for (i
= label_stack
->num_labels
; i
< limit
; i
++) {
161 label_stack
->label
[i
] = labels
[i
- offset
];
162 label_stack
->num_labels
++;
167 static struct mpls_label_stack
*
168 ospf_ti_lfa_create_label_stack(mpls_label_t labels
[], uint32_t num_labels
)
170 struct mpls_label_stack
*label_stack
;
174 for (i
= 0; i
< num_labels
; i
++) {
175 if (labels
[i
] == MPLS_INVALID_LABEL
)
179 label_stack
= XCALLOC(MTYPE_OSPF_Q_SPACE
,
180 sizeof(struct mpls_label_stack
)
181 + MPLS_MAX_LABELS
* sizeof(mpls_label_t
));
182 label_stack
->num_labels
= num_labels
;
184 for (i
= 0; i
< num_labels
; i
++)
185 label_stack
->label
[i
] = labels
[i
];
191 ospf_ti_lfa_map_path_to_pc_vertices(struct list
*path
,
192 struct list
*pc_vertex_list
)
194 struct listnode
*node
;
195 struct vertex
*vertex
, *pc_vertex
;
196 struct list
*pc_path
;
198 pc_path
= list_new();
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
);
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
)
213 struct list
*inner_pc_path
;
214 struct vertex
*current_vertex
;
215 struct listnode
*current_listnode
;
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
);
221 /* Note that the post-convergence paths are reversed. */
223 current_vertex
= listgetdata(current_listnode
);
224 listnode_add(inner_pc_path
, current_vertex
);
226 if (current_vertex
->id
.s_addr
== p_node
->id
.s_addr
)
229 current_listnode
= current_listnode
->next
;
232 return inner_pc_path
;
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
)
240 struct route_table
*new_table
;
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
;
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
;
259 start_vertex
= p_node_info
->node
;
260 end_vertex
= q_node_info
->node
;
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.
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
,
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
);
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
,
293 inner_backup_path_info
->p_node_info
.nexthop
=
294 vertex_parent
->nexthop
->router
;
298 inner_pc_path
= ospf_ti_lfa_cut_out_pc_path(p_space
->pc_vertex_list
,
300 start_vertex
, end_vertex
);
302 new_table
= route_table_init();
304 /* Copy the current state ... */
305 spf_orig
= area
->spf
;
306 vertex_list_orig
= area
->spf_vertex_list
;
307 p_spaces_orig
= area
->p_spaces
;
310 XCALLOC(MTYPE_OSPF_P_SPACE
, sizeof(struct p_spaces_head
));
312 /* dry run true, root node false */
313 ospf_spf_calculate(area
, start_vertex
->lsa_p
, new_table
, NULL
, NULL
,
316 q_node
= ospf_spf_vertex_find(end_vertex
->id
, area
->spf_vertex_list
);
318 ospf_ti_lfa_generate_p_space(area
, q_node
, protected_resource
, false,
321 /* There's just one P and Q space */
322 inner_p_space
= p_spaces_pop(area
->p_spaces
);
323 inner_q_space
= q_spaces_pop(inner_p_space
->q_spaces
);
325 /* Copy over inner backup path information from the inner q_space */
327 /* In case the outer P node is also the root of the P space */
328 if (start_label
!= MPLS_INVALID_LABEL
) {
329 inner_backup_path_info
->label_stack
=
330 ospf_ti_lfa_create_label_stack(&start_label
, 1);
331 ospf_ti_lfa_append_label_stack(
332 inner_backup_path_info
->label_stack
,
333 inner_q_space
->label_stack
->label
,
334 inner_q_space
->label_stack
->num_labels
);
335 inner_backup_path_info
->p_node_info
.node
= start_vertex
;
336 inner_backup_path_info
->p_node_info
.type
= OSPF_TI_LFA_P_NODE
;
337 vertex_parent
= ospf_spf_vertex_parent_find(p_space
->root
->id
,
339 inner_backup_path_info
->p_node_info
.nexthop
=
340 vertex_parent
->nexthop
->router
;
342 memcpy(inner_backup_path_info
->label_stack
,
343 inner_q_space
->label_stack
,
344 sizeof(struct mpls_label_stack
)
345 + sizeof(mpls_label_t
)
346 * inner_q_space
->label_stack
348 memcpy(&inner_backup_path_info
->p_node_info
,
349 inner_q_space
->p_node_info
,
350 sizeof(struct ospf_ti_lfa_node_info
));
353 /* In case the outer Q node is also the root of the Q space */
354 if (end_label
!= MPLS_INVALID_LABEL
) {
355 inner_backup_path_info
->q_node_info
.node
= end_vertex
;
356 inner_backup_path_info
->q_node_info
.type
= OSPF_TI_LFA_Q_NODE
;
358 memcpy(&inner_backup_path_info
->q_node_info
,
359 inner_q_space
->q_node_info
,
360 sizeof(struct ospf_ti_lfa_node_info
));
364 ospf_ti_lfa_free_p_spaces(area
);
365 ospf_spf_cleanup(area
->spf
, area
->spf_vertex_list
);
367 /* ... and copy the current state back. */
368 area
->spf
= spf_orig
;
369 area
->spf_vertex_list
= vertex_list_orig
;
370 area
->p_spaces
= p_spaces_orig
;
373 static void ospf_ti_lfa_generate_label_stack(struct ospf_area
*area
,
374 struct p_space
*p_space
,
375 struct q_space
*q_space
)
377 enum ospf_ti_lfa_p_q_space_adjacency adjacency_result
;
378 mpls_label_t labels
[MPLS_MAX_LABELS
];
379 struct vertex
*pc_node
;
380 struct ospf_ti_lfa_inner_backup_path_info inner_backup_path_info
;
382 if (IS_DEBUG_OSPF_TI_LFA
)
384 "%s: Generating Label stack for src %pI4 and dest %pI4.",
385 __func__
, &p_space
->root
->id
, &q_space
->root
->id
);
387 pc_node
= listnode_head(q_space
->pc_path
);
390 if (IS_DEBUG_OSPF_TI_LFA
)
392 "%s: There seems to be no post convergence path (yet).",
397 ospf_ti_lfa_find_q_node(pc_node
, p_space
, q_space
);
398 if (q_space
->q_node_info
->type
== OSPF_TI_LFA_UNDEFINED_NODE
) {
399 if (IS_DEBUG_OSPF_TI_LFA
)
400 zlog_debug("%s: Q node not found!", __func__
);
404 /* Found a PQ node? Then we are done here. */
405 if (q_space
->q_node_info
->type
== OSPF_TI_LFA_PQ_NODE
) {
407 * If the PQ node is a child of the root, then we can use an
408 * adjacency SID instead of a prefix SID for the backup path.
410 if (ospf_spf_vertex_parent_find(p_space
->root
->id
,
411 q_space
->q_node_info
->node
))
412 labels
[0] = ospf_sr_get_adj_sid_by_id(
414 &q_space
->q_node_info
->node
->id
);
416 labels
[0] = ospf_sr_get_prefix_sid_by_id(
417 &q_space
->q_node_info
->node
->id
);
419 q_space
->label_stack
=
420 ospf_ti_lfa_create_label_stack(labels
, 1);
421 q_space
->nexthop
= q_space
->q_node_info
->nexthop
;
426 /* Otherwise find a (hopefully adjacent) P node. */
427 pc_node
= ospf_spf_vertex_find(q_space
->q_node_info
->node
->id
,
428 p_space
->pc_vertex_list
);
429 adjacency_result
= ospf_ti_lfa_find_p_node(pc_node
, p_space
, q_space
);
431 if (q_space
->p_node_info
->type
== OSPF_TI_LFA_UNDEFINED_NODE
) {
432 if (IS_DEBUG_OSPF_TI_LFA
)
433 zlog_debug("%s: P node not found!", __func__
);
438 * This should be the regular case: P and Q space are adjacent or even
439 * overlapping. This is guaranteed for link protection when used with
442 if (adjacency_result
== OSPF_TI_LFA_P_Q_SPACE_ADJACENT
) {
444 * It can happen that the P node is the root itself, therefore
445 * we don't need a label for it. So just one adjacency SID for
448 if (q_space
->p_node_info
->node
->id
.s_addr
449 == p_space
->root
->id
.s_addr
) {
450 labels
[0] = ospf_sr_get_adj_sid_by_id(
452 &q_space
->q_node_info
->node
->id
);
453 q_space
->label_stack
=
454 ospf_ti_lfa_create_label_stack(labels
, 1);
455 q_space
->nexthop
= q_space
->q_node_info
->nexthop
;
460 * Otherwise we have a P and also a Q node (which are adjacent).
462 * It can happen that the P node is a child of the root,
463 * therefore we might just need the adjacency SID for the P node
464 * instead of the prefix SID. For the Q node always take the
467 if (ospf_spf_vertex_parent_find(p_space
->root
->id
,
468 q_space
->p_node_info
->node
))
469 labels
[0] = ospf_sr_get_adj_sid_by_id(
471 &q_space
->p_node_info
->node
->id
);
473 labels
[0] = ospf_sr_get_prefix_sid_by_id(
474 &q_space
->p_node_info
->node
->id
);
476 labels
[1] = ospf_sr_get_adj_sid_by_id(
477 &q_space
->p_node_info
->node
->id
,
478 &q_space
->q_node_info
->node
->id
);
480 q_space
->label_stack
=
481 ospf_ti_lfa_create_label_stack(labels
, 2);
482 q_space
->nexthop
= q_space
->p_node_info
->nexthop
;
486 * It can happen that the P and Q space are not adjacent when
487 * e.g. node protection or asymmetric weights are used. In this
488 * case the found P and Q nodes are used as a reference for
489 * another run of the algorithm!
491 * After having found the inner label stack it is stitched
492 * together with the outer labels.
494 inner_backup_path_info
.label_stack
= XCALLOC(
496 sizeof(struct mpls_label_stack
)
497 + sizeof(mpls_label_t
) * MPLS_MAX_LABELS
);
498 ospf_ti_lfa_generate_inner_label_stack(area
, p_space
, q_space
,
499 &inner_backup_path_info
);
502 * First stitch together the outer P node label with the inner
505 if (q_space
->p_node_info
->node
->id
.s_addr
506 == p_space
->root
->id
.s_addr
) {
508 * It can happen that the P node is the root itself,
509 * therefore we don't need a label for it. Just take
510 * the inner label stack first.
512 q_space
->label_stack
= ospf_ti_lfa_create_label_stack(
513 inner_backup_path_info
.label_stack
->label
,
514 inner_backup_path_info
.label_stack
->num_labels
);
516 /* Use the inner P or Q node for the nexthop */
517 if (inner_backup_path_info
.p_node_info
.type
518 != OSPF_TI_LFA_UNDEFINED_NODE
)
519 q_space
->nexthop
= inner_backup_path_info
520 .p_node_info
.nexthop
;
522 q_space
->nexthop
= inner_backup_path_info
523 .q_node_info
.nexthop
;
525 } else if (ospf_spf_vertex_parent_find(
527 q_space
->p_node_info
->node
)) {
529 * It can happen that the outer P node is a child of
530 * the root, therefore we might just need the
531 * adjacency SID for the outer P node instead of the
532 * prefix SID. Then just append the inner label stack.
534 labels
[0] = ospf_sr_get_adj_sid_by_id(
536 &q_space
->p_node_info
->node
->id
);
537 q_space
->label_stack
=
538 ospf_ti_lfa_create_label_stack(labels
, 1);
539 ospf_ti_lfa_append_label_stack(
540 q_space
->label_stack
,
541 inner_backup_path_info
.label_stack
->label
,
542 inner_backup_path_info
.label_stack
->num_labels
);
543 q_space
->nexthop
= q_space
->p_node_info
->nexthop
;
545 /* The outer P node needs a Prefix-SID here */
546 labels
[0] = ospf_sr_get_prefix_sid_by_id(
547 &q_space
->p_node_info
->node
->id
);
548 q_space
->label_stack
=
549 ospf_ti_lfa_create_label_stack(labels
, 1);
550 ospf_ti_lfa_append_label_stack(
551 q_space
->label_stack
,
552 inner_backup_path_info
.label_stack
->label
,
553 inner_backup_path_info
.label_stack
->num_labels
);
554 q_space
->nexthop
= q_space
->p_node_info
->nexthop
;
557 /* Now the outer Q node needs to be considered */
558 if (ospf_spf_vertex_parent_find(
559 inner_backup_path_info
.q_node_info
.node
->id
,
560 q_space
->q_node_info
->node
)) {
562 * The outer Q node can be a child of the inner Q node,
563 * hence just add an Adjacency-SID.
565 labels
[0] = ospf_sr_get_adj_sid_by_id(
566 &inner_backup_path_info
.q_node_info
.node
->id
,
567 &q_space
->q_node_info
->node
->id
);
568 ospf_ti_lfa_append_label_stack(q_space
->label_stack
,
571 /* Otherwise a Prefix-SID is needed */
572 labels
[0] = ospf_sr_get_prefix_sid_by_id(
573 &q_space
->q_node_info
->node
->id
);
574 ospf_ti_lfa_append_label_stack(q_space
->label_stack
,
578 * Note that there's also the case where the inner and outer Q
579 * node are the same, but then there's nothing to do!
585 ospf_ti_lfa_generate_post_convergence_path(struct list
*pc_vertex_list
,
588 struct list
*pc_path
;
589 struct vertex
*current_vertex
;
590 struct vertex_parent
*parent
;
592 current_vertex
= ospf_spf_vertex_find(dest
->id
, pc_vertex_list
);
593 if (!current_vertex
) {
594 if (IS_DEBUG_OSPF_TI_LFA
)
596 "%s: There seems to be no post convergence path (yet).",
601 pc_path
= list_new();
602 listnode_add(pc_path
, current_vertex
);
604 /* Generate a backup path in reverse order */
606 parent
= listnode_head(current_vertex
->parents
);
610 listnode_add(pc_path
, parent
->parent
);
611 current_vertex
= parent
->parent
;
617 static void ospf_ti_lfa_generate_q_spaces(struct ospf_area
*area
,
618 struct p_space
*p_space
,
619 struct vertex
*dest
, bool recursive
,
620 struct list
*pc_path
)
622 struct listnode
*node
;
623 struct vertex
*child
;
624 struct route_table
*new_table
;
625 struct q_space
*q_space
, q_space_search
;
626 char label_buf
[MPLS_LABEL_STRLEN
];
627 char res_buf
[PROTECTED_RESOURCE_STRLEN
];
630 ospf_print_protected_resource(p_space
->protected_resource
, res_buf
);
632 p_space
->protected_resource
->type
== OSPF_TI_LFA_NODE_PROTECTION
634 == p_space
->protected_resource
->router_id
.s_addr
;
637 * If node protection is used, don't build a Q space for the protected
638 * node of that particular P space. Move on with children instead.
640 if (node_protected
) {
642 /* Recursively generate Q spaces for all children */
643 for (ALL_LIST_ELEMENTS_RO(dest
->children
, node
, child
))
644 ospf_ti_lfa_generate_q_spaces(area
, p_space
,
651 /* Check if we already have a Q space for this destination */
652 q_space_search
.root
= dest
;
653 if (q_spaces_find(p_space
->q_spaces
, &q_space_search
))
656 q_space
= XCALLOC(MTYPE_OSPF_Q_SPACE
, sizeof(struct q_space
));
657 q_space
->p_node_info
= XCALLOC(MTYPE_OSPF_Q_SPACE
,
658 sizeof(struct ospf_ti_lfa_node_info
));
659 q_space
->q_node_info
= XCALLOC(MTYPE_OSPF_Q_SPACE
,
660 sizeof(struct ospf_ti_lfa_node_info
));
662 new_table
= route_table_init();
665 * Generate a new (reversed!) SPF tree for this vertex,
666 * dry run true, root node false
668 area
->spf_reversed
= true;
669 ospf_spf_calculate(area
, dest
->lsa_p
, new_table
, NULL
, NULL
, true,
672 /* Reset the flag for reverse SPF */
673 area
->spf_reversed
= false;
675 q_space
->root
= area
->spf
;
676 q_space
->vertex_list
= area
->spf_vertex_list
;
677 q_space
->label_stack
= NULL
;
680 q_space
->pc_path
= ospf_ti_lfa_map_path_to_pc_vertices(
681 pc_path
, p_space
->pc_vertex_list
);
683 q_space
->pc_path
= ospf_ti_lfa_generate_post_convergence_path(
684 p_space
->pc_vertex_list
, q_space
->root
);
686 /* If there's no backup path available then we are done here. */
687 if (!q_space
->pc_path
) {
689 "%s: NO backup path found for root %pI4 and destination %pI4 for %s, aborting ...",
690 __func__
, &p_space
->root
->id
, &q_space
->root
->id
,
693 XFREE(MTYPE_OSPF_Q_SPACE
, q_space
->p_node_info
);
694 XFREE(MTYPE_OSPF_Q_SPACE
, q_space
->q_node_info
);
695 XFREE(MTYPE_OSPF_Q_SPACE
, q_space
);
700 /* 'Cut' the protected resource out of the new SPF tree */
701 ospf_spf_remove_resource(q_space
->root
, q_space
->vertex_list
,
702 p_space
->protected_resource
);
705 * Generate the smallest possible label stack from the root of the P
706 * space to the root of the Q space.
708 ospf_ti_lfa_generate_label_stack(area
, p_space
, q_space
);
710 if (q_space
->label_stack
) {
711 mpls_label2str(q_space
->label_stack
->num_labels
,
712 q_space
->label_stack
->label
, label_buf
,
713 MPLS_LABEL_STRLEN
, 0, true);
715 "%s: Generated label stack %s for root %pI4 and destination %pI4 for %s",
716 __func__
, label_buf
, &p_space
->root
->id
,
717 &q_space
->root
->id
, res_buf
);
720 "%s: NO label stack generated for root %pI4 and destination %pI4 for %s",
721 __func__
, &p_space
->root
->id
, &q_space
->root
->id
,
725 /* We are finished, store the new Q space in the P space struct */
726 q_spaces_add(p_space
->q_spaces
, q_space
);
728 /* Recursively generate Q spaces for all children */
730 for (ALL_LIST_ELEMENTS_RO(dest
->children
, node
, child
))
731 ospf_ti_lfa_generate_q_spaces(area
, p_space
, child
,
736 static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area
*area
,
737 struct p_space
*p_space
)
739 struct route_table
*new_table
;
741 new_table
= route_table_init();
743 area
->spf_protected_resource
= p_space
->protected_resource
;
746 * The 'post convergence' SPF tree is generated here
747 * dry run true, root node false
749 * So how does this work? During the SPF calculation the algorithm
750 * checks if a link belongs to a protected resource and then just
752 * This is actually _NOT_ a good way to calculate the post
753 * convergence SPF tree. The preferred way would be to delete the
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.
760 ospf_spf_calculate(area
, area
->router_lsa_self
, new_table
, NULL
, NULL
,
763 p_space
->pc_spf
= area
->spf
;
764 p_space
->pc_vertex_list
= area
->spf_vertex_list
;
766 area
->spf_protected_resource
= NULL
;
770 ospf_ti_lfa_generate_p_space(struct ospf_area
*area
, struct vertex
*child
,
771 struct protected_resource
*protected_resource
,
772 bool recursive
, struct list
*pc_path
)
774 struct vertex
*spf_orig
;
775 struct list
*vertex_list
, *vertex_list_orig
;
776 struct p_space
*p_space
;
778 p_space
= XCALLOC(MTYPE_OSPF_P_SPACE
, sizeof(struct p_space
));
779 vertex_list
= list_new();
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
;
785 p_space
->protected_resource
= protected_resource
;
787 /* Initialize the Q spaces for this P space and protected resource */
789 XCALLOC(MTYPE_OSPF_Q_SPACE
, sizeof(struct q_spaces_head
));
790 q_spaces_init(p_space
->q_spaces
);
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
);
797 * Since we are going to calculate more SPF trees for Q spaces, keep the
798 * 'original' one here temporarily
800 spf_orig
= area
->spf
;
801 vertex_list_orig
= area
->spf_vertex_list
;
803 /* Generate the post convergence SPF as a blueprint for backup paths */
804 ospf_ti_lfa_generate_post_convergence_spf(area
, p_space
);
806 /* Generate the relevant Q spaces for this particular P space */
807 ospf_ti_lfa_generate_q_spaces(area
, p_space
, child
, recursive
, pc_path
);
809 /* Put the 'original' SPF tree back in place */
810 area
->spf
= spf_orig
;
811 area
->spf_vertex_list
= vertex_list_orig
;
813 /* We are finished, store the new P space */
814 p_spaces_add(area
->p_spaces
, p_space
);
817 void ospf_ti_lfa_generate_p_spaces(struct ospf_area
*area
,
818 enum protection_type protection_type
)
820 struct listnode
*node
, *inner_node
;
821 struct vertex
*root
, *child
;
822 struct vertex_parent
*vertex_parent
;
824 struct router_lsa_link
*l
= NULL
;
825 struct prefix stub_prefix
, child_prefix
;
826 struct protected_resource
*protected_resource
;
829 XCALLOC(MTYPE_OSPF_P_SPACE
, sizeof(struct p_spaces_head
));
830 p_spaces_init(area
->p_spaces
);
834 /* Root or its router LSA was not created yet? */
835 if (!root
|| !root
->lsa
)
838 stub_prefix
.family
= AF_INET
;
839 child_prefix
.family
= AF_INET
;
840 child_prefix
.prefixlen
= IPV4_MAX_BITLEN
;
842 p
= ((uint8_t *)root
->lsa
) + OSPF_LSA_HEADER_SIZE
+ 4;
843 lim
= ((uint8_t *)root
->lsa
) + ntohs(root
->lsa
->length
);
845 zlog_info("%s: Generating P spaces for area %pI4", __func__
,
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.
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
));
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(
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
,
869 ospf_ti_lfa_generate_p_space(
870 area
, child
, protected_resource
,
877 /* The rest is about link protection */
878 if (protection_type
!= OSPF_TI_LFA_LINK_PROTECTION
)
881 if (l
->m
[0].type
!= LSA_LINK_TYPE_STUB
)
884 stub_prefix
.prefixlen
= ip_masklen(l
->link_data
);
885 stub_prefix
.u
.prefix4
= l
->link_id
;
887 for (ALL_LIST_ELEMENTS_RO(root
->children
, node
, child
)) {
889 if (child
->type
!= OSPF_VERTEX_ROUTER
)
892 for (ALL_LIST_ELEMENTS_RO(child
->parents
, inner_node
,
895 child_prefix
.u
.prefix4
=
896 vertex_parent
->nexthop
->router
;
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.
905 if (prefix_match(&stub_prefix
, &child_prefix
)) {
907 "%s: Generating P space for %pI4",
908 __func__
, &l
->link_id
);
910 protected_resource
= XCALLOC(
913 protected_resource
));
914 protected_resource
->type
=
916 protected_resource
->link
= l
;
918 ospf_ti_lfa_generate_p_space(
919 area
, child
, protected_resource
,
927 static struct p_space
*ospf_ti_lfa_get_p_space_by_path(struct ospf_area
*area
,
928 struct ospf_path
*path
)
930 struct p_space
*p_space
;
931 struct router_lsa_link
*link
;
932 struct vertex
*child
;
935 frr_each(p_spaces
, area
->p_spaces
, p_space
) {
936 type
= p_space
->protected_resource
->type
;
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
))
945 if (type
== OSPF_TI_LFA_NODE_PROTECTION
) {
946 child
= ospf_spf_vertex_by_nexthop(area
->spf
,
949 && p_space
->protected_resource
->router_id
.s_addr
958 void ospf_ti_lfa_insert_backup_paths(struct ospf_area
*area
,
959 struct route_table
*new_table
)
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
;
968 char label_buf
[MPLS_LABEL_STRLEN
];
970 for (rn
= route_top(new_table
); rn
; rn
= route_next(rn
)) {
975 /* Insert a backup path for all OSPF paths */
976 for (ALL_LIST_ELEMENTS_RO(or->paths
, node
, path
)) {
978 if (path
->adv_router
.s_addr
== INADDR_ANY
979 || path
->nexthop
.s_addr
== INADDR_ANY
)
982 if (IS_DEBUG_OSPF_TI_LFA
)
984 "%s: attempting to insert backup path for prefix %pFX, router id %pI4 and nexthop %pI4.",
985 __func__
, &rn
->p
, &path
->adv_router
,
988 p_space
= ospf_ti_lfa_get_p_space_by_path(area
, path
);
990 if (IS_DEBUG_OSPF_TI_LFA
)
992 "%s: P space not found for router id %pI4 and nexthop %pI4.",
993 __func__
, &path
->adv_router
,
998 root_search
.id
= path
->adv_router
;
999 q_space_search
.root
= &root_search
;
1000 q_space
= q_spaces_find(p_space
->q_spaces
,
1003 if (IS_DEBUG_OSPF_TI_LFA
)
1005 "%s: Q space not found for advertising router %pI4.",
1006 __func__
, &path
->adv_router
);
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(
1015 sizeof(struct mpls_label_stack
)
1016 + sizeof(mpls_label_t
)
1017 * q_space
->label_stack
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
1029 /* Set the backup nexthop too */
1030 path
->srni
.backup_nexthop
= q_space
->nexthop
;
1033 if (path
->srni
.backup_label_stack
) {
1035 path
->srni
.backup_label_stack
1037 path
->srni
.backup_label_stack
->label
,
1038 label_buf
, MPLS_LABEL_STRLEN
, 0, true);
1039 if (IS_DEBUG_OSPF_TI_LFA
)
1041 "%s: inserted backup path %s for prefix %pFX, router id %pI4 and nexthop %pI4.",
1042 __func__
, label_buf
, &rn
->p
,
1046 if (IS_DEBUG_OSPF_TI_LFA
)
1048 "%s: inserted NO backup path for prefix %pFX, router id %pI4 and nexthop %pI4.",
1057 void ospf_ti_lfa_free_p_spaces(struct ospf_area
*area
)
1059 struct p_space
*p_space
;
1060 struct q_space
*q_space
;
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
);
1066 if (q_space
->pc_path
)
1067 list_delete(&q_space
->pc_path
);
1069 XFREE(MTYPE_OSPF_Q_SPACE
, q_space
->p_node_info
);
1070 XFREE(MTYPE_OSPF_Q_SPACE
, q_space
->q_node_info
);
1071 XFREE(MTYPE_OSPF_Q_SPACE
, q_space
->label_stack
);
1072 XFREE(MTYPE_OSPF_Q_SPACE
, q_space
);
1075 ospf_spf_cleanup(p_space
->root
, p_space
->vertex_list
);
1076 ospf_spf_cleanup(p_space
->pc_spf
, p_space
->pc_vertex_list
);
1077 XFREE(MTYPE_OSPF_P_SPACE
, p_space
->protected_resource
);
1079 q_spaces_fini(p_space
->q_spaces
);
1080 XFREE(MTYPE_OSPF_Q_SPACE
, p_space
->q_spaces
);
1083 p_spaces_fini(area
->p_spaces
);
1084 XFREE(MTYPE_OSPF_P_SPACE
, area
->p_spaces
);
1087 void ospf_ti_lfa_compute(struct ospf_area
*area
, struct route_table
*new_table
,
1088 enum protection_type protection_type
)
1091 * Generate P spaces per protected link/node and their respective Q
1092 * spaces, generate backup paths (MPLS label stacks) by finding P/Q
1095 ospf_ti_lfa_generate_p_spaces(area
, protection_type
);
1097 /* Insert the generated backup paths into the routing table. */
1098 ospf_ti_lfa_insert_backup_paths(area
, new_table
);
1100 /* Cleanup P spaces and related datastructures including Q spaces. */
1101 ospf_ti_lfa_free_p_spaces(area
);