Coverage Report

Created: 2025-08-26 06:20

/src/frr/lib/nexthop.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* A generic nexthop structure
3
 * Copyright (C) 2013 Cumulus Networks, Inc.
4
 */
5
#include <zebra.h>
6
7
#include "prefix.h"
8
#include "table.h"
9
#include "memory.h"
10
#include "command.h"
11
#include "log.h"
12
#include "sockunion.h"
13
#include "linklist.h"
14
#include "prefix.h"
15
#include "nexthop.h"
16
#include "mpls.h"
17
#include "jhash.h"
18
#include "printfrr.h"
19
#include "vrf.h"
20
#include "nexthop_group.h"
21
22
DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop");
23
DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label");
24
DEFINE_MTYPE_STATIC(LIB, NH_SRV6, "Nexthop srv6");
25
26
static int _nexthop_labels_cmp(const struct nexthop *nh1,
27
             const struct nexthop *nh2)
28
0
{
29
0
  const struct mpls_label_stack *nhl1 = NULL;
30
0
  const struct mpls_label_stack *nhl2 = NULL;
31
32
0
  nhl1 = nh1->nh_label;
33
0
  nhl2 = nh2->nh_label;
34
35
  /* No labels is a match */
36
0
  if (!nhl1 && !nhl2)
37
0
    return 0;
38
39
0
  if (nhl1 && !nhl2)
40
0
    return 1;
41
42
0
  if (nhl2 && !nhl1)
43
0
    return -1;
44
45
0
  if (nhl1->num_labels > nhl2->num_labels)
46
0
    return 1;
47
48
0
  if (nhl1->num_labels < nhl2->num_labels)
49
0
    return -1;
50
51
0
  return memcmp(nhl1->label, nhl2->label,
52
0
          (nhl1->num_labels * sizeof(mpls_label_t)));
53
0
}
54
55
static int _nexthop_srv6_cmp(const struct nexthop *nh1,
56
           const struct nexthop *nh2)
57
0
{
58
0
  int ret = 0;
59
60
0
  if (!nh1->nh_srv6 && !nh2->nh_srv6)
61
0
    return 0;
62
63
0
  if (nh1->nh_srv6 && !nh2->nh_srv6)
64
0
    return 1;
65
66
0
  if (!nh1->nh_srv6 && nh2->nh_srv6)
67
0
    return -1;
68
69
0
  if (nh1->nh_srv6->seg6local_action > nh2->nh_srv6->seg6local_action)
70
0
    return 1;
71
72
0
  if (nh2->nh_srv6->seg6local_action < nh1->nh_srv6->seg6local_action)
73
0
    return -1;
74
75
0
  ret = memcmp(&nh1->nh_srv6->seg6local_ctx,
76
0
         &nh2->nh_srv6->seg6local_ctx,
77
0
         sizeof(struct seg6local_context));
78
0
  if (ret != 0)
79
0
    return ret;
80
81
0
  ret = memcmp(&nh1->nh_srv6->seg6_segs,
82
0
         &nh2->nh_srv6->seg6_segs,
83
0
         sizeof(struct in6_addr));
84
85
0
  return ret;
86
0
}
87
88
int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1,
89
           const union g_addr *addr2)
90
0
{
91
0
  int ret = 0;
92
93
0
  switch (type) {
94
0
  case NEXTHOP_TYPE_IPV4:
95
0
  case NEXTHOP_TYPE_IPV4_IFINDEX:
96
0
    ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4);
97
0
    break;
98
0
  case NEXTHOP_TYPE_IPV6:
99
0
  case NEXTHOP_TYPE_IPV6_IFINDEX:
100
0
    ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6);
101
0
    break;
102
0
  case NEXTHOP_TYPE_IFINDEX:
103
0
  case NEXTHOP_TYPE_BLACKHOLE:
104
    /* No addr here */
105
0
    break;
106
0
  }
107
108
0
  return ret;
109
0
}
110
111
static int _nexthop_gateway_cmp(const struct nexthop *nh1,
112
        const struct nexthop *nh2)
113
0
{
114
0
  return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
115
0
}
116
117
static int _nexthop_source_cmp(const struct nexthop *nh1,
118
             const struct nexthop *nh2)
119
0
{
120
0
  return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
121
0
}
122
123
static int _nexthop_cmp_no_labels(const struct nexthop *next1,
124
          const struct nexthop *next2)
