Coverage Report

Created: 2026-05-04 06:53

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