]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
Merge pull request #7220 from idryzhov/fix-clear-isis
[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_firsthop(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 if (intf) {
957 struct interface *ifp = if_lookup_by_name_all_vrf(intf);
958
959 if (ifp)
960 ifp->configured = true;
961 }
962 return CMD_SUCCESS;
963 }
964
965 static int nexthop_group_write(struct vty *vty);
966 static struct cmd_node nexthop_group_node = {
967 .name = "nexthop-group",
968 .node = NH_GROUP_NODE,
969 .parent_node = CONFIG_NODE,
970 .prompt = "%s(config-nh-group)# ",
971 .config_write = nexthop_group_write,
972 };
973
974 void nexthop_group_write_nexthop_simple(struct vty *vty,
975 const struct nexthop *nh,
976 char *altifname)
977 {
978 char buf[100];
979 char *ifname;
980
981 vty_out(vty, "nexthop ");
982
983 if (altifname)
984 ifname = altifname;
985 else
986 ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
987
988 switch (nh->type) {
989 case NEXTHOP_TYPE_IFINDEX:
990 vty_out(vty, "%s", ifname);
991 break;
992 case NEXTHOP_TYPE_IPV4:
993 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
994 break;
995 case NEXTHOP_TYPE_IPV4_IFINDEX:
996 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), ifname);
997 break;
998 case NEXTHOP_TYPE_IPV6:
999 vty_out(vty, "%s",
1000 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1001 break;
1002 case NEXTHOP_TYPE_IPV6_IFINDEX:
1003 vty_out(vty, "%s %s",
1004 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
1005 ifname);
1006 break;
1007 case NEXTHOP_TYPE_BLACKHOLE:
1008 break;
1009 }
1010 }
1011
1012 void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
1013 {
1014 struct vrf *vrf;
1015 int i;
1016
1017 nexthop_group_write_nexthop_simple(vty, nh, NULL);
1018
1019 if (nh->vrf_id != VRF_DEFAULT) {
1020 vrf = vrf_lookup_by_id(nh->vrf_id);
1021 vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
1022 }
1023
1024 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1025 char buf[200];
1026
1027 mpls_label2str(nh->nh_label->num_labels,
1028 nh->nh_label->label,
1029 buf, sizeof(buf), 0);
1030 vty_out(vty, " label %s", buf);
1031 }
1032
1033 if (nh->weight)
1034 vty_out(vty, " weight %u", nh->weight);
1035
1036 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1037 vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
1038
1039 for (i = 1; i < nh->backup_num; i++)
1040 vty_out(vty, ",%d", nh->backup_idx[i]);
1041 }
1042
1043 vty_out(vty, "\n");
1044 }
1045
1046 void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
1047 {
1048 char buf[100];
1049 struct vrf *vrf;
1050 json_object *json_backups = NULL;
1051 int i;
1052
1053 switch (nh->type) {
1054 case NEXTHOP_TYPE_IFINDEX:
1055 json_object_string_add(j, "nexthop",
1056 ifindex2ifname(nh->ifindex, nh->vrf_id));
1057 break;
1058 case NEXTHOP_TYPE_IPV4:
1059 json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4));
1060 break;
1061 case NEXTHOP_TYPE_IPV4_IFINDEX:
1062 json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4));
1063 json_object_string_add(j, "vrfId",
1064 ifindex2ifname(nh->ifindex, nh->vrf_id));
1065 break;
1066 case NEXTHOP_TYPE_IPV6:
1067 json_object_string_add(
1068 j, "nexthop",
1069 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1070 break;
1071 case NEXTHOP_TYPE_IPV6_IFINDEX:
1072 json_object_string_add(
1073 j, "nexthop",
1074 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
1075 json_object_string_add(j, "vrfId",
1076 ifindex2ifname(nh->ifindex, nh->vrf_id));
1077 break;
1078 case NEXTHOP_TYPE_BLACKHOLE:
1079 break;
1080 }
1081
1082 if (nh->vrf_id != VRF_DEFAULT) {
1083 vrf = vrf_lookup_by_id(nh->vrf_id);
1084 json_object_string_add(j, "targetVrf", vrf->name);
1085 }
1086
1087 if (nh->nh_label && nh->nh_label->num_labels > 0) {
1088 char buf[200];
1089
1090 mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
1091 buf, sizeof(buf), 0);
1092 json_object_string_add(j, "label", buf);
1093 }
1094
1095 if (nh->weight)
1096 json_object_int_add(j, "weight", nh->weight);
1097
1098 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
1099 json_backups = json_object_new_array();
1100 for (i = 0; i < nh->backup_num; i++)
1101 json_object_array_add(
1102 json_backups,
1103 json_object_new_int(nh->backup_idx[i]));
1104
1105 json_object_object_add(j, "backupIdx", json_backups);
1106 }
1107 }
1108
1109 static void nexthop_group_write_nexthop_internal(struct vty *vty,
1110 const struct nexthop_hold *nh)
1111 {
1112 char buf[100];
1113
1114 vty_out(vty, "nexthop");
1115
1116 if (nh->addr)
1117 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
1118
1119 if (nh->intf)
1120 vty_out(vty, " %s", nh->intf);
1121
1122 if (nh->onlink)
1123 vty_out(vty, " onlink");
1124
1125 if (nh->nhvrf_name)
1126 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1127
1128 if (nh->labels)
1129 vty_out(vty, " label %s", nh->labels);
1130
1131 if (nh->weight)
1132 vty_out(vty, " weight %u", nh->weight);
1133
1134 if (nh->backup_str)
1135 vty_out(vty, " backup-idx %s", nh->backup_str);
1136
1137 vty_out(vty, "\n");
1138 }
1139
1140 static int nexthop_group_write(struct vty *vty)
1141 {
1142 struct nexthop_group_cmd *nhgc;
1143 struct nexthop_hold *nh;
1144
1145 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1146 struct listnode *node;
1147
1148 vty_out(vty, "nexthop-group %s\n", nhgc->name);
1149
1150 if (nhgc->backup_list_name[0])
1151 vty_out(vty, " backup-group %s\n",
1152 nhgc->backup_list_name);
1153
1154 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
1155 vty_out(vty, " ");
1156 nexthop_group_write_nexthop_internal(vty, nh);
1157 }
1158
1159 vty_out(vty, "!\n");
1160 }
1161
1162 return 1;
1163 }
1164
1165 void nexthop_group_enable_vrf(struct vrf *vrf)
1166 {
1167 struct nexthop_group_cmd *nhgc;
1168 struct nexthop_hold *nhh;
1169
1170 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1171 struct listnode *node;
1172
1173 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1174 struct nexthop nhop;
1175 struct nexthop *nh;
1176
1177 if (!nexthop_group_parse_nhh(&nhop, nhh))
1178 continue;
1179
1180 nh = nexthop_exists(&nhgc->nhg, &nhop);
1181
1182 if (nh)
1183 continue;
1184
1185 if (nhop.vrf_id != vrf->vrf_id)
1186 continue;
1187
1188 nh = nexthop_new();
1189
1190 memcpy(nh, &nhop, sizeof(nhop));
1191 _nexthop_add(&nhgc->nhg.nexthop, nh);
1192
1193 if (nhg_hooks.add_nexthop)
1194 nhg_hooks.add_nexthop(nhgc, nh);
1195 }
1196 }
1197 }
1198
1199 void nexthop_group_disable_vrf(struct vrf *vrf)
1200 {
1201 struct nexthop_group_cmd *nhgc;
1202 struct nexthop_hold *nhh;
1203
1204 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1205 struct listnode *node;
1206
1207 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1208 struct nexthop nhop;
1209 struct nexthop *nh;
1210
1211 if (!nexthop_group_parse_nhh(&nhop, nhh))
1212 continue;
1213
1214 nh = nexthop_exists(&nhgc->nhg, &nhop);
1215
1216 if (!nh)
1217 continue;
1218
1219 if (nh->vrf_id != vrf->vrf_id)
1220 continue;
1221
1222 _nexthop_del(&nhgc->nhg, nh);
1223
1224 if (nhg_hooks.del_nexthop)
1225 nhg_hooks.del_nexthop(nhgc, nh);
1226
1227 nexthop_free(nh);
1228 }
1229 }
1230 }
1231
1232 void nexthop_group_interface_state_change(struct interface *ifp,
1233 ifindex_t oldifindex)
1234 {
1235 struct nexthop_group_cmd *nhgc;
1236 struct nexthop_hold *nhh;
1237
1238 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1239 struct listnode *node;
1240 struct nexthop *nh;
1241
1242 if (if_is_up(ifp)) {
1243 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1244 struct nexthop nhop;
1245
1246 if (!nexthop_group_parse_nhh(&nhop, nhh))
1247 continue;
1248
1249 switch (nhop.type) {
1250 case NEXTHOP_TYPE_IPV4:
1251 case NEXTHOP_TYPE_IPV6:
1252 case NEXTHOP_TYPE_BLACKHOLE:
1253 continue;
1254 case NEXTHOP_TYPE_IFINDEX:
1255 case NEXTHOP_TYPE_IPV4_IFINDEX:
1256 case NEXTHOP_TYPE_IPV6_IFINDEX:
1257 break;
1258 }
1259 nh = nexthop_exists(&nhgc->nhg, &nhop);
1260
1261 if (nh)
1262 continue;
1263
1264 if (ifp->ifindex != nhop.ifindex)
1265 continue;
1266
1267 ifp->configured = true;
1268 nh = nexthop_new();
1269
1270 memcpy(nh, &nhop, sizeof(nhop));
1271 _nexthop_add(&nhgc->nhg.nexthop, nh);
1272
1273 if (nhg_hooks.add_nexthop)
1274 nhg_hooks.add_nexthop(nhgc, nh);
1275 }
1276 } else {
1277 struct nexthop *next_nh;
1278
1279 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1280 next_nh = nh->next;
1281 switch (nh->type) {
1282 case NEXTHOP_TYPE_IPV4:
1283 case NEXTHOP_TYPE_IPV6:
1284 case NEXTHOP_TYPE_BLACKHOLE:
1285 continue;
1286 case NEXTHOP_TYPE_IFINDEX:
1287 case NEXTHOP_TYPE_IPV4_IFINDEX:
1288 case NEXTHOP_TYPE_IPV6_IFINDEX:
1289 break;
1290 }
1291
1292 if (oldifindex != nh->ifindex)
1293 continue;
1294
1295 _nexthop_del(&nhgc->nhg, nh);
1296
1297 if (nhg_hooks.del_nexthop)
1298 nhg_hooks.del_nexthop(nhgc, nh);
1299
1300 nexthop_free(nh);
1301 }
1302 }
1303 }
1304 }
1305
1306 static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1307 {
1308 struct nexthop_group_cmd *nhgc;
1309
1310 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1311 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1312 }
1313 }
1314
1315 static const struct cmd_variable_handler nhg_name_handlers[] = {
1316 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1317 {.completions = NULL}};
1318
1319 void nexthop_group_init(void (*new)(const char *name),
1320 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1321 const struct nexthop *nhop),
1322 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1323 const struct nexthop *nhop),
1324 void (*delete)(const char *name))
1325 {
1326 RB_INIT(nhgc_entry_head, &nhgc_entries);
1327
1328 cmd_variable_handler_register(nhg_name_handlers);
1329
1330 install_node(&nexthop_group_node);
1331 install_element(CONFIG_NODE, &nexthop_group_cmd);
1332 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1333
1334 install_default(NH_GROUP_NODE);
1335 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1336 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
1337 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1338
1339 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1340
1341 if (new)
1342 nhg_hooks.new = new;
1343 if (add_nexthop)
1344 nhg_hooks.add_nexthop = add_nexthop;
1345 if (del_nexthop)
1346 nhg_hooks.del_nexthop = del_nexthop;
1347 if (delete)
1348 nhg_hooks.delete = delete;
1349 }