125
0
{
126
0
  int ret = 0;
127
128
0
  if (next1->vrf_id < next2->vrf_id)
129
0
    return -1;
130
131
0
  if (next1->vrf_id > next2->vrf_id)
132
0
    return 1;
133
134
0
  if (next1->type < next2->type)
135
0
    return -1;
136
137
0
  if (next1->type > next2->type)
138
0
    return 1;
139
140
0
  if (next1->weight < next2->weight)
141
0
    return -1;
142
143
0
  if (next1->weight > next2->weight)
144
0
    return 1;
145
146
0
  switch (next1->type) {
147
0
  case NEXTHOP_TYPE_IPV4:
148
0
  case NEXTHOP_TYPE_IPV6:
149
0
    ret = _nexthop_gateway_cmp(next1, next2);
150
0
    if (ret != 0)
151
0
      return ret;
152
0
    break;
153
0
  case NEXTHOP_TYPE_IPV4_IFINDEX:
154
0
  case NEXTHOP_TYPE_IPV6_IFINDEX:
155
0
    ret = _nexthop_gateway_cmp(next1, next2);
156
0
    if (ret != 0)
157
0
      return ret;
158
    /* Intentional Fall-Through */
159
0
  case NEXTHOP_TYPE_IFINDEX:
160
0
    if (next1->ifindex < next2->ifindex)
161
0
      return -1;
162
163
0
    if (next1->ifindex > next2->ifindex)
164
0
      return 1;
165
0
    break;
166
0
  case NEXTHOP_TYPE_BLACKHOLE:
167
0
    if (next1->bh_type < next2->bh_type)
168
0
      return -1;
169
170
0
    if (next1->bh_type > next2->bh_type)
171
0
      return 1;
172
0
    break;
173
0
  }
174
175
0
  if (next1->srte_color < next2->srte_color)
176
0
    return -1;
177
0
  if (next1->srte_color > next2->srte_color)
178
0
    return 1;
179
180
0
  ret = _nexthop_source_cmp(next1, next2);
181
0
  if (ret != 0)
182
0
    goto done;
183
184
0
  if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
185
0
      !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
186
0
    return 0;
187
188
0
  if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
189
0
      CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
190
0
    return -1;
191
192
0
  if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
193
0
      !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
194
0
    return 1;
195
196
0
  if (next1->backup_num == 0 && next2->backup_num == 0)
197
0
    goto done;
198
199
0
  if (next1->backup_num < next2->backup_num)
200
0
    return -1;
201
202
0
  if (next1->backup_num > next2->backup_num)
203
0
    return 1;
204
205
0
  ret = memcmp(next1->backup_idx,
206
0
         next2->backup_idx, next1->backup_num);
207
208
0
done:
209
0
  return ret;
210
0
}
211
212
int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
213
0
{
214
0
  int ret = 0;
215
216
0
  ret = _nexthop_cmp_no_labels(next1, next2);
217
0
  if (ret != 0)
218
0
    return ret;
219
220
0
  ret = _nexthop_labels_cmp(next1, next2);
221
0
  if (ret != 0)
222
0
    return ret;
223
224
0
  ret = _nexthop_srv6_cmp(next1, next2);
225
226
0
  return ret;
227
0
}
228
229
/*
230
 * More-limited comparison function used to detect duplicate
231
 * nexthops. This is used in places where we don't need the full
232
 * comparison of 'nexthop_cmp()'.
233
 */
234
int nexthop_cmp_basic(const struct nexthop *nh1,
235
          const struct nexthop *nh2)
236
0
{
237
0
  int ret = 0;
238
0
  const struct mpls_label_stack *nhl1 = NULL;
239
0
  const struct mpls_label_stack *nhl2 = NULL;
240
241
0
  if (nh1 == NULL && nh2 == NULL)
242
0
    return 0;
243
244
0
  if (nh1 && !nh2)
245
0
    return 1;
246
247
0
  if (!nh1 && nh2)
248
0
    return -1;
249
250
0
  if (nh1->vrf_id < nh2->vrf_id)
251
0
    return -1;
252
253
0
  if (nh1->vrf_id > nh2->vrf_id)
254
0
    return 1;
255
256
0
  if (nh1->type < nh2->type)
257
0
    return -1;
258
259
0
  if (nh1->type > nh2->type)
260
0
    return 1;
261
262
0
  if (nh1->weight < nh2->weight)
263
0
    return -1;
264
265
0
  if (nh1->weight > nh2->weight)
266
0
    return 1;
267
268
0
  switch (nh1->type) {
269
0
  case NEXTHOP_TYPE_IPV4:
270
0
  case NEXTHOP_TYPE_IPV6:
271
0
    ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
272
0
    if (ret != 0)
273
0
      return ret;
274
0
    break;
275
0
  case NEXTHOP_TYPE_IPV4_IFINDEX:
276
0
  case NEXTHOP_TYPE_IPV6_IFINDEX:
277
0
    ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
278
0
    if (ret != 0)
279
0
      return ret;
280
    /* Intentional Fall-Through */
281
0
  case NEXTHOP_TYPE_IFINDEX:
282
0
    if (nh1->ifindex < nh2->ifindex)
283
0
      return -1;
284
285
0
    if (nh1->ifindex > nh2->ifindex)
286
0
      return 1;
287
0
    break;
288
0
  case NEXTHOP_TYPE_BLACKHOLE:
289
0
    if (nh1->bh_type < nh2->bh_type)
290
0
      return -1;
291
292
0
    if (nh1->bh_type > nh2->bh_type)
293
0
      return 1;
294
0
    break;
295
0
  }
296
297
  /* Compare source addr */
298
0
  ret = nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
299
0
  if (ret != 0)
300
0
    goto done;
301
302
0
  nhl1 = nh1->nh_label;
303
0
  nhl2 = nh2->nh_label;
304
305
  /* No labels is a match */
306
0
  if (!nhl1 && !nhl2)
307
0
    return 0;
308
309
0
  if (nhl1 && !nhl2)
310
0
    return 1;
311
312
0
  if (nhl2 && !nhl1)
313
0
    return -1;
314
315
0
  if (nhl1->num_labels > nhl2->num_labels)
316
0
    return 1;
317
318
0
  if (nhl1->num_labels < nhl2->num_labels)
319
0
    return -1;
320
321
0
  ret = memcmp(nhl1->label, nhl2->label,
322
0
         (nhl1->num_labels * sizeof(mpls_label_t)));
323
324
0
done:
325
0
  return ret;
326
0
}
327
328
/*
329
 * nexthop_type_to_str
330
 */
