]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop_group.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[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
52struct nhgc_entry_head nhgc_entries;
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
191static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
192{
193 int ret;
194
195 ret = sockunion_cmp(&nh1->addr, &nh2->addr);
196 if (ret)
197 return ret;
198
199 ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
200 if (ret)
201 return ret;
202
203 return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
204}
205
206static void nhgl_delete(struct nexthop_hold *nh)
207{
208 if (nh->intf)
209 XFREE(MTYPE_TMP, nh->intf);
210
211 if (nh->nhvrf_name)
212 XFREE(MTYPE_TMP, nh->nhvrf_name);
213
214 XFREE(MTYPE_TMP, nh);
215}
216
31919191
DS
217static struct nexthop_group_cmd *nhgc_get(const char *name)
218{
219 struct nexthop_group_cmd *nhgc;
220
221 nhgc = nhgc_find(name);
222 if (!nhgc) {
223 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
224 strlcpy(nhgc->name, name, sizeof(nhgc->name));
225
226 QOBJ_REG(nhgc, nexthop_group_cmd);
227 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
228
c57bd6bb
DS
229 nhgc->nhg_list = list_new();
230 nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
231 nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
232
31919191
DS
233 if (nhg_hooks.new)
234 nhg_hooks.new(name);
235 }
236
237 return nhgc;
238}
239
240static void nhgc_delete(struct nexthop_group_cmd *nhgc)
dba32923 241{
31919191
DS
242 nhgc_delete_nexthops(nhgc);
243
244 if (nhg_hooks.delete)
245 nhg_hooks.delete(nhgc->name);
246
247 RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
c57bd6bb 248
6a154c88 249 list_delete(&nhgc->nhg_list);
c57bd6bb
DS
250
251 XFREE(MTYPE_TMP, nhgc);
31919191
DS
252}
253
254DEFINE_QOBJ_TYPE(nexthop_group_cmd)
255
256DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME",
257 "Enter into the nexthop-group submode\n"
258 "Specify the NAME of the nexthop-group\n")
259{
260 const char *nhg_name = argv[1]->arg;
261 struct nexthop_group_cmd *nhgc = NULL;
262
263 nhgc = nhgc_get(nhg_name);
264 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
265
266 return CMD_SUCCESS;
267}
268
269DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME",
270 NO_STR
271 "Delete the nexthop-group\n"
272 "Specify the NAME of the nexthop-group\n")
273{
274 const char *nhg_name = argv[2]->arg;
275 struct nexthop_group_cmd *nhgc = NULL;
276
277 nhgc = nhgc_find(nhg_name);
278 if (nhgc)
279 nhgc_delete(nhgc);
280
281 return CMD_SUCCESS;
282}
283
c57bd6bb
DS
284static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
285 const char *nhvrf_name,
286 const union sockunion *addr,
287 const char *intf)
288{
289 struct nexthop_hold *nh;
290
291 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
292
293 if (nhvrf_name)
294 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
295 if (intf)
296 nh->intf = XSTRDUP(MTYPE_TMP, intf);
297
298 nh->addr = *addr;
299
300 listnode_add_sort(nhgc->nhg_list, nh);
301}
302
303static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
304 const char *nhvrf_name,
305 const union sockunion *addr,
306 const char *intf)
307{
308 struct nexthop_hold *nh;
309 struct listnode *node;
310
311 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
312 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
313 sockunion_cmp(addr, &nh->addr) == 0 &&
314 nhgc_cmp_helper(intf, nh->intf) == 0)
315 break;
316 }
317
318 /*
319 * Something has gone seriously wrong, fail gracefully
320 */
321 if (!nh)
322 return;
323
324 list_delete_node(nhgc->nhg_list, node);
325
326 if (nh->nhvrf_name)
327 XFREE(MTYPE_TMP, nh->nhvrf_name);
328 if (nh->intf)
329 XFREE(MTYPE_TMP, nh->intf);
330
331 XFREE(MTYPE_TMP, nh);
332}
333
98cbbaea
DS
334static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
335 const union sockunion *addr,
336 const char *intf, const char *name)
31919191 337{
31919191 338 struct vrf *vrf;
98cbbaea
DS
339
340 memset(nhop, 0, sizeof(*nhop));
31919191
DS
341
342 if (name)
343 vrf = vrf_lookup_by_name(name);
344 else
345 vrf = vrf_lookup_by_id(VRF_DEFAULT);
346
98cbbaea
DS
347 if (!vrf)
348 return false;
31919191 349
98cbbaea 350 nhop->vrf_id = vrf->vrf_id;
31919191
DS
351
352 if (addr->sa.sa_family == AF_INET) {
98cbbaea 353 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
31919191 354 if (intf) {
98cbbaea
DS
355 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
356 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
357 if (nhop->ifindex == IFINDEX_INTERNAL)
358 return false;
31919191 359 } else
98cbbaea 360 nhop->type = NEXTHOP_TYPE_IPV4;
31919191 361 } else {
98cbbaea 362 memcpy(&nhop->gate.ipv6, &addr->sin6.sin6_addr, 16);
31919191 363 if (intf) {
98cbbaea
DS
364 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
365 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
366 if (nhop->ifindex == IFINDEX_INTERNAL)
367 return false;
368 } else
369 nhop->type = NEXTHOP_TYPE_IPV6;
370 }
371
372 return true;
373}
374
375DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
376 "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
377 NO_STR
378 "Specify one of the nexthops in this ECMP group\n"
379 "v4 Address\n"
380 "v6 Address\n"
381 "Interface to use\n"
382 "If the nexthop is in a different vrf tell us\n"
383 "The nexthop-vrf Name\n")
384{
385 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
386 struct nexthop nhop;
387 struct nexthop *nh;
388 bool legal;
389
b51016eb
DS
390 /*
391 * This is impossible to happen as that the cli parser refuses
392 * to let you get here without an addr, but the SA system
393 * does not understand this intricacy
394 */
395 assert(addr);
396
98cbbaea
DS
397 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name);
398
399 if (nhop.type == NEXTHOP_TYPE_IPV6
400 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
401 vty_out(vty,
402 "Specified a v6 LL with no interface, rejecting\n");
403 return CMD_WARNING_CONFIG_FAILED;
31919191
DS
404 }
405
406 nh = nexthop_exists(&nhgc->nhg, &nhop);
407
408 if (no) {
98cbbaea 409 nexthop_group_unsave_nhop(nhgc, name, addr, intf);
31919191
DS
410 if (nh) {
411 nexthop_del(&nhgc->nhg, nh);
412
413 if (nhg_hooks.del_nexthop)
414 nhg_hooks.del_nexthop(nhgc, nh);
415
416 nexthop_free(nh);
417 }
418 } else if (!nh) {
419 /* must be adding new nexthop since !no and !nexthop_exists */
98cbbaea
DS
420 if (legal) {
421 nh = nexthop_new();
31919191 422
98cbbaea
DS
423 memcpy(nh, &nhop, sizeof(nhop));
424 nexthop_add(&nhgc->nhg.nexthop, nh);
425 }
31919191 426
c57bd6bb
DS
427 nexthop_group_save_nhop(nhgc, name, addr, intf);
428
98cbbaea 429 if (legal && nhg_hooks.add_nexthop)
31919191
DS
430 nhg_hooks.add_nexthop(nhgc, nh);
431 }
432
dba32923
DS
433 return CMD_SUCCESS;
434}
435
436struct cmd_node nexthop_group_node = {
437 NH_GROUP_NODE,
438 "%s(config-nh-group)# ",
439 1
440};
441
1b7bce04
DS
442void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
443{
444 char buf[100];
445 struct vrf *vrf;
446
57cdafc4 447 vty_out(vty, "nexthop ");
1b7bce04
DS
448
449 switch (nh->type) {
450 case NEXTHOP_TYPE_IFINDEX:
451 vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id));
452 break;
453 case NEXTHOP_TYPE_IPV4:
454 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
455 break;
456 case NEXTHOP_TYPE_IPV4_IFINDEX:
457 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4),
458 ifindex2ifname(nh->ifindex, nh->vrf_id));
459 break;
460 case NEXTHOP_TYPE_IPV6:
461 vty_out(vty, "%s",
462 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
463 break;
464 case NEXTHOP_TYPE_IPV6_IFINDEX:
465 vty_out(vty, "%s %s",
466 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
467 ifindex2ifname(nh->ifindex, nh->vrf_id));
468 break;
469 case NEXTHOP_TYPE_BLACKHOLE:
470 break;
471 }
472
473 if (nh->vrf_id != VRF_DEFAULT) {
474 vrf = vrf_lookup_by_id(nh->vrf_id);
475 vty_out(vty, " nexthop-vrf %s", vrf->name);
476 }
477 vty_out(vty, "\n");
478}
479
c57bd6bb
DS
480static void nexthop_group_write_nexthop_internal(struct vty *vty,
481 struct nexthop_hold *nh)
482{
483 char buf[100];
484
485 vty_out(vty, "nexthop ");
486
487 vty_out(vty, "%s", sockunion2str(&nh->addr, buf, sizeof(buf)));
488
489 if (nh->intf)
490 vty_out(vty, " %s", nh->intf);
491
492 if (nh->nhvrf_name)
493 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
494
495 vty_out(vty, "\n");
496}
497
dba32923
DS
498static int nexthop_group_write(struct vty *vty)
499{
31919191 500 struct nexthop_group_cmd *nhgc;
c57bd6bb 501 struct nexthop_hold *nh;
31919191
DS
502
503 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
c57bd6bb
DS
504 struct listnode *node;
505
31919191
DS
506 vty_out(vty, "nexthop-group %s\n", nhgc->name);
507
c57bd6bb 508 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
811f859f 509 vty_out(vty, " ");
c57bd6bb 510 nexthop_group_write_nexthop_internal(vty, nh);
811f859f 511 }
31919191 512
31919191
DS
513 vty_out(vty, "!\n");
514 }
dba32923
DS
515
516 return 1;
517}
518
98cbbaea
DS
519void nexthop_group_enable_vrf(struct vrf *vrf)
520{
521 struct nexthop_group_cmd *nhgc;
522 struct nexthop_hold *nhh;
523
524 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
525 struct listnode *node;
526
527 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
528 struct nexthop nhop;
529 struct nexthop *nh;
530
531 if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
532 nhh->intf,
533 nhh->nhvrf_name))
534 continue;
535
536 nh = nexthop_exists(&nhgc->nhg, &nhop);
537
538 if (nh)
539 continue;
540
541 if (nhop.vrf_id != vrf->vrf_id)
542 continue;
543
544 nh = nexthop_new();
545
546 memcpy(nh, &nhop, sizeof(nhop));
547 nexthop_add(&nhgc->nhg.nexthop, nh);
548
549 if (nhg_hooks.add_nexthop)
550 nhg_hooks.add_nexthop(nhgc, nh);
551 }
552 }
553}
554
555void nexthop_group_disable_vrf(struct vrf *vrf)
556{
557 struct nexthop_group_cmd *nhgc;
558 struct nexthop_hold *nhh;
559
560 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
561 struct listnode *node;
562
563 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
564 struct nexthop nhop;
565 struct nexthop *nh;
566
567 if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
568 nhh->intf,
569 nhh->nhvrf_name))
570 continue;
571
572 nh = nexthop_exists(&nhgc->nhg, &nhop);
573
574 if (!nh)
575 continue;
576
577 if (nh->vrf_id != vrf->vrf_id)
578 continue;
579
580 nexthop_del(&nhgc->nhg, nh);
581
582 if (nhg_hooks.del_nexthop)
583 nhg_hooks.del_nexthop(nhgc, nh);
584
585 nexthop_free(nh);
586 }
587 }
588}
589
590void nexthop_group_interface_state_change(struct interface *ifp,
591 ifindex_t oldifindex)
592{
593 struct nexthop_group_cmd *nhgc;
594 struct nexthop_hold *nhh;
595
596 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
597 struct listnode *node;
598 struct nexthop *nh;
599
600 if (if_is_up(ifp)) {
601 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
602 struct nexthop nhop;
603
604 if (!nexthop_group_parse_nexthop(
605 &nhop, &nhh->addr, nhh->intf,
606 nhh->nhvrf_name))
607 continue;
608
609 switch (nhop.type) {
610 case NEXTHOP_TYPE_IPV4:
611 case NEXTHOP_TYPE_IPV6:
612 case NEXTHOP_TYPE_BLACKHOLE:
613 continue;
614 case NEXTHOP_TYPE_IFINDEX:
615 case NEXTHOP_TYPE_IPV4_IFINDEX:
616 case NEXTHOP_TYPE_IPV6_IFINDEX:
617 break;
618 }
619 nh = nexthop_exists(&nhgc->nhg, &nhop);
620
621 if (nh)
622 continue;
623
624 if (ifp->ifindex != nhop.ifindex)
625 continue;
626
627 nh = nexthop_new();
628
629 memcpy(nh, &nhop, sizeof(nhop));
630 nexthop_add(&nhgc->nhg.nexthop, nh);
631
632 if (nhg_hooks.add_nexthop)
633 nhg_hooks.add_nexthop(nhgc, nh);
634 }
635 } else {
636 struct nexthop *next_nh;
637
638 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
639 next_nh = nh->next;
640 switch (nh->type) {
641 case NEXTHOP_TYPE_IPV4:
642 case NEXTHOP_TYPE_IPV6:
643 case NEXTHOP_TYPE_BLACKHOLE:
644 continue;
645 case NEXTHOP_TYPE_IFINDEX:
646 case NEXTHOP_TYPE_IPV4_IFINDEX:
647 case NEXTHOP_TYPE_IPV6_IFINDEX:
648 break;
649 }
650
651 if (oldifindex != nh->ifindex)
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
31919191
DS
665void nexthop_group_init(void (*new)(const char *name),
666 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
667 const struct nexthop *nhop),
668 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
669 const struct nexthop *nhop),
670 void (*delete)(const char *name))
dba32923 671{
31919191
DS
672 RB_INIT(nhgc_entry_head, &nhgc_entries);
673
dba32923
DS
674 install_node(&nexthop_group_node, nexthop_group_write);
675 install_element(CONFIG_NODE, &nexthop_group_cmd);
31919191
DS
676 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
677
678 install_default(NH_GROUP_NODE);
679 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
680
681 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
682
683 if (new)
684 nhg_hooks.new = new;
685 if (add_nexthop)
686 nhg_hooks.add_nexthop = add_nexthop;
687 if (del_nexthop)
688 nhg_hooks.del_nexthop = del_nexthop;
689 if (delete)
690 nhg_hooks.delete = delete;
dba32923 691}