Coverage Report

Created: 2026-06-06 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/nexthop_group.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Nexthop Group structure definition.
4
 * Copyright (C) 2018 Cumulus Networks, Inc.
5
 *                    Donald Sharp
6
 */
7
#include <zebra.h>
8
9
#include <vrf.h>
10
#include <sockunion.h>
11
#include <nexthop.h>
12
#include <nexthop_group.h>
13
#include <nexthop_group_private.h>
14
#include <vty.h>
15
#include <command.h>
16
#include <jhash.h>
17
18
#include "lib/nexthop_group_clippy.c"
19
20
6
DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
21
6
22
6
/*
23
6
 * Internal struct used to hold nhg config strings
24
6
 */
25
6
struct nexthop_hold {
26
6
  char *nhvrf_name;
27
6
  union sockunion *addr;
28
6
  char *intf;
29
6
  bool onlink;
30
6
  char *labels;
31
6
  vni_t vni;
32
6
  uint32_t weight;
33
6
  char *backup_str;
34
6
};
35
6
36
6
struct nexthop_group_hooks {
37
6
  void (*new)(const char *name);
38
6
  void (*modify)(const struct nexthop_group_cmd *nhgc);
39
6
  void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
40
6
          const struct nexthop *nhop);
41
6
  void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
42
6
          const struct nexthop *nhop);
43
6
  void (*delete)(const char *name);
44
6
};
45
6
46
6
static struct nexthop_group_hooks nhg_hooks;
47
6
48
6
static inline int
49
6
nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
50
6
        const struct nexthop_group_cmd *nhgc2);
51
6
RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
52
6
      nexthop_group_cmd_compare)
53
6
54
6
static struct nhgc_entry_head nhgc_entries;
55
6
56
6
static inline int
57
6
nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
58
6
        const struct nexthop_group_cmd *nhgc2)
59
6
{
60
0
  return strcmp(nhgc1->name, nhgc2->name);
61
0
}
62
63
static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
64
0
{
65
0
  struct nexthop *nexthop = nhg->nexthop;
66
67
0
  while (nexthop && nexthop->next)
68
0
    nexthop = nexthop->next;
69
70
0
  return nexthop;
71
0
}
72
73
uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
74
0
{
75
0
  struct nexthop *nhop;
76
0
  uint8_t num = 0;
77
78
0
  for (ALL_NEXTHOPS_PTR(nhg, nhop))
79
0
    num++;
80
81
0
  return num;
82
0
}
83
84
uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
85
0
{
86
0
  struct nexthop *nhop;
87
0
  uint8_t num = 0;
88
89
0
  for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
90
0
    num++;
91
92
0
  return num;
93
0
}
94
95
uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
96
0
{
97
0
  struct nexthop *nhop;
98
0
  uint8_t num = 0;
99
100
0
  for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
101
0
    if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
102
0
      num++;
103
0
  }
104
105
0
  return num;
106
0
}
107
108
uint8_t
109
nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
110
0
{
111
0
  struct nexthop *nhop;
112
0
  uint8_t num = 0;
113
114
0
  for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
115
0
    if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
116
0
      num++;
117
0
  }
118
119
0
  return num;
120
0
}
121
122
bool nexthop_group_has_label(const struct nexthop_group *nhg)
123
0
{
124
0
  struct nexthop *nhop;
125
126
0
  for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
127
0
    if (nhop->nh_label)
128
0
      return true;
129
0
  }
130
131
0
  return false;
132
0
}
133
134
struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
135
             const struct nexthop *nh)
136
0
{
137
0
  struct nexthop *nexthop;
138
139
0
  for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
140
0
    if (nexthop_same(nh, nexthop))
141
0
      return nexthop;
142
0
  }
143
144
0
  return NULL;
145
0
}
146
147
/*
148
 * Helper that locates a nexthop in an nhg config list. Note that
149
 * this uses a specific matching / equality rule that's different from
150
 * the complete match performed by 'nexthop_same()'.
151
 */
152
static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
153
           const struct nexthop *nh)
154
0
{
155
0
  struct nexthop *nexthop;
156
0
  int ret;
157
158
  /* We compare: vrf, gateway, and interface */
159
160
0
  for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
161
162
    /* Compare vrf and type */
163
0
    if (nexthop->vrf_id != nh->vrf_id)
164
0
      continue;
165
0
    if (nexthop->type != nh->type)
166
0
      continue;
167
168
    /* Compare gateway */
169
0
    switch (nexthop->type) {
170
0
    case NEXTHOP_TYPE_IPV4:
171
0
    case NEXTHOP_TYPE_IPV6:
172
0
      ret = nexthop_g_addr_cmp(nexthop->type,
173
0
             &nexthop->gate, &nh->gate);
174
0
      if (ret != 0)
175
0
        continue;
176
0
      break;
177
0
    case NEXTHOP_TYPE_IPV4_IFINDEX:
178
0
    case NEXTHOP_TYPE_IPV6_IFINDEX:
179
0
      ret = nexthop_g_addr_cmp(nexthop->type,
180
0
             &nexthop->gate, &nh->gate);
181
0
      if (ret != 0)
182
0
        continue;
183
      /* Intentional Fall-Through */
184
0
    case NEXTHOP_TYPE_IFINDEX:
185
0
      if (nexthop->ifindex != nh->ifindex)
186
0
        continue;
187
0
      break;
188
0
    case NEXTHOP_TYPE_BLACKHOLE:
189
0
      if (nexthop->bh_type != nh->bh_type)
190
0
        continue;
191
0
      break;
192
0
    }
193
194
0
    return nexthop;
195
0
  }
196
197
0
  return NULL;
198
0
}
199
200
static bool
201
nexthop_group_equal_common(const struct nexthop_group *nhg1,
202
         const struct nexthop_group *nhg2,
203
         uint8_t (*nexthop_group_nexthop_num_func)(
204
           const struct nexthop_group *nhg))
