1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Nexthop Group structure definition.
4 * Copyright (C) 2018 Cumulus Networks, Inc.
10 #include <sockunion.h>
12 #include <nexthop_group.h>
13 #include <nexthop_group_private.h>
18 #include "lib/nexthop_group_clippy.c"
20 DEFINE_MTYPE_STATIC(LIB
, NEXTHOP_GROUP
, "Nexthop Group");
23 * Internal struct used to hold nhg config strings
27 union sockunion
*addr
;
35 struct nexthop_group_hooks
{
36 void (*new)(const char *name
);
37 void (*modify
)(const struct nexthop_group_cmd
*nhgc
);
38 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
39 const struct nexthop
*nhop
);
40 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
41 const struct nexthop
*nhop
);
42 void (*delete)(const char *name
);
45 static struct nexthop_group_hooks nhg_hooks
;
48 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
49 const struct nexthop_group_cmd
*nhgc2
);
50 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
51 nexthop_group_cmd_compare
)
53 static struct nhgc_entry_head nhgc_entries
;
56 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
57 const struct nexthop_group_cmd
*nhgc2
)
59 return strcmp(nhgc1
->name
, nhgc2
->name
);
62 static struct nexthop
*nexthop_group_tail(const struct nexthop_group
*nhg
)
64 struct nexthop
*nexthop
= nhg
->nexthop
;
66 while (nexthop
&& nexthop
->next
)
67 nexthop
= nexthop
->next
;
72 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
77 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
83 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
88 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
)
94 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
99 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
100 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
108 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
110 struct nexthop
*nhop
;
113 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
) {
114 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
121 struct nexthop
*nexthop_exists(const struct nexthop_group
*nhg
,
122 const struct nexthop
*nh
)
124 struct nexthop
*nexthop
;
126 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
127 if (nexthop_same(nh
, nexthop
))
135 * Helper that locates a nexthop in an nhg config list. Note that
136 * this uses a specific matching / equality rule that's different from
137 * the complete match performed by 'nexthop_same()'.
139 static struct nexthop
*nhg_nh_find(const struct nexthop_group
*nhg
,
140 const struct nexthop
*nh
)
142 struct nexthop
*nexthop
;
145 /* We compare: vrf, gateway, and interface */
147 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
149 /* Compare vrf and type */
150 if (nexthop
->vrf_id
!= nh
->vrf_id
)
152 if (nexthop
->type
!= nh
->type
)
155 /* Compare gateway */
156 switch (nexthop
->type
) {
157 case NEXTHOP_TYPE_IPV4
:
158 case NEXTHOP_TYPE_IPV6
:
159 ret
= nexthop_g_addr_cmp(nexthop
->type
,
160 &nexthop
->gate
, &nh
->gate
);
164 case NEXTHOP_TYPE_IPV4_IFINDEX
:
165 case NEXTHOP_TYPE_IPV6_IFINDEX
:
166 ret
= nexthop_g_addr_cmp(nexthop
->type
,
167 &nexthop
->gate
, &nh
->gate
);
170 /* Intentional Fall-Through */
171 case NEXTHOP_TYPE_IFINDEX
:
172 if (nexthop
->ifindex
!= nh
->ifindex
)
175 case NEXTHOP_TYPE_BLACKHOLE
:
176 if (nexthop
->bh_type
!= nh
->bh_type
)
188 nexthop_group_equal_common(const struct nexthop_group
*nhg1
,
189 const struct nexthop_group
*nhg2
,
190 uint8_t (*nexthop_group_nexthop_num_func
)(
191 const struct nexthop_group
*nhg
))
202 if (nexthop_group_nexthop_num_func(nhg1
)
203 != nexthop_group_nexthop_num_func(nhg2
))
209 /* This assumes ordered */
210 bool nexthop_group_equal_no_recurse(const struct nexthop_group
*nhg1
,
211 const struct nexthop_group
*nhg2
)
213 struct nexthop
*nh1
= NULL
;
214 struct nexthop
*nh2
= NULL
;
216 if (!nexthop_group_equal_common(nhg1
, nhg2
,
217 &nexthop_group_nexthop_num_no_recurse
))
220 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
221 nh1
= nh1
->next
, nh2
= nh2
->next
) {
226 if (!nexthop_same(nh1
, nh2
))
233 /* This assumes ordered */
234 bool nexthop_group_equal(const struct nexthop_group
*nhg1
,
235 const struct nexthop_group
*nhg2
)
237 struct nexthop
*nh1
= NULL
;
238 struct nexthop
*nh2
= NULL
;
240 if (!nexthop_group_equal_common(nhg1
, nhg2
, &nexthop_group_nexthop_num
))
243 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
244 nh1
= nexthop_next(nh1
), nh2
= nexthop_next(nh2
)) {
249 if (!nexthop_same(nh1
, nh2
))
255 struct nexthop_group
*nexthop_group_new(void)
257 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
260 void nexthop_group_copy(struct nexthop_group
*to
,
261 const struct nexthop_group
*from
)
263 to
->nhgr
= from
->nhgr
;
264 /* Copy everything, including recursive info */
265 copy_nexthops(&to
->nexthop
, from
->nexthop
, NULL
);
268 void nexthop_group_delete(struct nexthop_group
**nhg
)
270 /* OK to call with NULL group */
275 nexthops_free((*nhg
)->nexthop
);
277 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
280 /* Add nexthop to the end of a nexthop list. */
281 void _nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
283 struct nexthop
*last
;
285 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
288 last
->next
= nexthop
;
291 nexthop
->prev
= last
;
294 /* Add nexthop to sorted list of nexthops */
295 static void _nexthop_add_sorted(struct nexthop
**head
,
296 struct nexthop
*nexthop
)
298 struct nexthop
*position
, *prev
;
300 assert(!nexthop
->next
);
302 for (position
= *head
, prev
= NULL
; position
;
303 prev
= position
, position
= position
->next
) {
304 if (nexthop_cmp(position
, nexthop
) > 0) {
305 nexthop
->next
= position
;
306 nexthop
->prev
= prev
;
309 nexthop
->prev
->next
= nexthop
;
313 position
->prev
= nexthop
;
318 nexthop
->prev
= prev
;
320 prev
->next
= nexthop
;
325 void nexthop_group_add_sorted(struct nexthop_group
*nhg
,
326 struct nexthop
*nexthop
)
328 struct nexthop
*tail
;
330 assert(!nexthop
->next
);
332 /* Try to just append to the end first;
333 * trust the list is already sorted
335 tail
= nexthop_group_tail(nhg
);
337 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
338 tail
->next
= nexthop
;
339 nexthop
->prev
= tail
;
344 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
347 /* Delete nexthop from a nexthop list. */
348 void _nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
350 struct nexthop
*nexthop
;
352 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
353 if (nexthop_same(nh
, nexthop
))
360 nexthop
->prev
->next
= nexthop
->next
;
362 nhg
->nexthop
= nexthop
->next
;
365 nexthop
->next
->prev
= nexthop
->prev
;
371 /* Unlink a nexthop from the list it's on, unconditionally */
372 static void nexthop_unlink(struct nexthop_group
*nhg
, struct nexthop
*nexthop
)
376 nexthop
->prev
->next
= nexthop
->next
;
378 assert(nhg
->nexthop
== nexthop
);
379 assert(nexthop
->prev
== NULL
);
380 nhg
->nexthop
= nexthop
->next
;
384 nexthop
->next
->prev
= nexthop
->prev
;
386 nexthop
->prev
= NULL
;
387 nexthop
->next
= NULL
;
391 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
393 void nexthop_group_copy_nh_sorted(struct nexthop_group
*nhg
,
394 const struct nexthop
*nh
)
396 struct nexthop
*nexthop
, *tail
;
397 const struct nexthop
*nh1
;
399 /* We'll try to append to the end of the new list;
400 * if the original list in nh is already sorted, this eliminates
401 * lots of comparison operations.
403 tail
= nexthop_group_tail(nhg
);
405 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
406 nexthop
= nexthop_dup(nh1
, NULL
);
408 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
409 tail
->next
= nexthop
;
410 nexthop
->prev
= tail
;
416 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
423 /* Copy a list of nexthops, no effort made to sort or order them. */
424 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
425 struct nexthop
*rparent
)
427 struct nexthop
*nexthop
;
428 const struct nexthop
*nh1
;
430 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
431 nexthop
= nexthop_dup(nh1
, rparent
);
432 _nexthop_add(tnh
, nexthop
);
436 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group
*nhg
)
442 * We are not interested in hashing over any recursively
445 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
446 key
= jhash_1word(nexthop_hash(nh
), key
);
451 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
456 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
457 key
= jhash_1word(nexthop_hash(nh
), key
);
462 void nexthop_group_mark_duplicates(struct nexthop_group
*nhg
)
464 struct nexthop
*nexthop
, *prev
;
466 for (ALL_NEXTHOPS_PTR(nhg
, nexthop
)) {
467 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
);
468 for (ALL_NEXTHOPS_PTR(nhg
, prev
)) {
471 if (nexthop_same(nexthop
, prev
)) {
472 SET_FLAG(nexthop
->flags
,
473 NEXTHOP_FLAG_DUPLICATE
);
480 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
482 struct nexthop
*nexthop
;
484 nexthop
= nhgc
->nhg
.nexthop
;
486 struct nexthop
*next
= nexthop_next(nexthop
);
488 _nexthop_del(&nhgc
->nhg
, nexthop
);
489 if (nhg_hooks
.del_nexthop
)
490 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
492 nexthop_free(nexthop
);
498 struct nexthop_group_cmd
*nhgc_find(const char *name
)
500 struct nexthop_group_cmd find
;
502 strlcpy(find
.name
, name
, sizeof(find
.name
));
504 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
507 static int nhgc_cmp_helper(const char *a
, const char *b
)
521 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
532 return sockunion_cmp(a
, b
);
535 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
539 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
543 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
547 ret
= nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
551 ret
= ((int)nh2
->onlink
) - ((int)nh1
->onlink
);
555 return nhgc_cmp_helper(nh1
->labels
, nh2
->labels
);
558 static void nhgl_delete(struct nexthop_hold
*nh
)
560 XFREE(MTYPE_TMP
, nh
->intf
);
562 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
565 sockunion_free(nh
->addr
);
567 XFREE(MTYPE_TMP
, nh
->labels
);
569 XFREE(MTYPE_TMP
, nh
);
572 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
574 struct nexthop_group_cmd
*nhgc
;
576 nhgc
= nhgc_find(name
);
578 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
579 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
581 QOBJ_REG(nhgc
, nexthop_group_cmd
);
582 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
584 nhgc
->nhg_list
= list_new();
585 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
586 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
595 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
597 nhgc_delete_nexthops(nhgc
);
599 if (nhg_hooks
.delete)
600 nhg_hooks
.delete(nhgc
->name
);
602 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
604 list_delete(&nhgc
->nhg_list
);
607 XFREE(MTYPE_TMP
, nhgc
);
610 DEFINE_QOBJ_TYPE(nexthop_group_cmd
);
612 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NHGNAME",
613 "Enter into the nexthop-group submode\n"
614 "Specify the NAME of the nexthop-group\n")
616 const char *nhg_name
= argv
[1]->arg
;
617 struct nexthop_group_cmd
*nhgc
= NULL
;
619 nhgc
= nhgc_get(nhg_name
);
620 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
625 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NHGNAME",
627 "Delete the nexthop-group\n"
628 "Specify the NAME of the nexthop-group\n")
630 const char *nhg_name
= argv
[2]->arg
;
631 struct nexthop_group_cmd
*nhgc
= NULL
;
633 nhgc
= nhgc_find(nhg_name
);
640 DEFPY(nexthop_group_backup
, nexthop_group_backup_cmd
,
641 "backup-group WORD$name",
642 "Specify a group name containing backup nexthops\n"
643 "The name of the backup group\n")
645 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
647 strlcpy(nhgc
->backup_list_name
, name
, sizeof(nhgc
->backup_list_name
));
652 DEFPY(no_nexthop_group_backup
, no_nexthop_group_backup_cmd
,
653 "no backup-group [WORD$name]",
655 "Clear group name containing backup nexthops\n"
656 "The name of the backup group\n")
658 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
660 nhgc
->backup_list_name
[0] = 0;
665 DEFPY(nexthop_group_resilience
,
666 nexthop_group_resilience_cmd
,
667 "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
668 "A resilient Nexthop Group\n"
669 "Buckets in the Hash for this Group\n"
670 "Number of buckets\n"
671 "The Idle timer for this Resilient Nexthop Group in seconds\n"
672 "Number of seconds of Idle time\n"
673 "The length of time that the Nexthop Group can be unbalanced\n"
674 "Number of seconds of Unbalanced time\n")
676 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
678 nhgc
->nhg
.nhgr
.buckets
= buckets
;
679 nhgc
->nhg
.nhgr
.idle_timer
= idle_timer
;
680 nhgc
->nhg
.nhgr
.unbalanced_timer
= unbalanced_timer
;
682 if (nhg_hooks
.modify
)
683 nhg_hooks
.modify(nhgc
);
688 DEFPY(no_nexthop_group_resilience
,
689 no_nexthop_group_resilience_cmd
,
690 "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
692 "A resilient Nexthop Group\n"
693 "Buckets in the Hash for this Group\n"
694 "Number of buckets\n"
695 "The Idle timer for this Resilient Nexthop Group in seconds\n"
696 "Number of seconds of Idle time\n"
697 "The length of time that the Nexthop Group can be unbalanced\n"
698 "Number of seconds of Unbalanced time\n")
700 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
702 nhgc
->nhg
.nhgr
.buckets
= 0;
703 nhgc
->nhg
.nhgr
.idle_timer
= 0;
704 nhgc
->nhg
.nhgr
.unbalanced_timer
= 0;
709 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
710 const char *nhvrf_name
,
711 const union sockunion
*addr
,
712 const char *intf
, bool onlink
,
713 const char *labels
, const uint32_t weight
,
714 const char *backup_str
)
716 struct nexthop_hold
*nh
;
718 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
721 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
723 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
725 nh
->addr
= sockunion_dup(addr
);
727 nh
->labels
= XSTRDUP(MTYPE_TMP
, labels
);
734 nh
->backup_str
= XSTRDUP(MTYPE_TMP
, backup_str
);
736 listnode_add_sort(nhgc
->nhg_list
, nh
);
740 * Remove config info about a nexthop from group 'nhgc'. Note that we
741 * use only a subset of the available attributes here to determine
743 * Note that this doesn't change the list of nexthops, only the config
746 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
747 const char *nhvrf_name
,
748 const union sockunion
*addr
,
751 struct nexthop_hold
*nh
;
752 struct listnode
*node
;
754 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
755 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0
756 && nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0
757 && nhgc_cmp_helper(intf
, nh
->intf
) == 0)
762 * Something has gone seriously wrong, fail gracefully
767 list_delete_node(nhgc
->nhg_list
, node
);
772 * Parse the config strings we support for a single nexthop. This gets used
773 * in a couple of different ways, and we distinguish between transient
774 * failures - such as a still-unprocessed interface - and fatal errors
775 * from label-string parsing.
777 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
778 const union sockunion
*addr
,
779 const char *intf
, bool onlink
,
780 const char *name
, const char *labels
,
781 int *lbl_ret
, uint32_t weight
,
782 const char *backup_str
)
788 memset(nhop
, 0, sizeof(*nhop
));
791 vrf
= vrf_lookup_by_name(name
);
793 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
798 nhop
->vrf_id
= vrf
->vrf_id
;
801 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
802 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
807 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_ONLINK
);
810 if (addr
->sa
.sa_family
== AF_INET
) {
811 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
813 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
815 nhop
->type
= NEXTHOP_TYPE_IPV4
;
817 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
819 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
821 nhop
->type
= NEXTHOP_TYPE_IPV6
;
824 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
828 mpls_label_t larray
[MPLS_MAX_LABELS
];
830 ret
= mpls_str2label(labels
, &num
, larray
);
832 /* Return label parse result */
839 nexthop_add_labels(nhop
, ZEBRA_LSP_NONE
,
843 nhop
->weight
= weight
;
846 /* Parse backup indexes */
847 ret
= nexthop_str2backups(backup_str
,
848 &num
, nhop
->backup_idx
);
850 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
);
851 nhop
->backup_num
= num
;
860 * Wrapper to parse the strings in a 'nexthop_hold'
862 static bool nexthop_group_parse_nhh(struct nexthop
*nhop
,
863 const struct nexthop_hold
*nhh
)
865 return (nexthop_group_parse_nexthop(
866 nhop
, nhh
->addr
, nhh
->intf
, nhh
->onlink
, nhh
->nhvrf_name
,
867 nhh
->labels
, NULL
, nhh
->weight
, nhh
->backup_str
));
870 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
873 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
877 nexthop-vrf NAME$vrf_name \
883 "Specify one of the nexthops in this ECMP group\n"
887 "Treat nexthop as directly attached to the interface\n"
889 "If the nexthop is in a different vrf tell us\n"
890 "The nexthop-vrf Name\n"
891 "Specify label(s) for this nexthop\n"
892 "One or more labels in the range (16-1048575) separated by '/'\n"
893 "Weight to be used by the nexthop for purposes of ECMP\n"
894 "Weight value to be used\n"
895 "Specify backup nexthop indexes in another group\n"
896 "One or more indexes in the range (0-254) separated by ','\n")
898 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
904 uint8_t backups
[NEXTHOP_MAX_BACKUPS
];
907 /* Pre-parse backup string to validate */
909 lbl_ret
= nexthop_str2backups(backup_idx
, &num
, backups
);
911 vty_out(vty
, "%% Invalid backups\n");
912 return CMD_WARNING_CONFIG_FAILED
;
916 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, !!onlink
,
917 vrf_name
, label
, &lbl_ret
, weight
,
920 if (nhop
.type
== NEXTHOP_TYPE_IPV6
921 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
923 "Specified a v6 LL with no interface, rejecting\n");
924 return CMD_WARNING_CONFIG_FAILED
;
927 /* Handle label-string errors */
928 if (!legal
&& lbl_ret
< 0) {
931 vty_out(vty
, "%% Malformed label(s)\n");
935 "%% Cannot use reserved label(s) (%d-%d)\n",
936 MPLS_LABEL_RESERVED_MIN
,
937 MPLS_LABEL_RESERVED_MAX
);
941 "%% Too many labels. Enter %d or fewer\n",
945 return CMD_WARNING_CONFIG_FAILED
;
948 /* Look for an existing nexthop in the config. Note that the test
949 * here tests only some attributes - it's not a complete comparison.
950 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
951 * that represent config that may or may not be valid (yet), and
952 * actual nexthops that have been validated and parsed.
954 nh
= nhg_nh_find(&nhgc
->nhg
, &nhop
);
956 /* Always attempt to remove old config info. */
957 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
959 /* Remove any existing nexthop, for delete and replace cases. */
961 nexthop_unlink(&nhgc
->nhg
, nh
);
963 if (nhg_hooks
.del_nexthop
)
964 nhg_hooks
.del_nexthop(nhgc
, nh
);
969 /* Add/replace case: capture nexthop if valid, and capture
970 * config info always.
975 memcpy(nh
, &nhop
, sizeof(nhop
));
976 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
979 /* Save config always */
980 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
, !!onlink
,
981 label
, weight
, backup_idx
);
983 if (legal
&& nhg_hooks
.add_nexthop
)
984 nhg_hooks
.add_nexthop(nhgc
, nh
);
990 static int nexthop_group_write(struct vty
*vty
);
991 static struct cmd_node nexthop_group_node
= {
992 .name
= "nexthop-group",
993 .node
= NH_GROUP_NODE
,
994 .parent_node
= CONFIG_NODE
,
995 .prompt
= "%s(config-nh-group)# ",
996 .config_write
= nexthop_group_write
,
999 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
1000 const struct nexthop
*nh
,
1005 vty_out(vty
, "nexthop ");
1010 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
1013 case NEXTHOP_TYPE_IFINDEX
:
1014 vty_out(vty
, "%s", ifname
);
1016 case NEXTHOP_TYPE_IPV4
:
1017 vty_out(vty
, "%pI4", &nh
->gate
.ipv4
);
1019 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1020 vty_out(vty
, "%pI4 %s", &nh
->gate
.ipv4
, ifname
);
1022 case NEXTHOP_TYPE_IPV6
:
1023 vty_out(vty
, "%pI6", &nh
->gate
.ipv6
);
1025 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1026 vty_out(vty
, "%pI6 %s", &nh
->gate
.ipv6
, ifname
);
1028 case NEXTHOP_TYPE_BLACKHOLE
:
1033 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1038 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1040 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1041 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1042 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1045 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1048 mpls_label2str(nh
->nh_label
->num_labels
,
1049 nh
->nh_label
->label
,
1050 buf
, sizeof(buf
), 0);
1051 vty_out(vty
, " label %s", buf
);
1055 vty_out(vty
, " weight %u", nh
->weight
);
1057 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1058 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1060 for (i
= 1; i
< nh
->backup_num
; i
++)
1061 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1067 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1070 json_object
*json_backups
= NULL
;
1074 case NEXTHOP_TYPE_IFINDEX
:
1075 json_object_string_add(j
, "nexthop",
1076 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1078 case NEXTHOP_TYPE_IPV4
:
1079 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1081 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1082 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1083 json_object_string_add(j
, "vrfId",
1084 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1086 case NEXTHOP_TYPE_IPV6
:
1087 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1089 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1090 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1091 json_object_string_add(j
, "vrfId",
1092 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1094 case NEXTHOP_TYPE_BLACKHOLE
:
1098 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1099 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1100 json_object_string_add(j
, "targetVrf", vrf
->name
);
1103 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1106 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1107 buf
, sizeof(buf
), 0);
1108 json_object_string_add(j
, "label", buf
);
1112 json_object_int_add(j
, "weight", nh
->weight
);
1114 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1115 json_backups
= json_object_new_array();
1116 for (i
= 0; i
< nh
->backup_num
; i
++)
1117 json_object_array_add(
1119 json_object_new_int(nh
->backup_idx
[i
]));
1121 json_object_object_add(j
, "backupIdx", json_backups
);
1125 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1126 const struct nexthop_hold
*nh
)
1128 vty_out(vty
, "nexthop");
1131 vty_out(vty
, " %pSU", nh
->addr
);
1134 vty_out(vty
, " %s", nh
->intf
);
1137 vty_out(vty
, " onlink");
1140 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1143 vty_out(vty
, " label %s", nh
->labels
);
1146 vty_out(vty
, " weight %u", nh
->weight
);
1149 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1154 static int nexthop_group_write(struct vty
*vty
)
1156 struct nexthop_group_cmd
*nhgc
;
1157 struct nexthop_hold
*nh
;
1159 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1160 struct listnode
*node
;
1162 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1164 if (nhgc
->nhg
.nhgr
.buckets
)
1166 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1167 nhgc
->nhg
.nhgr
.buckets
,
1168 nhgc
->nhg
.nhgr
.idle_timer
,
1169 nhgc
->nhg
.nhgr
.unbalanced_timer
);
1171 if (nhgc
->backup_list_name
[0])
1172 vty_out(vty
, " backup-group %s\n",
1173 nhgc
->backup_list_name
);
1175 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1177 nexthop_group_write_nexthop_internal(vty
, nh
);
1180 vty_out(vty
, "exit\n");
1181 vty_out(vty
, "!\n");
1187 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1189 struct nexthop_group_cmd
*nhgc
;
1190 struct nexthop_hold
*nhh
;
1192 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1193 struct listnode
*node
;
1195 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1196 struct nexthop nhop
;
1199 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1202 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1207 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1212 memcpy(nh
, &nhop
, sizeof(nhop
));
1213 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1215 if (nhg_hooks
.add_nexthop
)
1216 nhg_hooks
.add_nexthop(nhgc
, nh
);
1221 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1223 struct nexthop_group_cmd
*nhgc
;
1224 struct nexthop_hold
*nhh
;
1226 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1227 struct listnode
*node
;
1229 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1230 struct nexthop nhop
;
1233 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1236 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1241 if (nh
->vrf_id
!= vrf
->vrf_id
)
1244 _nexthop_del(&nhgc
->nhg
, nh
);
1246 if (nhg_hooks
.del_nexthop
)
1247 nhg_hooks
.del_nexthop(nhgc
, nh
);
1254 void nexthop_group_interface_state_change(struct interface
*ifp
,
1255 ifindex_t oldifindex
)
1257 struct nexthop_group_cmd
*nhgc
;
1258 struct nexthop_hold
*nhh
;
1260 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1261 struct listnode
*node
;
1264 if (if_is_up(ifp
)) {
1265 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1266 struct nexthop nhop
;
1268 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1271 switch (nhop
.type
) {
1272 case NEXTHOP_TYPE_IPV4
:
1273 case NEXTHOP_TYPE_IPV6
:
1274 case NEXTHOP_TYPE_BLACKHOLE
:
1276 case NEXTHOP_TYPE_IFINDEX
:
1277 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1278 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1281 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1286 if (ifp
->ifindex
!= nhop
.ifindex
)
1291 memcpy(nh
, &nhop
, sizeof(nhop
));
1292 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1294 if (nhg_hooks
.add_nexthop
)
1295 nhg_hooks
.add_nexthop(nhgc
, nh
);
1298 struct nexthop
*next_nh
;
1300 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1303 case NEXTHOP_TYPE_IPV4
:
1304 case NEXTHOP_TYPE_IPV6
:
1305 case NEXTHOP_TYPE_BLACKHOLE
:
1307 case NEXTHOP_TYPE_IFINDEX
:
1308 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1309 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1313 if (oldifindex
!= nh
->ifindex
)
1316 _nexthop_del(&nhgc
->nhg
, nh
);
1318 if (nhg_hooks
.del_nexthop
)
1319 nhg_hooks
.del_nexthop(nhgc
, nh
);
1327 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1329 struct nexthop_group_cmd
*nhgc
;
1331 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1332 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1336 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1337 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1338 {.completions
= NULL
}};
1340 void nexthop_group_init(void (*new)(const char *name
),
1341 void (*modify
)(const struct nexthop_group_cmd
*nhgc
),
1342 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1343 const struct nexthop
*nhop
),
1344 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1345 const struct nexthop
*nhop
),
1346 void (*delete)(const char *name
))
1348 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1350 cmd_variable_handler_register(nhg_name_handlers
);
1352 install_node(&nexthop_group_node
);
1353 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1354 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1356 install_default(NH_GROUP_NODE
);
1357 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1358 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1359 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1361 install_element(NH_GROUP_NODE
, &nexthop_group_resilience_cmd
);
1362 install_element(NH_GROUP_NODE
, &no_nexthop_group_resilience_cmd
);
1364 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1367 nhg_hooks
.new = new;
1369 nhg_hooks
.modify
= modify
;
1371 nhg_hooks
.add_nexthop
= add_nexthop
;
1373 nhg_hooks
.del_nexthop
= del_nexthop
;
1375 nhg_hooks
.delete = delete;