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