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