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