205
0
{
206
0
  if (nhg1 && !nhg2)
207
0
    return false;
208
209
0
  if (!nhg1 && nhg2)
210
0
    return false;
211
212
0
  if (nhg1 == nhg2)
213
0
    return true;
214
215
0
  if (nexthop_group_nexthop_num_func(nhg1)
216
0
      != nexthop_group_nexthop_num_func(nhg2))
217
0
    return false;
218
219
0
  return true;
220
0
}
221
222
/* This assumes ordered */
223
bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
224
            const struct nexthop_group *nhg2)
225
0
{
226
0
  struct nexthop *nh1 = NULL;
227
0
  struct nexthop *nh2 = NULL;
228
229
0
  if (!nexthop_group_equal_common(nhg1, nhg2,
230
0
          &nexthop_group_nexthop_num_no_recurse))
231
0
    return false;
232
233
0
  for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
234
0
       nh1 = nh1->next, nh2 = nh2->next) {
235
0
    if (nh1 && !nh2)
236
0
      return false;
237
0
    if (!nh1 && nh2)
238
0
      return false;
239
0
    if (!nexthop_same(nh1, nh2))
240
0
      return false;
241
0
  }
242
243
0
  return true;
244
0
}
245
246
/* This assumes ordered */
247
bool nexthop_group_equal(const struct nexthop_group *nhg1,
248
       const struct nexthop_group *nhg2)
249
0
{
250
0
  struct nexthop *nh1 = NULL;
251
0
  struct nexthop *nh2 = NULL;
252
253
0
  if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
254
0
    return false;
255
256
0
  for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
257
0
       nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
258
0
    if (nh1 && !nh2)
259
0
      return false;
260
0
    if (!nh1 && nh2)
261
0
      return false;
262
0
    if (!nexthop_same(nh1, nh2))
263
0
      return false;
264
0
  }
265
266
0
  return true;
267
0
}
268
struct nexthop_group *nexthop_group_new(void)
269
0
{
270
0
  return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
271
0
}
272
273
void nexthop_group_copy(struct nexthop_group *to,
274
      const struct nexthop_group *from)
275
0
{
276
0
  to->nhgr = from->nhgr;
277
  /* Copy everything, including recursive info */
278
0
  copy_nexthops(&to->nexthop, from->nexthop, NULL);
279
0
}
280
281
void nexthop_group_delete(struct nexthop_group **nhg)
282
0
{
283
  /* OK to call with NULL group */
284
0
  if ((*nhg) == NULL)
285
0
    return;
286
287
0
  if ((*nhg)->nexthop)
288
0
    nexthops_free((*nhg)->nexthop);
289
290
0
  XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
291
0
}
292
293
/* Add nexthop to the end of a nexthop list.  */
294
void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
295
0
{
296
0
  struct nexthop *last;
297
298
0
  for (last = *target; last && last->next; last = last->next)
299
0
    ;
300
0
  if (last)
301
0
    last->next = nexthop;
302
0
  else
303
0
    *target = nexthop;
304
0
  nexthop->prev = last;
305
0
}
306
307
/* Add nexthop to sorted list of nexthops */
308
static void _nexthop_add_sorted(struct nexthop **head,
309
        struct nexthop *nexthop)
310
0
{
311
0
  struct nexthop *position, *prev;
312
313
0
  assert(!nexthop->next);
314
315
0
  for (position = *head, prev = NULL; position;
316
0
       prev = position, position = position->next) {
317
0
    if (nexthop_cmp(position, nexthop) > 0) {
318
0
      nexthop->next = position;
319
0
      nexthop->prev = prev;
320
321
0
      if (nexthop->prev)
322
0
        nexthop->prev->next = nexthop;
323
0
      else
324
0
        *head = nexthop;
325
326
0
      position->prev = nexthop;
327
0
      return;
328
0
    }
329
0
  }
330
331
0
  nexthop->prev = prev;
332
0
  if (prev)
333
0
    prev->next = nexthop;
334
0
  else
335
0
    *head = nexthop;
336
0
}
337
338
void nexthop_group_add_sorted(struct nexthop_group *nhg,
339
            struct nexthop *nexthop)
340
0
{
341
0
  struct nexthop *tail;
342
343
0
  assert(!nexthop->next);
344
345
  /* Try to just append to the end first;
346
   * trust the list is already sorted
347
   */
348
0
  tail = nexthop_group_tail(nhg);
349
350
0
  if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
351
0
    tail->next = nexthop;
352
0
    nexthop->prev = tail;
353
354
0
    return;
355
0
  }
356
357
0
  _nexthop_add_sorted(&nhg->nexthop, nexthop);
358
0
}
359
360
/* Delete nexthop from a nexthop list.  */
361
void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
362
0
{
363
0
  struct nexthop *nexthop;
364
365
0
  for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
366
0
    if (nexthop_same(nh, nexthop))
367
0
      break;
368
0
  }
369
370
0
  assert(nexthop);
371
372
0
  if (nexthop->prev)
373
0
    nexthop->prev->next = nexthop->next;
374
0
  else
375
0
    nhg->nexthop = nexthop->next;
376
377
0
  if (nexthop->next)
378
0
    nexthop->next->prev = nexthop->prev;
379
380
0
  nh->prev = NULL;
381
0
  nh->next = NULL;
382
0
}
383
384
/* Unlink a nexthop from the list it's on, unconditionally */
385
static void nexthop_unlink(struct nexthop_group *nhg, struct nexthop *nexthop)
386
0
{
387
388
0
  if (nexthop->prev)
389
0
    nexthop->prev->next = nexthop->next;
390
0
  else {
391
0
    assert(nhg->nexthop == nexthop);
392
0
    assert(nexthop->prev == NULL);
393
0
    nhg->nexthop = nexthop->next;
394
0
  }
395
396
0
  if (nexthop->next)
397
0
    nexthop->next->prev = nexthop->prev;
398
399
0
  nexthop->prev = NULL;
400
0
  nexthop->next = NULL;
401
0
}
402
403
/*
404
 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
405
 */
