2 * Nexthop Group structure definition.
3 * Copyright (C) 2018 Cumulus Networks, Inc.
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)
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
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
23 #include <sockunion.h>
25 #include <nexthop_group.h>
26 #include <nexthop_group_private.h>
31 #ifndef VTYSH_EXTRACT_PL
32 #include "lib/nexthop_group_clippy.c"
35 DEFINE_MTYPE_STATIC(LIB
, NEXTHOP_GROUP
, "Nexthop Group")
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
);
46 static struct nexthop_group_hooks nhg_hooks
;
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
)
54 static struct nhgc_entry_head nhgc_entries
;
57 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
58 const struct nexthop_group_cmd
*nhgc2
)
60 return strcmp(nhgc1
->name
, nhgc2
->name
);
63 static struct nexthop
*nexthop_group_tail(const struct nexthop_group
*nhg
)
65 struct nexthop
*nexthop
= nhg
->nexthop
;
67 while (nexthop
&& nexthop
->next
)
68 nexthop
= nexthop
->next
;
73 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
78 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
84 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
89 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
)
95 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
100 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
101 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
109 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
111 struct nexthop
*nhop
;
114 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
) {
115 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
122 struct nexthop
*nexthop_exists(const struct nexthop_group
*nhg
,
123 const struct nexthop
*nh
)
125 struct nexthop
*nexthop
;
127 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
128 if (nexthop_same(nh
, nexthop
))
136 nexthop_group_equal_common(const struct nexthop_group
*nhg1
,
137 const struct nexthop_group
*nhg2
,
138 uint8_t (*nexthop_group_nexthop_num_func
)(
139 const struct nexthop_group
*nhg
))
150 if (nexthop_group_nexthop_num_func(nhg1
)
151 != nexthop_group_nexthop_num_func(nhg2
))
157 /* This assumes ordered */
158 bool nexthop_group_equal_no_recurse(const struct nexthop_group
*nhg1
,
159 const struct nexthop_group
*nhg2
)
161 struct nexthop
*nh1
= NULL
;
162 struct nexthop
*nh2
= NULL
;
164 if (!nexthop_group_equal_common(nhg1
, nhg2
,
165 &nexthop_group_nexthop_num_no_recurse
))
168 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
169 nh1
= nh1
->next
, nh2
= nh2
->next
) {
174 if (!nexthop_same(nh1
, nh2
))
181 /* This assumes ordered */
182 bool nexthop_group_equal(const struct nexthop_group
*nhg1
,
183 const struct nexthop_group
*nhg2
)
185 struct nexthop
*nh1
= NULL
;
186 struct nexthop
*nh2
= NULL
;
188 if (!nexthop_group_equal_common(nhg1
, nhg2
, &nexthop_group_nexthop_num
))
191 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
192 nh1
= nexthop_next(nh1
), nh2
= nexthop_next(nh2
)) {
197 if (!nexthop_same(nh1
, nh2
))
203 struct nexthop_group
*nexthop_group_new(void)
205 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
208 void nexthop_group_copy(struct nexthop_group
*to
, struct nexthop_group
*from
)
210 /* Copy everything, including recursive info */
211 copy_nexthops(&to
->nexthop
, from
->nexthop
, NULL
);
214 void nexthop_group_delete(struct nexthop_group
**nhg
)
216 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
219 void nexthop_group_free_delete(struct nexthop_group
**nhg
)
222 nexthops_free((*nhg
)->nexthop
);
223 nexthop_group_delete(nhg
);
226 /* Add nexthop to the end of a nexthop list. */
227 void _nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
229 struct nexthop
*last
;
231 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
234 last
->next
= nexthop
;
237 nexthop
->prev
= last
;
240 void _nexthop_group_add_sorted(struct nexthop_group
*nhg
,
241 struct nexthop
*nexthop
)
243 struct nexthop
*position
, *prev
, *tail
;
245 /* Try to just append to the end first
246 * This trust it is already sorted
249 tail
= nexthop_group_tail(nhg
);
251 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
252 tail
->next
= nexthop
;
253 nexthop
->prev
= tail
;
258 for (position
= nhg
->nexthop
, prev
= NULL
; position
;
259 prev
= position
, position
= position
->next
) {
260 if (nexthop_cmp(position
, nexthop
) > 0) {
261 nexthop
->next
= position
;
262 nexthop
->prev
= prev
;
265 nexthop
->prev
->next
= nexthop
;
267 nhg
->nexthop
= nexthop
;
269 position
->prev
= nexthop
;
274 nexthop
->prev
= prev
;
276 prev
->next
= nexthop
;
278 nhg
->nexthop
= nexthop
;
281 /* Delete nexthop from a nexthop list. */
282 void _nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
284 struct nexthop
*nexthop
;
286 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
287 if (nexthop_same(nh
, nexthop
))
294 nexthop
->prev
->next
= nexthop
->next
;
296 nhg
->nexthop
= nexthop
->next
;
299 nexthop
->next
->prev
= nexthop
->prev
;
305 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
306 struct nexthop
*rparent
)
308 struct nexthop
*nexthop
;
309 const struct nexthop
*nh1
;
311 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
312 nexthop
= nexthop_dup(nh1
, rparent
);
313 _nexthop_add(tnh
, nexthop
);
315 if (CHECK_FLAG(nh1
->flags
, NEXTHOP_FLAG_RECURSIVE
))
316 copy_nexthops(&nexthop
->resolved
, nh1
->resolved
,
321 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group
*nhg
)
327 * We are not interested in hashing over any recursively
330 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
331 key
= jhash_1word(nexthop_hash(nh
), key
);
336 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
341 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
342 key
= jhash_1word(nexthop_hash(nh
), key
);
347 void nexthop_group_mark_duplicates(struct nexthop_group
*nhg
)
349 struct nexthop
*nexthop
, *prev
;
351 for (ALL_NEXTHOPS_PTR(nhg
, nexthop
)) {
352 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
);
353 for (ALL_NEXTHOPS_PTR(nhg
, prev
)) {
356 if (nexthop_same_firsthop(nexthop
, prev
)) {
357 SET_FLAG(nexthop
->flags
,
358 NEXTHOP_FLAG_DUPLICATE
);
365 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
367 struct nexthop
*nexthop
;
369 nexthop
= nhgc
->nhg
.nexthop
;
371 struct nexthop
*next
= nexthop_next(nexthop
);
373 _nexthop_del(&nhgc
->nhg
, nexthop
);
374 if (nhg_hooks
.del_nexthop
)
375 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
377 nexthop_free(nexthop
);
383 struct nexthop_group_cmd
*nhgc_find(const char *name
)
385 struct nexthop_group_cmd find
;
387 strlcpy(find
.name
, name
, sizeof(find
.name
));
389 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
392 static int nhgc_cmp_helper(const char *a
, const char *b
)
406 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
417 return sockunion_cmp(a
, b
);
420 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
424 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
428 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
432 return nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
435 static void nhgl_delete(struct nexthop_hold
*nh
)
437 XFREE(MTYPE_TMP
, nh
->intf
);
439 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
442 sockunion_free(nh
->addr
);
444 XFREE(MTYPE_TMP
, nh
);
447 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
449 struct nexthop_group_cmd
*nhgc
;
451 nhgc
= nhgc_find(name
);
453 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
454 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
456 QOBJ_REG(nhgc
, nexthop_group_cmd
);
457 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
459 nhgc
->nhg_list
= list_new();
460 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
461 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
470 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
472 nhgc_delete_nexthops(nhgc
);
474 if (nhg_hooks
.delete)
475 nhg_hooks
.delete(nhgc
->name
);
477 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
479 list_delete(&nhgc
->nhg_list
);
481 XFREE(MTYPE_TMP
, nhgc
);
484 DEFINE_QOBJ_TYPE(nexthop_group_cmd
)
486 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NHGNAME",
487 "Enter into the nexthop-group submode\n"
488 "Specify the NAME of the nexthop-group\n")
490 const char *nhg_name
= argv
[1]->arg
;
491 struct nexthop_group_cmd
*nhgc
= NULL
;
493 nhgc
= nhgc_get(nhg_name
);
494 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
499 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NHGNAME",
501 "Delete the nexthop-group\n"
502 "Specify the NAME of the nexthop-group\n")
504 const char *nhg_name
= argv
[2]->arg
;
505 struct nexthop_group_cmd
*nhgc
= NULL
;
507 nhgc
= nhgc_find(nhg_name
);
514 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
515 const char *nhvrf_name
,
516 const union sockunion
*addr
,
519 struct nexthop_hold
*nh
;
521 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
524 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
526 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
528 nh
->addr
= sockunion_dup(addr
);
530 listnode_add_sort(nhgc
->nhg_list
, nh
);
533 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
534 const char *nhvrf_name
,
535 const union sockunion
*addr
,
538 struct nexthop_hold
*nh
;
539 struct listnode
*node
;
541 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
542 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0 &&
543 nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0 &&
544 nhgc_cmp_helper(intf
, nh
->intf
) == 0)
549 * Something has gone seriously wrong, fail gracefully
554 list_delete_node(nhgc
->nhg_list
, node
);
558 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
559 const union sockunion
*addr
,
560 const char *intf
, const char *name
)
564 memset(nhop
, 0, sizeof(*nhop
));
567 vrf
= vrf_lookup_by_name(name
);
569 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
574 nhop
->vrf_id
= vrf
->vrf_id
;
577 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
578 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
583 if (addr
->sa
.sa_family
== AF_INET
) {
584 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
586 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
588 nhop
->type
= NEXTHOP_TYPE_IPV4
;
590 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
592 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
594 nhop
->type
= NEXTHOP_TYPE_IPV6
;
597 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
602 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
605 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
608 [nexthop-vrf NAME$vrf_name]",
610 "Specify one of the nexthops in this ECMP group\n"
615 "If the nexthop is in a different vrf tell us\n"
616 "The nexthop-vrf Name\n")
618 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
623 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, vrf_name
);
625 if (nhop
.type
== NEXTHOP_TYPE_IPV6
626 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
628 "Specified a v6 LL with no interface, rejecting\n");
629 return CMD_WARNING_CONFIG_FAILED
;
632 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
635 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
637 _nexthop_del(&nhgc
->nhg
, nh
);
639 if (nhg_hooks
.del_nexthop
)
640 nhg_hooks
.del_nexthop(nhgc
, nh
);
645 /* must be adding new nexthop since !no and !nexthop_exists */
649 memcpy(nh
, &nhop
, sizeof(nhop
));
650 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
653 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
);
655 if (legal
&& nhg_hooks
.add_nexthop
)
656 nhg_hooks
.add_nexthop(nhgc
, nh
);
662 struct cmd_node nexthop_group_node
= {
664 "%s(config-nh-group)# ",
668 void nexthop_group_write_nexthop(struct vty
*vty
, struct nexthop
*nh
)
673 vty_out(vty
, "nexthop ");
676 case NEXTHOP_TYPE_IFINDEX
:
677 vty_out(vty
, "%s", ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
679 case NEXTHOP_TYPE_IPV4
:
680 vty_out(vty
, "%s", inet_ntoa(nh
->gate
.ipv4
));
682 case NEXTHOP_TYPE_IPV4_IFINDEX
:
683 vty_out(vty
, "%s %s", inet_ntoa(nh
->gate
.ipv4
),
684 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
686 case NEXTHOP_TYPE_IPV6
:
688 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)));
690 case NEXTHOP_TYPE_IPV6_IFINDEX
:
691 vty_out(vty
, "%s %s",
692 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)),
693 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
695 case NEXTHOP_TYPE_BLACKHOLE
:
699 if (nh
->vrf_id
!= VRF_DEFAULT
) {
700 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
701 vty_out(vty
, " nexthop-vrf %s", vrf
->name
);
706 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
707 struct nexthop_hold
*nh
)
711 vty_out(vty
, "nexthop");
714 vty_out(vty
, " %s", sockunion2str(nh
->addr
, buf
, sizeof(buf
)));
717 vty_out(vty
, " %s", nh
->intf
);
720 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
725 static int nexthop_group_write(struct vty
*vty
)
727 struct nexthop_group_cmd
*nhgc
;
728 struct nexthop_hold
*nh
;
730 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
731 struct listnode
*node
;
733 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
735 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
737 nexthop_group_write_nexthop_internal(vty
, nh
);
746 void nexthop_group_enable_vrf(struct vrf
*vrf
)
748 struct nexthop_group_cmd
*nhgc
;
749 struct nexthop_hold
*nhh
;
751 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
752 struct listnode
*node
;
754 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
758 if (!nexthop_group_parse_nexthop(&nhop
, nhh
->addr
,
763 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
768 if (nhop
.vrf_id
!= vrf
->vrf_id
)
773 memcpy(nh
, &nhop
, sizeof(nhop
));
774 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
776 if (nhg_hooks
.add_nexthop
)
777 nhg_hooks
.add_nexthop(nhgc
, nh
);
782 void nexthop_group_disable_vrf(struct vrf
*vrf
)
784 struct nexthop_group_cmd
*nhgc
;
785 struct nexthop_hold
*nhh
;
787 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
788 struct listnode
*node
;
790 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
794 if (!nexthop_group_parse_nexthop(&nhop
, nhh
->addr
,
799 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
804 if (nh
->vrf_id
!= vrf
->vrf_id
)
807 _nexthop_del(&nhgc
->nhg
, nh
);
809 if (nhg_hooks
.del_nexthop
)
810 nhg_hooks
.del_nexthop(nhgc
, nh
);
817 void nexthop_group_interface_state_change(struct interface
*ifp
,
818 ifindex_t oldifindex
)
820 struct nexthop_group_cmd
*nhgc
;
821 struct nexthop_hold
*nhh
;
823 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
824 struct listnode
*node
;
828 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
831 if (!nexthop_group_parse_nexthop(
832 &nhop
, nhh
->addr
, nhh
->intf
,
837 case NEXTHOP_TYPE_IPV4
:
838 case NEXTHOP_TYPE_IPV6
:
839 case NEXTHOP_TYPE_BLACKHOLE
:
841 case NEXTHOP_TYPE_IFINDEX
:
842 case NEXTHOP_TYPE_IPV4_IFINDEX
:
843 case NEXTHOP_TYPE_IPV6_IFINDEX
:
846 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
851 if (ifp
->ifindex
!= nhop
.ifindex
)
856 memcpy(nh
, &nhop
, sizeof(nhop
));
857 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
859 if (nhg_hooks
.add_nexthop
)
860 nhg_hooks
.add_nexthop(nhgc
, nh
);
863 struct nexthop
*next_nh
;
865 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
868 case NEXTHOP_TYPE_IPV4
:
869 case NEXTHOP_TYPE_IPV6
:
870 case NEXTHOP_TYPE_BLACKHOLE
:
872 case NEXTHOP_TYPE_IFINDEX
:
873 case NEXTHOP_TYPE_IPV4_IFINDEX
:
874 case NEXTHOP_TYPE_IPV6_IFINDEX
:
878 if (oldifindex
!= nh
->ifindex
)
881 _nexthop_del(&nhgc
->nhg
, nh
);
883 if (nhg_hooks
.del_nexthop
)
884 nhg_hooks
.del_nexthop(nhgc
, nh
);
892 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
894 struct nexthop_group_cmd
*nhgc
;
896 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
897 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
901 static const struct cmd_variable_handler nhg_name_handlers
[] = {
902 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
903 {.completions
= NULL
}};
905 void nexthop_group_init(void (*new)(const char *name
),
906 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
907 const struct nexthop
*nhop
),
908 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
909 const struct nexthop
*nhop
),
910 void (*delete)(const char *name
))
912 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
914 cmd_variable_handler_register(nhg_name_handlers
);
916 install_node(&nexthop_group_node
, nexthop_group_write
);
917 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
918 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
920 install_default(NH_GROUP_NODE
);
921 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
923 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
928 nhg_hooks
.add_nexthop
= add_nexthop
;
930 nhg_hooks
.del_nexthop
= del_nexthop
;
932 nhg_hooks
.delete = delete;