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