]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
lib: fix doc comment of the "cli_show_end" northbound callback
[mirror_frr.git] / lib / nexthop_group.c
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
22 #include <vrf.h>
23 #include <sockunion.h>
24 #include <nexthop.h>
25 #include <nexthop_group.h>
26 #include <nexthop_group_private.h>
27 #include <vty.h>
28 #include <command.h>
29 #include <jhash.h>
30
31 #ifndef VTYSH_EXTRACT_PL
32 #include "lib/nexthop_group_clippy.c"
33 #endif
34
35 DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group")
36
37 struct nexthop_group_hooks {
38 void (*new)(const char *name);
39 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
40 const struct nexthop *nhop);
41 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
42 const struct nexthop *nhop);
43 void (*delete)(const char *name);
44 };
45
46 static struct nexthop_group_hooks nhg_hooks;
47
48 static inline int
49 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
50 const struct nexthop_group_cmd *nhgc2);
51 RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
52 nexthop_group_cmd_compare)
53
54 static struct nhgc_entry_head nhgc_entries;
55
56 static inline int
57 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
58 const struct nexthop_group_cmd *nhgc2)
59 {
60 return strcmp(nhgc1->name, nhgc2->name);
61 }
62
63 uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
64 {
65 struct nexthop *nhop;
66 uint8_t num = 0;
67
68 for (ALL_NEXTHOPS_PTR(nhg, nhop))
69 num++;
70
71 return num;
72 }
73
74 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
75 {
76 struct nexthop *nhop;
77 uint8_t num = 0;
78
79 for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
80 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
81 num++;
82 }
83
84 return num;
85 }
86
87 struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh)
88 {
89 struct nexthop *nexthop;
90
91 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
92 if (nexthop_same(nh, nexthop))
93 return nexthop;
94 }
95
96 return NULL;
97 }
98
99 struct nexthop_group *nexthop_group_new(void)
100 {
101 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
102 }
103
104 void nexthop_group_copy(struct nexthop_group *to, struct nexthop_group *from)
105 {
106 /* Copy everything, including recursive info */
107 copy_nexthops(&to->nexthop, from->nexthop, NULL);
108 }
109
110 void nexthop_group_delete(struct nexthop_group **nhg)
111 {
112 XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
113 }
114
115 /* Add nexthop to the end of a nexthop list. */
116 void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
117 {
118 struct nexthop *last;
119
120 for (last = *target; last && last->next; last = last->next)
121 ;
122 if (last)
123 last->next = nexthop;
124 else
125 *target = nexthop;
126 nexthop->prev = last;
127 }
128
129 void _nexthop_group_add_sorted(struct nexthop_group *nhg,
130 struct nexthop *nexthop)
131 {
132 struct nexthop *position, *prev;
133
134 for (position = nhg->nexthop, prev = NULL; position;
135 prev = position, position = position->next) {
136 if (nexthop_cmp(position, nexthop) > 0) {
137 nexthop->next = position;
138 nexthop->prev = prev;
139
140 if (nexthop->prev)
141 nexthop->prev->next = nexthop;
142 else
143 nhg->nexthop = nexthop;
144
145 position->prev = nexthop;
146 return;
147 }
148 }
149
150 nexthop->prev = prev;
151 if (prev)
152 prev->next = nexthop;
153 else
154 nhg->nexthop = nexthop;
155 }
156
157 /* Delete nexthop from a nexthop list. */
158 void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
159 {
160 struct nexthop *nexthop;
161
162 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
163 if (nexthop_same(nh, nexthop))
164 break;
165 }
166
167 assert(nexthop);
168
169 if (nexthop->prev)
170 nexthop->prev->next = nexthop->next;
171 else
172 nhg->nexthop = nexthop->next;
173
174 if (nexthop->next)
175 nexthop->next->prev = nexthop->prev;
176
177 nh->prev = NULL;
178 nh->next = NULL;
179 }
180
181 void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
182 struct nexthop *rparent)
183 {
184 struct nexthop *nexthop;
185 const struct nexthop *nh1;
186
187 for (nh1 = nh; nh1; nh1 = nh1->next) {
188 nexthop = nexthop_dup(nh1, rparent);
189 _nexthop_add(tnh, nexthop);
190
191 if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
192 copy_nexthops(&nexthop->resolved, nh1->resolved,
193 nexthop);
194 }
195 }
196
197 uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
198 {
199 struct nexthop *nh;
200 uint32_t key = 0;
201
202 /*
203 * We are not interested in hashing over any recursively
204 * resolved nexthops
205 */
206 for (nh = nhg->nexthop; nh; nh = nh->next)
207 key = jhash_1word(nexthop_hash(nh), key);
208
209 return key;
210 }
211
212 static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
213 {
214 struct nexthop *nexthop;
215
216 nexthop = nhgc->nhg.nexthop;
217 while (nexthop) {
218 struct nexthop *next = nexthop_next(nexthop);
219
220 _nexthop_del(&nhgc->nhg, nexthop);
221 if (nhg_hooks.del_nexthop)
222 nhg_hooks.del_nexthop(nhgc, nexthop);
223
224 nexthop_free(nexthop);
225
226 nexthop = next;
227 }
228 }
229
230 struct nexthop_group_cmd *nhgc_find(const char *name)
231 {
232 struct nexthop_group_cmd find;
233
234 strlcpy(find.name, name, sizeof(find.name));
235
236 return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
237 }
238
239 static int nhgc_cmp_helper(const char *a, const char *b)
240 {
241 if (!a && !b)
242 return 0;
243
244 if (a && !b)
245 return -1;
246
247 if (!a && b)
248 return 1;
249
250 return strcmp(a, b);
251 }
252
253 static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
254 {
255 if (!a && !b)
256 return 0;
257
258 if (a && !b)
259 return -1;
260
261 if (!a && b)
262 return 1;
263
264 return sockunion_cmp(a, b);
265 }
266
267 static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
268 {
269 int ret;
270
271 ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
272 if (ret)
273 return ret;
274
275 ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
276 if (ret)
277 return ret;
278
279 return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
280 }
281
282 static void nhgl_delete(struct nexthop_hold *nh)
283 {
284 XFREE(MTYPE_TMP, nh->intf);
285
286 XFREE(MTYPE_TMP, nh->nhvrf_name);
287
288 if (nh->addr)
289 sockunion_free(nh->addr);
290
291 XFREE(MTYPE_TMP, nh);
292 }
293
294 static struct nexthop_group_cmd *nhgc_get(const char *name)
295 {
296 struct nexthop_group_cmd *nhgc;
297
298 nhgc = nhgc_find(name);
299 if (!nhgc) {
300 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
301 strlcpy(nhgc->name, name, sizeof(nhgc->name));
302
303 QOBJ_REG(nhgc, nexthop_group_cmd);
304 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
305
306 nhgc->nhg_list = list_new();
307 nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
308 nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
309
310 if (nhg_hooks.new)
311 nhg_hooks.new(name);
312 }
313
314 return nhgc;
315 }
316
317 static void nhgc_delete(struct nexthop_group_cmd *nhgc)
318 {
319 nhgc_delete_nexthops(nhgc);
320
321 if (nhg_hooks.delete)
322 nhg_hooks.delete(nhgc->name);
323
324 RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
325
326 list_delete(&nhgc->nhg_list);
327
328 XFREE(MTYPE_TMP, nhgc);
329 }
330
331 DEFINE_QOBJ_TYPE(nexthop_group_cmd)
332
333 DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
334 "Enter into the nexthop-group submode\n"
335 "Specify the NAME of the nexthop-group\n")
336 {
337 const char *nhg_name = argv[1]->arg;
338 struct nexthop_group_cmd *nhgc = NULL;
339
340 nhgc = nhgc_get(nhg_name);
341 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
342
343 return CMD_SUCCESS;
344 }
345
346 DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
347 NO_STR
348 "Delete the nexthop-group\n"
349 "Specify the NAME of the nexthop-group\n")
350 {
351 const char *nhg_name = argv[2]->arg;
352 struct nexthop_group_cmd *nhgc = NULL;
353
354 nhgc = nhgc_find(nhg_name);
355 if (nhgc)
356 nhgc_delete(nhgc);
357
358 return CMD_SUCCESS;
359 }
360
361 static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
362 const char *nhvrf_name,
363 const union sockunion *addr,
364 const char *intf)
365 {
366 struct nexthop_hold *nh;
367
368 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
369
370 if (nhvrf_name)
371 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
372 if (intf)
373 nh->intf = XSTRDUP(MTYPE_TMP, intf);
374 if (addr)
375 nh->addr = sockunion_dup(addr);
376
377 listnode_add_sort(nhgc->nhg_list, nh);
378 }
379
380 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
381 const char *nhvrf_name,
382 const union sockunion *addr,
383 const char *intf)
384 {
385 struct nexthop_hold *nh;
386 struct listnode *node;
387
388 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
389 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
390 nhgc_addr_cmp_helper(addr, nh->addr) == 0 &&
391 nhgc_cmp_helper(intf, nh->intf) == 0)
392 break;
393 }
394
395 /*
396 * Something has gone seriously wrong, fail gracefully
397 */
398 if (!nh)
399 return;
400
401 list_delete_node(nhgc->nhg_list, node);
402 nhgl_delete(nh);
403 }
404
405 static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
406 const union sockunion *addr,
407 const char *intf, const char *name)
408 {
409 struct vrf *vrf;
410
411 memset(nhop, 0, sizeof(*nhop));
412
413 if (name)
414 vrf = vrf_lookup_by_name(name);
415 else
416 vrf = vrf_lookup_by_id(VRF_DEFAULT);
417
418 if (!vrf)
419 return false;
420
421 nhop->vrf_id = vrf->vrf_id;
422
423 if (intf) {
424 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
425 if (nhop->ifindex == IFINDEX_INTERNAL)
426 return false;
427 }
428
429 if (addr) {
430 if (addr->sa.sa_family == AF_INET) {
431 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
432 if (intf)
433 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
434 else
435 nhop->type = NEXTHOP_TYPE_IPV4;
436 } else {
437 nhop->gate.ipv6 = addr->sin6.sin6_addr;
438 if (intf)
439 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
440 else
441 nhop->type = NEXTHOP_TYPE_IPV6;
442 }
443 } else
444 nhop->type = NEXTHOP_TYPE_IFINDEX;
445
446 return true;
447 }
448
449 DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
450 "[no] nexthop\
451 <\
452 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
453 |INTERFACE$intf\
454 >\
455 [nexthop-vrf NAME$name]",
456 NO_STR
457 "Specify one of the nexthops in this ECMP group\n"
458 "v4 Address\n"
459 "v6 Address\n"
460 "Interface to use\n"
461 "Interface to use\n"
462 "If the nexthop is in a different vrf tell us\n"
463 "The nexthop-vrf Name\n")
464 {
465 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
466 struct nexthop nhop;
467 struct nexthop *nh;
468 bool legal;
469
470 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name);
471
472 if (nhop.type == NEXTHOP_TYPE_IPV6
473 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
474 vty_out(vty,
475 "Specified a v6 LL with no interface, rejecting\n");
476 return CMD_WARNING_CONFIG_FAILED;
477 }
478
479 nh = nexthop_exists(&nhgc->nhg, &nhop);
480
481 if (no) {
482 nexthop_group_unsave_nhop(nhgc, name, addr, intf);
483 if (nh) {
484 _nexthop_del(&nhgc->nhg, nh);
485
486 if (nhg_hooks.del_nexthop)
487 nhg_hooks.del_nexthop(nhgc, nh);
488
489 nexthop_free(nh);
490 }
491 } else if (!nh) {
492 /* must be adding new nexthop since !no and !nexthop_exists */
493 if (legal) {
494 nh = nexthop_new();
495
496 memcpy(nh, &nhop, sizeof(nhop));
497 _nexthop_add(&nhgc->nhg.nexthop, nh);
498 }
499
500 nexthop_group_save_nhop(nhgc, name, addr, intf);
501
502 if (legal && nhg_hooks.add_nexthop)
503 nhg_hooks.add_nexthop(nhgc, nh);
504 }
505
506 return CMD_SUCCESS;
507 }
508
509 struct cmd_node nexthop_group_node = {
510 NH_GROUP_NODE,
511 "%s(config-nh-group)# ",
512 1
513 };
514
515 void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
516 {
517 char buf[100];
518 struct vrf *vrf;
519
520 vty_out(vty, "nexthop ");
521
522 switch (nh->type) {
523 case NEXTHOP_TYPE_IFINDEX:
524 vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id));
525 break;
526 case NEXTHOP_TYPE_IPV4:
527 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
528 break;
529 case NEXTHOP_TYPE_IPV4_IFINDEX:
530 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4),
531 ifindex2ifname(nh->ifindex, nh->vrf_id));
532 break;
533 case NEXTHOP_TYPE_IPV6:
534 vty_out(vty, "%s",
535 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
536 break;
537 case NEXTHOP_TYPE_IPV6_IFINDEX:
538 vty_out(vty, "%s %s",
539 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
540 ifindex2ifname(nh->ifindex, nh->vrf_id));
541 break;
542 case NEXTHOP_TYPE_BLACKHOLE:
543 break;
544 }
545
546 if (nh->vrf_id != VRF_DEFAULT) {
547 vrf = vrf_lookup_by_id(nh->vrf_id);
548 vty_out(vty, " nexthop-vrf %s", vrf->name);
549 }
550 vty_out(vty, "\n");
551 }
552
553 static void nexthop_group_write_nexthop_internal(struct vty *vty,
554 struct nexthop_hold *nh)
555 {
556 char buf[100];
557
558 vty_out(vty, "nexthop");
559
560 if (nh->addr)
561 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
562
563 if (nh->intf)
564 vty_out(vty, " %s", nh->intf);
565
566 if (nh->nhvrf_name)
567 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
568
569 vty_out(vty, "\n");
570 }
571
572 static int nexthop_group_write(struct vty *vty)
573 {
574 struct nexthop_group_cmd *nhgc;
575 struct nexthop_hold *nh;
576
577 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
578 struct listnode *node;
579
580 vty_out(vty, "nexthop-group %s\n", nhgc->name);
581
582 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
583 vty_out(vty, " ");
584 nexthop_group_write_nexthop_internal(vty, nh);
585 }
586
587 vty_out(vty, "!\n");
588 }
589
590 return 1;
591 }
592
593 void nexthop_group_enable_vrf(struct vrf *vrf)
594 {
595 struct nexthop_group_cmd *nhgc;
596 struct nexthop_hold *nhh;
597
598 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
599 struct listnode *node;
600
601 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
602 struct nexthop nhop;
603 struct nexthop *nh;
604
605 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
606 nhh->intf,
607 nhh->nhvrf_name))
608 continue;
609
610 nh = nexthop_exists(&nhgc->nhg, &nhop);
611
612 if (nh)
613 continue;
614
615 if (nhop.vrf_id != vrf->vrf_id)
616 continue;
617
618 nh = nexthop_new();
619
620 memcpy(nh, &nhop, sizeof(nhop));
621 _nexthop_add(&nhgc->nhg.nexthop, nh);
622
623 if (nhg_hooks.add_nexthop)
624 nhg_hooks.add_nexthop(nhgc, nh);
625 }
626 }
627 }
628
629 void nexthop_group_disable_vrf(struct vrf *vrf)
630 {
631 struct nexthop_group_cmd *nhgc;
632 struct nexthop_hold *nhh;
633
634 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
635 struct listnode *node;
636
637 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
638 struct nexthop nhop;
639 struct nexthop *nh;
640
641 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
642 nhh->intf,
643 nhh->nhvrf_name))
644 continue;
645
646 nh = nexthop_exists(&nhgc->nhg, &nhop);
647
648 if (!nh)
649 continue;
650
651 if (nh->vrf_id != vrf->vrf_id)
652 continue;
653
654 _nexthop_del(&nhgc->nhg, nh);
655
656 if (nhg_hooks.del_nexthop)
657 nhg_hooks.del_nexthop(nhgc, nh);
658
659 nexthop_free(nh);
660 }
661 }
662 }
663
664 void nexthop_group_interface_state_change(struct interface *ifp,
665 ifindex_t oldifindex)
666 {
667 struct nexthop_group_cmd *nhgc;
668 struct nexthop_hold *nhh;
669
670 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
671 struct listnode *node;
672 struct nexthop *nh;
673
674 if (if_is_up(ifp)) {
675 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
676 struct nexthop nhop;
677
678 if (!nexthop_group_parse_nexthop(
679 &nhop, nhh->addr, nhh->intf,
680 nhh->nhvrf_name))
681 continue;
682
683 switch (nhop.type) {
684 case NEXTHOP_TYPE_IPV4:
685 case NEXTHOP_TYPE_IPV6:
686 case NEXTHOP_TYPE_BLACKHOLE:
687 continue;
688 case NEXTHOP_TYPE_IFINDEX:
689 case NEXTHOP_TYPE_IPV4_IFINDEX:
690 case NEXTHOP_TYPE_IPV6_IFINDEX:
691 break;
692 }
693 nh = nexthop_exists(&nhgc->nhg, &nhop);
694
695 if (nh)
696 continue;
697
698 if (ifp->ifindex != nhop.ifindex)
699 continue;
700
701 nh = nexthop_new();
702
703 memcpy(nh, &nhop, sizeof(nhop));
704 _nexthop_add(&nhgc->nhg.nexthop, nh);
705
706 if (nhg_hooks.add_nexthop)
707 nhg_hooks.add_nexthop(nhgc, nh);
708 }
709 } else {
710 struct nexthop *next_nh;
711
712 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
713 next_nh = nh->next;
714 switch (nh->type) {
715 case NEXTHOP_TYPE_IPV4:
716 case NEXTHOP_TYPE_IPV6:
717 case NEXTHOP_TYPE_BLACKHOLE:
718 continue;
719 case NEXTHOP_TYPE_IFINDEX:
720 case NEXTHOP_TYPE_IPV4_IFINDEX:
721 case NEXTHOP_TYPE_IPV6_IFINDEX:
722 break;
723 }
724
725 if (oldifindex != nh->ifindex)
726 continue;
727
728 _nexthop_del(&nhgc->nhg, nh);
729
730 if (nhg_hooks.del_nexthop)
731 nhg_hooks.del_nexthop(nhgc, nh);
732
733 nexthop_free(nh);
734 }
735 }
736 }
737 }
738
739 static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
740 {
741 struct nexthop_group_cmd *nhgc;
742
743 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
744 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
745 }
746 }
747
748 static const struct cmd_variable_handler nhg_name_handlers[] = {
749 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
750 {.completions = NULL}};
751
752 void nexthop_group_init(void (*new)(const char *name),
753 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
754 const struct nexthop *nhop),
755 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
756 const struct nexthop *nhop),
757 void (*delete)(const char *name))
758 {
759 RB_INIT(nhgc_entry_head, &nhgc_entries);
760
761 cmd_variable_handler_register(nhg_name_handlers);
762
763 install_node(&nexthop_group_node, nexthop_group_write);
764 install_element(CONFIG_NODE, &nexthop_group_cmd);
765 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
766
767 install_default(NH_GROUP_NODE);
768 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
769
770 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
771
772 if (new)
773 nhg_hooks.new = new;
774 if (add_nexthop)
775 nhg_hooks.add_nexthop = add_nexthop;
776 if (del_nexthop)
777 nhg_hooks.del_nexthop = del_nexthop;
778 if (delete)
779 nhg_hooks.delete = delete;
780 }