]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop_group.c
lib: Call nexthop g_addr hashes together
[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
31919191
DS
305static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
306{
307 struct nexthop *nexthop;
308
309 nexthop = nhgc->nhg.nexthop;
310 while (nexthop) {
311 struct nexthop *next = nexthop_next(nexthop);
312
50d89650 313 _nexthop_del(&nhgc->nhg, nexthop);
31919191
DS
314 if (nhg_hooks.del_nexthop)
315 nhg_hooks.del_nexthop(nhgc, nexthop);
316
317 nexthop_free(nexthop);
318
319 nexthop = next;
320 }
321}
322
d604266c 323struct nexthop_group_cmd *nhgc_find(const char *name)
31919191
DS
324{
325 struct nexthop_group_cmd find;
326
327 strlcpy(find.name, name, sizeof(find.name));
328
329 return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
330}
331
c57bd6bb
DS
332static int nhgc_cmp_helper(const char *a, const char *b)
333{
334 if (!a && !b)
335 return 0;
336
337 if (a && !b)
338 return -1;
339
340 if (!a && b)
341 return 1;
342
343 return strcmp(a, b);
344}
345
1c869b64
RW
346static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
347{
348 if (!a && !b)
349 return 0;
350
351 if (a && !b)
352 return -1;
353
354 if (!a && b)
355 return 1;
356
357 return sockunion_cmp(a, b);
358}
359
c57bd6bb
DS
360static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
361{
362 int ret;
363
1c869b64 364 ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
c57bd6bb
DS
365 if (ret)
366 return ret;
367
368 ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
369 if (ret)
370 return ret;
371
372 return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
373}
374
375static void nhgl_delete(struct nexthop_hold *nh)
376{
0a22ddfb 377 XFREE(MTYPE_TMP, nh->intf);
c57bd6bb 378
0a22ddfb 379 XFREE(MTYPE_TMP, nh->nhvrf_name);
c57bd6bb 380
1c869b64
RW
381 if (nh->addr)
382 sockunion_free(nh->addr);
b43bb64f 383
c57bd6bb
DS
384 XFREE(MTYPE_TMP, nh);
385}
386
31919191
DS
387static struct nexthop_group_cmd *nhgc_get(const char *name)
388{
389 struct nexthop_group_cmd *nhgc;
390
391 nhgc = nhgc_find(name);
392 if (!nhgc) {
393 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
394 strlcpy(nhgc->name, name, sizeof(nhgc->name));
395
396 QOBJ_REG(nhgc, nexthop_group_cmd);
397 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
398
c57bd6bb
DS
399 nhgc->nhg_list = list_new();
400 nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
401 nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
402
31919191
DS
403 if (nhg_hooks.new)
404 nhg_hooks.new(name);
405 }
406
407 return nhgc;
408}
409
410static void nhgc_delete(struct nexthop_group_cmd *nhgc)
dba32923 411{
31919191
DS
412 nhgc_delete_nexthops(nhgc);
413
414 if (nhg_hooks.delete)
415 nhg_hooks.delete(nhgc->name);
416
417 RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
c57bd6bb 418
6a154c88 419 list_delete(&nhgc->nhg_list);
c57bd6bb
DS
420
421 XFREE(MTYPE_TMP, nhgc);
31919191
DS
422}
423
424DEFINE_QOBJ_TYPE(nexthop_group_cmd)
425
868ee86c 426DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
31919191
DS
427 "Enter into the nexthop-group submode\n"
428 "Specify the NAME of the nexthop-group\n")
429{
430 const char *nhg_name = argv[1]->arg;
431 struct nexthop_group_cmd *nhgc = NULL;
432
433 nhgc = nhgc_get(nhg_name);
434 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
435
436 return CMD_SUCCESS;
437}
438
868ee86c 439DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
31919191
DS
440 NO_STR
441 "Delete the nexthop-group\n"
442 "Specify the NAME of the nexthop-group\n")
443{
444 const char *nhg_name = argv[2]->arg;
445 struct nexthop_group_cmd *nhgc = NULL;
446
447 nhgc = nhgc_find(nhg_name);
448 if (nhgc)
449 nhgc_delete(nhgc);
450
451 return CMD_SUCCESS;
452}
453
c57bd6bb
DS
454static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
455 const char *nhvrf_name,
456 const union sockunion *addr,
457 const char *intf)
458{
459 struct nexthop_hold *nh;
460
461 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
462
463 if (nhvrf_name)
464 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
465 if (intf)
466 nh->intf = XSTRDUP(MTYPE_TMP, intf);
1c869b64
RW
467 if (addr)
468 nh->addr = sockunion_dup(addr);
c57bd6bb
DS
469
470 listnode_add_sort(nhgc->nhg_list, nh);
471}
472
473static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
474 const char *nhvrf_name,
475 const union sockunion *addr,
476 const char *intf)
477{
478 struct nexthop_hold *nh;
479 struct listnode *node;
480
481 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
482 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
1c869b64 483 nhgc_addr_cmp_helper(addr, nh->addr) == 0 &&
c57bd6bb
DS
484 nhgc_cmp_helper(intf, nh->intf) == 0)
485 break;
486 }
487
488 /*
489 * Something has gone seriously wrong, fail gracefully
490 */
491 if (!nh)
492 return;
493
494 list_delete_node(nhgc->nhg_list, node);
e5a501c2 495 nhgl_delete(nh);
c57bd6bb
DS
496}
497
98cbbaea
DS
498static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
499 const union sockunion *addr,
500 const char *intf, const char *name)
31919191 501{
31919191 502 struct vrf *vrf;
98cbbaea
DS
503
504 memset(nhop, 0, sizeof(*nhop));
31919191
DS
505
506 if (name)
507 vrf = vrf_lookup_by_name(name);
508 else
509 vrf = vrf_lookup_by_id(VRF_DEFAULT);
510
98cbbaea
DS
511 if (!vrf)
512 return false;
31919191 513
98cbbaea 514 nhop->vrf_id = vrf->vrf_id;
31919191 515
1c869b64
RW
516 if (intf) {
517 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
518 if (nhop->ifindex == IFINDEX_INTERNAL)
519 return false;
98cbbaea
DS
520 }
521
1c869b64
RW
522 if (addr) {
523 if (addr->sa.sa_family == AF_INET) {
524 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
525 if (intf)
526 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
527 else
528 nhop->type = NEXTHOP_TYPE_IPV4;
529 } else {
530 nhop->gate.ipv6 = addr->sin6.sin6_addr;
531 if (intf)
532 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
533 else
534 nhop->type = NEXTHOP_TYPE_IPV6;
535 }
536 } else
537 nhop->type = NEXTHOP_TYPE_IFINDEX;
538
98cbbaea
DS
539 return true;
540}
541
542DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
1c869b64
RW
543 "[no] nexthop\
544 <\
545 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
546 |INTERFACE$intf\
547 >\
e429a2a0 548 [nexthop-vrf NAME$vrf_name]",
98cbbaea
DS
549 NO_STR
550 "Specify one of the nexthops in this ECMP group\n"
551 "v4 Address\n"
552 "v6 Address\n"
553 "Interface to use\n"
1c869b64 554 "Interface to use\n"
98cbbaea
DS
555 "If the nexthop is in a different vrf tell us\n"
556 "The nexthop-vrf Name\n")
557{
558 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
559 struct nexthop nhop;
560 struct nexthop *nh;
561 bool legal;
562
e429a2a0 563 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name);
98cbbaea
DS
564
565 if (nhop.type == NEXTHOP_TYPE_IPV6
566 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
567 vty_out(vty,
568 "Specified a v6 LL with no interface, rejecting\n");
569 return CMD_WARNING_CONFIG_FAILED;
31919191
DS
570 }
571
572 nh = nexthop_exists(&nhgc->nhg, &nhop);
573
574 if (no) {
e429a2a0 575 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
31919191 576 if (nh) {
50d89650 577 _nexthop_del(&nhgc->nhg, nh);
31919191
DS
578
579 if (nhg_hooks.del_nexthop)
580 nhg_hooks.del_nexthop(nhgc, nh);
581
582 nexthop_free(nh);
583 }
584 } else if (!nh) {
585 /* must be adding new nexthop since !no and !nexthop_exists */
98cbbaea
DS
586 if (legal) {
587 nh = nexthop_new();
31919191 588
98cbbaea 589 memcpy(nh, &nhop, sizeof(nhop));
50d89650 590 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea 591 }
31919191 592
e429a2a0 593 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf);
c57bd6bb 594
98cbbaea 595 if (legal && nhg_hooks.add_nexthop)
31919191
DS
596 nhg_hooks.add_nexthop(nhgc, nh);
597 }
598
dba32923
DS
599 return CMD_SUCCESS;
600}
601
602struct cmd_node nexthop_group_node = {
603 NH_GROUP_NODE,
604 "%s(config-nh-group)# ",
605 1
606};
607
1b7bce04
DS
608void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
609{
610 char buf[100];
611 struct vrf *vrf;
612
57cdafc4 613 vty_out(vty, "nexthop ");
1b7bce04
DS
614
615 switch (nh->type) {
616 case NEXTHOP_TYPE_IFINDEX:
617 vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id));
618 break;
619 case NEXTHOP_TYPE_IPV4:
620 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
621 break;
622 case NEXTHOP_TYPE_IPV4_IFINDEX:
623 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4),
624 ifindex2ifname(nh->ifindex, nh->vrf_id));
625 break;
626 case NEXTHOP_TYPE_IPV6:
627 vty_out(vty, "%s",
628 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
629 break;
630 case NEXTHOP_TYPE_IPV6_IFINDEX:
631 vty_out(vty, "%s %s",
632 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
633 ifindex2ifname(nh->ifindex, nh->vrf_id));
634 break;
635 case NEXTHOP_TYPE_BLACKHOLE:
636 break;
637 }
638
639 if (nh->vrf_id != VRF_DEFAULT) {
640 vrf = vrf_lookup_by_id(nh->vrf_id);
641 vty_out(vty, " nexthop-vrf %s", vrf->name);
642 }
643 vty_out(vty, "\n");
644}
645
c57bd6bb
DS
646static void nexthop_group_write_nexthop_internal(struct vty *vty,
647 struct nexthop_hold *nh)
648{
649 char buf[100];
650
1c869b64 651 vty_out(vty, "nexthop");
c57bd6bb 652
1c869b64
RW
653 if (nh->addr)
654 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
c57bd6bb
DS
655
656 if (nh->intf)
657 vty_out(vty, " %s", nh->intf);
658
659 if (nh->nhvrf_name)
660 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
661
662 vty_out(vty, "\n");
663}
664
dba32923
DS
665static int nexthop_group_write(struct vty *vty)
666{
31919191 667 struct nexthop_group_cmd *nhgc;
c57bd6bb 668 struct nexthop_hold *nh;
31919191
DS
669
670 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
c57bd6bb
DS
671 struct listnode *node;
672
31919191
DS
673 vty_out(vty, "nexthop-group %s\n", nhgc->name);
674
c57bd6bb 675 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
7dce96f0 676 vty_out(vty, " ");
c57bd6bb 677 nexthop_group_write_nexthop_internal(vty, nh);
811f859f 678 }
31919191 679
31919191
DS
680 vty_out(vty, "!\n");
681 }
dba32923
DS
682
683 return 1;
684}
685
98cbbaea
DS
686void nexthop_group_enable_vrf(struct vrf *vrf)
687{
688 struct nexthop_group_cmd *nhgc;
689 struct nexthop_hold *nhh;
690
691 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
692 struct listnode *node;
693
694 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
695 struct nexthop nhop;
696 struct nexthop *nh;
697
b43bb64f 698 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
98cbbaea
DS
699 nhh->intf,
700 nhh->nhvrf_name))
701 continue;
702
703 nh = nexthop_exists(&nhgc->nhg, &nhop);
704
705 if (nh)
706 continue;
707
708 if (nhop.vrf_id != vrf->vrf_id)
709 continue;
710
711 nh = nexthop_new();
712
713 memcpy(nh, &nhop, sizeof(nhop));
50d89650 714 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
715
716 if (nhg_hooks.add_nexthop)
717 nhg_hooks.add_nexthop(nhgc, nh);
718 }
719 }
720}
721
722void nexthop_group_disable_vrf(struct vrf *vrf)
723{
724 struct nexthop_group_cmd *nhgc;
725 struct nexthop_hold *nhh;
726
727 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
728 struct listnode *node;
729
730 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
731 struct nexthop nhop;
732 struct nexthop *nh;
733
b43bb64f 734 if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
98cbbaea
DS
735 nhh->intf,
736 nhh->nhvrf_name))
737 continue;
738
739 nh = nexthop_exists(&nhgc->nhg, &nhop);
740
741 if (!nh)
742 continue;
743
744 if (nh->vrf_id != vrf->vrf_id)
745 continue;
746
50d89650 747 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
748
749 if (nhg_hooks.del_nexthop)
750 nhg_hooks.del_nexthop(nhgc, nh);
751
752 nexthop_free(nh);
753 }
754 }
755}
756
757void nexthop_group_interface_state_change(struct interface *ifp,
758 ifindex_t oldifindex)
759{
760 struct nexthop_group_cmd *nhgc;
761 struct nexthop_hold *nhh;
762
763 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
764 struct listnode *node;
765 struct nexthop *nh;
766
767 if (if_is_up(ifp)) {
768 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
769 struct nexthop nhop;
770
771 if (!nexthop_group_parse_nexthop(
b43bb64f 772 &nhop, nhh->addr, nhh->intf,
98cbbaea
DS
773 nhh->nhvrf_name))
774 continue;
775
776 switch (nhop.type) {
777 case NEXTHOP_TYPE_IPV4:
778 case NEXTHOP_TYPE_IPV6:
779 case NEXTHOP_TYPE_BLACKHOLE:
780 continue;
781 case NEXTHOP_TYPE_IFINDEX:
782 case NEXTHOP_TYPE_IPV4_IFINDEX:
783 case NEXTHOP_TYPE_IPV6_IFINDEX:
784 break;
785 }
786 nh = nexthop_exists(&nhgc->nhg, &nhop);
787
788 if (nh)
789 continue;
790
791 if (ifp->ifindex != nhop.ifindex)
792 continue;
793
794 nh = nexthop_new();
795
796 memcpy(nh, &nhop, sizeof(nhop));
50d89650 797 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
798
799 if (nhg_hooks.add_nexthop)
800 nhg_hooks.add_nexthop(nhgc, nh);
801 }
802 } else {
803 struct nexthop *next_nh;
804
805 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
806 next_nh = nh->next;
807 switch (nh->type) {
808 case NEXTHOP_TYPE_IPV4:
809 case NEXTHOP_TYPE_IPV6:
810 case NEXTHOP_TYPE_BLACKHOLE:
811 continue;
812 case NEXTHOP_TYPE_IFINDEX:
813 case NEXTHOP_TYPE_IPV4_IFINDEX:
814 case NEXTHOP_TYPE_IPV6_IFINDEX:
815 break;
816 }
817
818 if (oldifindex != nh->ifindex)
819 continue;
820
50d89650 821 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
822
823 if (nhg_hooks.del_nexthop)
824 nhg_hooks.del_nexthop(nhgc, nh);
825
826 nexthop_free(nh);
827 }
828 }
829 }
830}
831
868ee86c
DS
832static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
833{
834 struct nexthop_group_cmd *nhgc;
835
836 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
837 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
838 }
839}
840
841static const struct cmd_variable_handler nhg_name_handlers[] = {
842 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
843 {.completions = NULL}};
844
31919191
DS
845void nexthop_group_init(void (*new)(const char *name),
846 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
847 const struct nexthop *nhop),
848 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
849 const struct nexthop *nhop),
850 void (*delete)(const char *name))
dba32923 851{
31919191
DS
852 RB_INIT(nhgc_entry_head, &nhgc_entries);
853
868ee86c
DS
854 cmd_variable_handler_register(nhg_name_handlers);
855
dba32923
DS
856 install_node(&nexthop_group_node, nexthop_group_write);
857 install_element(CONFIG_NODE, &nexthop_group_cmd);
31919191
DS
858 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
859
860 install_default(NH_GROUP_NODE);
861 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
862
863 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
864
865 if (new)
866 nhg_hooks.new = new;
867 if (add_nexthop)
868 nhg_hooks.add_nexthop = add_nexthop;
869 if (del_nexthop)
870 nhg_hooks.del_nexthop = del_nexthop;
871 if (delete)
872 nhg_hooks.delete = delete;
dba32923 873}