331
const char *nexthop_type_to_str(enum nexthop_types_t nh_type)
332
0
{
333
0
  static const char *const desc[] = {
334
0
    "none",    "Directly connected",
335
0
    "IPv4 nexthop",  "IPv4 nexthop with ifindex",
336
0
    "IPv6 nexthop",  "IPv6 nexthop with ifindex",
337
0
    "Null0 nexthop",
338
0
  };
339
340
0
  return desc[nh_type];
341
0
}
342
343
/*
344
 * Check if the labels match for the 2 nexthops specified.
345
 */
346
bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
347
0
{
348
0
  if (_nexthop_labels_cmp(nh1, nh2) != 0)
349
0
    return false;
350
351
0
  return true;
352
0
}
353
354
struct nexthop *nexthop_new(void)
355
0
{
356
0
  struct nexthop *nh;
357
358
0
  nh = XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop));
359
360
  /*
361
   * Default the weight to 1 here for all nexthops.
362
   * The linux kernel does some weird stuff with adding +1 to
363
   * all nexthop weights it gets over netlink.
364
   * To handle this, just default everything to 1 right from
365
   * from the beginning so we don't have to special case
366
   * default weights in the linux netlink code.
367
   *
368
   * 1 should be a valid on all platforms anyway.
369
   */
370
0
  nh->weight = 1;
371
372
0
  return nh;
373
0
}
374
375
/* Free nexthop. */
376
void nexthop_free(struct nexthop *nexthop)
377
0
{
378
0
  nexthop_del_labels(nexthop);
379
0
  nexthop_del_srv6_seg6local(nexthop);
380
0
  nexthop_del_srv6_seg6(nexthop);
381
0
  if (nexthop->resolved)
382
0
    nexthops_free(nexthop->resolved);
383
0
  XFREE(MTYPE_NEXTHOP, nexthop);
384
0
}
385
386
/* Frees a list of nexthops */
387
void nexthops_free(struct nexthop *nexthop)
388
0
{
389
0
  struct nexthop *nh, *next;
390
391
0
  for (nh = nexthop; nh; nh = next) {
392
0
    next = nh->next;
393
0
    nexthop_free(nh);
394
0
  }
395
0
}
396
397
bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2)
398
0
{
399
0
  if (nh1 && !nh2)
400
0
    return false;
401
402
0
  if (!nh1 && nh2)
403
0
    return false;
404
405
0
  if (nh1 == nh2)
406
0
    return true;
407
408
0
  if (nexthop_cmp(nh1, nh2) != 0)
409
0
    return false;
410
411
0
  return true;
412
0
}
413
414
bool nexthop_same_no_labels(const struct nexthop *nh1,
415
          const struct nexthop *nh2)
416
0
{
417
0
  if (nh1 && !nh2)
418
0
    return false;
419
420
0
  if (!nh1 && nh2)
421
0
    return false;
422
423
0
  if (nh1 == nh2)
424
0
    return true;
425
426
0
  if (_nexthop_cmp_no_labels(nh1, nh2) != 0)
427
0
    return false;
428
429
0
  return true;
430
0
}
431
432
/*
433
 * Allocate a new nexthop object and initialize it from various args.
434
 */
435
struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
436
0
{
437
0
  struct nexthop *nexthop;
438
439
0
  nexthop = nexthop_new();
440
0
  nexthop->type = NEXTHOP_TYPE_IFINDEX;
441
0
  nexthop->ifindex = ifindex;
442
0
  nexthop->vrf_id = vrf_id;
443
444
0
  return nexthop;
445
0
}
446
447
struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
448
          const struct in_addr *src,
