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