]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
Merge pull request #11437 from rgirada/ospf_nbr
[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 vty_out(vty, "nexthop");
1098
1099 if (nh->addr)
1100 vty_out(vty, " %pSU", nh->addr);
1101
1102 if (nh->intf)
1103 vty_out(vty, " %s", nh->intf);
1104
1105 if (nh->onlink)
1106 vty_out(vty, " onlink");
1107
1108 if (nh->nhvrf_name)
1109 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
1110
1111 if (nh->labels)
1112 vty_out(vty, " label %s", nh->labels);
1113
1114 if (nh->weight)
1115 vty_out(vty, " weight %u", nh->weight);
1116
1117 if (nh->backup_str)
1118 vty_out(vty, " backup-idx %s", nh->backup_str);
1119
1120 vty_out(vty, "\n");
1121 }
1122
1123 static int nexthop_group_write(struct vty *vty)
1124 {
1125 struct nexthop_group_cmd *nhgc;
1126 struct nexthop_hold *nh;
1127
1128 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1129 struct listnode *node;
1130
1131 vty_out(vty, "nexthop-group %s\n", nhgc->name);
1132
1133 if (nhgc->backup_list_name[0])
1134 vty_out(vty, " backup-group %s\n",
1135 nhgc->backup_list_name);
1136
1137 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
1138 vty_out(vty, " ");
1139 nexthop_group_write_nexthop_internal(vty, nh);
1140 }
1141
1142 vty_out(vty, "exit\n");
1143 vty_out(vty, "!\n");
1144 }
1145
1146 return 1;
1147 }
1148
1149 void nexthop_group_enable_vrf(struct vrf *vrf)
1150 {
1151 struct nexthop_group_cmd *nhgc;
1152 struct nexthop_hold *nhh;
1153
1154 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1155 struct listnode *node;
1156
1157 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1158 struct nexthop nhop;
1159 struct nexthop *nh;
1160
1161 if (!nexthop_group_parse_nhh(&nhop, nhh))
1162 continue;
1163
1164 nh = nexthop_exists(&nhgc->nhg, &nhop);
1165
1166 if (nh)
1167 continue;
1168
1169 if (nhop.vrf_id != vrf->vrf_id)
1170 continue;
1171
1172 nh = nexthop_new();
1173
1174 memcpy(nh, &nhop, sizeof(nhop));
1175 _nexthop_add(&nhgc->nhg.nexthop, nh);
1176
1177 if (nhg_hooks.add_nexthop)
1178 nhg_hooks.add_nexthop(nhgc, nh);
1179 }
1180 }
1181 }
1182
1183 void nexthop_group_disable_vrf(struct vrf *vrf)
1184 {
1185 struct nexthop_group_cmd *nhgc;
1186 struct nexthop_hold *nhh;
1187
1188 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1189 struct listnode *node;
1190
1191 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1192 struct nexthop nhop;
1193 struct nexthop *nh;
1194
1195 if (!nexthop_group_parse_nhh(&nhop, nhh))
1196 continue;
1197
1198 nh = nexthop_exists(&nhgc->nhg, &nhop);
1199
1200 if (!nh)
1201 continue;
1202
1203 if (nh->vrf_id != vrf->vrf_id)
1204 continue;
1205
1206 _nexthop_del(&nhgc->nhg, nh);
1207
1208 if (nhg_hooks.del_nexthop)
1209 nhg_hooks.del_nexthop(nhgc, nh);
1210
1211 nexthop_free(nh);
1212 }
1213 }
1214 }
1215
1216 void nexthop_group_interface_state_change(struct interface *ifp,
1217 ifindex_t oldifindex)
1218 {
1219 struct nexthop_group_cmd *nhgc;
1220 struct nexthop_hold *nhh;
1221
1222 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1223 struct listnode *node;
1224 struct nexthop *nh;
1225
1226 if (if_is_up(ifp)) {
1227 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
1228 struct nexthop nhop;
1229
1230 if (!nexthop_group_parse_nhh(&nhop, nhh))
1231 continue;
1232
1233 switch (nhop.type) {
1234 case NEXTHOP_TYPE_IPV4:
1235 case NEXTHOP_TYPE_IPV6:
1236 case NEXTHOP_TYPE_BLACKHOLE:
1237 continue;
1238 case NEXTHOP_TYPE_IFINDEX:
1239 case NEXTHOP_TYPE_IPV4_IFINDEX:
1240 case NEXTHOP_TYPE_IPV6_IFINDEX:
1241 break;
1242 }
1243 nh = nexthop_exists(&nhgc->nhg, &nhop);
1244
1245 if (nh)
1246 continue;
1247
1248 if (ifp->ifindex != nhop.ifindex)
1249 continue;
1250
1251 nh = nexthop_new();
1252
1253 memcpy(nh, &nhop, sizeof(nhop));
1254 _nexthop_add(&nhgc->nhg.nexthop, nh);
1255
1256 if (nhg_hooks.add_nexthop)
1257 nhg_hooks.add_nexthop(nhgc, nh);
1258 }
1259 } else {
1260 struct nexthop *next_nh;
1261
1262 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1263 next_nh = nh->next;
1264 switch (nh->type) {
1265 case NEXTHOP_TYPE_IPV4:
1266 case NEXTHOP_TYPE_IPV6:
1267 case NEXTHOP_TYPE_BLACKHOLE:
1268 continue;
1269 case NEXTHOP_TYPE_IFINDEX:
1270 case NEXTHOP_TYPE_IPV4_IFINDEX:
1271 case NEXTHOP_TYPE_IPV6_IFINDEX:
1272 break;
1273 }
1274
1275 if (oldifindex != nh->ifindex)
1276 continue;
1277
1278 _nexthop_del(&nhgc->nhg, nh);
1279
1280 if (nhg_hooks.del_nexthop)
1281 nhg_hooks.del_nexthop(nhgc, nh);
1282
1283 nexthop_free(nh);
1284 }
1285 }
1286 }
1287 }
1288
1289 static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1290 {
1291 struct nexthop_group_cmd *nhgc;
1292
1293 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1294 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1295 }
1296 }
1297
1298 static const struct cmd_variable_handler nhg_name_handlers[] = {
1299 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1300 {.completions = NULL}};
1301
1302 void nexthop_group_init(void (*new)(const char *name),
1303 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1304 const struct nexthop *nhop),
1305 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1306 const struct nexthop *nhop),
1307 void (*delete)(const char *name))
1308 {
1309 RB_INIT(nhgc_entry_head, &nhgc_entries);
1310
1311 cmd_variable_handler_register(nhg_name_handlers);
1312
1313 install_node(&nexthop_group_node);
1314 install_element(CONFIG_NODE, &nexthop_group_cmd);
1315 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1316
1317 install_default(NH_GROUP_NODE);
1318 install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
1319 install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
1320 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1321
1322 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1323
1324 if (new)
1325 nhg_hooks.new = new;
1326 if (add_nexthop)
1327 nhg_hooks.add_nexthop = add_nexthop;
1328 if (del_nexthop)
1329 nhg_hooks.del_nexthop = del_nexthop;
1330 if (delete)
1331 nhg_hooks.delete = delete;
1332 }