406
void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
407
          const struct nexthop *nh)
408
0
{
409
0
  struct nexthop *nexthop, *tail;
410
0
  const struct nexthop *nh1;
411
412
  /* We'll try to append to the end of the new list;
413
   * if the original list in nh is already sorted, this eliminates
414
   * lots of comparison operations.
415
   */
416
0
  tail = nexthop_group_tail(nhg);
417
418
0
  for (nh1 = nh; nh1; nh1 = nh1->next) {
419
0
    nexthop = nexthop_dup(nh1, NULL);
420
421
0
    if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
422
0
      tail->next = nexthop;
423
0
      nexthop->prev = tail;
424
425
0
      tail = nexthop;
426
0
      continue;
427
0
    }
428
429
0
    _nexthop_add_sorted(&nhg->nexthop, nexthop);
430
431
0
    if (tail == NULL)
432
0
      tail = nexthop;
433
0
  }
434
0
}
435
436
/* Copy a list of nexthops, no effort made to sort or order them. */
437
void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
438
       struct nexthop *rparent)
439
0
{
440
0
  struct nexthop *nexthop;
441
0
  const struct nexthop *nh1;
442
443
0
  for (nh1 = nh; nh1; nh1 = nh1->next) {
444
0
    nexthop = nexthop_dup(nh1, rparent);
445
0
    _nexthop_add(tnh, nexthop);
446
0
  }
447
0
}
448
449
uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
450
0
{
451
0
  struct nexthop *nh;
452
0
  uint32_t key = 0;
453
454
  /*
455
   * We are not interested in hashing over any recursively
456
   * resolved nexthops
457
   */
458
0
  for (nh = nhg->nexthop; nh; nh = nh->next)
459
0
    key = jhash_1word(nexthop_hash(nh), key);
460
461
0
  return key;
462
0
}
463
464
uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
465
0
{
466
0
  struct nexthop *nh;
467
0
  uint32_t key = 0;
468
469
0
  for (ALL_NEXTHOPS_PTR(nhg, nh))
470
0
    key = jhash_1word(nexthop_hash(nh), key);
471
472
0
  return key;
473
0
}
474
475
void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
476
0
{
477
0
  struct nexthop *nexthop, *prev;
478
479
0
  for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
480
0
    UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
481
0
    for (ALL_NEXTHOPS_PTR(nhg, prev)) {
482
0
      if (prev == nexthop)
483
0
        break;
484
0
      if (nexthop_same(nexthop, prev)) {
485
0
        SET_FLAG(nexthop->flags,
486
0
           NEXTHOP_FLAG_DUPLICATE);
487
0
        break;
488
0
      }
489
0
    }
490
0
  }
491
0
}
492
493
static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
494
0
{
495
0
  struct nexthop *nexthop;
496
497
0
  nexthop = nhgc->nhg.nexthop;
498
0
  while (nexthop) {
499
0
    struct nexthop *next = nexthop_next(nexthop);
500
501
0
    _nexthop_del(&nhgc->nhg, nexthop);
502
0
    if (nhg_hooks.del_nexthop)
503
0
      nhg_hooks.del_nexthop(nhgc, nexthop);
504
505
0
    nexthop_free(nexthop);
506
507
0
    nexthop = next;
508
0
  }
509
0
}
510
511
struct nexthop_group_cmd *nhgc_find(const char *name)
512
0
{
513
0
  struct nexthop_group_cmd find;
514
515
0
  strlcpy(find.name, name, sizeof(find.name));
516
517
0
  return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
518
0
}
519
520
static int nhgc_cmp_helper(const char *a, const char *b)
521
0
{
522
0
  if (!a && !b)
523
0
    return 0;
524
525
0
  if (a && !b)
526
0
    return -1;
527
528
0
  if (!a && b)
529
0
    return 1;
530
531
0
  return strcmp(a, b);
532
0
}
533
534
static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
535
0
{
536
0
  if (!a && !b)
537
0
    return 0;
538
539
0
  if (a && !b)
540
0
    return -1;
541
542
0
  if (!a && b)
543
0
    return 1;
544
545
0
  return sockunion_cmp(a, b);
546
0
}
547
548
static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
549
0
{
550
0
  int ret;
551
552
0
  ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
553
0
  if (ret)
554
0
    return ret;
555
556
0
  ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
557
0
  if (ret)
558
0
    return ret;
559
560
0
  ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
561
0
  if (ret)
562
0
    return ret;
563
564
0
  ret = ((int)nh2->onlink) - ((int)nh1->onlink);
565
0
  if (ret)
566
0
    return ret;
567
568
0
  return nhgc_cmp_helper(nh1->labels, nh2->labels);
569
0
}
570
571
static void nhgl_delete(struct nexthop_hold *nh)
572
0
{
573
0
  XFREE(MTYPE_TMP, nh->intf);
574
575
0
  XFREE(MTYPE_TMP, nh->nhvrf_name);
576
577
0
  if (nh->addr)
578
0
    sockunion_free(nh->addr);
579
580
0
  XFREE(MTYPE_TMP, nh->labels);
581
582
0
  XFREE(MTYPE_TMP, nh);
583
0
}
584
585
static struct nexthop_group_cmd *nhgc_get(const char *name)
586
0
{
587
0
  struct nexthop_group_cmd *nhgc;
588
589
0
  nhgc = nhgc_find(name);
590
0
  if (!nhgc) {
591
0
    nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
592
0
    strlcpy(nhgc->name, name, sizeof(nhgc->name));
593
594
0
    QOBJ_REG(nhgc, nexthop_group_cmd);
595
0
    RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
596
597
0
    nhgc->nhg_list = list_new();
598
0
    nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
599
0
    nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
600
601
0
    if (nhg_hooks.new)
602
0
      nhg_hooks.new(name);
603
0
  }
604
605
0
  return nhgc;
606
0
}
607
608
static void nhgc_delete(struct nexthop_group_cmd *nhgc)
609
0
{
610
0
  nhgc_delete_nexthops(nhgc);
611
612
0
  if (nhg_hooks.delete)
613
0
    nhg_hooks.delete(nhgc->name);
614
615
0
  RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
616
617
0
  list_delete(&nhgc->nhg_list);
618
619
0
  QOBJ_UNREG(nhgc);
620
0
  XFREE(MTYPE_TMP, nhgc);
621
0
}
622
623
DEFINE_QOBJ_TYPE(nexthop_group_cmd);
624
625
DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
626
     "Enter into the nexthop-group submode\n"
