]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
Merge pull request #10070 from idryzhov/ospf6-memcmp-cleanup
[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 #ifndef VTYSH_EXTRACT_PL
32 #include "lib/nexthop_group_clippy.c"
33 #endif
34
35 DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
36
37 /*
38 * Internal struct used to hold nhg config strings
39 */
40 struct nexthop_hold {
41 char *nhvrf_name;
42 union sockunion *addr;
43 char *intf;
44 bool onlink;
45 char *labels;
46 uint32_t weight;
47 char *backup_str;
48 };
49
50 struct nexthop_group_hooks {
51 void (*new)(const char *name);
52 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
53 const struct nexthop *nhop);
54 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
55 const struct nexthop *nhop);
56 void (*delete)(const char *name);
57 };
58
59 static struct nexthop_group_hooks nhg_hooks;
60
61 static inline int
62 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
63 const struct nexthop_group_cmd *nhgc2);
64 RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
65 nexthop_group_cmd_compare)
66
67 static struct nhgc_entry_head nhgc_entries;
68
69 static inline int
70 nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
71 const struct nexthop_group_cmd *nhgc2)
72 {
73 return strcmp(nhgc1->name, nhgc2->name);
74 }
75
76 static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
77 {
78 struct nexthop *nexthop = nhg->nexthop;
79
80 while (nexthop && nexthop->next)
81 nexthop = nexthop->next;
82
83 return nexthop;
84 }
85
86 uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
87 {
88 struct nexthop *nhop;
89 uint8_t num = 0;
90
91 for (ALL_NEXTHOPS_PTR(nhg, nhop))
92 num++;
93
94 return num;
95 }
96
97 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
98 {
99 struct nexthop *nhop;
100 uint8_t num = 0;
101
102 for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
103 num++;
104
105 return num;
106 }
107
108 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
109 {
110 struct nexthop *nhop;
111 uint8_t num = 0;
112
113 for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
114 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
115 num++;
116 }
117
118 return num;
119 }
120
121 uint8_t
122 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
123 {
124 struct nexthop *nhop;
125 uint8_t num = 0;
126
127 for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
128 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
129 num++;
130 }
131
132 return num;
133 }
134
135 struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
136 const struct nexthop *nh)
137 {
138 struct nexthop *nexthop;
139
140 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
141 if (nexthop_same(nh, nexthop))
142 return nexthop;
143 }
144
145 return NULL;
146 }
147
148 /*
149 * Helper that locates a nexthop in an nhg config list. Note that
150 * this uses a specific matching / equality rule that's different from
151 * the complete match performed by 'nexthop_same()'.
152 */
153 static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
154 const struct nexthop *nh)
155 {
156 struct nexthop *nexthop;
157 int ret;
158
159 /* We compare: vrf, gateway, and interface */
160
161 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
162
163 /* Compare vrf and type */
164 if (nexthop->vrf_id != nh->vrf_id)
165 continue;
166 if (nexthop->type != nh->type)
167 continue;
168
169 /* Compare gateway */
170 switch (nexthop->type) {
171 case NEXTHOP_TYPE_IPV4:
172 case NEXTHOP_TYPE_IPV6:
173 ret = nexthop_g_addr_cmp(nexthop->type,
174 &nexthop->gate, &nh->gate);
175 if (ret != 0)
176 continue;
177 break;
178 case NEXTHOP_TYPE_IPV4_IFINDEX:
179 case NEXTHOP_TYPE_IPV6_IFINDEX:
180 ret = nexthop_g_addr_cmp(nexthop->type,
181 &nexthop->gate, &nh->gate);
182 if (ret != 0)
183 continue;
184 /* Intentional Fall-Through */
185 case NEXTHOP_TYPE_IFINDEX:
186 if (nexthop->ifindex != nh->ifindex)
187 continue;
188 break;
189 case NEXTHOP_TYPE_BLACKHOLE:
190 if (nexthop->bh_type != nh->bh_type)
191 continue;
192 break;
193 }
194
195 return nexthop;
196 }
197
198 return NULL;
199 }
200
201 static bool
202 nexthop_group_equal_common(const struct nexthop_group *nhg1,
203 const struct nexthop_group *nhg2,
204 uint8_t (*nexthop_group_nexthop_num_func)(
205 const struct nexthop_group *nhg))
206 {
207 if (nhg1 && !nhg2)
208 return false;
209
210 if (!nhg1 && nhg2)
211 return false;
212
213 if (nhg1 == nhg2)
214 return true;
215
216 if (nexthop_group_nexthop_num_func(nhg1)
217 != nexthop_group_nexthop_num_func(nhg2))
218 return false;
219
220 return true;
221 }
222
223 /* This assumes ordered */
224 bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
225 const struct nexthop_group *nhg2)
226 {
227 struct nexthop *nh1 = NULL;
228 struct nexthop *nh2 = NULL;
229
230 if (!nexthop_group_equal_common(nhg1, nhg2,
231 &nexthop_group_nexthop_num_no_recurse))
232 return false;
233
234 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
235 nh1 = nh1->next, nh2 = nh2->next) {
236 if (nh1 && !nh2)
237 return false;
238 if (!nh1 && nh2)
239 return false;
240 if (!nexthop_same(nh1, nh2))
241 return false;
242 }
243
244 return true;
245 }
246
247 /* This assumes ordered */
248 bool nexthop_group_equal(const struct nexthop_group *nhg1,
249 const struct nexthop_group *nhg2)
250 {
251 struct nexthop *nh1 = NULL;
252 struct nexthop *nh2 = NULL;
253
254 if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
255 return false;
256
257 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
258 nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
259 if (nh1 && !nh2)
260 return false;
261 if (!nh1 && nh2)
262 return false;
263 if (!nexthop_same(nh1, nh2))
264 return false;
265 }
266
267 return true;
268 }
269 struct nexthop_group *nexthop_group_new(void)
270 {
271 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
272 }
273
274 void nexthop_group_copy(struct nexthop_group *to,
275 const struct nexthop_group *from)
276 {
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 static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
679 const char *nhvrf_name,
680 const union sockunion *addr,
681 const char *intf, bool onlink,
682 const char *labels, const uint32_t weight,
683 const char *backup_str)
684 {
685 struct nexthop_hold *nh;
686
687 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
688
689 if (nhvrf_name)
690 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
691 if (intf)
692 nh->intf = XSTRDUP(MTYPE_TMP, intf);
693 if (addr)
694 nh->addr = sockunion_dup(addr);
695 if (labels)
696 nh->labels = XSTRDUP(MTYPE_TMP, labels);
697
698 nh->onlink = onlink;
699
700 nh->weight = weight;
701
702 if (backup_str)
703 nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
704
705 listnode_add_sort(nhgc->nhg_list, nh);
706 }
707
708 /*
709 * Remove config info about a nexthop from group 'nhgc'. Note that we
710 * use only a subset of the available attributes here to determine
711 * a 'match'.
712 * Note that this doesn't change the list of nexthops, only the config
713 * information.
714 */
715 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
716 const char *nhvrf_name,
717 const union sockunion *addr,
718 const char *intf)
719 {
720 struct nexthop_hold *nh;
721 struct listnode *node;
722
723 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
724 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
725 && nhgc_addr_cmp_helper(addr, nh->addr) == 0
726 && nhgc_cmp_helper(intf, nh->intf) == 0)
727 break;
728 }
729
730 /*
731 * Something has gone seriously wrong, fail gracefully
732 */
733 if (!nh)
734 return;
735
736 list_delete_node(nhgc->nhg_list, node);
737 nhgl_delete(nh);
738 }
739
740 /*
741 * Parse the config strings we support for a single nexthop. This gets used
742 * in a couple of different ways, and we distinguish between transient
743 * failures - such as a still-unprocessed interface - and fatal errors
744 * from label-string parsing.
745 */
746 static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
747 const union sockunion *addr,
748 const char *intf, bool onlink,
749 const char *name, const char *labels,
750 int *lbl_ret, uint32_t weight,
751 const char *backup_str)
752 {
753 int ret = 0;
754 struct vrf *vrf;
755 int num;
756
757 memset(nhop, 0, sizeof(*nhop));
758
759 if (name)
760 vrf = vrf_lookup_by_name(name);
761 else
762 vrf = vrf_lookup_by_id(VRF_DEFAULT);
763
764 if (!vrf)
765 return false;
766
767 nhop->vrf_id = vrf->vrf_id;
768
769 if (intf) {
770 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
771 if (nhop->ifindex == IFINDEX_INTERNAL)
772 return false;
773 }
774
775 if (onlink)
776 SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
777
778 if (addr) {
779 if (addr->sa.sa_family == AF_INET) {
780 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
781 if (intf)
782 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
783 else
784 nhop->type = NEXTHOP_TYPE_IPV4;
785 } else {
786 nhop->gate.ipv6 = addr->sin6.sin6_addr;
787 if (intf)
788 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
789 else
790 nhop->type = NEXTHOP_TYPE_IPV6;
791 }
792 } else
793 nhop->type = NEXTHOP_TYPE_IFINDEX;
794
795 if (labels) {
796 uint8_t num = 0;
797 mpls_label_t larray[MPLS_MAX_LABELS];
798
799 ret = mpls_str2label(labels, &num, larray);
800
801 /* Return label parse result */
802 if (lbl_ret)
803 *lbl_ret = ret;
804
805 if (ret < 0)
806 return false;
807 else if (num > 0)
808 nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
809 num, larray);
810 }
811
812 nhop->weight = weight;
813
814 if (backup_str) {
815 /* Parse backup indexes */
816 ret = nexthop_str2backups(backup_str,
817 &num, nhop->backup_idx);
818 if (ret == 0) {
819 SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
820 nhop->backup_num = num;
821 } else
822 return false;
823 }
824
825 return true;
826 }
827
828 /*
829 * Wrapper to parse the strings in a 'nexthop_hold'
830 */
831 static bool nexthop_group_parse_nhh(struct nexthop *nhop,
832 const struct nexthop_hold *nhh)
833 {
834 return (nexthop_group_parse_nexthop(
835 nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
836 nhh->labels, NULL, nhh->weight, nhh->backup_str));
837 }
838
839 DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
840 "[no] nexthop\
841 <\
842 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
843 |INTERFACE$intf\
844 >\
845 [{ \
846 nexthop-vrf NAME$vrf_name \
847 |label WORD \
848 |weight (1-255) \
849 |backup-idx WORD \
850 }]",
851 NO_STR
852 "Specify one of the nexthops in this ECMP group\n"
853 "v4 Address\n"
854 "v6 Address\n"
855 "Interface to use\n"
856 "Treat nexthop as directly attached to the interface\n"
857 "Interface to use\n"
858 "If the nexthop is in a different vrf tell us\n"
859 "The nexthop-vrf Name\n"
860 "Specify label(s) for this nexthop\n"
861 "One or more labels in the range (16-1048575) separated by '/'\n"
862 "Weight to be used by the nexthop for purposes of ECMP\n"
863 "Weight value to be used\n"
864 "Specify backup nexthop indexes in another group\n"
865 "One or more indexes in the range (0-254) separated by ','\n")
866 {
867 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
868 struct nexthop nhop;
869 struct nexthop *nh;
870 int lbl_ret = 0;
871 bool legal;
872 int num;
873 uint8_t backups[NEXTHOP_MAX_BACKUPS];
874 bool yes = !no;
875
876 /* Pre-parse backup string to validate */
877 if (backup_idx) {
878 lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
879 if (lbl_ret < 0) {
880 vty_out(vty, "%% Invalid backups\n");
881 return CMD_WARNING_CONFIG_FAILED;
882 }
883 }
884
885 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
886 vrf_name, label, &lbl_ret, weight,
887 backup_idx);
888
889 if (nhop.type == NEXTHOP_TYPE_IPV6
890 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
891 vty_out(vty,
892 "Specified a v6 LL with no interface, rejecting\n");
893 return CMD_WARNING_CONFIG_FAILED;
894 }
895
896 /* Handle label-string errors */
897 if (!legal && lbl_ret < 0) {
898 switch (lbl_ret) {
899 case -1:
900 vty_out(vty, "%% Malformed label(s)\n");
901 break;
902 case -2:
903 vty_out(vty,
904 "%% Cannot use reserved label(s) (%d-%d)\n",
905 MPLS_LABEL_RESERVED_MIN,
906 MPLS_LABEL_RESERVED_MAX);
907 break;
908 case -3:
909 vty_out(vty,
910 "%% Too many labels. Enter %d or fewer\n",
911 MPLS_MAX_LABELS);
912 break;
913 }
914 return CMD_WARNING_CONFIG_FAILED;
915 }
916
917 /* Look for an existing nexthop in the config. Note that the test
918 * here tests only some attributes - it's not a complete comparison.
919 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
920 * that represent config that may or may not be valid (yet), and
921 * actual nexthops that have been validated and parsed.
922 */
923 nh = nhg_nh_find(&nhgc->nhg, &nhop);
924
925 /* Always attempt to remove old config info. */
926 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
927
928 /* Remove any existing nexthop, for delete and replace cases. */
929 if (nh) {
930 nexthop_unlink(&nhgc->nhg, nh);
931
932 if (nhg_hooks.del_nexthop)
933 nhg_hooks.del_nexthop(nhgc, nh);
934
935 nexthop_free(nh);
936 }
937 if (yes) {
938 /* Add/replace case: capture nexthop if valid, and capture
939 * config info always.
940 */
941 if (legal) {
942 nh = nexthop_new();
943
944 memcpy(nh, &nhop, sizeof(nhop));
945 _nexthop_add(&nhgc->nhg.nexthop, nh);
946 }
947
948 /* Save config always */
949 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
950 label, weight, backup_idx);
951
952 if (legal && nhg_hooks.add_nexthop)
953 nhg_hooks.add_nexthop(nhgc, nh);
954 }
955
956 return CMD_SUCCESS;
957 }
958
959 static int nexthop_group_write(struct vty *vty);
960 static struct cmd_node nexthop_group_node = {
961 .name = "nexthop-group",
962 .node = NH_GROUP_NODE,
963 .parent_node = CONFIG_NODE,
964 .prompt = "%s(config-nh-group)# ",
965 .config_write = nexthop_group_write,
966 };
967
968 void nexthop_group_write_nexthop_simple(struct vty *vty,
969 const struct nexthop *nh,
970 char *altifname)
971 {
972 char *ifname;
973
974 vty_out(vty, "nexthop ");
975
976 if (altifname)
977 ifname = altifname;
978 else
979 ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
980
981 switch (nh->type) {
982 case NEXTHOP_TYPE_IFINDEX:
983 vty_out(vty, "%s", ifname);
984 break;
985 case NEXTHOP_TYPE_IPV4:
986 vty_out(vty, "%pI4", &nh->gate.ipv4);
987 break;
988 case NEXTHOP_TYPE_IPV4_IFINDEX:
989 vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
990 break;
991 case NEXTHOP_TYPE_IPV6:
992 vty_out(vty, "%pI6", &nh->gate.ipv6);
993 break;
994 case NEXTHOP_TYPE_IPV6_IFINDEX:
995 vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
996 break;
997 case NEXTHOP_TYPE_BLACKHOLE:
998 break;
999 }
1000 }
1001
1002 void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1003 {
1004 struct vrf *vrf;
1005 int i;
1006
1007 nexthop_group_write_nexthop_simple(vty, nh, NULL);
1008
1009 if (nh->vrf_id != VRF_DEFAULT) {
1010 vrf = vrf_lookup_by_id(nh->vrf_id);
1011 vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1012 }
1013
1014 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1015 char buf[200];
1016
1017 mpls_label2str(nh->nh_label->num_labels,
1018 nh->nh_label->label,
1019 buf, sizeof(buf), 0);
1020 vty_out(vty, " label %s", buf);
1021 }
1022
1023 if (nh->weight)
1024 vty_out(vty, " weight %u", nh->weight);
1025
1026 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1027 vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
1028
1029 for (i = 1; i < nh->backup_num; i++)
1030 vty_out(vty, ",%d", nh->backup_idx[i]);
1031 }
1032
1033 vty_out(vty, "\n");
1034 }
1035
1036 void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
1037 {
1038 struct vrf *vrf;
1039 json_object *json_backups = NULL;
1040 int i;
1041
1042 switch (nh->type) {
1043 case NEXTHOP_TYPE_IFINDEX:
1044 json_object_string_add(j, "nexthop",
1045 ifindex2ifname(nh->ifindex, nh->vrf_id));
1046 break;
1047 case NEXTHOP_TYPE_IPV4:
1048 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1049 break;
1050 case NEXTHOP_TYPE_IPV4_IFINDEX:
1051 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
1052 json_object_string_add(j, "vrfId",
1053 ifindex2ifname(nh->ifindex, nh->vrf_id));
1054 break;
1055 case NEXTHOP_TYPE_IPV6:
1056 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1057 break;
1058 case NEXTHOP_TYPE_IPV6_IFINDEX:
1059 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
1060 json_object_string_add(j, "vrfId",
1061 ifindex2ifname(nh->ifindex, nh->vrf_id));
1062 break;
1063 case NEXTHOP_TYPE_BLACKHOLE:
1064 break;
1065 }
1066
1067 if (nh->vrf_id != VRF_DEFAULT) {
1068 vrf = vrf_lookup_by_id(nh->vrf_id);
1069 json_object_string_add(j, "targetVrf", vrf->name);
1070 }
1071
1072 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1073 char buf[200];
1074
1075 mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1076 buf, sizeof(buf), 0);
1077 json_object_string_add(j, "label", buf);
1078 }
1079
1080 if (nh->weight)
1081 json_object_int_add(j, "weight", nh->weight);
1082
1083 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1084 json_backups = json_object_new_array();
1085 for (i = 0; i < nh->backup_num; i++)
1086 json_object_array_add(
1087 json_backups,
1088 json_object_new_int(nh->backup_idx[i]));
1089
1090 json_object_object_add(j, "backupIdx", json_backups);
1091 }
1092 }
1093
1094 static void nexthop_group_write_nexthop_internal(struct vty *vty,
1095 const struct nexthop_hold *nh)
1096 {
1097 char buf[100];
1098
1099 vty_out(vty, "nexthop");
1100
1101 if (nh->addr)
1102 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
1103
1104 if (nh->intf)
1105 vty_out(vty, " %s", nh->intf);
1106
1107 if (nh->onlink)
1108 vty_out(vty, " onlink");
1109
1110 if (nh->nhvrf_name)
1111 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1112
1113 if (nh->labels)
1114 vty_out(vty, " label %s", nh->labels);
1115
1116 if (nh->weight)
1117 vty_out(vty, " weight %u", nh->weight);
1118
1119 if (nh->backup_str)
1120 vty_out(vty, " backup-idx %s", nh->backup_str);
1121
1122 vty_out(vty, "\n");
1123 }
1124
1125 static int nexthop_group_write(struct vty *vty)
1126 {
1127 struct nexthop_group_cmd *nhgc;
1128 struct nexthop_hold *nh;
1129
1130 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1131 struct listnode *node;
1132
1133 vty_out(vty, "nexthop-group %s\n", nhgc->name);
1134
1135 if (nhgc->backup_list_name[0])
1136 vty_out(vty, " backup-group %s\n",
1137 nhgc->backup_list_name);
1138
1139 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
1140 vty_out(vty, " ");
1141 nexthop_group_write_nexthop_internal(vty, nh);
1142 }
1143
1144 vty_out(vty, "exit\n");
1145 vty_out(vty, "!\n");
1146 }
1147
1148 return 1;
1149 }
1150
1151 void nexthop_group_enable_vrf(struct vrf *vrf)
1152 {
1153 struct nexthop_group_cmd *nhgc;
1154 struct nexthop_hold *nhh;
1155
1156 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1157 struct listnode *node;
1158
1159 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1160 struct nexthop nhop;
1161 struct nexthop *nh;
1162
1163 if (!nexthop_group_parse_nhh(&nhop, nhh))
1164 continue;
1165
1166 nh = nexthop_exists(&nhgc->nhg, &nhop);
1167
1168 if (nh)
1169 continue;
1170
1171 if (nhop.vrf_id != vrf->vrf_id)
1172 continue;
1173
1174 nh = nexthop_new();
1175
1176 memcpy(nh, &nhop, sizeof(nhop));
1177 _nexthop_add(&nhgc->nhg.nexthop, nh);
1178
1179 if (nhg_hooks.add_nexthop)
1180 nhg_hooks.add_nexthop(nhgc, nh);
1181 }
1182 }
1183 }
1184
1185 void nexthop_group_disable_vrf(struct vrf *vrf)
1186 {
1187 struct nexthop_group_cmd *nhgc;
1188 struct nexthop_hold *nhh;
1189
1190 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1191 struct listnode *node;
1192
1193 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1194 struct nexthop nhop;
1195 struct nexthop *nh;
1196
1197 if (!nexthop_group_parse_nhh(&nhop, nhh))
1198 continue;
1199
1200 nh = nexthop_exists(&nhgc->nhg, &nhop);
1201
1202 if (!nh)
1203 continue;
1204
1205 if (nh->vrf_id != vrf->vrf_id)
1206 continue;
1207
1208 _nexthop_del(&nhgc->nhg, nh);
1209
1210 if (nhg_hooks.del_nexthop)
1211 nhg_hooks.del_nexthop(nhgc, nh);
1212
1213 nexthop_free(nh);
1214 }
1215 }
1216 }
1217
1218 void nexthop_group_interface_state_change(struct interface *ifp,
1219 ifindex_t oldifindex)
1220 {
1221 struct nexthop_group_cmd *nhgc;
1222 struct nexthop_hold *nhh;
1223
1224 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1225 struct listnode *node;
1226 struct nexthop *nh;
1227
1228 if (if_is_up(ifp)) {
1229 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1230 struct nexthop nhop;
1231
1232 if (!nexthop_group_parse_nhh(&nhop, nhh))
1233 continue;
1234
1235 switch (nhop.type) {
1236 case NEXTHOP_TYPE_IPV4:
1237 case NEXTHOP_TYPE_IPV6:
1238 case NEXTHOP_TYPE_BLACKHOLE:
1239 continue;
1240 case NEXTHOP_TYPE_IFINDEX:
1241 case NEXTHOP_TYPE_IPV4_IFINDEX:
1242 case NEXTHOP_TYPE_IPV6_IFINDEX:
1243 break;
1244 }
1245 nh = nexthop_exists(&nhgc->nhg, &nhop);
1246
1247 if (nh)
1248 continue;
1249
1250 if (ifp->ifindex != nhop.ifindex)
1251 continue;
1252
1253 nh = nexthop_new();
1254
1255 memcpy(nh, &nhop, sizeof(nhop));
1256 _nexthop_add(&nhgc->nhg.nexthop, nh);
1257
1258 if (nhg_hooks.add_nexthop)
1259 nhg_hooks.add_nexthop(nhgc, nh);
1260 }
1261 } else {
1262 struct nexthop *next_nh;
1263
1264 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1265 next_nh = nh->next;
1266 switch (nh->type) {
1267 case NEXTHOP_TYPE_IPV4:
1268 case NEXTHOP_TYPE_IPV6:
1269 case NEXTHOP_TYPE_BLACKHOLE:
1270 continue;
1271 case NEXTHOP_TYPE_IFINDEX:
1272 case NEXTHOP_TYPE_IPV4_IFINDEX:
1273 case NEXTHOP_TYPE_IPV6_IFINDEX:
1274 break;
1275 }
1276
1277 if (oldifindex != nh->ifindex)
1278 continue;
1279
1280 _nexthop_del(&nhgc->nhg, nh);
1281
1282 if (nhg_hooks.del_nexthop)
1283 nhg_hooks.del_nexthop(nhgc, nh);
1284
1285 nexthop_free(nh);
1286 }
1287 }
1288 }
1289 }
1290
1291 static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1292 {
1293 struct nexthop_group_cmd *nhgc;
1294
1295 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1296 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1297 }
1298 }
1299
1300 static const struct cmd_variable_handler nhg_name_handlers[] = {
1301 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1302 {.completions = NULL}};
1303
1304 void nexthop_group_init(void (*new)(const char *name),
1305 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1306 const struct nexthop *nhop),
1307 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1308 const struct nexthop *nhop),
1309 void (*delete)(const char *name))
1310 {
1311 RB_INIT(nhgc_entry_head, &nhgc_entries);
1312
1313 cmd_variable_handler_register(nhg_name_handlers);
1314
1315 install_node(&nexthop_group_node);
1316 install_element(CONFIG_NODE, &nexthop_group_cmd);
1317 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1318
1319 install_default(NH_GROUP_NODE);
1320 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1321 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
1322 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1323
1324 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1325
1326 if (new)
1327 nhg_hooks.new = new;
1328 if (add_nexthop)
1329 nhg_hooks.add_nexthop = add_nexthop;
1330 if (del_nexthop)
1331 nhg_hooks.del_nexthop = del_nexthop;
1332 if (delete)
1333 nhg_hooks.delete = delete;
1334 }