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