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