]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
Merge pull request #12260 from opensourcerouting/pim-plist-masklen
[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 uint32_t weight;
45 char *backup_str;
46 };
47
48 struct nexthop_group_hooks {
49 void (*new)(const char *name);
50 void (*modify)(const struct nexthop_group_cmd *nhgc);
51 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
52 const struct nexthop *nhop);
53 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
54 const struct nexthop *nhop);
55 void (*delete)(const char *name);
56 };
57
58 static struct nexthop_group_hooks nhg_hooks;
59
60 static inline int
61 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
62 const struct nexthop_group_cmd *nhgc2);
63 RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
64 nexthop_group_cmd_compare)
65
66 static struct nhgc_entry_head nhgc_entries;
67
68 static inline int
69 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
70 const struct nexthop_group_cmd *nhgc2)
71 {
72 return strcmp(nhgc1->name, nhgc2->name);
73 }
74
75 static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
76 {
77 struct nexthop *nexthop = nhg->nexthop;
78
79 while (nexthop && nexthop->next)
80 nexthop = nexthop->next;
81
82 return nexthop;
83 }
84
85 uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
86 {
87 struct nexthop *nhop;
88 uint8_t num = 0;
89
90 for (ALL_NEXTHOPS_PTR(nhg, nhop))
91 num++;
92
93 return num;
94 }
95
96 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
97 {
98 struct nexthop *nhop;
99 uint8_t num = 0;
100
101 for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
102 num++;
103
104 return num;
105 }
106
107 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
108 {
109 struct nexthop *nhop;
110 uint8_t num = 0;
111
112 for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
113 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
114 num++;
115 }
116
117 return num;
118 }
119
120 uint8_t
121 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
122 {
123 struct nexthop *nhop;
124 uint8_t num = 0;
125
126 for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
127 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
128 num++;
129 }
130
131 return num;
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 int *lbl_ret, uint32_t weight,
795 const char *backup_str)
796 {
797 int ret = 0;
798 struct vrf *vrf;
799 int num;
800
801 memset(nhop, 0, sizeof(*nhop));
802
803 if (name)
804 vrf = vrf_lookup_by_name(name);
805 else
806 vrf = vrf_lookup_by_id(VRF_DEFAULT);
807
808 if (!vrf)
809 return false;
810
811 nhop->vrf_id = vrf->vrf_id;
812
813 if (intf) {
814 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
815 if (nhop->ifindex == IFINDEX_INTERNAL)
816 return false;
817 }
818
819 if (onlink)
820 SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
821
822 if (addr) {
823 if (addr->sa.sa_family == AF_INET) {
824 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
825 if (intf)
826 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
827 else
828 nhop->type = NEXTHOP_TYPE_IPV4;
829 } else {
830 nhop->gate.ipv6 = addr->sin6.sin6_addr;
831 if (intf)
832 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
833 else
834 nhop->type = NEXTHOP_TYPE_IPV6;
835 }
836 } else
837 nhop->type = NEXTHOP_TYPE_IFINDEX;
838
839 if (labels) {
840 uint8_t num = 0;
841 mpls_label_t larray[MPLS_MAX_LABELS];
842
843 ret = mpls_str2label(labels, &num, 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 (num > 0)
852 nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
853 num, larray);
854 }
855
856 nhop->weight = weight;
857
858 if (backup_str) {
859 /* Parse backup indexes */
860 ret = nexthop_str2backups(backup_str,
861 &num, nhop->backup_idx);
862 if (ret == 0) {
863 SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
864 nhop->backup_num = num;
865 } else
866 return false;
867 }
868
869 return true;
870 }
871
872 /*
873 * Wrapper to parse the strings in a 'nexthop_hold'
874 */
875 static bool nexthop_group_parse_nhh(struct nexthop *nhop,
876 const struct nexthop_hold *nhh)
877 {
878 return (nexthop_group_parse_nexthop(
879 nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
880 nhh->labels, NULL, nhh->weight, nhh->backup_str));
881 }
882
883 DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
884 "[no] nexthop\
885 <\
886 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
887 |INTERFACE$intf\
888 >\
889 [{ \
890 nexthop-vrf NAME$vrf_name \
891 |label WORD \
892 |weight (1-255) \
893 |backup-idx WORD \
894 }]",
895 NO_STR
896 "Specify one of the nexthops in this ECMP group\n"
897 "v4 Address\n"
898 "v6 Address\n"
899 "Interface to use\n"
900 "Treat nexthop as directly attached to the interface\n"
901 "Interface to use\n"
902 "If the nexthop is in a different vrf tell us\n"
903 "The nexthop-vrf Name\n"
904 "Specify label(s) for this nexthop\n"
905 "One or more labels in the range (16-1048575) separated by '/'\n"
906 "Weight to be used by the nexthop for purposes of ECMP\n"
907 "Weight value to be used\n"
908 "Specify backup nexthop indexes in another group\n"
909 "One or more indexes in the range (0-254) separated by ','\n")
910 {
911 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
912 struct nexthop nhop;
913 struct nexthop *nh;
914 int lbl_ret = 0;
915 bool legal;
916 int num;
917 uint8_t backups[NEXTHOP_MAX_BACKUPS];
918 bool yes = !no;
919
920 /* Pre-parse backup string to validate */
921 if (backup_idx) {
922 lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
923 if (lbl_ret < 0) {
924 vty_out(vty, "%% Invalid backups\n");
925 return CMD_WARNING_CONFIG_FAILED;
926 }
927 }
928
929 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
930 vrf_name, label, &lbl_ret, weight,
931 backup_idx);
932
933 if (nhop.type == NEXTHOP_TYPE_IPV6
934 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
935 vty_out(vty,
936 "Specified a v6 LL with no interface, rejecting\n");
937 return CMD_WARNING_CONFIG_FAILED;
938 }
939
940 /* Handle label-string errors */
941 if (!legal && lbl_ret < 0) {
942 switch (lbl_ret) {
943 case -1:
944 vty_out(vty, "%% Malformed label(s)\n");
945 break;
946 case -2:
947 vty_out(vty,
948 "%% Cannot use reserved label(s) (%d-%d)\n",
949 MPLS_LABEL_RESERVED_MIN,
950 MPLS_LABEL_RESERVED_MAX);
951 break;
952 case -3:
953 vty_out(vty,
954 "%% Too many labels. Enter %d or fewer\n",
955 MPLS_MAX_LABELS);
956 break;
957 }
958 return CMD_WARNING_CONFIG_FAILED;
959 }
960
961 /* Look for an existing nexthop in the config. Note that the test
962 * here tests only some attributes - it's not a complete comparison.
963 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
964 * that represent config that may or may not be valid (yet), and
965 * actual nexthops that have been validated and parsed.
966 */
967 nh = nhg_nh_find(&nhgc->nhg, &nhop);
968
969 /* Always attempt to remove old config info. */
970 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
971
972 /* Remove any existing nexthop, for delete and replace cases. */
973 if (nh) {
974 nexthop_unlink(&nhgc->nhg, nh);
975
976 if (nhg_hooks.del_nexthop)
977 nhg_hooks.del_nexthop(nhgc, nh);
978
979 nexthop_free(nh);
980 }
981 if (yes) {
982 /* Add/replace case: capture nexthop if valid, and capture
983 * config info always.
984 */
985 if (legal) {
986 nh = nexthop_new();
987
988 memcpy(nh, &nhop, sizeof(nhop));
989 _nexthop_add(&nhgc->nhg.nexthop, nh);
990 }
991
992 /* Save config always */
993 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
994 label, weight, backup_idx);
995
996 if (legal && nhg_hooks.add_nexthop)
997 nhg_hooks.add_nexthop(nhgc, nh);
998 }
999
1000 return CMD_SUCCESS;
1001 }
1002
1003 static int nexthop_group_write(struct vty *vty);
1004 static struct cmd_node nexthop_group_node = {
1005 .name = "nexthop-group",
1006 .node = NH_GROUP_NODE,
1007 .parent_node = CONFIG_NODE,
1008 .prompt = "%s(config-nh-group)# ",
1009 .config_write = nexthop_group_write,
1010 };
1011
1012 void nexthop_group_write_nexthop_simple(struct vty *vty,
1013 const struct nexthop *nh,
1014 char *altifname)
1015 {
1016 char *ifname;
1017
1018 vty_out(vty, "nexthop ");
1019
1020 if (altifname)
1021 ifname = altifname;
1022 else
1023 ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
1024
1025 switch (nh->type) {
1026 case NEXTHOP_TYPE_IFINDEX:
1027 vty_out(vty, "%s", ifname);
1028 break;
1029 case NEXTHOP_TYPE_IPV4:
1030 vty_out(vty, "%pI4", &nh->gate.ipv4);
1031 break;
1032 case NEXTHOP_TYPE_IPV4_IFINDEX:
1033 vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
1034 break;
1035 case NEXTHOP_TYPE_IPV6:
1036 vty_out(vty, "%pI6", &nh->gate.ipv6);
1037 break;
1038 case NEXTHOP_TYPE_IPV6_IFINDEX:
1039 vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
1040 break;
1041 case NEXTHOP_TYPE_BLACKHOLE:
1042 break;
1043 }
1044 }
1045
1046 void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1047 {
1048 struct vrf *vrf;
1049 int i;
1050
1051 nexthop_group_write_nexthop_simple(vty, nh, NULL);
1052
1053 if (nh->vrf_id != VRF_DEFAULT) {
1054 vrf = vrf_lookup_by_id(nh->vrf_id);
1055 vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1056 }
1057
1058 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1059 char buf[200];
1060
1061 mpls_label2str(nh->nh_label->num_labels,
1062 nh->nh_label->label,
1063 buf, sizeof(buf), 0);
1064 vty_out(vty, " label %s", buf);
1065 }
1066
1067 if (nh->weight)
1068 vty_out(vty, " weight %u", nh->weight);
1069
1070 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1071 vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
1072
1073 for (i = 1; i < nh->backup_num; i++)
1074 vty_out(vty, ",%d", nh->backup_idx[i]);
1075 }
1076
1077 vty_out(vty, "\n");
1078 }
1079
1080 void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
1081 {
1082 struct vrf *vrf;
1083 json_object *json_backups = NULL;
1084 int i;
1085
1086 switch (nh->type) {
1087 case NEXTHOP_TYPE_IFINDEX:
1088 json_object_string_add(j, "nexthop",
1089 ifindex2ifname(nh->ifindex, nh->vrf_id));
1090 break;
1091 case NEXTHOP_TYPE_IPV4:
1092 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1093 break;
1094 case NEXTHOP_TYPE_IPV4_IFINDEX:
1095 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1096 json_object_string_add(j, "vrfId",
1097 ifindex2ifname(nh->ifindex, nh->vrf_id));
1098 break;
1099 case NEXTHOP_TYPE_IPV6:
1100 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1101 break;
1102 case NEXTHOP_TYPE_IPV6_IFINDEX:
1103 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1104 json_object_string_add(j, "vrfId",
1105 ifindex2ifname(nh->ifindex, nh->vrf_id));
1106 break;
1107 case NEXTHOP_TYPE_BLACKHOLE:
1108 break;
1109 }
1110
1111 if (nh->vrf_id != VRF_DEFAULT) {
1112 vrf = vrf_lookup_by_id(nh->vrf_id);
1113 json_object_string_add(j, "targetVrf", vrf->name);
1114 }
1115
1116 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1117 char buf[200];
1118
1119 mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1120 buf, sizeof(buf), 0);
1121 json_object_string_add(j, "label", buf);
1122 }
1123
1124 if (nh->weight)
1125 json_object_int_add(j, "weight", nh->weight);
1126
1127 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1128 json_backups = json_object_new_array();
1129 for (i = 0; i < nh->backup_num; i++)
1130 json_object_array_add(
1131 json_backups,
1132 json_object_new_int(nh->backup_idx[i]));
1133
1134 json_object_object_add(j, "backupIdx", json_backups);
1135 }
1136 }
1137
1138 static void nexthop_group_write_nexthop_internal(struct vty *vty,
1139 const struct nexthop_hold *nh)
1140 {
1141 vty_out(vty, "nexthop");
1142
1143 if (nh->addr)
1144 vty_out(vty, " %pSU", nh->addr);
1145
1146 if (nh->intf)
1147 vty_out(vty, " %s", nh->intf);
1148
1149 if (nh->onlink)
1150 vty_out(vty, " onlink");
1151
1152 if (nh->nhvrf_name)
1153 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1154
1155 if (nh->labels)
1156 vty_out(vty, " label %s", nh->labels);
1157
1158 if (nh->weight)
1159 vty_out(vty, " weight %u", nh->weight);
1160
1161 if (nh->backup_str)
1162 vty_out(vty, " backup-idx %s", nh->backup_str);
1163
1164 vty_out(vty, "\n");
1165 }
1166
1167 static int nexthop_group_write(struct vty *vty)
1168 {
1169 struct nexthop_group_cmd *nhgc;
1170 struct nexthop_hold *nh;
1171
1172 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1173 struct listnode *node;
1174
1175 vty_out(vty, "nexthop-group %s\n", nhgc->name);
1176
1177 if (nhgc->nhg.nhgr.buckets)
1178 vty_out(vty,
1179 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1180 nhgc->nhg.nhgr.buckets,
1181 nhgc->nhg.nhgr.idle_timer,
1182 nhgc->nhg.nhgr.unbalanced_timer);
1183
1184 if (nhgc->backup_list_name[0])
1185 vty_out(vty, " backup-group %s\n",
1186 nhgc->backup_list_name);
1187
1188 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
1189 vty_out(vty, " ");
1190 nexthop_group_write_nexthop_internal(vty, nh);
1191 }
1192
1193 vty_out(vty, "exit\n");
1194 vty_out(vty, "!\n");
1195 }
1196
1197 return 1;
1198 }
1199
1200 void nexthop_group_enable_vrf(struct vrf *vrf)
1201 {
1202 struct nexthop_group_cmd *nhgc;
1203 struct nexthop_hold *nhh;
1204
1205 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1206 struct listnode *node;
1207
1208 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1209 struct nexthop nhop;
1210 struct nexthop *nh;
1211
1212 if (!nexthop_group_parse_nhh(&nhop, nhh))
1213 continue;
1214
1215 nh = nexthop_exists(&nhgc->nhg, &nhop);
1216
1217 if (nh)
1218 continue;
1219
1220 if (nhop.vrf_id != vrf->vrf_id)
1221 continue;
1222
1223 nh = nexthop_new();
1224
1225 memcpy(nh, &nhop, sizeof(nhop));
1226 _nexthop_add(&nhgc->nhg.nexthop, nh);
1227
1228 if (nhg_hooks.add_nexthop)
1229 nhg_hooks.add_nexthop(nhgc, nh);
1230 }
1231 }
1232 }
1233
1234 void nexthop_group_disable_vrf(struct vrf *vrf)
1235 {
1236 struct nexthop_group_cmd *nhgc;
1237 struct nexthop_hold *nhh;
1238
1239 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1240 struct listnode *node;
1241
1242 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1243 struct nexthop nhop;
1244 struct nexthop *nh;
1245
1246 if (!nexthop_group_parse_nhh(&nhop, nhh))
1247 continue;
1248
1249 nh = nexthop_exists(&nhgc->nhg, &nhop);
1250
1251 if (!nh)
1252 continue;
1253
1254 if (nh->vrf_id != vrf->vrf_id)
1255 continue;
1256
1257 _nexthop_del(&nhgc->nhg, nh);
1258
1259 if (nhg_hooks.del_nexthop)
1260 nhg_hooks.del_nexthop(nhgc, nh);
1261
1262 nexthop_free(nh);
1263 }
1264 }
1265 }
1266
1267 void nexthop_group_interface_state_change(struct interface *ifp,
1268 ifindex_t oldifindex)
1269 {
1270 struct nexthop_group_cmd *nhgc;
1271 struct nexthop_hold *nhh;
1272
1273 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1274 struct listnode *node;
1275 struct nexthop *nh;
1276
1277 if (if_is_up(ifp)) {
1278 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1279 struct nexthop nhop;
1280
1281 if (!nexthop_group_parse_nhh(&nhop, nhh))
1282 continue;
1283
1284 switch (nhop.type) {
1285 case NEXTHOP_TYPE_IPV4:
1286 case NEXTHOP_TYPE_IPV6:
1287 case NEXTHOP_TYPE_BLACKHOLE:
1288 continue;
1289 case NEXTHOP_TYPE_IFINDEX:
1290 case NEXTHOP_TYPE_IPV4_IFINDEX:
1291 case NEXTHOP_TYPE_IPV6_IFINDEX:
1292 break;
1293 }
1294 nh = nexthop_exists(&nhgc->nhg, &nhop);
1295
1296 if (nh)
1297 continue;
1298
1299 if (ifp->ifindex != nhop.ifindex)
1300 continue;
1301
1302 nh = nexthop_new();
1303
1304 memcpy(nh, &nhop, sizeof(nhop));
1305 _nexthop_add(&nhgc->nhg.nexthop, nh);
1306
1307 if (nhg_hooks.add_nexthop)
1308 nhg_hooks.add_nexthop(nhgc, nh);
1309 }
1310 } else {
1311 struct nexthop *next_nh;
1312
1313 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1314 next_nh = nh->next;
1315 switch (nh->type) {
1316 case NEXTHOP_TYPE_IPV4:
1317 case NEXTHOP_TYPE_IPV6:
1318 case NEXTHOP_TYPE_BLACKHOLE:
1319 continue;
1320 case NEXTHOP_TYPE_IFINDEX:
1321 case NEXTHOP_TYPE_IPV4_IFINDEX:
1322 case NEXTHOP_TYPE_IPV6_IFINDEX:
1323 break;
1324 }
1325
1326 if (oldifindex != nh->ifindex)
1327 continue;
1328
1329 _nexthop_del(&nhgc->nhg, nh);
1330
1331 if (nhg_hooks.del_nexthop)
1332 nhg_hooks.del_nexthop(nhgc, nh);
1333
1334 nexthop_free(nh);
1335 }
1336 }
1337 }
1338 }
1339
1340 static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1341 {
1342 struct nexthop_group_cmd *nhgc;
1343
1344 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1345 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1346 }
1347 }
1348
1349 static const struct cmd_variable_handler nhg_name_handlers[] = {
1350 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1351 {.completions = NULL}};
1352
1353 void nexthop_group_init(void (*new)(const char *name),
1354 void (*modify)(const struct nexthop_group_cmd *nhgc),
1355 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1356 const struct nexthop *nhop),
1357 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1358 const struct nexthop *nhop),
1359 void (*delete)(const char *name))
1360 {
1361 RB_INIT(nhgc_entry_head, &nhgc_entries);
1362
1363 cmd_variable_handler_register(nhg_name_handlers);
1364
1365 install_node(&nexthop_group_node);
1366 install_element(CONFIG_NODE, &nexthop_group_cmd);
1367 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1368
1369 install_default(NH_GROUP_NODE);
1370 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1371 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
1372 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1373
1374 install_element(NH_GROUP_NODE, &nexthop_group_resilience_cmd);
1375 install_element(NH_GROUP_NODE, &no_nexthop_group_resilience_cmd);
1376
1377 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1378
1379 if (new)
1380 nhg_hooks.new = new;
1381 if (modify)
1382 nhg_hooks.modify = modify;
1383 if (add_nexthop)
1384 nhg_hooks.add_nexthop = add_nexthop;
1385 if (del_nexthop)
1386 nhg_hooks.del_nexthop = del_nexthop;
1387 if (delete)
1388 nhg_hooks.delete = delete;
1389 }