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