627
     "Specify the NAME of the nexthop-group\n")
628
0
{
629
0
  const char *nhg_name = argv[1]->arg;
630
0
  struct nexthop_group_cmd *nhgc = NULL;
631
632
0
  nhgc = nhgc_get(nhg_name);
633
0
  VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
634
635
0
  return CMD_SUCCESS;
636
0
}
637
638
DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
639
     NO_STR
640
     "Delete the nexthop-group\n"
641
     "Specify the NAME of the nexthop-group\n")
642
0
{
643
0
  const char *nhg_name = argv[2]->arg;
644
0
  struct nexthop_group_cmd *nhgc = NULL;
645
646
0
  nhgc = nhgc_find(nhg_name);
647
0
  if (nhgc)
648
0
    nhgc_delete(nhgc);
649
650
0
  return CMD_SUCCESS;
651
0
}
652
653
DEFPY(nexthop_group_backup, nexthop_group_backup_cmd,
654
      "backup-group WORD$name",
655
      "Specify a group name containing backup nexthops\n"
656
      "The name of the backup group\n")
657
0
{
658
0
  VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
659
660
0
  strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name));
661
662
0
  return CMD_SUCCESS;
663
0
}
664
665
DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd,
666
      "no backup-group [WORD$name]",
667
      NO_STR
668
      "Clear group name containing backup nexthops\n"
669
      "The name of the backup group\n")
670
0
{
671
0
  VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
672
673
0
  nhgc->backup_list_name[0] = 0;
674
675
0
  return CMD_SUCCESS;
676
0
}
677
678
DEFPY(nexthop_group_resilience,
679
      nexthop_group_resilience_cmd,
680
      "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
681
      "A resilient Nexthop Group\n"
682
      "Buckets in the Hash for this Group\n"
683
      "Number of buckets\n"
684
      "The Idle timer for this Resilient Nexthop Group in seconds\n"
685
      "Number of seconds of Idle time\n"
686
      "The length of time that the Nexthop Group can be unbalanced\n"
687
      "Number of seconds of Unbalanced time\n")
688
0
{
689
0
  VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
690
691
0
  nhgc->nhg.nhgr.buckets = buckets;
692
0
  nhgc->nhg.nhgr.idle_timer = idle_timer;
693
0
  nhgc->nhg.nhgr.unbalanced_timer = unbalanced_timer;
694
695
0
  if (nhg_hooks.modify)
696
0
    nhg_hooks.modify(nhgc);
697
698
0
  return CMD_SUCCESS;
699
0
}
700
701
DEFPY(no_nexthop_group_resilience,
702
      no_nexthop_group_resilience_cmd,
703
      "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
704
      NO_STR
705
      "A resilient Nexthop Group\n"
706
      "Buckets in the Hash for this Group\n"
707
      "Number of buckets\n"
708
      "The Idle timer for this Resilient Nexthop Group in seconds\n"
709
      "Number of seconds of Idle time\n"
710
      "The length of time that the Nexthop Group can be unbalanced\n"
711
      "Number of seconds of Unbalanced time\n")
712
0
{
713
0
  VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
714
715
0
  nhgc->nhg.nhgr.buckets = 0;
716
0
  nhgc->nhg.nhgr.idle_timer = 0;
717
0
  nhgc->nhg.nhgr.unbalanced_timer = 0;
718
719
0
  return CMD_SUCCESS;
720
0
}
721
722
static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
723
            const char *nhvrf_name,
724
            const union sockunion *addr,
725
            const char *intf, bool onlink,
726
            const char *labels, const uint32_t weight,
727
            const char *backup_str)
728
0
{
729
0
  struct nexthop_hold *nh;
730
731
0
  nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
732
733
0
  if (nhvrf_name)
734
0
    nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
735
0
  if (intf)
736
0
    nh->intf = XSTRDUP(MTYPE_TMP, intf);
737
0
  if (addr)
738
0
    nh->addr = sockunion_dup(addr);
739
0
  if (labels)
740
0
    nh->labels = XSTRDUP(MTYPE_TMP, labels);
741
742
0
  nh->onlink = onlink;
743
744
0
  nh->weight = weight;
745
746
0
  if (backup_str)
747
0
    nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
748
749
0
  listnode_add_sort(nhgc->nhg_list, nh);
750
0
}
751
752
/*
753
 * Remove config info about a nexthop from group 'nhgc'. Note that we
754
 * use only a subset of the available attributes here to determine
755
 * a 'match'.
756
 * Note that this doesn't change the list of nexthops, only the config
757
 * information.
758
 */
759
static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
760
              const char *nhvrf_name,
761
              const union sockunion *addr,
762
              const char *intf)
763
0
{
764
0
  struct nexthop_hold *nh;
765
0
  struct listnode *node;
766
767
0
  for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
768
0
    if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
769
0
        && nhgc_addr_cmp_helper(addr, nh->addr) == 0
770
0
        && nhgc_cmp_helper(intf, nh->intf) == 0)
