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