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