449
          vrf_id_t vrf_id)
450
0
{
451
0
  struct nexthop *nexthop;
452
453
0
  nexthop = nexthop_new();
454
0
  nexthop->type = NEXTHOP_TYPE_IPV4;
455
0
  nexthop->vrf_id = vrf_id;
456
0
  nexthop->gate.ipv4 = *ipv4;
457
0
  if (src)
458
0
    nexthop->src.ipv4 = *src;
459
460
0
  return nexthop;
461
0
}
462
463
struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
464
            const struct in_addr *src,
465
            ifindex_t ifindex, vrf_id_t vrf_id)
466
0
{
467
0
  struct nexthop *nexthop;
468
469
0
  nexthop = nexthop_new();
470
0
  nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
471
0
  nexthop->vrf_id = vrf_id;
472
0
  nexthop->gate.ipv4 = *ipv4;
473
0
  if (src)
474
0
    nexthop->src.ipv4 = *src;
475
0
  nexthop->ifindex = ifindex;
476
477
0
  return nexthop;
478
0
}
479
480
struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
481
          vrf_id_t vrf_id)
482
0
{
483
0
  struct nexthop *nexthop;
484
485
0
  nexthop = nexthop_new();
486
0
  nexthop->vrf_id = vrf_id;
487
0
  nexthop->type = NEXTHOP_TYPE_IPV6;
488
0
  nexthop->gate.ipv6 = *ipv6;
489
490
0
  return nexthop;
491
0
}
492
493
struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
494
            ifindex_t ifindex, vrf_id_t vrf_id)
495
0
{
496
0
  struct nexthop *nexthop;
497
498
0
  nexthop = nexthop_new();
499
0
  nexthop->vrf_id = vrf_id;
500
0
  nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
501
0
  nexthop->gate.ipv6 = *ipv6;
502
0
  nexthop->ifindex = ifindex;
503
504
0
  return nexthop;
505
0
}
506
507
struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type,
508
               vrf_id_t nh_vrf_id)
509
0
{
510
0
  struct nexthop *nexthop;
511
512
0
  nexthop = nexthop_new();
513
0
  nexthop->vrf_id = nh_vrf_id;
514
0
  nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
515
0
  nexthop->bh_type = bh_type;
516
517
0
  return nexthop;
518
0
}
519
520
/* Update nexthop with label information. */
521
void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype,
522
      uint8_t num_labels, const mpls_label_t *labels)
523
0
{
524
0
  struct mpls_label_stack *nh_label;
525
0
  int i;
526
527
0
  if (num_labels == 0)
528
0
    return;
529
530
  /* Enforce limit on label stack size */
531
0
  if (num_labels > MPLS_MAX_LABELS)
532
0
    num_labels = MPLS_MAX_LABELS;
533
534
0
  nexthop->nh_label_type = ltype;
535
536
0
  nh_label = XCALLOC(MTYPE_NH_LABEL,
537
0
         sizeof(struct mpls_label_stack)
538
0
           + num_labels * sizeof(mpls_label_t));
539
0
  nh_label->num_labels = num_labels;
540
0
  for (i = 0; i < num_labels; i++)
541
0
    nh_label->label[i] = *(labels + i);
542
0
  nexthop->nh_label = nh_label;
543
0
}
544
545
/* Free label information of nexthop, if present. */
546
void nexthop_del_labels(struct nexthop *nexthop)
547
0
{
548
0
  XFREE(MTYPE_NH_LABEL, nexthop->nh_label);
549
0
  nexthop->nh_label_type = ZEBRA_LSP_NONE;
550
0
}
551
552
void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action,
553
        const struct seg6local_context *ctx)
554
0
{
555
0
  if (action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
556
0
    return;
557
558
0
  if (!nexthop->nh_srv6)
559
0
    nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
560
0
             sizeof(struct nexthop_srv6));
561
562
0
  nexthop->nh_srv6->seg6local_action = action;
563
0
  nexthop->nh_srv6->seg6local_ctx = *ctx;
564
0
}
565
566
void nexthop_del_srv6_seg6local(struct nexthop *nexthop)
567
0
{
568
0
  if (!nexthop->nh_srv6)
569
0
    return;
570
571
0
  nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
572
573
0
  if (sid_zero(&nexthop->nh_srv6->seg6_segs))
574
0
    XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
575
0
}
576
577
void nexthop_add_srv6_seg6(struct nexthop *nexthop,
578
         const struct in6_addr *segs)
579
0
{
580
0
  if (!segs)
581
0
    return;
582
583
0
  if (!nexthop->nh_srv6)
584
0
    nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
585
0
             sizeof(struct nexthop_srv6));