771
0
      break;
772
0
  }
773
774
  /*
775
   * Something has gone seriously wrong, fail gracefully
776
   */
777
0
  if (!nh)
778
0
    return;
779
780
0
  list_delete_node(nhgc->nhg_list, node);
781
0
  nhgl_delete(nh);
782
0
}
783
784
/*
785
 * Parse the config strings we support for a single nexthop. This gets used
786
 * in a couple of different ways, and we distinguish between transient
787
 * failures - such as a still-unprocessed interface - and fatal errors
788
 * from label-string parsing.
789
 */
790
static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
791
          const union sockunion *addr,
792
          const char *intf, bool onlink,
793
          const char *name, const char *labels,
794
          vni_t vni, int *lbl_ret,
795
          uint32_t weight, const char *backup_str)
796
0
{
797
0
  int ret = 0;
798
0
  struct vrf *vrf;
799
0
  int num;
800
0
  uint8_t labelnum = 0;
801
802
0
  memset(nhop, 0, sizeof(*nhop));
803
804
0
  if (name)
805
0
    vrf = vrf_lookup_by_name(name);
806
0
  else
807
0
    vrf = vrf_lookup_by_id(VRF_DEFAULT);
808
809
0
  if (!vrf)
810
0
    return false;
811
812
0
  nhop->vrf_id = vrf->vrf_id;
813
814
0
  if (intf) {
815
0
    nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
816
0
    if (nhop->ifindex == IFINDEX_INTERNAL)
817
0
      return false;
818
0
  }
819
820
0
  if (onlink)
821
0
    SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
822
823
0
  if (addr) {
824
0
    if (addr->sa.sa_family == AF_INET) {
825
0
      nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
826
0
      if (intf)
827
0
        nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
828
0
      else
829
0
        nhop->type = NEXTHOP_TYPE_IPV4;
830
0
    } else {
831
0
      nhop->gate.ipv6 = addr->sin6.sin6_addr;
832
0
      if (intf)
833
0
        nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
834
0
      else
835
0
        nhop->type = NEXTHOP_TYPE_IPV6;
836
0
    }
837
0
  } else
838
0
    nhop->type = NEXTHOP_TYPE_IFINDEX;
839
840
0
  if (labels) {
841
0
    mpls_label_t larray[MPLS_MAX_LABELS];
842
843
0
    ret = mpls_str2label(labels, &labelnum, larray);
844
845
    /* Return label parse result */
846
0
    if (lbl_ret)
847
0
      *lbl_ret = ret;
848
849
0
    if (ret < 0)
850
0
      return false;
851
0
    else if (labelnum > 0)
852
0
      nexthop_add_labels(nhop, ZEBRA_LSP_NONE, labelnum,
853
0
             larray);
854
0
  } else if (vni) {
855
0
    mpls_label_t label = MPLS_INVALID_LABEL;
856
857
0
    vni2label(vni, &label);
858
0
    nexthop_add_labels(nhop, ZEBRA_LSP_EVPN, 1, &label);
859
0
  }
860
861
0
  nhop->weight = weight;
862
863
0
  if (backup_str) {
864
    /* Parse backup indexes */
865
0
    ret = nexthop_str2backups(backup_str,
866
0
            &num, nhop->backup_idx);
867
0
    if (ret == 0) {
868
0
      SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
869
0
      nhop->backup_num = num;
870
0
    } else
871
0
      return false;
872
0
  }
873
874
0
  return true;
875
0
}
876
877
/*
878
 * Wrapper to parse the strings in a 'nexthop_hold'
879
 */
880
static bool nexthop_group_parse_nhh(struct nexthop *nhop,
881
            const struct nexthop_hold *nhh)
882
0
{
883
0
  return (nexthop_group_parse_nexthop(
884
0
    nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
885
0
    nhh->labels, nhh->vni, NULL, nhh->weight, nhh->backup_str));
886
0
}
887
888
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
889
      "[no] nexthop\
890
        <\
891
    <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
892
    |INTERFACE$intf\
893
  >\
894
  [{ \
895
     nexthop-vrf NAME$vrf_name \
896
     |label WORD \
897
     |vni (1-16777215) \
898
           |weight (1-255) \
899
           |backup-idx WORD \
900
  }]",
901
      NO_STR
902
      "Specify one of the nexthops in this ECMP group\n"
903
      "v4 Address\n"
904
      "v6 Address\n"
905
      "Interface to use\n"
906
      "Treat nexthop as directly attached to the interface\n"
907
      "Interface to use\n"
908
      "If the nexthop is in a different vrf tell us\n"
909
      "The nexthop-vrf Name\n"
910
      "Specify label(s) for this nexthop\n"
911
      "One or more labels in the range (16-1048575) separated by '/'\n"
912
      "Specify VNI(s) for this nexthop\n"
913
      "VNI in the range (1-16777215)\n"
914
      "Weight to be used by the nexthop for purposes of ECMP\n"
915
      "Weight value to be used\n"
916
      "Specify backup nexthop indexes in another group\n"
917
      "One or more indexes in the range (0-254) separated by ','\n")
918
0
{
919
0
  VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
920
0
  struct nexthop nhop;
921
0
  struct nexthop *nh;
922
0
  int lbl_ret = 0;
923
0
  bool legal;
924
0
  int num;
925
0
  uint8_t backups[NEXTHOP_MAX_BACKUPS];
926
0
  bool yes = !no;
927
928
  /* Pre-parse backup string to validate */
929
0
  if (backup_idx) {
930
0
    lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
931
0
    if (lbl_ret < 0) {
932
0
      vty_out(vty, "%% Invalid backups\n");
933
0
      return CMD_WARNING_CONFIG_FAILED;
934
0
    }
935
0
  }
936
937
0
  legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
938
0
              vrf_name, label, vni, &lbl_ret,
939
0
              weight, backup_idx);
