]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop_group.c
Merge pull request #7232 from idryzhov/more-topotests-vrf-id
[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;
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;
484 if (nexthop_same_firsthop(nexthop, prev)) {
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
623DEFINE_QOBJ_TYPE(nexthop_group_cmd)
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
DS
977{
978 char buf[100];
a251884b 979 char *ifname;
1b7bce04 980
57cdafc4 981 vty_out(vty, "nexthop ");
1b7bce04 982
a251884b
DS
983 if (altifname)
984 ifname = altifname;
985 else
986 ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
987
1b7bce04
DS
988 switch (nh->type) {
989 case NEXTHOP_TYPE_IFINDEX:
a251884b 990 vty_out(vty, "%s", ifname);
1b7bce04
DS
991 break;
992 case NEXTHOP_TYPE_IPV4:
993 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
994 break;
995 case NEXTHOP_TYPE_IPV4_IFINDEX:
a251884b 996 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), ifname);
1b7bce04
DS
997 break;
998 case NEXTHOP_TYPE_IPV6:
999 vty_out(vty, "%s",
1000 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1001 break;
1002 case NEXTHOP_TYPE_IPV6_IFINDEX:
1003 vty_out(vty, "%s %s",
1004 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
a251884b 1005 ifname);
1b7bce04
DS
1006 break;
1007 case NEXTHOP_TYPE_BLACKHOLE:
1008 break;
1009 }
79924520
SW
1010}
1011
1012void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1013{
1014 struct vrf *vrf;
1015 int i;
1016
a251884b 1017 nexthop_group_write_nexthop_simple(vty, nh, NULL);
1b7bce04
DS
1018
1019 if (nh->vrf_id != VRF_DEFAULT) {
1020 vrf = vrf_lookup_by_id(nh->vrf_id);
c40e9806 1021 vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1b7bce04 1022 }
db2fede9
MS
1023
1024 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1025 char buf[200];
1026
1027 mpls_label2str(nh->nh_label->num_labels,
1028 nh->nh_label->label,
1029 buf, sizeof(buf), 0);
1030 vty_out(vty, " label %s", buf);
1031 }
1032
597371a6
DS
1033 if (nh->weight)
1034 vty_out(vty, " weight %u", nh->weight);
1035
28593bcc 1036 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
474aebd9 1037 vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
0a8881b4 1038
28593bcc
MS
1039 for (i = 1; i < nh->backup_num; i++)
1040 vty_out(vty, ",%d", nh->backup_idx[i]);
1041 }
1042
1b7bce04
DS
1043 vty_out(vty, "\n");
1044}
1045
28593bcc 1046void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
010dd8ed
WC
1047{
1048 char buf[100];
1049 struct vrf *vrf;
28593bcc
MS
1050 json_object *json_backups = NULL;
1051 int i;
010dd8ed
WC
1052
1053 switch (nh->type) {
1054 case NEXTHOP_TYPE_IFINDEX:
3e81618d 1055 json_object_string_add(j, "nexthop",
010dd8ed
WC
1056 ifindex2ifname(nh->ifindex, nh->vrf_id));
1057 break;
1058 case NEXTHOP_TYPE_IPV4:
3e81618d 1059 json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4));
010dd8ed
WC
1060 break;
1061 case NEXTHOP_TYPE_IPV4_IFINDEX:
3e81618d 1062 json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4));
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
31919191
DS
1159 vty_out(vty, "!\n");
1160 }
dba32923
DS
1161
1162 return 1;
1163}
1164
98cbbaea
DS
1165void nexthop_group_enable_vrf(struct vrf *vrf)
1166{
1167 struct nexthop_group_cmd *nhgc;
1168 struct nexthop_hold *nhh;
1169
1170 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1171 struct listnode *node;
1172
1173 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1174 struct nexthop nhop;
1175 struct nexthop *nh;
1176
db2fede9 1177 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1178 continue;
1179
1180 nh = nexthop_exists(&nhgc->nhg, &nhop);
1181
1182 if (nh)
1183 continue;
1184
1185 if (nhop.vrf_id != vrf->vrf_id)
1186 continue;
1187
1188 nh = nexthop_new();
1189
1190 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1191 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1192
1193 if (nhg_hooks.add_nexthop)
1194 nhg_hooks.add_nexthop(nhgc, nh);
1195 }
1196 }
1197}
1198
1199void nexthop_group_disable_vrf(struct vrf *vrf)
1200{
1201 struct nexthop_group_cmd *nhgc;
1202 struct nexthop_hold *nhh;
1203
1204 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1205 struct listnode *node;
1206
1207 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1208 struct nexthop nhop;
1209 struct nexthop *nh;
1210
db2fede9 1211 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1212 continue;
1213
1214 nh = nexthop_exists(&nhgc->nhg, &nhop);
1215
1216 if (!nh)
1217 continue;
1218
1219 if (nh->vrf_id != vrf->vrf_id)
1220 continue;
1221
50d89650 1222 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1223
1224 if (nhg_hooks.del_nexthop)
1225 nhg_hooks.del_nexthop(nhgc, nh);
1226
1227 nexthop_free(nh);
1228 }
1229 }
1230}
1231
1232void nexthop_group_interface_state_change(struct interface *ifp,
1233 ifindex_t oldifindex)
1234{
1235 struct nexthop_group_cmd *nhgc;
1236 struct nexthop_hold *nhh;
1237
1238 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1239 struct listnode *node;
1240 struct nexthop *nh;
1241
1242 if (if_is_up(ifp)) {
1243 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1244 struct nexthop nhop;
1245
db2fede9 1246 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1247 continue;
1248
1249 switch (nhop.type) {
1250 case NEXTHOP_TYPE_IPV4:
1251 case NEXTHOP_TYPE_IPV6:
1252 case NEXTHOP_TYPE_BLACKHOLE:
1253 continue;
1254 case NEXTHOP_TYPE_IFINDEX:
1255 case NEXTHOP_TYPE_IPV4_IFINDEX:
1256 case NEXTHOP_TYPE_IPV6_IFINDEX:
1257 break;
1258 }
1259 nh = nexthop_exists(&nhgc->nhg, &nhop);
1260
1261 if (nh)
1262 continue;
1263
1264 if (ifp->ifindex != nhop.ifindex)
1265 continue;
1266
e7ff0253 1267 ifp->configured = true;
98cbbaea
DS
1268 nh = nexthop_new();
1269
1270 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1271 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1272
1273 if (nhg_hooks.add_nexthop)
1274 nhg_hooks.add_nexthop(nhgc, nh);
1275 }
1276 } else {
1277 struct nexthop *next_nh;
1278
1279 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1280 next_nh = nh->next;
1281 switch (nh->type) {
1282 case NEXTHOP_TYPE_IPV4:
1283 case NEXTHOP_TYPE_IPV6:
1284 case NEXTHOP_TYPE_BLACKHOLE:
1285 continue;
1286 case NEXTHOP_TYPE_IFINDEX:
1287 case NEXTHOP_TYPE_IPV4_IFINDEX:
1288 case NEXTHOP_TYPE_IPV6_IFINDEX:
1289 break;
1290 }
1291
1292 if (oldifindex != nh->ifindex)
1293 continue;
1294
50d89650 1295 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1296
1297 if (nhg_hooks.del_nexthop)
1298 nhg_hooks.del_nexthop(nhgc, nh);
1299
1300 nexthop_free(nh);
1301 }
1302 }
1303 }
1304}
1305
868ee86c
DS
1306static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1307{
1308 struct nexthop_group_cmd *nhgc;
1309
1310 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1311 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1312 }
1313}
1314
1315static const struct cmd_variable_handler nhg_name_handlers[] = {
1316 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1317 {.completions = NULL}};
1318
cc6a0d7d
SW
1319void nexthop_group_init(void (*new)(const char *name),
1320 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1321 const struct nexthop *nhop),
1322 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1323 const struct nexthop *nhop),
1324 void (*delete)(const char *name))
dba32923 1325{
31919191
DS
1326 RB_INIT(nhgc_entry_head, &nhgc_entries);
1327
868ee86c
DS
1328 cmd_variable_handler_register(nhg_name_handlers);
1329
612c2c15 1330 install_node(&nexthop_group_node);
dba32923 1331 install_element(CONFIG_NODE, &nexthop_group_cmd);
31919191
DS
1332 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1333
1334 install_default(NH_GROUP_NODE);
0a8881b4
MS
1335 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1336 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
31919191
DS
1337 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1338
1339 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1340
1341 if (new)
1342 nhg_hooks.new = new;
1343 if (add_nexthop)
1344 nhg_hooks.add_nexthop = add_nexthop;
1345 if (del_nexthop)
1346 nhg_hooks.del_nexthop = del_nexthop;
1347 if (delete)
1348 nhg_hooks.delete = delete;
dba32923 1349}