586
587
0
  nexthop->nh_srv6->seg6_segs = *segs;
588
0
}
589
590
void nexthop_del_srv6_seg6(struct nexthop *nexthop)
591
0
{
592
0
  if (!nexthop->nh_srv6)
593
0
    return;
594
595
0
  memset(&nexthop->nh_srv6->seg6_segs, 0,
596
0
         sizeof(nexthop->nh_srv6->seg6_segs));
597
598
0
  if (nexthop->nh_srv6->seg6local_action ==
599
0
      ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
600
0
    XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
601
0
}
602
603
const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
604
0
{
605
0
  switch (nexthop->type) {
606
0
  case NEXTHOP_TYPE_IFINDEX:
607
0
    snprintf(str, size, "if %u", nexthop->ifindex);
608
0
    break;
609
0
  case NEXTHOP_TYPE_IPV4:
610
0
  case NEXTHOP_TYPE_IPV4_IFINDEX:
611
0
    snprintfrr(str, size, "%pI4 if %u", &nexthop->gate.ipv4,
612
0
         nexthop->ifindex);
613
0
    break;
614
0
  case NEXTHOP_TYPE_IPV6:
615
0
  case NEXTHOP_TYPE_IPV6_IFINDEX:
616
0
    snprintfrr(str, size, "%pI6 if %u", &nexthop->gate.ipv6,
617
0
         nexthop->ifindex);
618
0
    break;
619
0
  case NEXTHOP_TYPE_BLACKHOLE:
620
0
    snprintf(str, size, "blackhole");
621
0
    break;
622
0
  }
623
624
0
  return str;
625
0
}
626
627
/*
628
 * Iteration step for ALL_NEXTHOPS macro:
629
 * This is the tricky part. Check if `nexthop' has
630
 * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' has
631
 * at least one nexthop attached to `nexthop->resolved', which will be
632
 * the next one.
633
 *
634
 * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its
635
 * current chain. In case its current chain end is reached, it will move
636
 * upwards in the recursion levels and progress there. Whenever a step
637
 * forward in a chain is done, recursion will be checked again.
638
 * In a nustshell, it's equivalent to a pre-traversal order assuming that
639
 * left branch is 'resolved' and right branch is 'next':
640
 * https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg
641
 */
642
struct nexthop *nexthop_next(const struct nexthop *nexthop)
643
0
{
644
0
  if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
645
0
    return nexthop->resolved;
646
647
0
  if (nexthop->next)
648
0
    return nexthop->next;
649
650
0
  for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
651
0
    if (par->next)
652
0
      return par->next;
653
654
0
  return NULL;
655
0
}
656
657
/* Return the next nexthop in the tree that is resolved and active */
658
struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
659
0
{
660
0
  struct nexthop *next = nexthop_next(nexthop);
661
662
0
  while (next
663
0
         && (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE)
664
0
       || !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE)))
665
0
    next = nexthop_next(next);
666
667
0
  return next;
668
0
}
669
670
unsigned int nexthop_level(const struct nexthop *nexthop)
671
0
{
672
0
  unsigned int rv = 0;
673
674
0
  for (const struct nexthop *par = nexthop->rparent;
675
0
       par; par = par->rparent)
676
0
    rv++;
677
678
0
  return rv;
679
0
}
680
681
/* Only hash word-sized things, let cmp do the rest. */
682
uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
683
0
{
684
0
  uint32_t key = 0x45afe398;
685
0
  int i;
686
687
0
  key = jhash_3words(nexthop->type, nexthop->vrf_id,
688
0
         nexthop->nh_label_type, key);
689
690
0
  if (nexthop->nh_label) {
691
0
    int labels = nexthop->nh_label->num_labels;
692
693
0
    i = 0;
694
695
0
    while (labels >= 3) {
696
0
      key = jhash_3words(nexthop->nh_label->label[i],
697
0
             nexthop->nh_label->label[i + 1],
698
0
             nexthop->nh_label->label[i + 2],
699
0
             key);
700
0
      labels -= 3;
701
0
      i += 3;
702
0
    }
703
704
0
    if (labels >= 2) {
705
0
      key = jhash_2words(nexthop->nh_label->label[i],
706
0
             nexthop->nh_label->label[i + 1],
707
0
             key);
708
0
      labels -= 2;
709
0
      i += 2;
710
0
    }
711
712
0
    if (labels >= 1)
713
0
      key = jhash_1word(nexthop->nh_label->label[i], key);
714
0
  }
715
716
0
  key = jhash_2words(nexthop->ifindex,
717
0
         CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
718
0
         key);
719
720
  /* Include backup nexthops, if present */
721
0
  if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
722
0
    int backups = nexthop->backup_num;
723
724
0
    i = 0;
725
726
0
    while (backups >= 3) {
727
0
      key = jhash_3words(nexthop->backup_idx[i],
728
0
             nexthop->backup_idx[i + 1],
729
0
             nexthop->backup_idx[i + 2], key);
730
0
      backups -= 3;
731
0
      i += 3;
732
0
    }
733
734
0
    while (backups >= 2) {
735
0
      key = jhash_2words(nexthop->backup_idx[i],
736
0
             nexthop->backup_idx[i + 1], key);
737
0
      backups -= 2;
738
0
      i += 2;
739
0
    }
740
741
0
    if (backups >= 1)
742
0
      key = jhash_1word(nexthop->backup_idx[i], key);
743
0
  }