940
941
0
  if (nhop.type == NEXTHOP_TYPE_IPV6
942
0
      && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
943
0
    vty_out(vty,
944
0
      "Specified a v6 LL with no interface, rejecting\n");
945
0
    return CMD_WARNING_CONFIG_FAILED;
946
0
  }
947
948
  /* Handle label-string errors */
949
0
  if (!legal && lbl_ret < 0) {
950
0
    switch (lbl_ret) {
951
0
    case -1:
952
0
      vty_out(vty, "%% Malformed label(s)\n");
953
0
      break;
954
0
    case -2:
955
0
      vty_out(vty,
956
0
        "%% Cannot use reserved label(s) (%d-%d)\n",
957
0
        MPLS_LABEL_RESERVED_MIN,
958
0
        MPLS_LABEL_RESERVED_MAX);
959
0
      break;
960
0
    case -3:
961
0
      vty_out(vty,
962
0
        "%% Too many labels. Enter %d or fewer\n",
963
0
        MPLS_MAX_LABELS);
964
0
      break;
965
0
    }
966
0
    return CMD_WARNING_CONFIG_FAILED;
967
0
  }
968
969
  /* Look for an existing nexthop in the config. Note that the test
970
   * here tests only some attributes - it's not a complete comparison.
971
   * Note that we've got two kinds of objects to manage: 'nexthop_hold'
972
   * that represent config that may or may not be valid (yet), and
973
   * actual nexthops that have been validated and parsed.
974
   */
975
0
  nh = nhg_nh_find(&nhgc->nhg, &nhop);
976
977
  /* Always attempt to remove old config info. */
978
0
  nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
979
980
  /* Remove any existing nexthop, for delete and replace cases. */
981
0
  if (nh) {
982
0
    nexthop_unlink(&nhgc->nhg, nh);
983
984
0
    if (nhg_hooks.del_nexthop)
985
0
      nhg_hooks.del_nexthop(nhgc, nh);
986
987
0
    nexthop_free(nh);
988
0
  }
989
0
  if (yes) {
990
    /* Add/replace case: capture nexthop if valid, and capture
991
     * config info always.
992
     */
993
0
    if (legal) {
994
0
      nh = nexthop_new();
995
996
0
      memcpy(nh, &nhop, sizeof(nhop));
997
0
      _nexthop_add(&nhgc->nhg.nexthop, nh);
998
0
    }
999
1000
    /* Save config always */
1001
0
    nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
1002
0
          label, weight, backup_idx);
1003
1004
0
    if (legal && nhg_hooks.add_nexthop)
1005
0
      nhg_hooks.add_nexthop(nhgc, nh);
1006
0
  }
1007
1008
0
  return CMD_SUCCESS;
1009
0
}
1010
1011
static int nexthop_group_write(struct vty *vty);
1012
static struct cmd_node nexthop_group_node = {
1013
  .name = "nexthop-group",
1014
  .node = NH_GROUP_NODE,
1015
  .parent_node = CONFIG_NODE,
1016
  .prompt = "%s(config-nh-group)# ",
1017
  .config_write = nexthop_group_write,
1018
};
1019
1020
void nexthop_group_write_nexthop_simple(struct vty *vty,
1021
          const struct nexthop *nh,
1022
          char *altifname)
1023
0
{
1024
0
  char *ifname;
1025
1026
0
  vty_out(vty, "nexthop ");
1027
1028
0
  if (altifname)
1029
0
    ifname = altifname;
1030
0
  else
1031
0
    ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
1032
1033
0
  switch (nh->type) {
1034
0
  case NEXTHOP_TYPE_IFINDEX:
1035
0
    vty_out(vty, "%s", ifname);
1036
0
    break;
1037
0
  case NEXTHOP_TYPE_IPV4:
1038
0
    vty_out(vty, "%pI4", &nh->gate.ipv4);
1039
0
    break;
1040
0
  case NEXTHOP_TYPE_IPV4_IFINDEX:
1041
0
    vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
1042
0
    break;
1043
0
  case NEXTHOP_TYPE_IPV6:
1044
0
    vty_out(vty, "%pI6", &nh->gate.ipv6);
1045
0
    break;
1046
0
  case NEXTHOP_TYPE_IPV6_IFINDEX:
1047
0
    vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
1048
0
    break;
1049
0
  case NEXTHOP_TYPE_BLACKHOLE:
1050
0
    break;
1051
0
  }
1052
0
}
1053
1054
void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1055
0
{
1056
0
  struct vrf *vrf;
1057
0
  int i;
1058
1059
0
  nexthop_group_write_nexthop_simple(vty, nh, NULL);
1060
1061
0
  if (nh->vrf_id != VRF_DEFAULT) {
1062
0
    vrf = vrf_lookup_by_id(nh->vrf_id);
1063
0
    vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1064
0
  }
1065
1066
0
  if (nh->nh_label && nh->nh_label->num_labels > 0) {
1067
0
    char buf[200];
1068
1069
0
    mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1070
0
             buf, sizeof(buf), nh->nh_label_type, 0);
1071
0
    vty_out(vty, " label %s", buf);
1072
0
  }
1073
1074
0
  if (nh->weight)
1075
0
    vty_out(vty, " weight %u", nh->weight);
1076
1077
0
  if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1078
0
    vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
1079
1080
0
    for (i = 1; i < nh->backup_num; i++)
1081
0
      vty_out(vty, ",%d", nh->backup_idx[i]);
1082
0
  }
1083
1084
0
  vty_out(vty, "\n");
