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