744
745
0
  if (nexthop->nh_srv6) {
746
0
    key = jhash_1word(nexthop->nh_srv6->seg6local_action, key);
747
0
    key = jhash(&nexthop->nh_srv6->seg6local_ctx,
748
0
          sizeof(nexthop->nh_srv6->seg6local_ctx), key);
749
0
    key = jhash(&nexthop->nh_srv6->seg6_segs,
750
0
          sizeof(nexthop->nh_srv6->seg6_segs), key);
751
0
  }
752
753
0
  return key;
754
0
}
755
756
757
0
#define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */
758
759
/* For a more granular hash */
760
uint32_t nexthop_hash(const struct nexthop *nexthop)
761
0
{
762
0
  uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {};
763
  /* Get all the quick stuff */
764
0
  uint32_t key = nexthop_hash_quick(nexthop);
765
766
0
  assert(((sizeof(nexthop->gate) + sizeof(nexthop->src)
767
0
     + sizeof(nexthop->rmap_src))
768
0
    / 3)
769
0
         == (GATE_SIZE * sizeof(uint32_t)));
770
771
0
  memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE);
772
0
  memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE);
773
0
  memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src,
774
0
         GATE_SIZE);
775
776
0
  key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key);
777
778
0
  return key;
779
0
}
780
781
void nexthop_copy_no_recurse(struct nexthop *copy,
782
           const struct nexthop *nexthop,
783
           struct nexthop *rparent)
784
0
{
785
0
  copy->vrf_id = nexthop->vrf_id;
786
0
  copy->ifindex = nexthop->ifindex;
787
0
  copy->type = nexthop->type;
788
0
  copy->flags = nexthop->flags;
789
0
  copy->weight = nexthop->weight;
790
791
0
  assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS);
792
0
  copy->backup_num = nexthop->backup_num;
793
0
  if (copy->backup_num > 0)
794
0
    memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num);
795
796
0
  copy->srte_color = nexthop->srte_color;
797
0
  memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
798
0
  memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
799
0
  memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
800
0
  memcpy(&copy->rmac, &nexthop->rmac, sizeof(nexthop->rmac));
801
0
  copy->rparent = rparent;
802
0
  if (nexthop->nh_label)
803
0
    nexthop_add_labels(copy, nexthop->nh_label_type,
804
0
           nexthop->nh_label->num_labels,
805
0
           &nexthop->nh_label->label[0]);
806
807
0
  if (nexthop->nh_srv6) {
808
0
    if (nexthop->nh_srv6->seg6local_action !=
809
0
        ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
810
0
      nexthop_add_srv6_seg6local(copy,
811
0
        nexthop->nh_srv6->seg6local_action,
812
0
        &nexthop->nh_srv6->seg6local_ctx);
813
0
    if (!sid_zero(&nexthop->nh_srv6->seg6_segs))
814
0
      nexthop_add_srv6_seg6(copy,
815
0
        &nexthop->nh_srv6->seg6_segs);
816
0
  }
817
0
}
818
819
void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
820
      struct nexthop *rparent)
821
0
{
822
0
  nexthop_copy_no_recurse(copy, nexthop, rparent);
823
824
  /* Bit of a special case here, we need to handle the case
825
   * of a nexthop resolving to a group. Hence, we need to
826
   * use a nexthop_group API.
827
   */
828
0
  if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE))
829
0
    copy_nexthops(&copy->resolved, nexthop->resolved, copy);
830
0
}
831
832
struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
833
               struct nexthop *rparent)
834
0
{
835
0
  struct nexthop *new = nexthop_new();
836
837
0
  nexthop_copy_no_recurse(new, nexthop, rparent);
838
0
  return new;
839
0
}
840
841
struct nexthop *nexthop_dup(const struct nexthop *nexthop,
842
          struct nexthop *rparent)
843
0
{
844
0
  struct nexthop *new = nexthop_new();
845
846
0
  nexthop_copy(new, nexthop, rparent);
847
0
  return new;
848
0
}
849
850
/*
851
 * Parse one or more backup index values, as comma-separated numbers,
852
 * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
853
 * in size. Mails back the number of values converted, and returns 0 on
854
 * success, <0 if an error in parsing.
855
 */
