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