]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
7d8bcd1166c5c4abbce3612989e0360785f905a6
[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 static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
64 {
65 struct nexthop *nexthop = nhg->nexthop;
66
67 while (nexthop && nexthop->next)
68 nexthop = nexthop->next;
69
70 return nexthop;
71 }
72
73 uint8_t nexthop_group_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 num++;
80
81 return num;
82 }
83
84 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
85 {
86 struct nexthop *nhop;
87 uint8_t num = 0;
88
89 for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
90 num++;
91
92 return num;
93 }
94
95 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
96 {
97 struct nexthop *nhop;
98 uint8_t num = 0;
99
100 for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
101 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
102 num++;
103 }
104
105 return num;
106 }
107
108 uint8_t
109 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
110 {
111 struct nexthop *nhop;
112 uint8_t num = 0;
113
114 for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
115 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
116 num++;
117 }
118
119 return num;
120 }
121
122 struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
123 const struct nexthop *nh)
124 {
125 struct nexthop *nexthop;
126
127 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
128 if (nexthop_same(nh, nexthop))
129 return nexthop;
130 }
131
132 return NULL;
133 }
134
135 static bool
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))
140 {
141 if (nhg1 && !nhg2)
142 return false;
143
144 if (!nhg1 && nhg2)
145 return false;
146
147 if (nhg1 == nhg2)
148 return true;
149
150 if (nexthop_group_nexthop_num_func(nhg1)
151 != nexthop_group_nexthop_num_func(nhg2))
152 return false;
153
154 return true;
155 }
156
157 /* This assumes ordered */
158 bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
159 const struct nexthop_group *nhg2)
160 {
161 struct nexthop *nh1 = NULL;
162 struct nexthop *nh2 = NULL;
163
164 if (!nexthop_group_equal_common(nhg1, nhg2,
165 &nexthop_group_nexthop_num_no_recurse))
166 return false;
167
168 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
169 nh1 = nh1->next, nh2 = nh2->next) {
170 if (nh1 && !nh2)
171 return false;
172 if (!nh1 && nh2)
173 return false;
174 if (!nexthop_same(nh1, nh2))
175 return false;
176 }
177
178 return true;
179 }
180
181 /* This assumes ordered */
182 bool nexthop_group_equal(const struct nexthop_group *nhg1,
183 const struct nexthop_group *nhg2)
184 {
185 struct nexthop *nh1 = NULL;
186 struct nexthop *nh2 = NULL;
187
188 if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
189 return false;
190
191 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
192 nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
193 if (nh1 && !nh2)
194 return false;
195 if (!nh1 && nh2)
196 return false;
197 if (!nexthop_same(nh1, nh2))
198 return false;
199 }
200
201 return true;
202 }
203 struct nexthop_group *nexthop_group_new(void)
204 {
205 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
206 }
207
208 void nexthop_group_copy(struct nexthop_group *to, struct nexthop_group *from)
209 {
210 /* Copy everything, including recursive info */
211 copy_nexthops(&to->nexthop, from->nexthop, NULL);
212 }
213
214 void nexthop_group_delete(struct nexthop_group **nhg)
215 {
216 XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
217 }
218
219 void nexthop_group_free_delete(struct nexthop_group **nhg)
220 {
221 if ((*nhg)->nexthop)
222 nexthops_free((*nhg)->nexthop);
223 nexthop_group_delete(nhg);
224 }
225
226 /* Add nexthop to the end of a nexthop list. */
227 void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
228 {
229 struct nexthop *last;
230
231 for (last = *target; last && last->next; last = last->next)
232 ;
233 if (last)
234 last->next = nexthop;
235 else
236 *target = nexthop;
237 nexthop->prev = last;
238 }
239
240 void _nexthop_group_add_sorted(struct nexthop_group *nhg,
241 struct nexthop *nexthop)
242 {
243 struct nexthop *position, *prev, *tail;
244
245 /* Try to just append to the end first
246 * This trust it is already sorted
247 */
248
249 tail = nexthop_group_tail(nhg);
250
251 if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
252 tail->next = nexthop;
253 nexthop->prev = tail;
254
255 return;
256 }
257
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;
263
264 if (nexthop->prev)
265 nexthop->prev->next = nexthop;
266 else
267 nhg->nexthop = nexthop;
268
269 position->prev = nexthop;
270 return;
271 }
272 }
273
274 nexthop->prev = prev;
275 if (prev)
276 prev->next = nexthop;
277 else
278 nhg->nexthop = nexthop;
279 }
280
281 /* Delete nexthop from a nexthop list. */
282 void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
283 {
284 struct nexthop *nexthop;
285
286 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
287 if (nexthop_same(nh, nexthop))
288 break;
289 }
290
291 assert(nexthop);
292
293 if (nexthop->prev)
294 nexthop->prev->next = nexthop->next;
295 else
296 nhg->nexthop = nexthop->next;
297
298 if (nexthop->next)
299 nexthop->next->prev = nexthop->prev;
300
301 nh->prev = NULL;
302 nh->next = NULL;
303 }
304
305 void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
306 struct nexthop *rparent)
307 {
308 struct nexthop *nexthop;
309 const struct nexthop *nh1;
310
311 for (nh1 = nh; nh1; nh1 = nh1->next) {
312 nexthop = nexthop_dup(nh1, rparent);
313 _nexthop_add(tnh, nexthop);
314
315 if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
316 copy_nexthops(&nexthop->resolved, nh1->resolved,
317 nexthop);
318 }
319 }
320
321 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
322 {
323 struct nexthop *nh;
324 uint32_t key = 0;
325
326 /*
327 * We are not interested in hashing over any recursively
328 * resolved nexthops
329 */
330 for (nh = nhg->nexthop; nh; nh = nh->next)
331 key = jhash_1word(nexthop_hash(nh), key);
332
333 return key;
334 }
335
336 uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
337 {
338 struct nexthop *nh;
339 uint32_t key = 0;
340
341 for (ALL_NEXTHOPS_PTR(nhg, nh))
342 key = jhash_1word(nexthop_hash(nh), key);
343
344 return key;
345 }
346
347 void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
348 {
349 struct nexthop *nexthop, *prev;
350
351 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
352 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
353 for (ALL_NEXTHOPS_PTR(nhg, prev)) {
354 if (prev == nexthop)
355 break;
356 if (nexthop_same_firsthop(nexthop, prev)) {
357 SET_FLAG(nexthop->flags,
358 NEXTHOP_FLAG_DUPLICATE);
359 break;
360 }
361 }
362 }
363 }
364
365 static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
366 {
367 struct nexthop *nexthop;
368
369 nexthop = nhgc->nhg.nexthop;
370 while (nexthop) {
371 struct nexthop *next = nexthop_next(nexthop);
372
373 _nexthop_del(&nhgc->nhg, nexthop);
374 if (nhg_hooks.del_nexthop)
375 nhg_hooks.del_nexthop(nhgc, nexthop);
376
377 nexthop_free(nexthop);
378
379 nexthop = next;
380 }
381 }
382
383 struct nexthop_group_cmd *nhgc_find(const char *name)
384 {
385 struct nexthop_group_cmd find;
386
387 strlcpy(find.name, name, sizeof(find.name));
388
389 return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
390 }
391
392 static int nhgc_cmp_helper(const char *a, const char *b)
393 {
394 if (!a && !b)
395 return 0;
396
397 if (a && !b)
398 return -1;
399
400 if (!a && b)
401 return 1;
402
403 return strcmp(a, b);
404 }
405
406 static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
407 {
408 if (!a && !b)
409 return 0;
410
411 if (a && !b)
412 return -1;
413
414 if (!a && b)
415 return 1;
416
417 return sockunion_cmp(a, b);
418 }
419
420 static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
421 {
422 int ret;
423
424 ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
425 if (ret)
426 return ret;
427
428 ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
429 if (ret)
430 return ret;
431
432 return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
433 }
434
435 static void nhgl_delete(struct nexthop_hold *nh)
436 {
437 XFREE(MTYPE_TMP, nh->intf);
438
439 XFREE(MTYPE_TMP, nh->nhvrf_name);
440
441 if (nh->addr)
442 sockunion_free(nh->addr);
443
444 XFREE(MTYPE_TMP, nh);
445 }
446
447 static struct nexthop_group_cmd *nhgc_get(const char *name)
448 {
449 struct nexthop_group_cmd *nhgc;
450
451 nhgc = nhgc_find(name);
452 if (!nhgc) {
453 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
454 strlcpy(nhgc->name, name, sizeof(nhgc->name));
455
456 QOBJ_REG(nhgc, nexthop_group_cmd);
457 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
458
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;
462
463 if (nhg_hooks.new)
464 nhg_hooks.new(name);
465 }
466
467 return nhgc;
468 }
469
470 static void nhgc_delete(struct nexthop_group_cmd *nhgc)
471 {
472 nhgc_delete_nexthops(nhgc);
473
474 if (nhg_hooks.delete)
475 nhg_hooks.delete(nhgc->name);
476
477 RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
478
479 list_delete(&nhgc->nhg_list);
480
481 XFREE(MTYPE_TMP, nhgc);
482 }
483
484 DEFINE_QOBJ_TYPE(nexthop_group_cmd)
485
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")
489 {
490 const char *nhg_name = argv[1]->arg;
491 struct nexthop_group_cmd *nhgc = NULL;
492
493 nhgc = nhgc_get(nhg_name);
494 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
495
496 return CMD_SUCCESS;
497 }
498
499 DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
500 NO_STR
501 "Delete the nexthop-group\n"
502 "Specify the NAME of the nexthop-group\n")
503 {
504 const char *nhg_name = argv[2]->arg;
505 struct nexthop_group_cmd *nhgc = NULL;
506
507 nhgc = nhgc_find(nhg_name);
508 if (nhgc)
509 nhgc_delete(nhgc);
510
511 return CMD_SUCCESS;
512 }
513
514 static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
515 const char *nhvrf_name,
516 const union sockunion *addr,
517 const char *intf)
518 {
519 struct nexthop_hold *nh;
520
521 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
522
523 if (nhvrf_name)
524 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
525 if (intf)
526 nh->intf = XSTRDUP(MTYPE_TMP, intf);
527 if (addr)
528 nh->addr = sockunion_dup(addr);
529
530 listnode_add_sort(nhgc->nhg_list, nh);
531 }
532
533 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
534 const char *nhvrf_name,
535 const union sockunion *addr,
536 const char *intf)
537 {
538 struct nexthop_hold *nh;
539 struct listnode *node;
540
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)
545 break;
546 }
547
548 /*
549 * Something has gone seriously wrong, fail gracefully
550 */
551 if (!nh)
552 return;
553
554 list_delete_node(nhgc->nhg_list, node);
555 nhgl_delete(nh);
556 }
557
558 static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
559 const union sockunion *addr,
560 const char *intf, const char *name)
561 {
562 struct vrf *vrf;
563
564 memset(nhop, 0, sizeof(*nhop));
565
566 if (name)
567 vrf = vrf_lookup_by_name(name);
568 else
569 vrf = vrf_lookup_by_id(VRF_DEFAULT);
570
571 if (!vrf)
572 return false;
573
574 nhop->vrf_id = vrf->vrf_id;
575
576 if (intf) {
577 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
578 if (nhop->ifindex == IFINDEX_INTERNAL)
579 return false;
580 }
581
582 if (addr) {
583 if (addr->sa.sa_family == AF_INET) {
584 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
585 if (intf)
586 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
587 else
588 nhop->type = NEXTHOP_TYPE_IPV4;
589 } else {
590 nhop->gate.ipv6 = addr->sin6.sin6_addr;
591 if (intf)
592 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
593 else
594 nhop->type = NEXTHOP_TYPE_IPV6;
595 }
596 } else
597 nhop->type = NEXTHOP_TYPE_IFINDEX;
598
599 return true;
600 }
601
602 DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
603 "[no] nexthop\
604 <\
605 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
606 |INTERFACE$intf\
607 >\
608 [nexthop-vrf NAME$vrf_name]",
609 NO_STR
610 "Specify one of the nexthops in this ECMP group\n"
611 "v4 Address\n"
612 "v6 Address\n"
613 "Interface to use\n"
614 "Interface to use\n"
615 "If the nexthop is in a different vrf tell us\n"
616 "The nexthop-vrf Name\n")
617 {
618 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
619 struct nexthop nhop;
620 struct nexthop *nh;
621 bool legal;
622
623 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name);
624
625 if (nhop.type == NEXTHOP_TYPE_IPV6
626 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
627 vty_out(vty,
628 "Specified a v6 LL with no interface, rejecting\n");
629 return CMD_WARNING_CONFIG_FAILED;
630 }
631
632 nh = nexthop_exists(&nhgc->nhg, &nhop);
633
634 if (no) {
635 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
636 if (nh) {
637 _nexthop_del(&nhgc->nhg, nh);
638
639 if (nhg_hooks.del_nexthop)
640 nhg_hooks.del_nexthop(nhgc, nh);
641
642 nexthop_free(nh);
643 }
644 } else if (!nh) {
645 /* must be adding new nexthop since !no and !nexthop_exists */
646 if (legal) {
647 nh = nexthop_new();
648
649 memcpy(nh, &nhop, sizeof(nhop));
650 _nexthop_add(&nhgc->nhg.nexthop, nh);
651 }
652
653 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf);
654
655 if (legal && nhg_hooks.add_nexthop)
656 nhg_hooks.add_nexthop(nhgc, nh);
657 }
658
659 return CMD_SUCCESS;
660 }
661
662 struct cmd_node nexthop_group_node = {
663 NH_GROUP_NODE,
664 "%s(config-nh-group)# ",
665 1
666 };
667
668 void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
669 {
670 char buf[100];
671 struct vrf *vrf;
672
673 vty_out(vty, "nexthop ");
674
675 switch (nh->type) {
676 case NEXTHOP_TYPE_IFINDEX:
677 vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id));
678 break;
679 case NEXTHOP_TYPE_IPV4:
680 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
681 break;
682 case NEXTHOP_TYPE_IPV4_IFINDEX:
683 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4),
684 ifindex2ifname(nh->ifindex, nh->vrf_id));
685 break;
686 case NEXTHOP_TYPE_IPV6:
687 vty_out(vty, "%s",
688 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
689 break;
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));
694 break;
695 case NEXTHOP_TYPE_BLACKHOLE:
696 break;
697 }
698
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);
702 }
703 vty_out(vty, "\n");
704 }
705
706 static void nexthop_group_write_nexthop_internal(struct vty *vty,
707 struct nexthop_hold *nh)
708 {
709 char buf[100];
710
711 vty_out(vty, "nexthop");
712
713 if (nh->addr)
714 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
715
716 if (nh->intf)
717 vty_out(vty, " %s", nh->intf);
718
719 if (nh->nhvrf_name)
720 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
721
722 vty_out(vty, "\n");
723 }
724
725 static int nexthop_group_write(struct vty *vty)
726 {
727 struct nexthop_group_cmd *nhgc;
728 struct nexthop_hold *nh;
729
730 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
731 struct listnode *node;
732
733 vty_out(vty, "nexthop-group %s\n", nhgc->name);
734
735 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
736 vty_out(vty, " ");
737 nexthop_group_write_nexthop_internal(vty, nh);
738 }
739
740 vty_out(vty, "!\n");
741 }
742
743 return 1;
744 }
745
746 void nexthop_group_enable_vrf(struct vrf *vrf)
747 {
748 struct nexthop_group_cmd *nhgc;
749 struct nexthop_hold *nhh;
750
751 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
752 struct listnode *node;
753
754 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
755 struct nexthop nhop;
756 struct nexthop *nh;
757
758 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
759 nhh->intf,
760 nhh->nhvrf_name))
761 continue;
762
763 nh = nexthop_exists(&nhgc->nhg, &nhop);
764
765 if (nh)
766 continue;
767
768 if (nhop.vrf_id != vrf->vrf_id)
769 continue;
770
771 nh = nexthop_new();
772
773 memcpy(nh, &nhop, sizeof(nhop));
774 _nexthop_add(&nhgc->nhg.nexthop, nh);
775
776 if (nhg_hooks.add_nexthop)
777 nhg_hooks.add_nexthop(nhgc, nh);
778 }
779 }
780 }
781
782 void nexthop_group_disable_vrf(struct vrf *vrf)
783 {
784 struct nexthop_group_cmd *nhgc;
785 struct nexthop_hold *nhh;
786
787 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
788 struct listnode *node;
789
790 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
791 struct nexthop nhop;
792 struct nexthop *nh;
793
794 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
795 nhh->intf,
796 nhh->nhvrf_name))
797 continue;
798
799 nh = nexthop_exists(&nhgc->nhg, &nhop);
800
801 if (!nh)
802 continue;
803
804 if (nh->vrf_id != vrf->vrf_id)
805 continue;
806
807 _nexthop_del(&nhgc->nhg, nh);
808
809 if (nhg_hooks.del_nexthop)
810 nhg_hooks.del_nexthop(nhgc, nh);
811
812 nexthop_free(nh);
813 }
814 }
815 }
816
817 void nexthop_group_interface_state_change(struct interface *ifp,
818 ifindex_t oldifindex)
819 {
820 struct nexthop_group_cmd *nhgc;
821 struct nexthop_hold *nhh;
822
823 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
824 struct listnode *node;
825 struct nexthop *nh;
826
827 if (if_is_up(ifp)) {
828 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
829 struct nexthop nhop;
830
831 if (!nexthop_group_parse_nexthop(
832 &nhop, nhh->addr, nhh->intf,
833 nhh->nhvrf_name))
834 continue;
835
836 switch (nhop.type) {
837 case NEXTHOP_TYPE_IPV4:
838 case NEXTHOP_TYPE_IPV6:
839 case NEXTHOP_TYPE_BLACKHOLE:
840 continue;
841 case NEXTHOP_TYPE_IFINDEX:
842 case NEXTHOP_TYPE_IPV4_IFINDEX:
843 case NEXTHOP_TYPE_IPV6_IFINDEX:
844 break;
845 }
846 nh = nexthop_exists(&nhgc->nhg, &nhop);
847
848 if (nh)
849 continue;
850
851 if (ifp->ifindex != nhop.ifindex)
852 continue;
853
854 nh = nexthop_new();
855
856 memcpy(nh, &nhop, sizeof(nhop));
857 _nexthop_add(&nhgc->nhg.nexthop, nh);
858
859 if (nhg_hooks.add_nexthop)
860 nhg_hooks.add_nexthop(nhgc, nh);
861 }
862 } else {
863 struct nexthop *next_nh;
864
865 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
866 next_nh = nh->next;
867 switch (nh->type) {
868 case NEXTHOP_TYPE_IPV4:
869 case NEXTHOP_TYPE_IPV6:
870 case NEXTHOP_TYPE_BLACKHOLE:
871 continue;
872 case NEXTHOP_TYPE_IFINDEX:
873 case NEXTHOP_TYPE_IPV4_IFINDEX:
874 case NEXTHOP_TYPE_IPV6_IFINDEX:
875 break;
876 }
877
878 if (oldifindex != nh->ifindex)
879 continue;
880
881 _nexthop_del(&nhgc->nhg, nh);
882
883 if (nhg_hooks.del_nexthop)
884 nhg_hooks.del_nexthop(nhgc, nh);
885
886 nexthop_free(nh);
887 }
888 }
889 }
890 }
891
892 static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
893 {
894 struct nexthop_group_cmd *nhgc;
895
896 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
897 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
898 }
899 }
900
901 static const struct cmd_variable_handler nhg_name_handlers[] = {
902 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
903 {.completions = NULL}};
904
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))
911 {
912 RB_INIT(nhgc_entry_head, &nhgc_entries);
913
914 cmd_variable_handler_register(nhg_name_handlers);
915
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);
919
920 install_default(NH_GROUP_NODE);
921 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
922
923 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
924
925 if (new)
926 nhg_hooks.new = new;
927 if (add_nexthop)
928 nhg_hooks.add_nexthop = add_nexthop;
929 if (del_nexthop)
930 nhg_hooks.del_nexthop = del_nexthop;
931 if (delete)
932 nhg_hooks.delete = delete;
933 }