856
int nexthop_str2backups(const char *str, int *num_backups,
857
      uint8_t *backups)
858
0
{
859
0
  char *ostr;       /* copy of string (start) */
860
0
  char *lstr;       /* working copy of string */
861
0
  char *nump;       /* pointer to next segment */
862
0
  char *endp;       /* end pointer */
863
0
  int i, ret;
864
0
  uint8_t tmp[NEXTHOP_MAX_BACKUPS];
865
0
  uint32_t lval;
866
867
  /* Copy incoming string; the parse is destructive */
868
0
  lstr = ostr = XSTRDUP(MTYPE_TMP, str);
869
0
  *num_backups = 0;
870
0
  ret = 0;
871
872
0
  for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) {
873
0
    nump = strsep(&lstr, ",");
874
0
    lval = strtoul(nump, &endp, 10);
875
876
    /* Format check */
877
0
    if (*endp != '\0') {
878
0
      ret = -1;
879
0
      break;
880
0
    }
881
882
    /* Empty value */
883
0
    if (endp == nump) {
884
0
      ret = -1;
885
0
      break;
886
0
    }
887
888
    /* Limit to one octet */
889
0
    if (lval > 255) {
890
0
      ret = -1;
891
0
      break;
892
0
    }
893
894
0
    tmp[i] = lval;
895
0
  }
896
897
  /* Excess values */
898
0
  if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr)
899
0
    ret = -1;
900
901
0
  if (ret == 0) {
902
0
    *num_backups = i;
903
0
    memcpy(backups, tmp, i);
904
0
  }
905
906
0
  XFREE(MTYPE_TMP, ostr);
907
908
0
  return ret;
909
0
}
910
911
ssize_t printfrr_nhs(struct fbuf *buf, const struct nexthop *nexthop)
912
0
{
913
0
  ssize_t ret = 0;
914
915
0
  if (!nexthop)
916
0
    return bputs(buf, "(null)");
917
918
0
  switch (nexthop->type) {
919
0
  case NEXTHOP_TYPE_IFINDEX:
920
0
    ret += bprintfrr(buf, "if %u", nexthop->ifindex);
921
0
    break;
922
0
  case NEXTHOP_TYPE_IPV4:
923
0
  case NEXTHOP_TYPE_IPV4_IFINDEX:
924
0
    ret += bprintfrr(buf, "%pI4 if %u", &nexthop->gate.ipv4,
925
0
         nexthop->ifindex);
926
0
    break;
927
0
  case NEXTHOP_TYPE_IPV6:
928
0
  case NEXTHOP_TYPE_IPV6_IFINDEX:
929
0
    ret += bprintfrr(buf, "%pI6 if %u", &nexthop->gate.ipv6,
930
0
         nexthop->ifindex);
931
0
    break;
932
0
  case NEXTHOP_TYPE_BLACKHOLE:
933
0
    ret += bputs(buf, "blackhole");
934
0
    break;
935
0
  }
936
0
  return ret;
937
0
}
938
939
/*
940
 * nexthop printing variants:
941
 *  %pNHvv
942
 *    via 1.2.3.4
943
 *    via 1.2.3.4, eth0
944
 *    is directly connected, eth0
945
 *    unreachable (blackhole)
946
 *  %pNHv
947
 *    1.2.3.4
948
 *    1.2.3.4, via eth0
949
 *    directly connected, eth0
950
 *    unreachable (blackhole)
951
 *  %pNHs
952
 *    nexthop2str()
953
 *  %pNHcg
954
 *    1.2.3.4
955
 *    (0-length if no IP address present)
956
 *  %pNHci
957
 *    eth0
958
 *    (0-length if no interface present)
959
 */
960
printfrr_ext_autoreg_p("NH", printfrr_nh);
961
static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea,
962
         const void *ptr)
