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