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