1085
0
}
1086
1087
void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
1088
0
{
1089
0
  struct vrf *vrf;
1090
0
  json_object *json_backups = NULL;
1091
0
  int i;
1092
1093
0
  switch (nh->type) {
1094
0
  case NEXTHOP_TYPE_IFINDEX:
1095
0
    json_object_string_add(j, "nexthop",
1096
0
               ifindex2ifname(nh->ifindex, nh->vrf_id));
1097
0
    break;
1098
0
  case NEXTHOP_TYPE_IPV4:
1099
0
    json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1100
0
    break;
1101
0
  case NEXTHOP_TYPE_IPV4_IFINDEX:
1102
0
    json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1103
0
    json_object_string_add(j, "vrfId",
1104
0
               ifindex2ifname(nh->ifindex, nh->vrf_id));
1105
0
    break;
1106
0
  case NEXTHOP_TYPE_IPV6:
1107
0
    json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1108
0
    break;
1109
0
  case NEXTHOP_TYPE_IPV6_IFINDEX:
1110
0
    json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1111
0
    json_object_string_add(j, "vrfId",
1112
0
               ifindex2ifname(nh->ifindex, nh->vrf_id));
1113
0
    break;
1114
0
  case NEXTHOP_TYPE_BLACKHOLE:
1115
0
    break;
1116
0
  }
1117
1118
0
  if (nh->vrf_id != VRF_DEFAULT) {
1119
0
    vrf = vrf_lookup_by_id(nh->vrf_id);
1120
0
    json_object_string_add(j, "targetVrf", vrf->name);
1121
0
  }
1122
1123
0
  if (nh->nh_label && nh->nh_label->num_labels > 0) {
1124
0
    char buf[200];
1125
1126
0
    mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1127
0
             buf, sizeof(buf), nh->nh_label_type, 0);
1128
0
    json_object_string_add(j, "label", buf);
1129
0
  }
1130
1131
0
  if (nh->weight)
1132
0
    json_object_int_add(j, "weight", nh->weight);
1133
1134
0
  if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1135
0
    json_backups = json_object_new_array();
1136
0
    for (i = 0; i < nh->backup_num; i++)
1137
0
      json_object_array_add(
1138
0
        json_backups,
1139
0
        json_object_new_int(nh->backup_idx[i]));
1140
1141
0
    json_object_object_add(j, "backupIdx", json_backups);
1142
0
  }
1143
0
}
1144
1145
static void nexthop_group_write_nexthop_internal(struct vty *vty,
1146
             const struct nexthop_hold *nh)
1147
0
{
1148
0
  vty_out(vty, "nexthop");
1149
1150
0
  if (nh->addr)
1151
0
    vty_out(vty, " %pSU", nh->addr);
1152
1153
0
  if (nh->intf)
1154
0
    vty_out(vty, " %s", nh->intf);
1155
1156
0
  if (nh->onlink)
1157
0
    vty_out(vty, " onlink");
1158
1159
0
  if (nh->nhvrf_name)
1160
0
    vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1161
1162
0
  if (nh->labels)
1163
0
    vty_out(vty, " label %s", nh->labels);
1164
1165
0
  if (nh->vni)
1166
0
    vty_out(vty, " vni %u", nh->vni);
1167
1168
0
  if (nh->weight)
1169
0
    vty_out(vty, " weight %u", nh->weight);
1170
1171
0
  if (nh->backup_str)
1172
0
    vty_out(vty, " backup-idx %s", nh->backup_str);
1173
1174
0
  vty_out(vty, "\n");
1175
0
}
1176
1177
static int nexthop_group_write(struct vty *vty)
1178
0
{
1179
0
  struct nexthop_group_cmd *nhgc;
1180
0
  struct nexthop_hold *nh;
1181
1182
0
  RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1183
0
    struct listnode *node;
1184
1185
0
    vty_out(vty, "nexthop-group %s\n", nhgc->name);
1186
1187
0
    if (nhgc->nhg.nhgr.buckets)
1188
0
      vty_out(vty,
1189
0
        " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1190
0
        nhgc->nhg.nhgr.buckets,
1191
0
        nhgc->nhg.nhgr.idle_timer,
1192
0
        nhgc->nhg.nhgr.unbalanced_timer);
1193
1194
0
    if (nhgc->backup_list_name[0])
1195
0
      vty_out(vty, " backup-group %s\n",
1196
0
        nhgc->backup_list_name);
1197
1198
0
    for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
1199
0
      vty_out(vty, " ");
1200
0
      nexthop_group_write_nexthop_internal(vty, nh);
1201
0
    }
1202
1203
0
    vty_out(vty, "exit\n");
1204
0
    vty_out(vty, "!\n");
1205
0
  }
1206
1207
0
  return 1;
1208
0
}
1209
1210
void nexthop_group_enable_vrf(struct vrf *vrf)
1211
1
{
1212
1
  struct nexthop_group_cmd *nhgc;
1213
1
  struct nexthop_hold *nhh;
1214
1215
1
  RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1216
0
    struct listnode *node;
1217
1218
0
    for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1219
0
      struct nexthop nhop;
1220
0
      struct nexthop *nh;
1221
1222
0
      if (!nexthop_group_parse_nhh(&nhop, nhh))
1223
0
        continue;
1224
1225
0
      nh = nexthop_exists(&nhgc->nhg, &nhop);
1226
1227
0
      if (nh)
1228
0
        continue;
1229
1230
0
      if (nhop.vrf_id != vrf->vrf_id)
1231
0
        continue;
1232
1233
0
      nh = nexthop_new();
1234
1235
0
      memcpy(nh, &nhop, sizeof(nhop));
1236
0
      _nexthop_add(&nhgc->nhg.nexthop, nh);
1237
1238
0
      if (nhg_hooks.add_nexthop)
1239
0
        nhg_hooks.add_nexthop(nhgc, nh);
1240
0
    }
1241
0
  }
