]> git.proxmox.com Git - mirror_frr.git/blame - lib/nexthop_group.c
lib, vtysh: Add ability to specify resilient nhgs
[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 30
dba32923 31#include "lib/nexthop_group_clippy.c"
7ee30f28 32
bf8d3d6a 33DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
31919191 34
db2fede9
MS
35/*
36 * Internal struct used to hold nhg config strings
37 */
38struct nexthop_hold {
39 char *nhvrf_name;
40 union sockunion *addr;
41 char *intf;
54c6fa8e 42 bool onlink;
db2fede9 43 char *labels;
597371a6 44 uint32_t weight;
28593bcc 45 char *backup_str;
db2fede9
MS
46};
47
31919191
DS
48struct nexthop_group_hooks {
49 void (*new)(const char *name);
50 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
51 const struct nexthop *nhop);
52 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
53 const struct nexthop *nhop);
54 void (*delete)(const char *name);
55};
56
57static struct nexthop_group_hooks nhg_hooks;
58
59static inline int
60nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
61 const struct nexthop_group_cmd *nhgc2);
62RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
63 nexthop_group_cmd_compare)
64
c17faa4b 65static struct nhgc_entry_head nhgc_entries;
31919191
DS
66
67static inline int
68nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
69 const struct nexthop_group_cmd *nhgc2)
70{
71 return strcmp(nhgc1->name, nhgc2->name);
72}
73
8c15fa95
SW
74static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
75{
76 struct nexthop *nexthop = nhg->nexthop;
77
78 while (nexthop && nexthop->next)
79 nexthop = nexthop->next;
80
81 return nexthop;
82}
83
454192f4
DS
84uint8_t nexthop_group_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 num++;
91
92 return num;
93}
94
98cda54a
SW
95uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
96{
97 struct nexthop *nhop;
98 uint8_t num = 0;
99
100 for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
101 num++;
102
103 return num;
104}
105
454192f4
DS
106uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
107{
108 struct nexthop *nhop;
109 uint8_t num = 0;
110
111 for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
112 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
113 num++;
114 }
115
116 return num;
117}
118
98cda54a
SW
119uint8_t
120nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
121{
122 struct nexthop *nhop;
123 uint8_t num = 0;
124
125 for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
126 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
127 num++;
128 }
129
130 return num;
131}
132
8f8d9845
SW
133struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
134 const struct nexthop *nh)
31919191
DS
135{
136 struct nexthop *nexthop;
137
138 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
139 if (nexthop_same(nh, nexthop))
140 return nexthop;
141 }
142
143 return NULL;
144}
145
4fda105e
MS
146/*
147 * Helper that locates a nexthop in an nhg config list. Note that
148 * this uses a specific matching / equality rule that's different from
149 * the complete match performed by 'nexthop_same()'.
150 */
151static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
152 const struct nexthop *nh)
153{
154 struct nexthop *nexthop;
155 int ret;
156
157 /* We compare: vrf, gateway, and interface */
158
159 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
160
161 /* Compare vrf and type */
162 if (nexthop->vrf_id != nh->vrf_id)
163 continue;
164 if (nexthop->type != nh->type)
165 continue;
166
167 /* Compare gateway */
168 switch (nexthop->type) {
169 case NEXTHOP_TYPE_IPV4:
170 case NEXTHOP_TYPE_IPV6:
171 ret = nexthop_g_addr_cmp(nexthop->type,
172 &nexthop->gate, &nh->gate);
173 if (ret != 0)
174 continue;
175 break;
176 case NEXTHOP_TYPE_IPV4_IFINDEX:
177 case NEXTHOP_TYPE_IPV6_IFINDEX:
178 ret = nexthop_g_addr_cmp(nexthop->type,
179 &nexthop->gate, &nh->gate);
180 if (ret != 0)
181 continue;
182 /* Intentional Fall-Through */
183 case NEXTHOP_TYPE_IFINDEX:
184 if (nexthop->ifindex != nh->ifindex)
185 continue;
186 break;
187 case NEXTHOP_TYPE_BLACKHOLE:
188 if (nexthop->bh_type != nh->bh_type)
189 continue;
190 break;
191 }
192
193 return nexthop;
194 }
195
196 return NULL;
197}
198
f17f2c5d
SW
199static bool
200nexthop_group_equal_common(const struct nexthop_group *nhg1,
201 const struct nexthop_group *nhg2,
202 uint8_t (*nexthop_group_nexthop_num_func)(
203 const struct nexthop_group *nhg))
8f8d9845 204{
8f8d9845
SW
205 if (nhg1 && !nhg2)
206 return false;
207
9c387098 208 if (!nhg1 && nhg2)
8f8d9845
SW
209 return false;
210
dd9546e1
SW
211 if (nhg1 == nhg2)
212 return true;
213
f17f2c5d
SW
214 if (nexthop_group_nexthop_num_func(nhg1)
215 != nexthop_group_nexthop_num_func(nhg2))
216 return false;
217
218 return true;
219}
220
221/* This assumes ordered */
222bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
223 const struct nexthop_group *nhg2)
224{
225 struct nexthop *nh1 = NULL;
226 struct nexthop *nh2 = NULL;
227
228 if (!nexthop_group_equal_common(nhg1, nhg2,
229 &nexthop_group_nexthop_num_no_recurse))
8f8d9845
SW
230 return false;
231
2171b19c 232 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
12ec584d 233 nh1 = nh1->next, nh2 = nh2->next) {
dd9546e1
SW
234 if (nh1 && !nh2)
235 return false;
236 if (!nh1 && nh2)
237 return false;
12ec584d 238 if (!nexthop_same(nh1, nh2))
8f8d9845
SW
239 return false;
240 }
241
242 return true;
243}
244
2171b19c
SW
245/* This assumes ordered */
246bool nexthop_group_equal(const struct nexthop_group *nhg1,
247 const struct nexthop_group *nhg2)
248{
249 struct nexthop *nh1 = NULL;
250 struct nexthop *nh2 = NULL;
251
f17f2c5d 252 if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
2171b19c
SW
253 return false;
254
255 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
256 nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
dd9546e1
SW
257 if (nh1 && !nh2)
258 return false;
259 if (!nh1 && nh2)
260 return false;
2171b19c
SW
261 if (!nexthop_same(nh1, nh2))
262 return false;
263 }
264
265 return true;
266}
31919191
DS
267struct nexthop_group *nexthop_group_new(void)
268{
269 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
270}
271
c13bfa74
MS
272void nexthop_group_copy(struct nexthop_group *to,
273 const struct nexthop_group *from)
6c8b51e1 274{
569e1411 275 to->nhgr = from->nhgr;
6c8b51e1
DS
276 /* Copy everything, including recursive info */
277 copy_nexthops(&to->nexthop, from->nexthop, NULL);
278}
279
31919191 280void nexthop_group_delete(struct nexthop_group **nhg)
0c8215cb 281{
e4a1ec74
MS
282 /* OK to call with NULL group */
283 if ((*nhg) == NULL)
284 return;
285
0c8215cb
SW
286 if ((*nhg)->nexthop)
287 nexthops_free((*nhg)->nexthop);
d3a35138
SW
288
289 XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
0c8215cb
SW
290}
291
7ee30f28 292/* Add nexthop to the end of a nexthop list. */
50d89650 293void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
7ee30f28
DS
294{
295 struct nexthop *last;
296
297 for (last = *target; last && last->next; last = last->next)
298 ;
299 if (last)
300 last->next = nexthop;
301 else
302 *target = nexthop;
303 nexthop->prev = last;
304}
305
e1f3a8eb
MS
306/* Add nexthop to sorted list of nexthops */
307static void _nexthop_add_sorted(struct nexthop **head,
308 struct nexthop *nexthop)
6c8b51e1 309{
e1f3a8eb 310 struct nexthop *position, *prev;
8c15fa95 311
89ca64c9 312 assert(!nexthop->next);
6c8b51e1 313
e1f3a8eb 314 for (position = *head, prev = NULL; position;
6c8b51e1
DS
315 prev = position, position = position->next) {
316 if (nexthop_cmp(position, nexthop) > 0) {
317 nexthop->next = position;
318 nexthop->prev = prev;
319
320 if (nexthop->prev)
321 nexthop->prev->next = nexthop;
322 else
e1f3a8eb 323 *head = nexthop;
6c8b51e1
DS
324
325 position->prev = nexthop;
326 return;
327 }
328 }
329
330 nexthop->prev = prev;
331 if (prev)
332 prev->next = nexthop;
333 else
e1f3a8eb
MS
334 *head = nexthop;
335}
336
337void nexthop_group_add_sorted(struct nexthop_group *nhg,
338 struct nexthop *nexthop)
339{
340 struct nexthop *tail;
341
89ca64c9
SW
342 assert(!nexthop->next);
343
e1f3a8eb
MS
344 /* Try to just append to the end first;
345 * trust the list is already sorted
346 */
347 tail = nexthop_group_tail(nhg);
348
349 if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
350 tail->next = nexthop;
351 nexthop->prev = tail;
352
353 return;
354 }
355
356 _nexthop_add_sorted(&nhg->nexthop, nexthop);
6c8b51e1
DS
357}
358
31919191 359/* Delete nexthop from a nexthop list. */
50d89650 360void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
31919191
DS
361{
362 struct nexthop *nexthop;
363
364 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
365 if (nexthop_same(nh, nexthop))
366 break;
367 }
368
369 assert(nexthop);
370
371 if (nexthop->prev)
372 nexthop->prev->next = nexthop->next;
373 else
374 nhg->nexthop = nexthop->next;
375
376 if (nexthop->next)
377 nexthop->next->prev = nexthop->prev;
ebee2bc4
DS
378
379 nh->prev = NULL;
380 nh->next = NULL;
31919191
DS
381}
382
4fda105e
MS
383/* Unlink a nexthop from the list it's on, unconditionally */
384static void nexthop_unlink(struct nexthop_group *nhg, struct nexthop *nexthop)
385{
386
387 if (nexthop->prev)
388 nexthop->prev->next = nexthop->next;
389 else {
390 assert(nhg->nexthop == nexthop);
391 assert(nexthop->prev == NULL);
392 nhg->nexthop = nexthop->next;
393 }
394
395 if (nexthop->next)
396 nexthop->next->prev = nexthop->prev;
397
398 nexthop->prev = NULL;
399 nexthop->next = NULL;
400}
401
e1f3a8eb
MS
402/*
403 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
404 */
405void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
406 const struct nexthop *nh)
407{
408 struct nexthop *nexthop, *tail;
409 const struct nexthop *nh1;
410
411 /* We'll try to append to the end of the new list;
412 * if the original list in nh is already sorted, this eliminates
413 * lots of comparison operations.
414 */
415 tail = nexthop_group_tail(nhg);
416
417 for (nh1 = nh; nh1; nh1 = nh1->next) {
418 nexthop = nexthop_dup(nh1, NULL);
419
420 if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
421 tail->next = nexthop;
422 nexthop->prev = tail;
423
424 tail = nexthop;
425 continue;
426 }
427
428 _nexthop_add_sorted(&nhg->nexthop, nexthop);
429
430 if (tail == NULL)
431 tail = nexthop;
432 }
433}
434
435/* Copy a list of nexthops, no effort made to sort or order them. */
deff170e 436void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
7ee30f28
DS
437 struct nexthop *rparent)
438{
439 struct nexthop *nexthop;
deff170e 440 const struct nexthop *nh1;
7ee30f28
DS
441
442 for (nh1 = nh; nh1; nh1 = nh1->next) {
504d0a40 443 nexthop = nexthop_dup(nh1, rparent);
50d89650 444 _nexthop_add(tnh, nexthop);
7ee30f28
DS
445 }
446}
dba32923 447
2f000944 448uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
1b1fe1c4
SW
449{
450 struct nexthop *nh;
451 uint32_t key = 0;
452
453 /*
454 * We are not interested in hashing over any recursively
455 * resolved nexthops
456 */
457 for (nh = nhg->nexthop; nh; nh = nh->next)
458 key = jhash_1word(nexthop_hash(nh), key);
459
460 return key;
2f000944
SW
461}
462
463uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
464{
465 struct nexthop *nh;
466 uint32_t key = 0;
467
468 for (ALL_NEXTHOPS_PTR(nhg, nh))
469 key = jhash_1word(nexthop_hash(nh), key);
470
471 return key;
1b1fe1c4
SW
472}
473
9ef49038
SW
474void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
475{
476 struct nexthop *nexthop, *prev;
477
478 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
479 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
480 for (ALL_NEXTHOPS_PTR(nhg, prev)) {
481 if (prev == nexthop)
482 break;
7403e155 483 if (nexthop_same(nexthop, prev)) {
9ef49038
SW
484 SET_FLAG(nexthop->flags,
485 NEXTHOP_FLAG_DUPLICATE);
486 break;
487 }
488 }
489 }
490}
491
31919191
DS
492static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
493{
494 struct nexthop *nexthop;
495
496 nexthop = nhgc->nhg.nexthop;
497 while (nexthop) {
498 struct nexthop *next = nexthop_next(nexthop);
499
50d89650 500 _nexthop_del(&nhgc->nhg, nexthop);
31919191
DS
501 if (nhg_hooks.del_nexthop)
502 nhg_hooks.del_nexthop(nhgc, nexthop);
503
504 nexthop_free(nexthop);
505
506 nexthop = next;
507 }
508}
509
d604266c 510struct nexthop_group_cmd *nhgc_find(const char *name)
31919191
DS
511{
512 struct nexthop_group_cmd find;
513
514 strlcpy(find.name, name, sizeof(find.name));
515
516 return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
517}
518
c57bd6bb
DS
519static int nhgc_cmp_helper(const char *a, const char *b)
520{
521 if (!a && !b)
522 return 0;
523
524 if (a && !b)
525 return -1;
526
527 if (!a && b)
528 return 1;
529
530 return strcmp(a, b);
531}
532
1c869b64
RW
533static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
534{
535 if (!a && !b)
536 return 0;
537
538 if (a && !b)
539 return -1;
540
541 if (!a && b)
542 return 1;
543
544 return sockunion_cmp(a, b);
545}
546
c57bd6bb
DS
547static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
548{
549 int ret;
550
1c869b64 551 ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
c57bd6bb
DS
552 if (ret)
553 return ret;
554
555 ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
556 if (ret)
557 return ret;
558
db2fede9
MS
559 ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
560 if (ret)
561 return ret;
562
54c6fa8e
SW
563 ret = ((int)nh2->onlink) - ((int)nh1->onlink);
564 if (ret)
565 return ret;
566
db2fede9 567 return nhgc_cmp_helper(nh1->labels, nh2->labels);
c57bd6bb
DS
568}
569
570static void nhgl_delete(struct nexthop_hold *nh)
571{
0a22ddfb 572 XFREE(MTYPE_TMP, nh->intf);
c57bd6bb 573
0a22ddfb 574 XFREE(MTYPE_TMP, nh->nhvrf_name);
c57bd6bb 575
1c869b64
RW
576 if (nh->addr)
577 sockunion_free(nh->addr);
b43bb64f 578
db2fede9
MS
579 XFREE(MTYPE_TMP, nh->labels);
580
c57bd6bb
DS
581 XFREE(MTYPE_TMP, nh);
582}
583
31919191
DS
584static struct nexthop_group_cmd *nhgc_get(const char *name)
585{
586 struct nexthop_group_cmd *nhgc;
587
588 nhgc = nhgc_find(name);
589 if (!nhgc) {
590 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
591 strlcpy(nhgc->name, name, sizeof(nhgc->name));
592
593 QOBJ_REG(nhgc, nexthop_group_cmd);
594 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
595
c57bd6bb
DS
596 nhgc->nhg_list = list_new();
597 nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
598 nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
599
31919191
DS
600 if (nhg_hooks.new)
601 nhg_hooks.new(name);
602 }
603
604 return nhgc;
605}
606
607static void nhgc_delete(struct nexthop_group_cmd *nhgc)
dba32923 608{
31919191
DS
609 nhgc_delete_nexthops(nhgc);
610
611 if (nhg_hooks.delete)
612 nhg_hooks.delete(nhgc->name);
613
614 RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
c57bd6bb 615
6a154c88 616 list_delete(&nhgc->nhg_list);
c57bd6bb 617
20953065 618 QOBJ_UNREG(nhgc);
c57bd6bb 619 XFREE(MTYPE_TMP, nhgc);
31919191
DS
620}
621
96244aca 622DEFINE_QOBJ_TYPE(nexthop_group_cmd);
31919191 623
868ee86c 624DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
31919191
DS
625 "Enter into the nexthop-group submode\n"
626 "Specify the NAME of the nexthop-group\n")
627{
628 const char *nhg_name = argv[1]->arg;
629 struct nexthop_group_cmd *nhgc = NULL;
630
631 nhgc = nhgc_get(nhg_name);
632 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
633
634 return CMD_SUCCESS;
635}
636
868ee86c 637DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
31919191
DS
638 NO_STR
639 "Delete the nexthop-group\n"
640 "Specify the NAME of the nexthop-group\n")
641{
642 const char *nhg_name = argv[2]->arg;
643 struct nexthop_group_cmd *nhgc = NULL;
644
645 nhgc = nhgc_find(nhg_name);
646 if (nhgc)
647 nhgc_delete(nhgc);
648
649 return CMD_SUCCESS;
650}
651
0a8881b4
MS
652DEFPY(nexthop_group_backup, nexthop_group_backup_cmd,
653 "backup-group WORD$name",
654 "Specify a group name containing backup nexthops\n"
655 "The name of the backup group\n")
656{
657 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
658
659 strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name));
660
661 return CMD_SUCCESS;
662}
663
664DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd,
665 "no backup-group [WORD$name]",
666 NO_STR
667 "Clear group name containing backup nexthops\n"
668 "The name of the backup group\n")
669{
670 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
671
672 nhgc->backup_list_name[0] = 0;
673
674 return CMD_SUCCESS;
675}
676
f0f618dc
DS
677DEFPY(nexthop_group_resilience,
678 nexthop_group_resilience_cmd,
679 "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
680 "A resilient Nexthop Group\n"
681 "Buckets in the Hash for this Group\n"
682 "Number of buckets\n"
683 "The Idle timer for this Resilient Nexthop Group in seconds\n"
684 "Number of seconds of Idle time\n"
685 "The length of time that the Nexthop Group can be unbalanced\n"
686 "Number of seconds of Unbalanced time\n")
687{
688 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
689
690 nhgc->nhg.nhgr.buckets = buckets;
691 nhgc->nhg.nhgr.idle_timer = idle_timer;
692 nhgc->nhg.nhgr.unbalanced_timer = unbalanced_timer;
693
694 return CMD_SUCCESS;
695}
696
697DEFPY(no_nexthop_group_resilience,
698 no_nexthop_group_resilience_cmd,
699 "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
700 NO_STR
701 "A resilient Nexthop Group\n"
702 "Buckets in the Hash for this Group\n"
703 "Number of buckets\n"
704 "The Idle timer for this Resilient Nexthop Group in seconds\n"
705 "Number of seconds of Idle time\n"
706 "The length of time that the Nexthop Group can be unbalanced\n"
707 "Number of seconds of Unbalanced time\n")
708{
709 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
710
711 nhgc->nhg.nhgr.buckets = 0;
712 nhgc->nhg.nhgr.idle_timer = 0;
713 nhgc->nhg.nhgr.unbalanced_timer = 0;
714
715 return CMD_SUCCESS;
716}
717
c57bd6bb
DS
718static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
719 const char *nhvrf_name,
720 const union sockunion *addr,
54c6fa8e
SW
721 const char *intf, bool onlink,
722 const char *labels, const uint32_t weight,
28593bcc 723 const char *backup_str)
c57bd6bb
DS
724{
725 struct nexthop_hold *nh;
726
727 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
728
729 if (nhvrf_name)
730 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
731 if (intf)
732 nh->intf = XSTRDUP(MTYPE_TMP, intf);
1c869b64
RW
733 if (addr)
734 nh->addr = sockunion_dup(addr);
db2fede9
MS
735 if (labels)
736 nh->labels = XSTRDUP(MTYPE_TMP, labels);
c57bd6bb 737
54c6fa8e
SW
738 nh->onlink = onlink;
739
597371a6
DS
740 nh->weight = weight;
741
28593bcc
MS
742 if (backup_str)
743 nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
0a8881b4 744
c57bd6bb
DS
745 listnode_add_sort(nhgc->nhg_list, nh);
746}
747
4fda105e
MS
748/*
749 * Remove config info about a nexthop from group 'nhgc'. Note that we
750 * use only a subset of the available attributes here to determine
751 * a 'match'.
752 * Note that this doesn't change the list of nexthops, only the config
753 * information.
754 */
c57bd6bb
DS
755static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
756 const char *nhvrf_name,
757 const union sockunion *addr,
4fda105e 758 const char *intf)
c57bd6bb
DS
759{
760 struct nexthop_hold *nh;
761 struct listnode *node;
762
763 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
597371a6
DS
764 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
765 && nhgc_addr_cmp_helper(addr, nh->addr) == 0
4fda105e 766 && nhgc_cmp_helper(intf, nh->intf) == 0)
c57bd6bb
DS
767 break;
768 }
769
770 /*
771 * Something has gone seriously wrong, fail gracefully
772 */
773 if (!nh)
774 return;
775
776 list_delete_node(nhgc->nhg_list, node);
e5a501c2 777 nhgl_delete(nh);
c57bd6bb
DS
778}
779
db2fede9
MS
780/*
781 * Parse the config strings we support for a single nexthop. This gets used
782 * in a couple of different ways, and we distinguish between transient
783 * failures - such as a still-unprocessed interface - and fatal errors
784 * from label-string parsing.
785 */
98cbbaea
DS
786static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
787 const union sockunion *addr,
54c6fa8e
SW
788 const char *intf, bool onlink,
789 const char *name, const char *labels,
790 int *lbl_ret, uint32_t weight,
791 const char *backup_str)
31919191 792{
db2fede9 793 int ret = 0;
31919191 794 struct vrf *vrf;
28593bcc 795 int num;
98cbbaea
DS
796
797 memset(nhop, 0, sizeof(*nhop));
31919191
DS
798
799 if (name)
800 vrf = vrf_lookup_by_name(name);
801 else
802 vrf = vrf_lookup_by_id(VRF_DEFAULT);
803
98cbbaea
DS
804 if (!vrf)
805 return false;
31919191 806
98cbbaea 807 nhop->vrf_id = vrf->vrf_id;
31919191 808
1c869b64
RW
809 if (intf) {
810 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
811 if (nhop->ifindex == IFINDEX_INTERNAL)
812 return false;
98cbbaea
DS
813 }
814
54c6fa8e
SW
815 if (onlink)
816 SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
817
1c869b64
RW
818 if (addr) {
819 if (addr->sa.sa_family == AF_INET) {
820 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
821 if (intf)
822 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
823 else
824 nhop->type = NEXTHOP_TYPE_IPV4;
825 } else {
826 nhop->gate.ipv6 = addr->sin6.sin6_addr;
827 if (intf)
828 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
829 else
830 nhop->type = NEXTHOP_TYPE_IPV6;
831 }
832 } else
833 nhop->type = NEXTHOP_TYPE_IFINDEX;
834
db2fede9
MS
835 if (labels) {
836 uint8_t num = 0;
837 mpls_label_t larray[MPLS_MAX_LABELS];
838
839 ret = mpls_str2label(labels, &num, larray);
840
841 /* Return label parse result */
842 if (lbl_ret)
843 *lbl_ret = ret;
844
845 if (ret < 0)
846 return false;
847 else if (num > 0)
848 nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
849 num, larray);
850 }
851
597371a6
DS
852 nhop->weight = weight;
853
28593bcc
MS
854 if (backup_str) {
855 /* Parse backup indexes */
856 ret = nexthop_str2backups(backup_str,
857 &num, nhop->backup_idx);
858 if (ret == 0) {
859 SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
860 nhop->backup_num = num;
861 } else
0a8881b4 862 return false;
0a8881b4
MS
863 }
864
98cbbaea
DS
865 return true;
866}
867
db2fede9
MS
868/*
869 * Wrapper to parse the strings in a 'nexthop_hold'
870 */
871static bool nexthop_group_parse_nhh(struct nexthop *nhop,
872 const struct nexthop_hold *nhh)
873{
54c6fa8e
SW
874 return (nexthop_group_parse_nexthop(
875 nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
876 nhh->labels, NULL, nhh->weight, nhh->backup_str));
db2fede9
MS
877}
878
98cbbaea 879DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
1c869b64
RW
880 "[no] nexthop\
881 <\
54c6fa8e 882 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
1c869b64
RW
883 |INTERFACE$intf\
884 >\
db2fede9
MS
885 [{ \
886 nexthop-vrf NAME$vrf_name \
887 |label WORD \
597371a6 888 |weight (1-255) \
28593bcc 889 |backup-idx WORD \
db2fede9 890 }]",
98cbbaea
DS
891 NO_STR
892 "Specify one of the nexthops in this ECMP group\n"
893 "v4 Address\n"
894 "v6 Address\n"
895 "Interface to use\n"
54c6fa8e 896 "Treat nexthop as directly attached to the interface\n"
1c869b64 897 "Interface to use\n"
98cbbaea 898 "If the nexthop is in a different vrf tell us\n"
db2fede9
MS
899 "The nexthop-vrf Name\n"
900 "Specify label(s) for this nexthop\n"
597371a6
DS
901 "One or more labels in the range (16-1048575) separated by '/'\n"
902 "Weight to be used by the nexthop for purposes of ECMP\n"
0a8881b4 903 "Weight value to be used\n"
28593bcc
MS
904 "Specify backup nexthop indexes in another group\n"
905 "One or more indexes in the range (0-254) separated by ','\n")
98cbbaea
DS
906{
907 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
908 struct nexthop nhop;
909 struct nexthop *nh;
db2fede9 910 int lbl_ret = 0;
98cbbaea 911 bool legal;
28593bcc
MS
912 int num;
913 uint8_t backups[NEXTHOP_MAX_BACKUPS];
4fda105e 914 bool yes = !no;
0a8881b4 915
28593bcc
MS
916 /* Pre-parse backup string to validate */
917 if (backup_idx) {
918 lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
919 if (lbl_ret < 0) {
920 vty_out(vty, "%% Invalid backups\n");
921 return CMD_WARNING_CONFIG_FAILED;
922 }
923 }
98cbbaea 924
54c6fa8e
SW
925 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
926 vrf_name, label, &lbl_ret, weight,
927 backup_idx);
98cbbaea
DS
928
929 if (nhop.type == NEXTHOP_TYPE_IPV6
930 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
931 vty_out(vty,
932 "Specified a v6 LL with no interface, rejecting\n");
933 return CMD_WARNING_CONFIG_FAILED;
31919191
DS
934 }
935
db2fede9
MS
936 /* Handle label-string errors */
937 if (!legal && lbl_ret < 0) {
938 switch (lbl_ret) {
939 case -1:
940 vty_out(vty, "%% Malformed label(s)\n");
941 break;
942 case -2:
943 vty_out(vty,
944 "%% Cannot use reserved label(s) (%d-%d)\n",
945 MPLS_LABEL_RESERVED_MIN,
946 MPLS_LABEL_RESERVED_MAX);
947 break;
948 case -3:
949 vty_out(vty,
950 "%% Too many labels. Enter %d or fewer\n",
951 MPLS_MAX_LABELS);
952 break;
953 }
954 return CMD_WARNING_CONFIG_FAILED;
955 }
956
4fda105e
MS
957 /* Look for an existing nexthop in the config. Note that the test
958 * here tests only some attributes - it's not a complete comparison.
959 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
960 * that represent config that may or may not be valid (yet), and
961 * actual nexthops that have been validated and parsed.
962 */
963 nh = nhg_nh_find(&nhgc->nhg, &nhop);
31919191 964
4fda105e
MS
965 /* Always attempt to remove old config info. */
966 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
0a8881b4 967
4fda105e
MS
968 /* Remove any existing nexthop, for delete and replace cases. */
969 if (nh) {
970 nexthop_unlink(&nhgc->nhg, nh);
31919191 971
4fda105e
MS
972 if (nhg_hooks.del_nexthop)
973 nhg_hooks.del_nexthop(nhgc, nh);
31919191 974
4fda105e 975 nexthop_free(nh);
0a8881b4 976 }
4fda105e
MS
977 if (yes) {
978 /* Add/replace case: capture nexthop if valid, and capture
979 * config info always.
980 */
98cbbaea
DS
981 if (legal) {
982 nh = nexthop_new();
31919191 983
98cbbaea 984 memcpy(nh, &nhop, sizeof(nhop));
50d89650 985 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea 986 }
31919191 987
0a8881b4 988 /* Save config always */
54c6fa8e
SW
989 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
990 label, weight, backup_idx);
c57bd6bb 991
98cbbaea 992 if (legal && nhg_hooks.add_nexthop)
31919191
DS
993 nhg_hooks.add_nexthop(nhgc, nh);
994 }
995
dba32923
DS
996 return CMD_SUCCESS;
997}
998
612c2c15 999static int nexthop_group_write(struct vty *vty);
1b3e9a21 1000static struct cmd_node nexthop_group_node = {
f4b8291f 1001 .name = "nexthop-group",
62b346ee 1002 .node = NH_GROUP_NODE,
24389580 1003 .parent_node = CONFIG_NODE,
62b346ee 1004 .prompt = "%s(config-nh-group)# ",
612c2c15 1005 .config_write = nexthop_group_write,
dba32923
DS
1006};
1007
79924520 1008void nexthop_group_write_nexthop_simple(struct vty *vty,
a251884b
DS
1009 const struct nexthop *nh,
1010 char *altifname)
1b7bce04 1011{
a251884b 1012 char *ifname;
1b7bce04 1013
57cdafc4 1014 vty_out(vty, "nexthop ");
1b7bce04 1015
a251884b
DS
1016 if (altifname)
1017 ifname = altifname;
1018 else
1019 ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
1020
1b7bce04
DS
1021 switch (nh->type) {
1022 case NEXTHOP_TYPE_IFINDEX:
a251884b 1023 vty_out(vty, "%s", ifname);
1b7bce04
DS
1024 break;
1025 case NEXTHOP_TYPE_IPV4:
af3b34f6 1026 vty_out(vty, "%pI4", &nh->gate.ipv4);
1b7bce04
DS
1027 break;
1028 case NEXTHOP_TYPE_IPV4_IFINDEX:
af3b34f6 1029 vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
1b7bce04
DS
1030 break;
1031 case NEXTHOP_TYPE_IPV6:
af3b34f6 1032 vty_out(vty, "%pI6", &nh->gate.ipv6);
1b7bce04
DS
1033 break;
1034 case NEXTHOP_TYPE_IPV6_IFINDEX:
af3b34f6 1035 vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
1b7bce04
DS
1036 break;
1037 case NEXTHOP_TYPE_BLACKHOLE:
1038 break;
1039 }
79924520
SW
1040}
1041
1042void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1043{
1044 struct vrf *vrf;
1045 int i;
1046
a251884b 1047 nexthop_group_write_nexthop_simple(vty, nh, NULL);
1b7bce04
DS
1048
1049 if (nh->vrf_id != VRF_DEFAULT) {
1050 vrf = vrf_lookup_by_id(nh->vrf_id);
c40e9806 1051 vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1b7bce04 1052 }
db2fede9
MS
1053
1054 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1055 char buf[200];
1056
1057 mpls_label2str(nh->nh_label->num_labels,
1058 nh->nh_label->label,
1059 buf, sizeof(buf), 0);
1060 vty_out(vty, " label %s", buf);
1061 }
1062
597371a6
DS
1063 if (nh->weight)
1064 vty_out(vty, " weight %u", nh->weight);
1065
28593bcc 1066 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
474aebd9 1067 vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
0a8881b4 1068
28593bcc
MS
1069 for (i = 1; i < nh->backup_num; i++)
1070 vty_out(vty, ",%d", nh->backup_idx[i]);
1071 }
1072
1b7bce04
DS
1073 vty_out(vty, "\n");
1074}
1075
28593bcc 1076void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
010dd8ed 1077{
010dd8ed 1078 struct vrf *vrf;
28593bcc
MS
1079 json_object *json_backups = NULL;
1080 int i;
010dd8ed
WC
1081
1082 switch (nh->type) {
1083 case NEXTHOP_TYPE_IFINDEX:
3e81618d 1084 json_object_string_add(j, "nexthop",
010dd8ed
WC
1085 ifindex2ifname(nh->ifindex, nh->vrf_id));
1086 break;
1087 case NEXTHOP_TYPE_IPV4:
e46a4ac9 1088 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
010dd8ed
WC
1089 break;
1090 case NEXTHOP_TYPE_IPV4_IFINDEX:
e46a4ac9 1091 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
010dd8ed
WC
1092 json_object_string_add(j, "vrfId",
1093 ifindex2ifname(nh->ifindex, nh->vrf_id));
1094 break;
1095 case NEXTHOP_TYPE_IPV6:
e46a4ac9 1096 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
010dd8ed
WC
1097 break;
1098 case NEXTHOP_TYPE_IPV6_IFINDEX:
e46a4ac9 1099 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
010dd8ed
WC
1100 json_object_string_add(j, "vrfId",
1101 ifindex2ifname(nh->ifindex, nh->vrf_id));
1102 break;
1103 case NEXTHOP_TYPE_BLACKHOLE:
1104 break;
1105 }
1106
1107 if (nh->vrf_id != VRF_DEFAULT) {
1108 vrf = vrf_lookup_by_id(nh->vrf_id);
81c0078e 1109 json_object_string_add(j, "targetVrf", vrf->name);
010dd8ed
WC
1110 }
1111
1112 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1113 char buf[200];
1114
1115 mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1116 buf, sizeof(buf), 0);
1117 json_object_string_add(j, "label", buf);
1118 }
1119
1120 if (nh->weight)
1121 json_object_int_add(j, "weight", nh->weight);
1122
28593bcc
MS
1123 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1124 json_backups = json_object_new_array();
1125 for (i = 0; i < nh->backup_num; i++)
1126 json_object_array_add(
1127 json_backups,
1128 json_object_new_int(nh->backup_idx[i]));
1129
1130 json_object_object_add(j, "backupIdx", json_backups);
1131 }
010dd8ed
WC
1132}
1133
c57bd6bb 1134static void nexthop_group_write_nexthop_internal(struct vty *vty,
28593bcc 1135 const struct nexthop_hold *nh)
c57bd6bb 1136{
1c869b64 1137 vty_out(vty, "nexthop");
c57bd6bb 1138
1c869b64 1139 if (nh->addr)
47e12884 1140 vty_out(vty, " %pSU", nh->addr);
c57bd6bb
DS
1141
1142 if (nh->intf)
1143 vty_out(vty, " %s", nh->intf);
1144
54c6fa8e
SW
1145 if (nh->onlink)
1146 vty_out(vty, " onlink");
1147
c57bd6bb
DS
1148 if (nh->nhvrf_name)
1149 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1150
db2fede9
MS
1151 if (nh->labels)
1152 vty_out(vty, " label %s", nh->labels);
1153
597371a6
DS
1154 if (nh->weight)
1155 vty_out(vty, " weight %u", nh->weight);
1156
28593bcc
MS
1157 if (nh->backup_str)
1158 vty_out(vty, " backup-idx %s", nh->backup_str);
0a8881b4 1159
c57bd6bb
DS
1160 vty_out(vty, "\n");
1161}
1162
dba32923
DS
1163static int nexthop_group_write(struct vty *vty)
1164{
31919191 1165 struct nexthop_group_cmd *nhgc;
c57bd6bb 1166 struct nexthop_hold *nh;
31919191
DS
1167
1168 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
c57bd6bb
DS
1169 struct listnode *node;
1170
31919191
DS
1171 vty_out(vty, "nexthop-group %s\n", nhgc->name);
1172
f0f618dc
DS
1173 if (nhgc->nhg.nhgr.buckets)
1174 vty_out(vty,
1175 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1176 nhgc->nhg.nhgr.buckets,
1177 nhgc->nhg.nhgr.idle_timer,
1178 nhgc->nhg.nhgr.unbalanced_timer);
1179
0a8881b4
MS
1180 if (nhgc->backup_list_name[0])
1181 vty_out(vty, " backup-group %s\n",
1182 nhgc->backup_list_name);
1183
c57bd6bb 1184 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
7dce96f0 1185 vty_out(vty, " ");
c57bd6bb 1186 nexthop_group_write_nexthop_internal(vty, nh);
811f859f 1187 }
31919191 1188
07679ad9 1189 vty_out(vty, "exit\n");
31919191
DS
1190 vty_out(vty, "!\n");
1191 }
dba32923
DS
1192
1193 return 1;
1194}
1195
98cbbaea
DS
1196void nexthop_group_enable_vrf(struct vrf *vrf)
1197{
1198 struct nexthop_group_cmd *nhgc;
1199 struct nexthop_hold *nhh;
1200
1201 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1202 struct listnode *node;
1203
1204 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1205 struct nexthop nhop;
1206 struct nexthop *nh;
1207
db2fede9 1208 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1209 continue;
1210
1211 nh = nexthop_exists(&nhgc->nhg, &nhop);
1212
1213 if (nh)
1214 continue;
1215
1216 if (nhop.vrf_id != vrf->vrf_id)
1217 continue;
1218
1219 nh = nexthop_new();
1220
1221 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1222 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1223
1224 if (nhg_hooks.add_nexthop)
1225 nhg_hooks.add_nexthop(nhgc, nh);
1226 }
1227 }
1228}
1229
1230void nexthop_group_disable_vrf(struct vrf *vrf)
1231{
1232 struct nexthop_group_cmd *nhgc;
1233 struct nexthop_hold *nhh;
1234
1235 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1236 struct listnode *node;
1237
1238 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1239 struct nexthop nhop;
1240 struct nexthop *nh;
1241
db2fede9 1242 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1243 continue;
1244
1245 nh = nexthop_exists(&nhgc->nhg, &nhop);
1246
1247 if (!nh)
1248 continue;
1249
1250 if (nh->vrf_id != vrf->vrf_id)
1251 continue;
1252
50d89650 1253 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1254
1255 if (nhg_hooks.del_nexthop)
1256 nhg_hooks.del_nexthop(nhgc, nh);
1257
1258 nexthop_free(nh);
1259 }
1260 }
1261}
1262
1263void nexthop_group_interface_state_change(struct interface *ifp,
1264 ifindex_t oldifindex)
1265{
1266 struct nexthop_group_cmd *nhgc;
1267 struct nexthop_hold *nhh;
1268
1269 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1270 struct listnode *node;
1271 struct nexthop *nh;
1272
1273 if (if_is_up(ifp)) {
1274 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1275 struct nexthop nhop;
1276
db2fede9 1277 if (!nexthop_group_parse_nhh(&nhop, nhh))
98cbbaea
DS
1278 continue;
1279
1280 switch (nhop.type) {
1281 case NEXTHOP_TYPE_IPV4:
1282 case NEXTHOP_TYPE_IPV6:
1283 case NEXTHOP_TYPE_BLACKHOLE:
1284 continue;
1285 case NEXTHOP_TYPE_IFINDEX:
1286 case NEXTHOP_TYPE_IPV4_IFINDEX:
1287 case NEXTHOP_TYPE_IPV6_IFINDEX:
1288 break;
1289 }
1290 nh = nexthop_exists(&nhgc->nhg, &nhop);
1291
1292 if (nh)
1293 continue;
1294
1295 if (ifp->ifindex != nhop.ifindex)
1296 continue;
1297
1298 nh = nexthop_new();
1299
1300 memcpy(nh, &nhop, sizeof(nhop));
50d89650 1301 _nexthop_add(&nhgc->nhg.nexthop, nh);
98cbbaea
DS
1302
1303 if (nhg_hooks.add_nexthop)
1304 nhg_hooks.add_nexthop(nhgc, nh);
1305 }
1306 } else {
1307 struct nexthop *next_nh;
1308
1309 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1310 next_nh = nh->next;
1311 switch (nh->type) {
1312 case NEXTHOP_TYPE_IPV4:
1313 case NEXTHOP_TYPE_IPV6:
1314 case NEXTHOP_TYPE_BLACKHOLE:
1315 continue;
1316 case NEXTHOP_TYPE_IFINDEX:
1317 case NEXTHOP_TYPE_IPV4_IFINDEX:
1318 case NEXTHOP_TYPE_IPV6_IFINDEX:
1319 break;
1320 }
1321
1322 if (oldifindex != nh->ifindex)
1323 continue;
1324
50d89650 1325 _nexthop_del(&nhgc->nhg, nh);
98cbbaea
DS
1326
1327 if (nhg_hooks.del_nexthop)
1328 nhg_hooks.del_nexthop(nhgc, nh);
1329
1330 nexthop_free(nh);
1331 }
1332 }
1333 }
1334}
1335
868ee86c
DS
1336static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1337{
1338 struct nexthop_group_cmd *nhgc;
1339
1340 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1341 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1342 }
1343}
1344
1345static const struct cmd_variable_handler nhg_name_handlers[] = {
1346 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1347 {.completions = NULL}};
1348
cc6a0d7d
SW
1349void nexthop_group_init(void (*new)(const char *name),
1350 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1351 const struct nexthop *nhop),
1352 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1353 const struct nexthop *nhop),
1354 void (*delete)(const char *name))
dba32923 1355{
31919191
DS
1356 RB_INIT(nhgc_entry_head, &nhgc_entries);
1357
868ee86c
DS
1358 cmd_variable_handler_register(nhg_name_handlers);
1359
612c2c15 1360 install_node(&nexthop_group_node);
dba32923 1361 install_element(CONFIG_NODE, &nexthop_group_cmd);
31919191
DS
1362 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1363
1364 install_default(NH_GROUP_NODE);
0a8881b4
MS
1365 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1366 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
31919191
DS
1367 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1368
f0f618dc
DS
1369 install_element(NH_GROUP_NODE, &nexthop_group_resilience_cmd);
1370 install_element(NH_GROUP_NODE, &no_nexthop_group_resilience_cmd);
1371
31919191
DS
1372 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1373
1374 if (new)
1375 nhg_hooks.new = new;
1376 if (add_nexthop)
1377 nhg_hooks.add_nexthop = add_nexthop;
1378 if (del_nexthop)
1379 nhg_hooks.del_nexthop = del_nexthop;
1380 if (delete)
1381 nhg_hooks.delete = delete;
dba32923 1382}