]> git.proxmox.com Git - mirror_frr.git/blob - lib/nexthop_group.c
lib: Adding GR capabilites encode and decode.
[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 char *labels;
45 uint32_t weight;
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 static bool
147 nexthop_group_equal_common(const struct nexthop_group *nhg1,
148 const struct nexthop_group *nhg2,
149 uint8_t (*nexthop_group_nexthop_num_func)(
150 const struct nexthop_group *nhg))
151 {
152 if (nhg1 && !nhg2)
153 return false;
154
155 if (!nhg1 && nhg2)
156 return false;
157
158 if (nhg1 == nhg2)
159 return true;
160
161 if (nexthop_group_nexthop_num_func(nhg1)
162 != nexthop_group_nexthop_num_func(nhg2))
163 return false;
164
165 return true;
166 }
167
168 /* This assumes ordered */
169 bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
170 const struct nexthop_group *nhg2)
171 {
172 struct nexthop *nh1 = NULL;
173 struct nexthop *nh2 = NULL;
174
175 if (!nexthop_group_equal_common(nhg1, nhg2,
176 &nexthop_group_nexthop_num_no_recurse))
177 return false;
178
179 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
180 nh1 = nh1->next, nh2 = nh2->next) {
181 if (nh1 && !nh2)
182 return false;
183 if (!nh1 && nh2)
184 return false;
185 if (!nexthop_same(nh1, nh2))
186 return false;
187 }
188
189 return true;
190 }
191
192 /* This assumes ordered */
193 bool nexthop_group_equal(const struct nexthop_group *nhg1,
194 const struct nexthop_group *nhg2)
195 {
196 struct nexthop *nh1 = NULL;
197 struct nexthop *nh2 = NULL;
198
199 if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
200 return false;
201
202 for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
203 nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
204 if (nh1 && !nh2)
205 return false;
206 if (!nh1 && nh2)
207 return false;
208 if (!nexthop_same(nh1, nh2))
209 return false;
210 }
211
212 return true;
213 }
214 struct nexthop_group *nexthop_group_new(void)
215 {
216 return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
217 }
218
219 void nexthop_group_copy(struct nexthop_group *to, struct nexthop_group *from)
220 {
221 /* Copy everything, including recursive info */
222 copy_nexthops(&to->nexthop, from->nexthop, NULL);
223 }
224
225 void nexthop_group_delete(struct nexthop_group **nhg)
226 {
227 if ((*nhg)->nexthop)
228 nexthops_free((*nhg)->nexthop);
229
230 XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
231 }
232
233 /* Add nexthop to the end of a nexthop list. */
234 void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
235 {
236 struct nexthop *last;
237
238 for (last = *target; last && last->next; last = last->next)
239 ;
240 if (last)
241 last->next = nexthop;
242 else
243 *target = nexthop;
244 nexthop->prev = last;
245 }
246
247 /* Add nexthop to sorted list of nexthops */
248 static void _nexthop_add_sorted(struct nexthop **head,
249 struct nexthop *nexthop)
250 {
251 struct nexthop *position, *prev;
252
253 assert(!nexthop->next);
254
255 for (position = *head, prev = NULL; position;
256 prev = position, position = position->next) {
257 if (nexthop_cmp(position, nexthop) > 0) {
258 nexthop->next = position;
259 nexthop->prev = prev;
260
261 if (nexthop->prev)
262 nexthop->prev->next = nexthop;
263 else
264 *head = nexthop;
265
266 position->prev = nexthop;
267 return;
268 }
269 }
270
271 nexthop->prev = prev;
272 if (prev)
273 prev->next = nexthop;
274 else
275 *head = nexthop;
276 }
277
278 void nexthop_group_add_sorted(struct nexthop_group *nhg,
279 struct nexthop *nexthop)
280 {
281 struct nexthop *tail;
282
283 assert(!nexthop->next);
284
285 /* Try to just append to the end first;
286 * trust the list is already sorted
287 */
288 tail = nexthop_group_tail(nhg);
289
290 if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
291 tail->next = nexthop;
292 nexthop->prev = tail;
293
294 return;
295 }
296
297 _nexthop_add_sorted(&nhg->nexthop, nexthop);
298 }
299
300 /* Delete nexthop from a nexthop list. */
301 void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
302 {
303 struct nexthop *nexthop;
304
305 for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
306 if (nexthop_same(nh, nexthop))
307 break;
308 }
309
310 assert(nexthop);
311
312 if (nexthop->prev)
313 nexthop->prev->next = nexthop->next;
314 else
315 nhg->nexthop = nexthop->next;
316
317 if (nexthop->next)
318 nexthop->next->prev = nexthop->prev;
319
320 nh->prev = NULL;
321 nh->next = NULL;
322 }
323
324 /*
325 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
326 */
327 void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
328 const struct nexthop *nh)
329 {
330 struct nexthop *nexthop, *tail;
331 const struct nexthop *nh1;
332
333 /* We'll try to append to the end of the new list;
334 * if the original list in nh is already sorted, this eliminates
335 * lots of comparison operations.
336 */
337 tail = nexthop_group_tail(nhg);
338
339 for (nh1 = nh; nh1; nh1 = nh1->next) {
340 nexthop = nexthop_dup(nh1, NULL);
341
342 if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
343 tail->next = nexthop;
344 nexthop->prev = tail;
345
346 tail = nexthop;
347 continue;
348 }
349
350 _nexthop_add_sorted(&nhg->nexthop, nexthop);
351
352 if (tail == NULL)
353 tail = nexthop;
354 }
355 }
356
357 /* Copy a list of nexthops, no effort made to sort or order them. */
358 void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
359 struct nexthop *rparent)
360 {
361 struct nexthop *nexthop;
362 const struct nexthop *nh1;
363
364 for (nh1 = nh; nh1; nh1 = nh1->next) {
365 nexthop = nexthop_dup(nh1, rparent);
366 _nexthop_add(tnh, nexthop);
367 }
368 }
369
370 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
371 {
372 struct nexthop *nh;
373 uint32_t key = 0;
374
375 /*
376 * We are not interested in hashing over any recursively
377 * resolved nexthops
378 */
379 for (nh = nhg->nexthop; nh; nh = nh->next)
380 key = jhash_1word(nexthop_hash(nh), key);
381
382 return key;
383 }
384
385 uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
386 {
387 struct nexthop *nh;
388 uint32_t key = 0;
389
390 for (ALL_NEXTHOPS_PTR(nhg, nh))
391 key = jhash_1word(nexthop_hash(nh), key);
392
393 return key;
394 }
395
396 void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
397 {
398 struct nexthop *nexthop, *prev;
399
400 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
401 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
402 for (ALL_NEXTHOPS_PTR(nhg, prev)) {
403 if (prev == nexthop)
404 break;
405 if (nexthop_same_firsthop(nexthop, prev)) {
406 SET_FLAG(nexthop->flags,
407 NEXTHOP_FLAG_DUPLICATE);
408 break;
409 }
410 }
411 }
412 }
413
414 static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
415 {
416 struct nexthop *nexthop;
417
418 nexthop = nhgc->nhg.nexthop;
419 while (nexthop) {
420 struct nexthop *next = nexthop_next(nexthop);
421
422 _nexthop_del(&nhgc->nhg, nexthop);
423 if (nhg_hooks.del_nexthop)
424 nhg_hooks.del_nexthop(nhgc, nexthop);
425
426 nexthop_free(nexthop);
427
428 nexthop = next;
429 }
430 }
431
432 struct nexthop_group_cmd *nhgc_find(const char *name)
433 {
434 struct nexthop_group_cmd find;
435
436 strlcpy(find.name, name, sizeof(find.name));
437
438 return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
439 }
440
441 static int nhgc_cmp_helper(const char *a, const char *b)
442 {
443 if (!a && !b)
444 return 0;
445
446 if (a && !b)
447 return -1;
448
449 if (!a && b)
450 return 1;
451
452 return strcmp(a, b);
453 }
454
455 static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
456 {
457 if (!a && !b)
458 return 0;
459
460 if (a && !b)
461 return -1;
462
463 if (!a && b)
464 return 1;
465
466 return sockunion_cmp(a, b);
467 }
468
469 static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
470 {
471 int ret;
472
473 ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
474 if (ret)
475 return ret;
476
477 ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
478 if (ret)
479 return ret;
480
481 ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
482 if (ret)
483 return ret;
484
485 return nhgc_cmp_helper(nh1->labels, nh2->labels);
486 }
487
488 static void nhgl_delete(struct nexthop_hold *nh)
489 {
490 XFREE(MTYPE_TMP, nh->intf);
491
492 XFREE(MTYPE_TMP, nh->nhvrf_name);
493
494 if (nh->addr)
495 sockunion_free(nh->addr);
496
497 XFREE(MTYPE_TMP, nh->labels);
498
499 XFREE(MTYPE_TMP, nh);
500 }
501
502 static struct nexthop_group_cmd *nhgc_get(const char *name)
503 {
504 struct nexthop_group_cmd *nhgc;
505
506 nhgc = nhgc_find(name);
507 if (!nhgc) {
508 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
509 strlcpy(nhgc->name, name, sizeof(nhgc->name));
510
511 QOBJ_REG(nhgc, nexthop_group_cmd);
512 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
513
514 nhgc->nhg_list = list_new();
515 nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
516 nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
517
518 if (nhg_hooks.new)
519 nhg_hooks.new(name);
520 }
521
522 return nhgc;
523 }
524
525 static void nhgc_delete(struct nexthop_group_cmd *nhgc)
526 {
527 nhgc_delete_nexthops(nhgc);
528
529 if (nhg_hooks.delete)
530 nhg_hooks.delete(nhgc->name);
531
532 RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
533
534 list_delete(&nhgc->nhg_list);
535
536 XFREE(MTYPE_TMP, nhgc);
537 }
538
539 DEFINE_QOBJ_TYPE(nexthop_group_cmd)
540
541 DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
542 "Enter into the nexthop-group submode\n"
543 "Specify the NAME of the nexthop-group\n")
544 {
545 const char *nhg_name = argv[1]->arg;
546 struct nexthop_group_cmd *nhgc = NULL;
547
548 nhgc = nhgc_get(nhg_name);
549 VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
550
551 return CMD_SUCCESS;
552 }
553
554 DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
555 NO_STR
556 "Delete the nexthop-group\n"
557 "Specify the NAME of the nexthop-group\n")
558 {
559 const char *nhg_name = argv[2]->arg;
560 struct nexthop_group_cmd *nhgc = NULL;
561
562 nhgc = nhgc_find(nhg_name);
563 if (nhgc)
564 nhgc_delete(nhgc);
565
566 return CMD_SUCCESS;
567 }
568
569 static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
570 const char *nhvrf_name,
571 const union sockunion *addr,
572 const char *intf, const char *labels,
573 const uint32_t weight)
574 {
575 struct nexthop_hold *nh;
576
577 nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
578
579 if (nhvrf_name)
580 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
581 if (intf)
582 nh->intf = XSTRDUP(MTYPE_TMP, intf);
583 if (addr)
584 nh->addr = sockunion_dup(addr);
585 if (labels)
586 nh->labels = XSTRDUP(MTYPE_TMP, labels);
587
588 nh->weight = weight;
589
590 listnode_add_sort(nhgc->nhg_list, nh);
591 }
592
593 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
594 const char *nhvrf_name,
595 const union sockunion *addr,
596 const char *intf, const char *labels,
597 const uint32_t weight)
598 {
599 struct nexthop_hold *nh;
600 struct listnode *node;
601
602 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
603 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
604 && nhgc_addr_cmp_helper(addr, nh->addr) == 0
605 && nhgc_cmp_helper(intf, nh->intf) == 0
606 && nhgc_cmp_helper(labels, nh->labels) == 0
607 && weight == nh->weight)
608 break;
609 }
610
611 /*
612 * Something has gone seriously wrong, fail gracefully
613 */
614 if (!nh)
615 return;
616
617 list_delete_node(nhgc->nhg_list, node);
618 nhgl_delete(nh);
619 }
620
621 /*
622 * Parse the config strings we support for a single nexthop. This gets used
623 * in a couple of different ways, and we distinguish between transient
624 * failures - such as a still-unprocessed interface - and fatal errors
625 * from label-string parsing.
626 */
627 static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
628 const union sockunion *addr,
629 const char *intf, const char *name,
630 const char *labels, int *lbl_ret,
631 uint32_t weight)
632 {
633 int ret = 0;
634 struct vrf *vrf;
635
636 memset(nhop, 0, sizeof(*nhop));
637
638 if (name)
639 vrf = vrf_lookup_by_name(name);
640 else
641 vrf = vrf_lookup_by_id(VRF_DEFAULT);
642
643 if (!vrf)
644 return false;
645
646 nhop->vrf_id = vrf->vrf_id;
647
648 if (intf) {
649 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
650 if (nhop->ifindex == IFINDEX_INTERNAL)
651 return false;
652 }
653
654 if (addr) {
655 if (addr->sa.sa_family == AF_INET) {
656 nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
657 if (intf)
658 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
659 else
660 nhop->type = NEXTHOP_TYPE_IPV4;
661 } else {
662 nhop->gate.ipv6 = addr->sin6.sin6_addr;
663 if (intf)
664 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
665 else
666 nhop->type = NEXTHOP_TYPE_IPV6;
667 }
668 } else
669 nhop->type = NEXTHOP_TYPE_IFINDEX;
670
671 if (labels) {
672 uint8_t num = 0;
673 mpls_label_t larray[MPLS_MAX_LABELS];
674
675 ret = mpls_str2label(labels, &num, larray);
676
677 /* Return label parse result */
678 if (lbl_ret)
679 *lbl_ret = ret;
680
681 if (ret < 0)
682 return false;
683 else if (num > 0)
684 nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
685 num, larray);
686 }
687
688 nhop->weight = weight;
689
690 return true;
691 }
692
693 /*
694 * Wrapper to parse the strings in a 'nexthop_hold'
695 */
696 static bool nexthop_group_parse_nhh(struct nexthop *nhop,
697 const struct nexthop_hold *nhh)
698 {
699 return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
700 nhh->nhvrf_name, nhh->labels, NULL,
701 nhh->weight));
702 }
703
704 DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
705 "[no] nexthop\
706 <\
707 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
708 |INTERFACE$intf\
709 >\
710 [{ \
711 nexthop-vrf NAME$vrf_name \
712 |label WORD \
713 |weight (1-255) \
714 }]",
715 NO_STR
716 "Specify one of the nexthops in this ECMP group\n"
717 "v4 Address\n"
718 "v6 Address\n"
719 "Interface to use\n"
720 "Interface to use\n"
721 "If the nexthop is in a different vrf tell us\n"
722 "The nexthop-vrf Name\n"
723 "Specify label(s) for this nexthop\n"
724 "One or more labels in the range (16-1048575) separated by '/'\n"
725 "Weight to be used by the nexthop for purposes of ECMP\n"
726 "Weight value to be used\n")
727 {
728 VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
729 struct nexthop nhop;
730 struct nexthop *nh;
731 int lbl_ret = 0;
732 bool legal;
733
734 legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
735 &lbl_ret, weight);
736
737 if (nhop.type == NEXTHOP_TYPE_IPV6
738 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
739 vty_out(vty,
740 "Specified a v6 LL with no interface, rejecting\n");
741 return CMD_WARNING_CONFIG_FAILED;
742 }
743
744 /* Handle label-string errors */
745 if (!legal && lbl_ret < 0) {
746 switch (lbl_ret) {
747 case -1:
748 vty_out(vty, "%% Malformed label(s)\n");
749 break;
750 case -2:
751 vty_out(vty,
752 "%% Cannot use reserved label(s) (%d-%d)\n",
753 MPLS_LABEL_RESERVED_MIN,
754 MPLS_LABEL_RESERVED_MAX);
755 break;
756 case -3:
757 vty_out(vty,
758 "%% Too many labels. Enter %d or fewer\n",
759 MPLS_MAX_LABELS);
760 break;
761 }
762 return CMD_WARNING_CONFIG_FAILED;
763 }
764
765 nh = nexthop_exists(&nhgc->nhg, &nhop);
766
767 if (no) {
768 nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label,
769 weight);
770 if (nh) {
771 _nexthop_del(&nhgc->nhg, nh);
772
773 if (nhg_hooks.del_nexthop)
774 nhg_hooks.del_nexthop(nhgc, nh);
775
776 nexthop_free(nh);
777 }
778 } else if (!nh) {
779 /* must be adding new nexthop since !no and !nexthop_exists */
780 if (legal) {
781 nh = nexthop_new();
782
783 memcpy(nh, &nhop, sizeof(nhop));
784 _nexthop_add(&nhgc->nhg.nexthop, nh);
785 }
786
787 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label,
788 weight);
789
790 if (legal && nhg_hooks.add_nexthop)
791 nhg_hooks.add_nexthop(nhgc, nh);
792 }
793
794 return CMD_SUCCESS;
795 }
796
797 static struct cmd_node nexthop_group_node = {
798 NH_GROUP_NODE,
799 "%s(config-nh-group)# ",
800 1
801 };
802
803 void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
804 {
805 char buf[100];
806 struct vrf *vrf;
807
808 vty_out(vty, "nexthop ");
809
810 switch (nh->type) {
811 case NEXTHOP_TYPE_IFINDEX:
812 vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id));
813 break;
814 case NEXTHOP_TYPE_IPV4:
815 vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4));
816 break;
817 case NEXTHOP_TYPE_IPV4_IFINDEX:
818 vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4),
819 ifindex2ifname(nh->ifindex, nh->vrf_id));
820 break;
821 case NEXTHOP_TYPE_IPV6:
822 vty_out(vty, "%s",
823 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
824 break;
825 case NEXTHOP_TYPE_IPV6_IFINDEX:
826 vty_out(vty, "%s %s",
827 inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)),
828 ifindex2ifname(nh->ifindex, nh->vrf_id));
829 break;
830 case NEXTHOP_TYPE_BLACKHOLE:
831 break;
832 }
833
834 if (nh->vrf_id != VRF_DEFAULT) {
835 vrf = vrf_lookup_by_id(nh->vrf_id);
836 vty_out(vty, " nexthop-vrf %s", vrf->name);
837 }
838
839 if (nh->nh_label && nh->nh_label->num_labels > 0) {
840 char buf[200];
841
842 mpls_label2str(nh->nh_label->num_labels,
843 nh->nh_label->label,
844 buf, sizeof(buf), 0);
845 vty_out(vty, " label %s", buf);
846 }
847
848 if (nh->weight)
849 vty_out(vty, " weight %u", nh->weight);
850
851 vty_out(vty, "\n");
852 }
853
854 static void nexthop_group_write_nexthop_internal(struct vty *vty,
855 struct nexthop_hold *nh)
856 {
857 char buf[100];
858
859 vty_out(vty, "nexthop");
860
861 if (nh->addr)
862 vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
863
864 if (nh->intf)
865 vty_out(vty, " %s", nh->intf);
866
867 if (nh->nhvrf_name)
868 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
869
870 if (nh->labels)
871 vty_out(vty, " label %s", nh->labels);
872
873 if (nh->weight)
874 vty_out(vty, " weight %u", nh->weight);
875
876 vty_out(vty, "\n");
877 }
878
879 static int nexthop_group_write(struct vty *vty)
880 {
881 struct nexthop_group_cmd *nhgc;
882 struct nexthop_hold *nh;
883
884 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
885 struct listnode *node;
886
887 vty_out(vty, "nexthop-group %s\n", nhgc->name);
888
889 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
890 vty_out(vty, " ");
891 nexthop_group_write_nexthop_internal(vty, nh);
892 }
893
894 vty_out(vty, "!\n");
895 }
896
897 return 1;
898 }
899
900 void nexthop_group_enable_vrf(struct vrf *vrf)
901 {
902 struct nexthop_group_cmd *nhgc;
903 struct nexthop_hold *nhh;
904
905 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
906 struct listnode *node;
907
908 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
909 struct nexthop nhop;
910 struct nexthop *nh;
911
912 if (!nexthop_group_parse_nhh(&nhop, nhh))
913 continue;
914
915 nh = nexthop_exists(&nhgc->nhg, &nhop);
916
917 if (nh)
918 continue;
919
920 if (nhop.vrf_id != vrf->vrf_id)
921 continue;
922
923 nh = nexthop_new();
924
925 memcpy(nh, &nhop, sizeof(nhop));
926 _nexthop_add(&nhgc->nhg.nexthop, nh);
927
928 if (nhg_hooks.add_nexthop)
929 nhg_hooks.add_nexthop(nhgc, nh);
930 }
931 }
932 }
933
934 void nexthop_group_disable_vrf(struct vrf *vrf)
935 {
936 struct nexthop_group_cmd *nhgc;
937 struct nexthop_hold *nhh;
938
939 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
940 struct listnode *node;
941
942 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
943 struct nexthop nhop;
944 struct nexthop *nh;
945
946 if (!nexthop_group_parse_nhh(&nhop, nhh))
947 continue;
948
949 nh = nexthop_exists(&nhgc->nhg, &nhop);
950
951 if (!nh)
952 continue;
953
954 if (nh->vrf_id != vrf->vrf_id)
955 continue;
956
957 _nexthop_del(&nhgc->nhg, nh);
958
959 if (nhg_hooks.del_nexthop)
960 nhg_hooks.del_nexthop(nhgc, nh);
961
962 nexthop_free(nh);
963 }
964 }
965 }
966
967 void nexthop_group_interface_state_change(struct interface *ifp,
968 ifindex_t oldifindex)
969 {
970 struct nexthop_group_cmd *nhgc;
971 struct nexthop_hold *nhh;
972
973 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
974 struct listnode *node;
975 struct nexthop *nh;
976
977 if (if_is_up(ifp)) {
978 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
979 struct nexthop nhop;
980
981 if (!nexthop_group_parse_nhh(&nhop, nhh))
982 continue;
983
984 switch (nhop.type) {
985 case NEXTHOP_TYPE_IPV4:
986 case NEXTHOP_TYPE_IPV6:
987 case NEXTHOP_TYPE_BLACKHOLE:
988 continue;
989 case NEXTHOP_TYPE_IFINDEX:
990 case NEXTHOP_TYPE_IPV4_IFINDEX:
991 case NEXTHOP_TYPE_IPV6_IFINDEX:
992 break;
993 }
994 nh = nexthop_exists(&nhgc->nhg, &nhop);
995
996 if (nh)
997 continue;
998
999 if (ifp->ifindex != nhop.ifindex)
1000 continue;
1001
1002 nh = nexthop_new();
1003
1004 memcpy(nh, &nhop, sizeof(nhop));
1005 _nexthop_add(&nhgc->nhg.nexthop, nh);
1006
1007 if (nhg_hooks.add_nexthop)
1008 nhg_hooks.add_nexthop(nhgc, nh);
1009 }
1010 } else {
1011 struct nexthop *next_nh;
1012
1013 for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
1014 next_nh = nh->next;
1015 switch (nh->type) {
1016 case NEXTHOP_TYPE_IPV4:
1017 case NEXTHOP_TYPE_IPV6:
1018 case NEXTHOP_TYPE_BLACKHOLE:
1019 continue;
1020 case NEXTHOP_TYPE_IFINDEX:
1021 case NEXTHOP_TYPE_IPV4_IFINDEX:
1022 case NEXTHOP_TYPE_IPV6_IFINDEX:
1023 break;
1024 }
1025
1026 if (oldifindex != nh->ifindex)
1027 continue;
1028
1029 _nexthop_del(&nhgc->nhg, nh);
1030
1031 if (nhg_hooks.del_nexthop)
1032 nhg_hooks.del_nexthop(nhgc, nh);
1033
1034 nexthop_free(nh);
1035 }
1036 }
1037 }
1038 }
1039
1040 static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
1041 {
1042 struct nexthop_group_cmd *nhgc;
1043
1044 RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
1045 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
1046 }
1047 }
1048
1049 static const struct cmd_variable_handler nhg_name_handlers[] = {
1050 {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
1051 {.completions = NULL}};
1052
1053 void nexthop_group_init(void (*new)(const char *name),
1054 void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
1055 const struct nexthop *nhop),
1056 void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
1057 const struct nexthop *nhop),
1058 void (*delete)(const char *name))
1059 {
1060 RB_INIT(nhgc_entry_head, &nhgc_entries);
1061
1062 cmd_variable_handler_register(nhg_name_handlers);
1063
1064 install_node(&nexthop_group_node, nexthop_group_write);
1065 install_element(CONFIG_NODE, &nexthop_group_cmd);
1066 install_element(CONFIG_NODE, &no_nexthop_group_cmd);
1067
1068 install_default(NH_GROUP_NODE);
1069 install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
1070
1071 memset(&nhg_hooks, 0, sizeof(nhg_hooks));
1072
1073 if (new)
1074 nhg_hooks.new = new;
1075 if (add_nexthop)
1076 nhg_hooks.add_nexthop = add_nexthop;
1077 if (del_nexthop)
1078 nhg_hooks.del_nexthop = del_nexthop;
1079 if (delete)
1080 nhg_hooks.delete = delete;
1081 }