]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop_group.c
Merge pull request #9846 from idryzhov/lib-zebra-netns
[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
bf8d3d6a 35DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
31919191 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;
54c6fa8e 44 bool onlink;
db2fede9 45 char *labels;
597371a6 46 uint32_t weight;
28593bcc 47 char *backup_str;
db2fede9
MS
48};
49
31919191
DS
50struct nexthop_group_hooks {
51 void (*new)(const char *name);
52 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
53 const struct nexthop *nhop);
54 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
55 const struct nexthop *nhop);
56 void (*delete)(const char *name);
57};
58
59static struct nexthop_group_hooks nhg_hooks;
60
61static inline int
62nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
63 const struct nexthop_group_cmd *nhgc2);
64RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
65 nexthop_group_cmd_compare)
66
c17faa4b 67static struct nhgc_entry_head nhgc_entries;
31919191
DS
68
69static inline int
70nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
71 const struct nexthop_group_cmd *nhgc2)
72{
73 return strcmp(nhgc1->name, nhgc2->name);
74}
75
8c15fa95
SW
76static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
77{
78 struct nexthop *nexthop = nhg->nexthop;
79
80 while (nexthop && nexthop->next)
81 nexthop = nexthop->next;
82
83 return nexthop;
84}
85
454192f4
DS
86uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
87{
88 struct nexthop *nhop;
89 uint8_t num = 0;
90
91 for (ALL_NEXTHOPS_PTR(nhg, nhop))
92 num++;
93
94 return num;
95}
96
98cda54a
SW
97uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
98{
99 struct nexthop *nhop;
100 uint8_t num = 0;
101
102 for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
103 num++;
104
105 return num;
106}
107
454192f4
DS
108uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
109{
110 struct nexthop *nhop;
111 uint8_t num = 0;
112
113 for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
114 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
115 num++;
116 }
117
118 return num;
119}
120
98cda54a
SW
121uint8_t
122nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
123{
124 struct nexthop *nhop;
125 uint8_t num = 0;
126
127 for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
128 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
129 num++;
130 }
131
132 return num;
133}
134
8f8d9845
SW
135struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
136 const struct nexthop *nh)
31919191
DS
137{
138 struct nexthop *nexthop;
139
140 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
141 if (nexthop_same(nh, nexthop))
142 return nexthop;
143 }
144
145 return NULL;
146}
147
4fda105e
MS
148/*
149 * Helper that locates a nexthop in an nhg config list. Note that
150 * this uses a specific matching / equality rule that's different from
151 * the complete match performed by 'nexthop_same()'.
152 */
153static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
154 const struct nexthop *nh)
155{
156 struct nexthop *nexthop;
157 int ret;
158
159 /* We compare: vrf, gateway, and interface */
160
161 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
162
163 /* Compare vrf and type */
164 if (nexthop->vrf_id != nh->vrf_id)
165 continue;
166 if (nexthop->type != nh->type)
167 continue;
168
169 /* Compare gateway */
170 switch (nexthop->type) {
171 case NEXTHOP_TYPE_IPV4:
172 case NEXTHOP_TYPE_IPV6:
173 ret = nexthop_g_addr_cmp(nexthop->type,
174 &nexthop->gate, &nh->gate);
175 if (ret != 0)
176 continue;
177 break;
178 case NEXTHOP_TYPE_IPV4_IFINDEX:
179 case NEXTHOP_TYPE_IPV6_IFINDEX:
180 ret = nexthop_g_addr_cmp(nexthop->type,
181 &nexthop->gate, &nh->gate);
182 if (ret != 0)
183 continue;
184 /* Intentional Fall-Through */
185 case NEXTHOP_TYPE_IFINDEX:
186 if (nexthop->ifindex != nh->ifindex)
187 continue;
188 break;
189 case NEXTHOP_TYPE_BLACKHOLE:
190 if (nexthop->bh_type != nh->bh_type)
191 continue;
192 break;
193 }
194
195 return nexthop;
196 }
197
198 return NULL;
199}
200
f17f2c5d
SW
201static bool
202nexthop_group_equal_common(const struct nexthop_group *nhg1,
203 const struct nexthop_group *nhg2,
204 uint8_t (*nexthop_group_nexthop_num_func)(
205 const struct nexthop_group *nhg))
8f8d9845 206{
8f8d9845
SW
207 if (nhg1 && !nhg2)
208 return false;
209
9c387098 210 if (!nhg1 && nhg2)
8f8d9845
SW
211 return false;
212
dd9546e1
SW
213 if (nhg1 == nhg2)
214 return true;
215
f17f2c5d
SW
216 if (nexthop_group_nexthop_num_func(nhg1)
217 != nexthop_group_nexthop_num_func(nhg2))
218 return false;
219
220 return true;
221}
222
223/* This assumes ordered */
224bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
225 const struct nexthop_group *nhg2)
226{
227 struct nexthop *nh1 = NULL;
228 struct nexthop *nh2 = NULL;
229
230 if (!nexthop_group_equal_common(nhg1, nhg2,
231 &nexthop_group_nexthop_num_no_recurse))
8f8d9845
SW
232 return false;
233
2171b19c 234 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
12ec584d 235 nh1 = nh1->next, nh2 = nh2->next) {
dd9546e1
SW
236 if (nh1 && !nh2)
237 return false;
238 if (!nh1 && nh2)
239 return false;
12ec584d 240 if (!nexthop_same(nh1, nh2))
8f8d9845
SW
241 return false;
242 }
243
244 return true;
245}
246
2171b19c
SW
247/* This assumes ordered */
248bool nexthop_group_equal(const struct nexthop_group *nhg1,
249 const struct nexthop_group *nhg2)
250{
251 struct nexthop *nh1 = NULL;
252 struct nexthop *nh2 = NULL;
253
f17f2c5d 254 if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
2171b19c
SW
255 return false;
256
257 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
258 nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
dd9546e1
SW
259 if (nh1 && !nh2)
260 return false;
261 if (!nh1 && nh2)
262 return false;
2171b19c
SW
263 if (!nexthop_same(nh1, nh2))
264 return false;
265 }
266
267 return true;
268}
31919191
DS
269struct nexthop_group *nexthop_group_new(void)
270{
271 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
272}
273
c13bfa74
MS
274void nexthop_group_copy(struct nexthop_group *to,
275 const struct nexthop_group *from)
6c8b51e1
DS
276{
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
c57bd6bb
DS
678static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
679 const char *nhvrf_name,
680 const union sockunion *addr,
54c6fa8e
SW
681 const char *intf, bool onlink,
682 const char *labels, const uint32_t weight,
28593bcc 683 const char *backup_str)
c57bd6bb
DS
684{
685 struct nexthop_hold *nh;
686
687 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
688
689 if (nhvrf_name)
690 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
691 if (intf)
692 nh->intf = XSTRDUP(MTYPE_TMP, intf);
1c869b64
RW
693 if (addr)
694 nh->addr = sockunion_dup(addr);
db2fede9
MS
695 if (labels)
696 nh->labels = XSTRDUP(MTYPE_TMP, labels);
c57bd6bb 697
54c6fa8e
SW
698 nh->onlink = onlink;
699
597371a6
DS
700 nh->weight = weight;
701
28593bcc
MS
702 if (backup_str)
703 nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
0a8881b4 704
c57bd6bb
DS
705 listnode_add_sort(nhgc->nhg_list, nh);
706}
707
4fda105e
MS
708/*
709 * Remove config info about a nexthop from group 'nhgc'. Note that we
710 * use only a subset of the available attributes here to determine
711 * a 'match'.
712 * Note that this doesn't change the list of nexthops, only the config
713 * information.
714 */
c57bd6bb
DS
715static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
716 const char *nhvrf_name,
717 const union sockunion *addr,
4fda105e 718 const char *intf)
c57bd6bb
DS
719{
720 struct nexthop_hold *nh;
721 struct listnode *node;
722
723 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
597371a6
DS
724 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
725 && nhgc_addr_cmp_helper(addr, nh->addr) == 0
4fda105e 726 && nhgc_cmp_helper(intf, nh->intf) == 0)
c57bd6bb
DS
727 break;
728 }
729
730 /*
731 * Something has gone seriously wrong, fail gracefully
732 */
733 if (!nh)
734 return;
735
736 list_delete_node(nhgc->nhg_list, node);
e5a501c2 737 nhgl_delete(nh);
c57bd6bb
DS
738}
739
db2fede9
MS
740/*
741 * Parse the config strings we support for a single nexthop. This gets used
742 * in a couple of different ways, and we distinguish between transient
743 * failures - such as a still-unprocessed interface - and fatal errors
744 * from label-string parsing.
745 */
98cbbaea
DS
746static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
747 const union sockunion *addr,
54c6fa8e
SW
748 const char *intf, bool onlink,
749 const char *name, const char *labels,
750 int *lbl_ret, uint32_t weight,
751 const char *backup_str)
31919191 752{
db2fede9 753 int ret = 0;
31919191 754 struct vrf *vrf;
28593bcc 755 int num;
98cbbaea
DS
756
757 memset(nhop, 0, sizeof(*nhop));
31919191
DS
758
759 if (name)
760 vrf = vrf_lookup_by_name(name);
761 else
762 vrf = vrf_lookup_by_id(VRF_DEFAULT);
763
98cbbaea
DS
764 if (!vrf)
765 return false;
31919191 766
98cbbaea 767 nhop->vrf_id = vrf->vrf_id;
31919191 768
1c869b64
RW
769 if (intf) {
770 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
771 if (nhop->ifindex == IFINDEX_INTERNAL)
772 return false;
98cbbaea
DS
773 }
774
54c6fa8e
SW
775 if (onlink)
776 SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
777
1c869b64
RW
778 if (addr) {
779 if (addr->sa.sa_family == AF_INET) {
780 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
781 if (intf)
782 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
783 else
784 nhop->type = NEXTHOP_TYPE_IPV4;
785 } else {
786 nhop->gate.ipv6 = addr->sin6.sin6_addr;
787 if (intf)
788 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
789 else
790 nhop->type = NEXTHOP_TYPE_IPV6;
791 }
792 } else
793 nhop->type = NEXTHOP_TYPE_IFINDEX;
794
db2fede9
MS
795 if (labels) {
796 uint8_t num = 0;
797 mpls_label_t larray[MPLS_MAX_LABELS];
798
799 ret = mpls_str2label(labels, &num, larray);
800
801 /* Return label parse result */
802 if (lbl_ret)
803 *lbl_ret = ret;
804
805 if (ret < 0)
806 return false;
807 else if (num > 0)
808 nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
809 num, larray);
810 }
811
597371a6
DS
812 nhop->weight = weight;
813
28593bcc
MS
814 if (backup_str) {
815 /* Parse backup indexes */
816 ret = nexthop_str2backups(backup_str,
817 &num, nhop->backup_idx);
818 if (ret == 0) {
819 SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
820 nhop->backup_num = num;
821 } else
0a8881b4 822 return false;
0a8881b4
MS
823 }
824
98cbbaea
DS
825 return true;
826}
827
db2fede9
MS
828/*
829 * Wrapper to parse the strings in a 'nexthop_hold'
830 */
831static bool nexthop_group_parse_nhh(struct nexthop *nhop,
832 const struct nexthop_hold *nhh)
833{
54c6fa8e
SW
834 return (nexthop_group_parse_nexthop(
835 nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
836 nhh->labels, NULL, nhh->weight, nhh->backup_str));
db2fede9
MS
837}
838
98cbbaea 839DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
1c869b64
RW
840 "[no] nexthop\
841 <\
54c6fa8e 842 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
1c869b64
RW
843 |INTERFACE$intf\
844 >\
db2fede9
MS
845 [{ \
846 nexthop-vrf NAME$vrf_name \
847 |label WORD \
597371a6 848 |weight (1-255) \
28593bcc 849 |backup-idx WORD \
db2fede9 850 }]",
98cbbaea
DS
851 NO_STR
852 "Specify one of the nexthops in this ECMP group\n"
853 "v4 Address\n"
854 "v6 Address\n"
855 "Interface to use\n"
54c6fa8e 856 "Treat nexthop as directly attached to the interface\n"
1c869b64 857 "Interface to use\n"
98cbbaea 858 "If the nexthop is in a different vrf tell us\n"
db2fede9
MS
859 "The nexthop-vrf Name\n"
860 "Specify label(s) for this nexthop\n"
597371a6
DS
861 "One or more labels in the range (16-1048575) separated by '/'\n"
862 "Weight to be used by the nexthop for purposes of ECMP\n"
0a8881b4 863 "Weight value to be used\n"
28593bcc
MS
864 "Specify backup nexthop indexes in another group\n"
865 "One or more indexes in the range (0-254) separated by ','\n")
98cbbaea
DS
866{
867 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
868 struct nexthop nhop;
869 struct nexthop *nh;
db2fede9 870 int lbl_ret = 0;
98cbbaea 871 bool legal;
28593bcc
MS
872 int num;
873 uint8_t backups[NEXTHOP_MAX_BACKUPS];
4fda105e 874 bool yes = !no;
0a8881b4 875
28593bcc
MS
876 /* Pre-parse backup string to validate */
877 if (backup_idx) {
878 lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
879 if (lbl_ret < 0) {
880 vty_out(vty, "%% Invalid backups\n");
881 return CMD_WARNING_CONFIG_FAILED;
882 }
883 }
98cbbaea 884
54c6fa8e
SW
885 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
886 vrf_name, label, &lbl_ret, weight,
887 backup_idx);
98cbbaea
DS
888
889 if (nhop.type == NEXTHOP_TYPE_IPV6
890 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
891 vty_out(vty,
892 "Specified a v6 LL with no interface, rejecting\n");
893 return CMD_WARNING_CONFIG_FAILED;
31919191
DS
894 }
895
db2fede9
MS
896 /* Handle label-string errors */
897 if (!legal && lbl_ret < 0) {
898 switch (lbl_ret) {
899 case -1:
900 vty_out(vty, "%% Malformed label(s)\n");
901 break;
902 case -2:
903 vty_out(vty,
904 "%% Cannot use reserved label(s) (%d-%d)\n",
905 MPLS_LABEL_RESERVED_MIN,
906 MPLS_LABEL_RESERVED_MAX);
907 break;
908 case -3:
909 vty_out(vty,
910 "%% Too many labels. Enter %d or fewer\n",
911 MPLS_MAX_LABELS);
912 break;
913 }
914 return CMD_WARNING_CONFIG_FAILED;
915 }
916
4fda105e
MS
917 /* Look for an existing nexthop in the config. Note that the test
918 * here tests only some attributes - it's not a complete comparison.
919 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
920 * that represent config that may or may not be valid (yet), and
921 * actual nexthops that have been validated and parsed.
922 */
923 nh = nhg_nh_find(&nhgc->nhg, &nhop);
31919191 924
4fda105e
MS
925 /* Always attempt to remove old config info. */
926 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
0a8881b4 927
4fda105e
MS
928 /* Remove any existing nexthop, for delete and replace cases. */
929 if (nh) {
930 nexthop_unlink(&nhgc->nhg, nh);
31919191 931
4fda105e
MS
932 if (nhg_hooks.del_nexthop)
933 nhg_hooks.del_nexthop(nhgc, nh);
31919191 934
4fda105e 935 nexthop_free(nh);
0a8881b4 936 }
4fda105e
MS
937 if (yes) {
938 /* Add/replace case: capture nexthop if valid, and capture
939 * config info always.
940 */
98cbbaea
DS
941 if (legal) {
942 nh = nexthop_new();
31919191 943
98cbbaea 944 memcpy(nh, &nhop, sizeof(nhop));
50d89650 945 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea 946 }
31919191 947
0a8881b4 948 /* Save config always */
54c6fa8e
SW
949 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
950 label, weight, backup_idx);
c57bd6bb 951
98cbbaea 952 if (legal && nhg_hooks.add_nexthop)
31919191
DS
953 nhg_hooks.add_nexthop(nhgc, nh);
954 }
955
e7ff0253
DS
956 if (intf) {
957 struct interface *ifp = if_lookup_by_name_all_vrf(intf);
958
959 if (ifp)
960 ifp->configured = true;
961 }
dba32923
DS
962 return CMD_SUCCESS;
963}
964
612c2c15 965static int nexthop_group_write(struct vty *vty);
1b3e9a21 966static struct cmd_node nexthop_group_node = {
f4b8291f 967 .name = "nexthop-group",
62b346ee 968 .node = NH_GROUP_NODE,
24389580 969 .parent_node = CONFIG_NODE,
62b346ee 970 .prompt = "%s(config-nh-group)# ",
612c2c15 971 .config_write = nexthop_group_write,
dba32923
DS
972};
973
79924520 974void nexthop_group_write_nexthop_simple(struct vty *vty,
a251884b
DS
975 const struct nexthop *nh,
976 char *altifname)
1b7bce04 977{
a251884b 978 char *ifname;
1b7bce04 979
57cdafc4 980 vty_out(vty, "nexthop ");
1b7bce04 981
a251884b
DS
982 if (altifname)
983 ifname = altifname;
984 else
985 ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
986
1b7bce04
DS
987 switch (nh->type) {
988 case NEXTHOP_TYPE_IFINDEX:
a251884b 989 vty_out(vty, "%s", ifname);
1b7bce04
DS
990 break;
991 case NEXTHOP_TYPE_IPV4:
af3b34f6 992 vty_out(vty, "%pI4", &nh->gate.ipv4);
1b7bce04
DS
993 break;
994 case NEXTHOP_TYPE_IPV4_IFINDEX:
af3b34f6 995 vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
1b7bce04
DS
996 break;
997 case NEXTHOP_TYPE_IPV6:
af3b34f6 998 vty_out(vty, "%pI6", &nh->gate.ipv6);
1b7bce04
DS
999 break;
1000 case NEXTHOP_TYPE_IPV6_IFINDEX:
af3b34f6 1001 vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
1b7bce04
DS
1002 break;
1003 case NEXTHOP_TYPE_BLACKHOLE:
1004 break;
1005 }
79924520
SW
1006}
1007
1008void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1009{
1010 struct vrf *vrf;
1011 int i;
1012
a251884b 1013 nexthop_group_write_nexthop_simple(vty, nh, NULL);
1b7bce04
DS
1014
1015 if (nh->vrf_id != VRF_DEFAULT) {
1016 vrf = vrf_lookup_by_id(nh->vrf_id);
c40e9806 1017 vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1b7bce04 1018 }
db2fede9
MS
1019
1020 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1021 char buf[200];
1022
1023 mpls_label2str(nh->nh_label->num_labels,
1024 nh->nh_label->label,
1025 buf, sizeof(buf), 0);
1026 vty_out(vty, " label %s", buf);
1027 }
1028
597371a6
DS
1029 if (nh->weight)
1030 vty_out(vty, " weight %u", nh->weight);
1031
28593bcc 1032 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
474aebd9 1033 vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
0a8881b4 1034
28593bcc
MS
1035 for (i = 1; i < nh->backup_num; i++)
1036 vty_out(vty, ",%d", nh->backup_idx[i]);
1037 }
1038
1b7bce04
DS
1039 vty_out(vty, "\n");
1040}
1041
28593bcc 1042void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
010dd8ed
WC
1043{
1044 char buf[100];
1045 struct vrf *vrf;
28593bcc
MS
1046 json_object *json_backups = NULL;
1047 int i;
010dd8ed
WC
1048
1049 switch (nh->type) {
1050 case NEXTHOP_TYPE_IFINDEX:
3e81618d 1051 json_object_string_add(j, "nexthop",
010dd8ed
WC
1052 ifindex2ifname(nh->ifindex, nh->vrf_id));
1053 break;
1054 case NEXTHOP_TYPE_IPV4:
af3b34f6
DA
1055 json_object_string_add(
1056 j, "nexthop",
1057 inet_ntop(AF_INET, &nh->gate.ipv4, buf, sizeof(buf)));
010dd8ed
WC
1058 break;
1059 case NEXTHOP_TYPE_IPV4_IFINDEX:
af3b34f6
DA
1060 json_object_string_add(
1061 j, "nexthop",
1062 inet_ntop(AF_INET, &nh->gate.ipv4, buf, sizeof(buf)));
010dd8ed
WC
1063 json_object_string_add(j, "vrfId",
1064 ifindex2ifname(nh->ifindex, nh->vrf_id));
1065 break;
1066 case NEXTHOP_TYPE_IPV6:
1067 json_object_string_add(
3e81618d 1068 j, "nexthop",
010dd8ed
WC
1069 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1070 break;
1071 case NEXTHOP_TYPE_IPV6_IFINDEX:
1072 json_object_string_add(
3e81618d 1073 j, "nexthop",
010dd8ed
WC
1074 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1075 json_object_string_add(j, "vrfId",
1076 ifindex2ifname(nh->ifindex, nh->vrf_id));
1077 break;
1078 case NEXTHOP_TYPE_BLACKHOLE:
1079 break;
1080 }
1081
1082 if (nh->vrf_id != VRF_DEFAULT) {
1083 vrf = vrf_lookup_by_id(nh->vrf_id);
81c0078e 1084 json_object_string_add(j, "targetVrf", vrf->name);
010dd8ed
WC
1085 }
1086
1087 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1088 char buf[200];
1089
1090 mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1091 buf, sizeof(buf), 0);
1092 json_object_string_add(j, "label", buf);
1093 }
1094
1095 if (nh->weight)
1096 json_object_int_add(j, "weight", nh->weight);
1097
28593bcc
MS
1098 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1099 json_backups = json_object_new_array();
1100 for (i = 0; i < nh->backup_num; i++)
1101 json_object_array_add(
1102 json_backups,
1103 json_object_new_int(nh->backup_idx[i]));
1104
1105 json_object_object_add(j, "backupIdx", json_backups);
1106 }
010dd8ed
WC
1107}
1108
c57bd6bb 1109static void nexthop_group_write_nexthop_internal(struct vty *vty,
28593bcc 1110 const struct nexthop_hold *nh)
c57bd6bb
DS
1111{
1112 char buf[100];
1113
1c869b64 1114 vty_out(vty, "nexthop");
c57bd6bb 1115
1c869b64
RW
1116 if (nh->addr)
1117 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
c57bd6bb
DS
1118
1119 if (nh->intf)
1120 vty_out(vty, " %s", nh->intf);
1121
54c6fa8e
SW
1122 if (nh->onlink)
1123 vty_out(vty, " onlink");
1124
c57bd6bb
DS
1125 if (nh->nhvrf_name)
1126 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1127
db2fede9
MS
1128 if (nh->labels)
1129 vty_out(vty, " label %s", nh->labels);
1130
597371a6
DS
1131 if (nh->weight)
1132 vty_out(vty, " weight %u", nh->weight);
1133
28593bcc
MS
1134 if (nh->backup_str)
1135 vty_out(vty, " backup-idx %s", nh->backup_str);
0a8881b4 1136
c57bd6bb
DS
1137 vty_out(vty, "\n");
1138}
1139
dba32923
DS
1140static int nexthop_group_write(struct vty *vty)
1141{
31919191 1142 struct nexthop_group_cmd *nhgc;
c57bd6bb 1143 struct nexthop_hold *nh;
31919191
DS
1144
1145 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
c57bd6bb
DS
1146 struct listnode *node;
1147
31919191
DS
1148 vty_out(vty, "nexthop-group %s\n", nhgc->name);
1149
0a8881b4
MS
1150 if (nhgc->backup_list_name[0])
1151 vty_out(vty, " backup-group %s\n",
1152 nhgc->backup_list_name);
1153
c57bd6bb 1154 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
7dce96f0 1155 vty_out(vty, " ");
c57bd6bb 1156 nexthop_group_write_nexthop_internal(vty, nh);
811f859f 1157 }
31919191 1158
07679ad9 1159 vty_out(vty, "exit\n");
31919191
DS
1160 vty_out(vty, "!\n");
1161 }
dba32923
DS
1162
1163 return 1;
1164}
1165
98cbbaea
DS
1166void nexthop_group_enable_vrf(struct vrf *vrf)
1167{
1168 struct nexthop_group_cmd *nhgc;
1169 struct nexthop_hold *nhh;
1170
1171 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1172 struct listnode *node;
1173
1174 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1175 struct nexthop nhop;
1176 struct nexthop *nh;
1177
db2fede9 1178 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1179 continue;
1180
1181 nh = nexthop_exists(&nhgc->nhg, &nhop);
1182
1183 if (nh)
1184 continue;
1185
1186 if (nhop.vrf_id != vrf->vrf_id)
1187 continue;
1188
1189 nh = nexthop_new();
1190
1191 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1192 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1193
1194 if (nhg_hooks.add_nexthop)
1195 nhg_hooks.add_nexthop(nhgc, nh);
1196 }
1197 }
1198}
1199
1200void nexthop_group_disable_vrf(struct vrf *vrf)
1201{
1202 struct nexthop_group_cmd *nhgc;
1203 struct nexthop_hold *nhh;
1204
1205 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1206 struct listnode *node;
1207
1208 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1209 struct nexthop nhop;
1210 struct nexthop *nh;
1211
db2fede9 1212 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1213 continue;
1214
1215 nh = nexthop_exists(&nhgc->nhg, &nhop);
1216
1217 if (!nh)
1218 continue;
1219
1220 if (nh->vrf_id != vrf->vrf_id)
1221 continue;
1222
50d89650 1223 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1224
1225 if (nhg_hooks.del_nexthop)
1226 nhg_hooks.del_nexthop(nhgc, nh);
1227
1228 nexthop_free(nh);
1229 }
1230 }
1231}
1232
1233void nexthop_group_interface_state_change(struct interface *ifp,
1234 ifindex_t oldifindex)
1235{
1236 struct nexthop_group_cmd *nhgc;
1237 struct nexthop_hold *nhh;
1238
1239 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1240 struct listnode *node;
1241 struct nexthop *nh;
1242
1243 if (if_is_up(ifp)) {
1244 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1245 struct nexthop nhop;
1246
db2fede9 1247 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1248 continue;
1249
1250 switch (nhop.type) {
1251 case NEXTHOP_TYPE_IPV4:
1252 case NEXTHOP_TYPE_IPV6:
1253 case NEXTHOP_TYPE_BLACKHOLE:
1254 continue;
1255 case NEXTHOP_TYPE_IFINDEX:
1256 case NEXTHOP_TYPE_IPV4_IFINDEX:
1257 case NEXTHOP_TYPE_IPV6_IFINDEX:
1258 break;
1259 }
1260 nh = nexthop_exists(&nhgc->nhg, &nhop);
1261
1262 if (nh)
1263 continue;
1264
1265 if (ifp->ifindex != nhop.ifindex)
1266 continue;
1267
e7ff0253 1268 ifp->configured = true;
98cbbaea
DS
1269 nh = nexthop_new();
1270
1271 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1272 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1273
1274 if (nhg_hooks.add_nexthop)
1275 nhg_hooks.add_nexthop(nhgc, nh);
1276 }
1277 } else {
1278 struct nexthop *next_nh;
1279
1280 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1281 next_nh = nh->next;
1282 switch (nh->type) {
1283 case NEXTHOP_TYPE_IPV4:
1284 case NEXTHOP_TYPE_IPV6:
1285 case NEXTHOP_TYPE_BLACKHOLE:
1286 continue;
1287 case NEXTHOP_TYPE_IFINDEX:
1288 case NEXTHOP_TYPE_IPV4_IFINDEX:
1289 case NEXTHOP_TYPE_IPV6_IFINDEX:
1290 break;
1291 }
1292
1293 if (oldifindex != nh->ifindex)
1294 continue;
1295
50d89650 1296 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1297
1298 if (nhg_hooks.del_nexthop)
1299 nhg_hooks.del_nexthop(nhgc, nh);
1300
1301 nexthop_free(nh);
1302 }
1303 }
1304 }
1305}
1306
868ee86c
DS
1307static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1308{
1309 struct nexthop_group_cmd *nhgc;
1310
1311 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1312 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1313 }
1314}
1315
1316static const struct cmd_variable_handler nhg_name_handlers[] = {
1317 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1318 {.completions = NULL}};
1319
cc6a0d7d
SW
1320void nexthop_group_init(void (*new)(const char *name),
1321 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1322 const struct nexthop *nhop),
1323 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1324 const struct nexthop *nhop),
1325 void (*delete)(const char *name))
dba32923 1326{
31919191
DS
1327 RB_INIT(nhgc_entry_head, &nhgc_entries);
1328
868ee86c
DS
1329 cmd_variable_handler_register(nhg_name_handlers);
1330
612c2c15 1331 install_node(&nexthop_group_node);
dba32923 1332 install_element(CONFIG_NODE, &nexthop_group_cmd);
31919191
DS
1333 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1334
1335 install_default(NH_GROUP_NODE);
0a8881b4
MS
1336 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1337 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
31919191
DS
1338 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1339
1340 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1341
1342 if (new)
1343 nhg_hooks.new = new;
1344 if (add_nexthop)
1345 nhg_hooks.add_nexthop = add_nexthop;
1346 if (del_nexthop)
1347 nhg_hooks.del_nexthop = del_nexthop;
1348 if (delete)
1349 nhg_hooks.delete = delete;
dba32923 1350}