963
0
{
964
0
  const struct nexthop *nexthop = ptr;
965
0
  bool do_ifi = false;
966
0
  const char *v_is = "", *v_via = "", *v_viaif = "via ";
967
0
  ssize_t ret = 0;
968
969
0
  switch (*ea->fmt) {
970
0
  case 'v':
971
0
    ea->fmt++;
972
0
    if (*ea->fmt == 'v') {
973
0
      v_is = "is ";
974
0
      v_via = "via ";
975
0
      v_viaif = "";
976
0
      ea->fmt++;
977
0
    }
978
979
0
    if (!nexthop)
980
0
      return bputs(buf, "(null)");
981
982
0
    switch (nexthop->type) {
983
0
    case NEXTHOP_TYPE_IPV4:
984
0
    case NEXTHOP_TYPE_IPV4_IFINDEX:
985
0
      ret += bprintfrr(buf, "%s%pI4", v_via,
986
0
           &nexthop->gate.ipv4);
987
0
      do_ifi = true;
988
0
      break;
989
0
    case NEXTHOP_TYPE_IPV6:
990
0
    case NEXTHOP_TYPE_IPV6_IFINDEX:
991
0
      ret += bprintfrr(buf, "%s%pI6", v_via,
992
0
           &nexthop->gate.ipv6);
993
0
      do_ifi = true;
994
0
      break;
995
0
    case NEXTHOP_TYPE_IFINDEX:
996
0
      ret += bprintfrr(buf, "%sdirectly connected, %s", v_is,
997
0
           ifindex2ifname(nexthop->ifindex,
998
0
              nexthop->vrf_id));
999
0
      break;
1000
0
    case NEXTHOP_TYPE_BLACKHOLE:
1001
0
      ret += bputs(buf, "unreachable");
1002
1003
0
      switch (nexthop->bh_type) {
1004
0
      case BLACKHOLE_REJECT:
1005
0
        ret += bputs(buf, " (ICMP unreachable)");
1006
0
        break;
1007
0
      case BLACKHOLE_ADMINPROHIB:
1008
0
        ret += bputs(buf, " (ICMP admin-prohibited)");
1009
0
        break;
1010
0
      case BLACKHOLE_NULL:
1011
0
        ret += bputs(buf, " (blackhole)");
1012
0
        break;
1013
0
      case BLACKHOLE_UNSPEC:
1014
0
        break;
1015
0
      }
1016
0
      break;
1017
0
    }
1018
0
    if (do_ifi && nexthop->ifindex)
1019
0
      ret += bprintfrr(buf, ", %s%s", v_viaif,
1020
0
           ifindex2ifname(nexthop->ifindex,
1021
0
              nexthop->vrf_id));
1022
1023
0
    return ret;
1024
0
  case 's':
1025
0
    ea->fmt++;
1026
1027
0
    ret += printfrr_nhs(buf, nexthop);
1028
0
    return ret;
1029
0
  case 'c':
1030
0
    ea->fmt++;
1031
0
    if (*ea->fmt == 'g') {
1032
0
      ea->fmt++;
1033
0
      if (!nexthop)
1034
0
        return bputs(buf, "(null)");
1035
0
      switch (nexthop->type) {
1036
0
      case NEXTHOP_TYPE_IPV4:
1037
0
      case NEXTHOP_TYPE_IPV4_IFINDEX:
1038
0
        ret += bprintfrr(buf, "%pI4",
1039
0
             &nexthop->gate.ipv4);
1040
0
        break;
1041
0
      case NEXTHOP_TYPE_IPV6:
1042
0
      case NEXTHOP_TYPE_IPV6_IFINDEX:
1043
0
        ret += bprintfrr(buf, "%pI6",
1044
0
             &nexthop->gate.ipv6);
1045
0
        break;
1046
0
      case NEXTHOP_TYPE_IFINDEX:
1047
0
      case NEXTHOP_TYPE_BLACKHOLE:
1048
0
        break;
1049
0
      }
1050
0
    } else if (*ea->fmt == 'i') {
1051
0
      ea->fmt++;
1052
0
      if (!nexthop)
1053
0
        return bputs(buf, "(null)");
1054
0
      switch (nexthop->type) {
1055
0
      case NEXTHOP_TYPE_IFINDEX:
1056
0
        ret += bprintfrr(
1057
0
          buf, "%s",
1058
0
          ifindex2ifname(nexthop->ifindex,
1059
0
                   nexthop->vrf_id));
1060
0
        break;
1061
0
      case NEXTHOP_TYPE_IPV4:
1062
0
      case NEXTHOP_TYPE_IPV4_IFINDEX:
1063
0
      case NEXTHOP_TYPE_IPV6:
1064
0
      case NEXTHOP_TYPE_IPV6_IFINDEX:
1065
0
        if (nexthop->ifindex)
1066
0
          ret += bprintfrr(
1067
0
            buf, "%s",
1068
0
            ifindex2ifname(
1069
0
              nexthop->ifindex,
1070
0
              nexthop->vrf_id));
1071
0
        break;
1072
0
      case NEXTHOP_TYPE_BLACKHOLE:
1073
0
        break;
1074
0
      }
1075
0
    }
1076
0
    return ret;
1077
0
  }
1078
0
  return -1;
1079
0
}
1080
1081
bool nexthop_is_ifindex_type(const struct nexthop *nh)
1082
0
{
1083
0
  if (nh->type == NEXTHOP_TYPE_IFINDEX ||
1084
0
      nh->type == NEXTHOP_TYPE_IPV4_IFINDEX ||
1085
0
      nh->type == NEXTHOP_TYPE_IPV6_IFINDEX)
1086
0
    return true;
1087
0
  return false;
1088
0
}