1242
1
}
1243
1244
void nexthop_group_disable_vrf(struct vrf *vrf)
1245
0
{
1246
0
  struct nexthop_group_cmd *nhgc;
1247
0
  struct nexthop_hold *nhh;
1248
1249
0
  RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1250
0
    struct listnode *node;
1251
1252
0
    for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1253
0
      struct nexthop nhop;
1254
0
      struct nexthop *nh;
1255
1256
0
      if (!nexthop_group_parse_nhh(&nhop, nhh))
1257
0
        continue;
1258
1259
0
      nh = nexthop_exists(&nhgc->nhg, &nhop);
1260
1261
0
      if (!nh)
1262
0
        continue;
1263
1264
0
      if (nh->vrf_id != vrf->vrf_id)
1265
0
        continue;
1266
1267
0
      _nexthop_del(&nhgc->nhg, nh);
1268
1269
0
      if (nhg_hooks.del_nexthop)
1270
0
        nhg_hooks.del_nexthop(nhgc, nh);
1271
1272
0
      nexthop_free(nh);
1273
0
    }
1274
0
  }
1275
0
}
1276
1277
void nexthop_group_interface_state_change(struct interface *ifp,
1278
            ifindex_t oldifindex)
1279
0
{
1280
0
  struct nexthop_group_cmd *nhgc;
1281
0
  struct nexthop_hold *nhh;
1282
1283
0
  RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1284
0
    struct listnode *node;
1285
0
    struct nexthop *nh;
1286
1287
0
    if (if_is_up(ifp)) {
1288
0
      for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1289
0
        struct nexthop nhop;
1290
1291
0
        if (!nexthop_group_parse_nhh(&nhop, nhh))
1292
0
          continue;
1293
1294
0
        switch (nhop.type) {
1295
0
        case NEXTHOP_TYPE_IPV4:
1296
0
        case NEXTHOP_TYPE_IPV6:
1297
0
        case NEXTHOP_TYPE_BLACKHOLE:
1298
0
          continue;
1299
0
        case NEXTHOP_TYPE_IFINDEX:
1300
0
        case NEXTHOP_TYPE_IPV4_IFINDEX:
1301
0
        case NEXTHOP_TYPE_IPV6_IFINDEX:
1302
0
          break;
1303
0
        }
1304
0
        nh = nexthop_exists(&nhgc->nhg, &nhop);
1305
1306
0
        if (nh)
1307
0
          continue;
1308
1309
0
        if (ifp->ifindex != nhop.ifindex)
1310
0
          continue;
1311
1312
0
        nh = nexthop_new();
1313
1314
0
        memcpy(nh, &nhop, sizeof(nhop));
1315
0
        _nexthop_add(&nhgc->nhg.nexthop, nh);
1316
1317
0
        if (nhg_hooks.add_nexthop)
1318
0
          nhg_hooks.add_nexthop(nhgc, nh);
1319
0
      }
1320
0
    } else {
1321
0
      struct nexthop *next_nh;
1322
1323
0
      for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1324
0
        next_nh = nh->next;
1325
0
        switch (nh->type) {
1326
0
        case NEXTHOP_TYPE_IPV4:
1327
0
        case NEXTHOP_TYPE_IPV6:
1328
0
        case NEXTHOP_TYPE_BLACKHOLE:
1329
0
          continue;
1330
0
        case NEXTHOP_TYPE_IFINDEX:
1331
0
        case NEXTHOP_TYPE_IPV4_IFINDEX:
1332
0
        case NEXTHOP_TYPE_IPV6_IFINDEX:
1333
0
          break;
1334
0
        }
1335
1336
0
        if (oldifindex != nh->ifindex)
1337
0
          continue;
1338
1339
0
        _nexthop_del(&nhgc->nhg, nh);
1340
1341
0
        if (nhg_hooks.del_nexthop)
1342
0
          nhg_hooks.del_nexthop(nhgc, nh);
1343
1344
0
        nexthop_free(nh);
1345
0
      }
1346
0
    }
1347
0
  }
1348
0
}
1349
1350
static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1351
0
{
1352
0
  struct nexthop_group_cmd *nhgc;
1353
1354
0
  RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1355
0
    vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1356
0
  }
1357
0
}
1358
1359
static const struct cmd_variable_handler nhg_name_handlers[] = {
1360
  {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1361
  {.completions = NULL}};
1362
1363
void nexthop_group_init(void (*new)(const char *name),
1364
      void (*modify)(const struct nexthop_group_cmd *nhgc),
1365
      void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1366
              const struct nexthop *nhop),
1367
      void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1368
              const struct nexthop *nhop),
1369
      void (*delete)(const char *name))
1370
0
{
1371
0
  RB_INIT(nhgc_entry_head, &nhgc_entries);
1372
1373
0
  cmd_variable_handler_register(nhg_name_handlers);
1374
1375
0
  install_node(&nexthop_group_node);
1376
0
  install_element(CONFIG_NODE, &nexthop_group_cmd);
1377
0
  install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1378
1379
0
  install_default(NH_GROUP_NODE);
1380
0
  install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1381
0
  install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
1382
0
  install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1383
1384
0
  install_element(NH_GROUP_NODE, &nexthop_group_resilience_cmd);
1385
0
  install_element(NH_GROUP_NODE, &no_nexthop_group_resilience_cmd);
1386
1387
0
  memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1388
1389
0
  if (new)
1390
0
    nhg_hooks.new = new;
1391
0
  if (modify)
1392
0
    nhg_hooks.modify = modify;
1393
0
  if (add_nexthop)
1394
0
    nhg_hooks.add_nexthop = add_nexthop;
1395
0
  if (del_nexthop)
1396
0
    nhg_hooks.del_nexthop = del_nexthop;
1397
0
  if (delete)
1398
0
    nhg_hooks.delete = delete;
1399
0
}