2 * Nexthop Group structure definition.
3 * Copyright (C) 2018 Cumulus Networks, Inc.
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)
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
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
23 #include <sockunion.h>
25 #include <nexthop_group.h>
26 #include <nexthop_group_private.h>
31 #include "lib/nexthop_group_clippy.c"
33 DEFINE_MTYPE_STATIC(LIB
, NEXTHOP_GROUP
, "Nexthop Group");
36 * Internal struct used to hold nhg config strings
40 union sockunion
*addr
;
48 struct nexthop_group_hooks
{
49 void (*new)(const char *name
);
50 void (*modify
)(const struct nexthop_group_cmd
*nhgc
);
51 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
52 const struct nexthop
*nhop
);
53 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
54 const struct nexthop
*nhop
);
55 void (*delete)(const char *name
);
58 static struct nexthop_group_hooks nhg_hooks
;
61 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
62 const struct nexthop_group_cmd
*nhgc2
);
63 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
64 nexthop_group_cmd_compare
)
66 static struct nhgc_entry_head nhgc_entries
;
69 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
70 const struct nexthop_group_cmd
*nhgc2
)
72 return strcmp(nhgc1
->name
, nhgc2
->name
);
75 static struct nexthop
*nexthop_group_tail(const struct nexthop_group
*nhg
)
77 struct nexthop
*nexthop
= nhg
->nexthop
;
79 while (nexthop
&& nexthop
->next
)
80 nexthop
= nexthop
->next
;
85 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
90 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
96 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
101 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
)
107 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
109 struct nexthop
*nhop
;
112 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
113 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
121 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
123 struct nexthop
*nhop
;
126 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
) {
127 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
134 bool nexthop_group_has_label(const struct nexthop_group
*nhg
)
136 struct nexthop
*nhop
;
138 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
146 struct nexthop
*nexthop_exists(const struct nexthop_group
*nhg
,
147 const struct nexthop
*nh
)
149 struct nexthop
*nexthop
;
151 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
152 if (nexthop_same(nh
, nexthop
))
160 * Helper that locates a nexthop in an nhg config list. Note that
161 * this uses a specific matching / equality rule that's different from
162 * the complete match performed by 'nexthop_same()'.
164 static struct nexthop
*nhg_nh_find(const struct nexthop_group
*nhg
,
165 const struct nexthop
*nh
)
167 struct nexthop
*nexthop
;
170 /* We compare: vrf, gateway, and interface */
172 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
174 /* Compare vrf and type */
175 if (nexthop
->vrf_id
!= nh
->vrf_id
)
177 if (nexthop
->type
!= nh
->type
)
180 /* Compare gateway */
181 switch (nexthop
->type
) {
182 case NEXTHOP_TYPE_IPV4
:
183 case NEXTHOP_TYPE_IPV6
:
184 ret
= nexthop_g_addr_cmp(nexthop
->type
,
185 &nexthop
->gate
, &nh
->gate
);
189 case NEXTHOP_TYPE_IPV4_IFINDEX
:
190 case NEXTHOP_TYPE_IPV6_IFINDEX
:
191 ret
= nexthop_g_addr_cmp(nexthop
->type
,
192 &nexthop
->gate
, &nh
->gate
);
195 /* Intentional Fall-Through */
196 case NEXTHOP_TYPE_IFINDEX
:
197 if (nexthop
->ifindex
!= nh
->ifindex
)
200 case NEXTHOP_TYPE_BLACKHOLE
:
201 if (nexthop
->bh_type
!= nh
->bh_type
)
213 nexthop_group_equal_common(const struct nexthop_group
*nhg1
,
214 const struct nexthop_group
*nhg2
,
215 uint8_t (*nexthop_group_nexthop_num_func
)(
216 const struct nexthop_group
*nhg
))
227 if (nexthop_group_nexthop_num_func(nhg1
)
228 != nexthop_group_nexthop_num_func(nhg2
))
234 /* This assumes ordered */
235 bool nexthop_group_equal_no_recurse(const struct nexthop_group
*nhg1
,
236 const struct nexthop_group
*nhg2
)
238 struct nexthop
*nh1
= NULL
;
239 struct nexthop
*nh2
= NULL
;
241 if (!nexthop_group_equal_common(nhg1
, nhg2
,
242 &nexthop_group_nexthop_num_no_recurse
))
245 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
246 nh1
= nh1
->next
, nh2
= nh2
->next
) {
251 if (!nexthop_same(nh1
, nh2
))
258 /* This assumes ordered */
259 bool nexthop_group_equal(const struct nexthop_group
*nhg1
,
260 const struct nexthop_group
*nhg2
)
262 struct nexthop
*nh1
= NULL
;
263 struct nexthop
*nh2
= NULL
;
265 if (!nexthop_group_equal_common(nhg1
, nhg2
, &nexthop_group_nexthop_num
))
268 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
269 nh1
= nexthop_next(nh1
), nh2
= nexthop_next(nh2
)) {
274 if (!nexthop_same(nh1
, nh2
))
280 struct nexthop_group
*nexthop_group_new(void)
282 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
285 void nexthop_group_copy(struct nexthop_group
*to
,
286 const struct nexthop_group
*from
)
288 to
->nhgr
= from
->nhgr
;
289 /* Copy everything, including recursive info */
290 copy_nexthops(&to
->nexthop
, from
->nexthop
, NULL
);
293 void nexthop_group_delete(struct nexthop_group
**nhg
)
295 /* OK to call with NULL group */
300 nexthops_free((*nhg
)->nexthop
);
302 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
305 /* Add nexthop to the end of a nexthop list. */
306 void _nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
308 struct nexthop
*last
;
310 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
313 last
->next
= nexthop
;
316 nexthop
->prev
= last
;
319 /* Add nexthop to sorted list of nexthops */
320 static void _nexthop_add_sorted(struct nexthop
**head
,
321 struct nexthop
*nexthop
)
323 struct nexthop
*position
, *prev
;
325 assert(!nexthop
->next
);
327 for (position
= *head
, prev
= NULL
; position
;
328 prev
= position
, position
= position
->next
) {
329 if (nexthop_cmp(position
, nexthop
) > 0) {
330 nexthop
->next
= position
;
331 nexthop
->prev
= prev
;
334 nexthop
->prev
->next
= nexthop
;
338 position
->prev
= nexthop
;
343 nexthop
->prev
= prev
;
345 prev
->next
= nexthop
;
350 void nexthop_group_add_sorted(struct nexthop_group
*nhg
,
351 struct nexthop
*nexthop
)
353 struct nexthop
*tail
;
355 assert(!nexthop
->next
);
357 /* Try to just append to the end first;
358 * trust the list is already sorted
360 tail
= nexthop_group_tail(nhg
);
362 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
363 tail
->next
= nexthop
;
364 nexthop
->prev
= tail
;
369 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
372 /* Delete nexthop from a nexthop list. */
373 void _nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
375 struct nexthop
*nexthop
;
377 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
378 if (nexthop_same(nh
, nexthop
))
385 nexthop
->prev
->next
= nexthop
->next
;
387 nhg
->nexthop
= nexthop
->next
;
390 nexthop
->next
->prev
= nexthop
->prev
;
396 /* Unlink a nexthop from the list it's on, unconditionally */
397 static void nexthop_unlink(struct nexthop_group
*nhg
, struct nexthop
*nexthop
)
401 nexthop
->prev
->next
= nexthop
->next
;
403 assert(nhg
->nexthop
== nexthop
);
404 assert(nexthop
->prev
== NULL
);
405 nhg
->nexthop
= nexthop
->next
;
409 nexthop
->next
->prev
= nexthop
->prev
;
411 nexthop
->prev
= NULL
;
412 nexthop
->next
= NULL
;
416 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
418 void nexthop_group_copy_nh_sorted(struct nexthop_group
*nhg
,
419 const struct nexthop
*nh
)
421 struct nexthop
*nexthop
, *tail
;
422 const struct nexthop
*nh1
;
424 /* We'll try to append to the end of the new list;
425 * if the original list in nh is already sorted, this eliminates
426 * lots of comparison operations.
428 tail
= nexthop_group_tail(nhg
);
430 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
431 nexthop
= nexthop_dup(nh1
, NULL
);
433 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
434 tail
->next
= nexthop
;
435 nexthop
->prev
= tail
;
441 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
448 /* Copy a list of nexthops, no effort made to sort or order them. */
449 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
450 struct nexthop
*rparent
)
452 struct nexthop
*nexthop
;
453 const struct nexthop
*nh1
;
455 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
456 nexthop
= nexthop_dup(nh1
, rparent
);
457 _nexthop_add(tnh
, nexthop
);
461 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group
*nhg
)
467 * We are not interested in hashing over any recursively
470 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
471 key
= jhash_1word(nexthop_hash(nh
), key
);
476 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
481 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
482 key
= jhash_1word(nexthop_hash(nh
), key
);
487 void nexthop_group_mark_duplicates(struct nexthop_group
*nhg
)
489 struct nexthop
*nexthop
, *prev
;
491 for (ALL_NEXTHOPS_PTR(nhg
, nexthop
)) {
492 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
);
493 for (ALL_NEXTHOPS_PTR(nhg
, prev
)) {
496 if (nexthop_same(nexthop
, prev
)) {
497 SET_FLAG(nexthop
->flags
,
498 NEXTHOP_FLAG_DUPLICATE
);
505 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
507 struct nexthop
*nexthop
;
509 nexthop
= nhgc
->nhg
.nexthop
;
511 struct nexthop
*next
= nexthop_next(nexthop
);
513 _nexthop_del(&nhgc
->nhg
, nexthop
);
514 if (nhg_hooks
.del_nexthop
)
515 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
517 nexthop_free(nexthop
);
523 struct nexthop_group_cmd
*nhgc_find(const char *name
)
525 struct nexthop_group_cmd find
;
527 strlcpy(find
.name
, name
, sizeof(find
.name
));
529 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
532 static int nhgc_cmp_helper(const char *a
, const char *b
)
546 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
557 return sockunion_cmp(a
, b
);
560 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
564 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
568 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
572 ret
= nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
576 ret
= ((int)nh2
->onlink
) - ((int)nh1
->onlink
);
580 return nhgc_cmp_helper(nh1
->labels
, nh2
->labels
);
583 static void nhgl_delete(struct nexthop_hold
*nh
)
585 XFREE(MTYPE_TMP
, nh
->intf
);
587 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
590 sockunion_free(nh
->addr
);
592 XFREE(MTYPE_TMP
, nh
->labels
);
594 XFREE(MTYPE_TMP
, nh
);
597 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
599 struct nexthop_group_cmd
*nhgc
;
601 nhgc
= nhgc_find(name
);
603 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
604 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
606 QOBJ_REG(nhgc
, nexthop_group_cmd
);
607 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
609 nhgc
->nhg_list
= list_new();
610 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
611 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
620 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
622 nhgc_delete_nexthops(nhgc
);
624 if (nhg_hooks
.delete)
625 nhg_hooks
.delete(nhgc
->name
);
627 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
629 list_delete(&nhgc
->nhg_list
);
632 XFREE(MTYPE_TMP
, nhgc
);
635 DEFINE_QOBJ_TYPE(nexthop_group_cmd
);
637 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NHGNAME",
638 "Enter into the nexthop-group submode\n"
639 "Specify the NAME of the nexthop-group\n")
641 const char *nhg_name
= argv
[1]->arg
;
642 struct nexthop_group_cmd
*nhgc
= NULL
;
644 nhgc
= nhgc_get(nhg_name
);
645 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
650 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NHGNAME",
652 "Delete the nexthop-group\n"
653 "Specify the NAME of the nexthop-group\n")
655 const char *nhg_name
= argv
[2]->arg
;
656 struct nexthop_group_cmd
*nhgc
= NULL
;
658 nhgc
= nhgc_find(nhg_name
);
665 DEFPY(nexthop_group_backup
, nexthop_group_backup_cmd
,
666 "backup-group WORD$name",
667 "Specify a group name containing backup nexthops\n"
668 "The name of the backup group\n")
670 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
672 strlcpy(nhgc
->backup_list_name
, name
, sizeof(nhgc
->backup_list_name
));
677 DEFPY(no_nexthop_group_backup
, no_nexthop_group_backup_cmd
,
678 "no backup-group [WORD$name]",
680 "Clear group name containing backup nexthops\n"
681 "The name of the backup group\n")
683 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
685 nhgc
->backup_list_name
[0] = 0;
690 DEFPY(nexthop_group_resilience
,
691 nexthop_group_resilience_cmd
,
692 "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
693 "A resilient Nexthop Group\n"
694 "Buckets in the Hash for this Group\n"
695 "Number of buckets\n"
696 "The Idle timer for this Resilient Nexthop Group in seconds\n"
697 "Number of seconds of Idle time\n"
698 "The length of time that the Nexthop Group can be unbalanced\n"
699 "Number of seconds of Unbalanced time\n")
701 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
703 nhgc
->nhg
.nhgr
.buckets
= buckets
;
704 nhgc
->nhg
.nhgr
.idle_timer
= idle_timer
;
705 nhgc
->nhg
.nhgr
.unbalanced_timer
= unbalanced_timer
;
707 if (nhg_hooks
.modify
)
708 nhg_hooks
.modify(nhgc
);
713 DEFPY(no_nexthop_group_resilience
,
714 no_nexthop_group_resilience_cmd
,
715 "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
717 "A resilient Nexthop Group\n"
718 "Buckets in the Hash for this Group\n"
719 "Number of buckets\n"
720 "The Idle timer for this Resilient Nexthop Group in seconds\n"
721 "Number of seconds of Idle time\n"
722 "The length of time that the Nexthop Group can be unbalanced\n"
723 "Number of seconds of Unbalanced time\n")
725 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
727 nhgc
->nhg
.nhgr
.buckets
= 0;
728 nhgc
->nhg
.nhgr
.idle_timer
= 0;
729 nhgc
->nhg
.nhgr
.unbalanced_timer
= 0;
734 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
735 const char *nhvrf_name
,
736 const union sockunion
*addr
,
737 const char *intf
, bool onlink
,
738 const char *labels
, const uint32_t weight
,
739 const char *backup_str
)
741 struct nexthop_hold
*nh
;
743 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
746 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
748 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
750 nh
->addr
= sockunion_dup(addr
);
752 nh
->labels
= XSTRDUP(MTYPE_TMP
, labels
);
759 nh
->backup_str
= XSTRDUP(MTYPE_TMP
, backup_str
);
761 listnode_add_sort(nhgc
->nhg_list
, nh
);
765 * Remove config info about a nexthop from group 'nhgc'. Note that we
766 * use only a subset of the available attributes here to determine
768 * Note that this doesn't change the list of nexthops, only the config
771 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
772 const char *nhvrf_name
,
773 const union sockunion
*addr
,
776 struct nexthop_hold
*nh
;
777 struct listnode
*node
;
779 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
780 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0
781 && nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0
782 && nhgc_cmp_helper(intf
, nh
->intf
) == 0)
787 * Something has gone seriously wrong, fail gracefully
792 list_delete_node(nhgc
->nhg_list
, node
);
797 * Parse the config strings we support for a single nexthop. This gets used
798 * in a couple of different ways, and we distinguish between transient
799 * failures - such as a still-unprocessed interface - and fatal errors
800 * from label-string parsing.
802 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
803 const union sockunion
*addr
,
804 const char *intf
, bool onlink
,
805 const char *name
, const char *labels
,
806 int *lbl_ret
, uint32_t weight
,
807 const char *backup_str
)
813 memset(nhop
, 0, sizeof(*nhop
));
816 vrf
= vrf_lookup_by_name(name
);
818 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
823 nhop
->vrf_id
= vrf
->vrf_id
;
826 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
827 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
832 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_ONLINK
);
835 if (addr
->sa
.sa_family
== AF_INET
) {
836 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
838 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
840 nhop
->type
= NEXTHOP_TYPE_IPV4
;
842 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
844 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
846 nhop
->type
= NEXTHOP_TYPE_IPV6
;
849 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
853 mpls_label_t larray
[MPLS_MAX_LABELS
];
855 ret
= mpls_str2label(labels
, &num
, larray
);
857 /* Return label parse result */
864 nexthop_add_labels(nhop
, ZEBRA_LSP_NONE
,
868 nhop
->weight
= weight
;
871 /* Parse backup indexes */
872 ret
= nexthop_str2backups(backup_str
,
873 &num
, nhop
->backup_idx
);
875 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
);
876 nhop
->backup_num
= num
;
885 * Wrapper to parse the strings in a 'nexthop_hold'
887 static bool nexthop_group_parse_nhh(struct nexthop
*nhop
,
888 const struct nexthop_hold
*nhh
)
890 return (nexthop_group_parse_nexthop(
891 nhop
, nhh
->addr
, nhh
->intf
, nhh
->onlink
, nhh
->nhvrf_name
,
892 nhh
->labels
, NULL
, nhh
->weight
, nhh
->backup_str
));
895 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
898 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
902 nexthop-vrf NAME$vrf_name \
908 "Specify one of the nexthops in this ECMP group\n"
912 "Treat nexthop as directly attached to the interface\n"
914 "If the nexthop is in a different vrf tell us\n"
915 "The nexthop-vrf Name\n"
916 "Specify label(s) for this nexthop\n"
917 "One or more labels in the range (16-1048575) separated by '/'\n"
918 "Weight to be used by the nexthop for purposes of ECMP\n"
919 "Weight value to be used\n"
920 "Specify backup nexthop indexes in another group\n"
921 "One or more indexes in the range (0-254) separated by ','\n")
923 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
929 uint8_t backups
[NEXTHOP_MAX_BACKUPS
];
932 /* Pre-parse backup string to validate */
934 lbl_ret
= nexthop_str2backups(backup_idx
, &num
, backups
);
936 vty_out(vty
, "%% Invalid backups\n");
937 return CMD_WARNING_CONFIG_FAILED
;
941 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, !!onlink
,
942 vrf_name
, label
, &lbl_ret
, weight
,
945 if (nhop
.type
== NEXTHOP_TYPE_IPV6
946 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
948 "Specified a v6 LL with no interface, rejecting\n");
949 return CMD_WARNING_CONFIG_FAILED
;
952 /* Handle label-string errors */
953 if (!legal
&& lbl_ret
< 0) {
956 vty_out(vty
, "%% Malformed label(s)\n");
960 "%% Cannot use reserved label(s) (%d-%d)\n",
961 MPLS_LABEL_RESERVED_MIN
,
962 MPLS_LABEL_RESERVED_MAX
);
966 "%% Too many labels. Enter %d or fewer\n",
970 return CMD_WARNING_CONFIG_FAILED
;
973 /* Look for an existing nexthop in the config. Note that the test
974 * here tests only some attributes - it's not a complete comparison.
975 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
976 * that represent config that may or may not be valid (yet), and
977 * actual nexthops that have been validated and parsed.
979 nh
= nhg_nh_find(&nhgc
->nhg
, &nhop
);
981 /* Always attempt to remove old config info. */
982 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
984 /* Remove any existing nexthop, for delete and replace cases. */
986 nexthop_unlink(&nhgc
->nhg
, nh
);
988 if (nhg_hooks
.del_nexthop
)
989 nhg_hooks
.del_nexthop(nhgc
, nh
);
994 /* Add/replace case: capture nexthop if valid, and capture
995 * config info always.
1000 memcpy(nh
, &nhop
, sizeof(nhop
));
1001 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1004 /* Save config always */
1005 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
, !!onlink
,
1006 label
, weight
, backup_idx
);
1008 if (legal
&& nhg_hooks
.add_nexthop
)
1009 nhg_hooks
.add_nexthop(nhgc
, nh
);
1015 static int nexthop_group_write(struct vty
*vty
);
1016 static struct cmd_node nexthop_group_node
= {
1017 .name
= "nexthop-group",
1018 .node
= NH_GROUP_NODE
,
1019 .parent_node
= CONFIG_NODE
,
1020 .prompt
= "%s(config-nh-group)# ",
1021 .config_write
= nexthop_group_write
,
1024 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
1025 const struct nexthop
*nh
,
1030 vty_out(vty
, "nexthop ");
1035 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
1038 case NEXTHOP_TYPE_IFINDEX
:
1039 vty_out(vty
, "%s", ifname
);
1041 case NEXTHOP_TYPE_IPV4
:
1042 vty_out(vty
, "%pI4", &nh
->gate
.ipv4
);
1044 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1045 vty_out(vty
, "%pI4 %s", &nh
->gate
.ipv4
, ifname
);
1047 case NEXTHOP_TYPE_IPV6
:
1048 vty_out(vty
, "%pI6", &nh
->gate
.ipv6
);
1050 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1051 vty_out(vty
, "%pI6 %s", &nh
->gate
.ipv6
, ifname
);
1053 case NEXTHOP_TYPE_BLACKHOLE
:
1058 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1063 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1065 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1066 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1067 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1070 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1073 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1074 buf
, sizeof(buf
), nh
->nh_label_type
, 0);
1075 vty_out(vty
, " label %s", buf
);
1079 vty_out(vty
, " weight %u", nh
->weight
);
1081 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1082 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1084 for (i
= 1; i
< nh
->backup_num
; i
++)
1085 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1091 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1094 json_object
*json_backups
= NULL
;
1098 case NEXTHOP_TYPE_IFINDEX
:
1099 json_object_string_add(j
, "nexthop",
1100 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1102 case NEXTHOP_TYPE_IPV4
:
1103 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1105 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1106 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1107 json_object_string_add(j
, "vrfId",
1108 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1110 case NEXTHOP_TYPE_IPV6
:
1111 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1113 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1114 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1115 json_object_string_add(j
, "vrfId",
1116 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1118 case NEXTHOP_TYPE_BLACKHOLE
:
1122 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1123 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1124 json_object_string_add(j
, "targetVrf", vrf
->name
);
1127 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1130 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1131 buf
, sizeof(buf
), nh
->nh_label_type
, 0);
1132 json_object_string_add(j
, "label", buf
);
1136 json_object_int_add(j
, "weight", nh
->weight
);
1138 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1139 json_backups
= json_object_new_array();
1140 for (i
= 0; i
< nh
->backup_num
; i
++)
1141 json_object_array_add(
1143 json_object_new_int(nh
->backup_idx
[i
]));
1145 json_object_object_add(j
, "backupIdx", json_backups
);
1149 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1150 const struct nexthop_hold
*nh
)
1152 vty_out(vty
, "nexthop");
1155 vty_out(vty
, " %pSU", nh
->addr
);
1158 vty_out(vty
, " %s", nh
->intf
);
1161 vty_out(vty
, " onlink");
1164 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1167 vty_out(vty
, " label %s", nh
->labels
);
1170 vty_out(vty
, " weight %u", nh
->weight
);
1173 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1178 static int nexthop_group_write(struct vty
*vty
)
1180 struct nexthop_group_cmd
*nhgc
;
1181 struct nexthop_hold
*nh
;
1183 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1184 struct listnode
*node
;
1186 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1188 if (nhgc
->nhg
.nhgr
.buckets
)
1190 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1191 nhgc
->nhg
.nhgr
.buckets
,
1192 nhgc
->nhg
.nhgr
.idle_timer
,
1193 nhgc
->nhg
.nhgr
.unbalanced_timer
);
1195 if (nhgc
->backup_list_name
[0])
1196 vty_out(vty
, " backup-group %s\n",
1197 nhgc
->backup_list_name
);
1199 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1201 nexthop_group_write_nexthop_internal(vty
, nh
);
1204 vty_out(vty
, "exit\n");
1205 vty_out(vty
, "!\n");
1211 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1213 struct nexthop_group_cmd
*nhgc
;
1214 struct nexthop_hold
*nhh
;
1216 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1217 struct listnode
*node
;
1219 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1220 struct nexthop nhop
;
1223 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1226 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1231 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1236 memcpy(nh
, &nhop
, sizeof(nhop
));
1237 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1239 if (nhg_hooks
.add_nexthop
)
1240 nhg_hooks
.add_nexthop(nhgc
, nh
);
1245 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1247 struct nexthop_group_cmd
*nhgc
;
1248 struct nexthop_hold
*nhh
;
1250 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1251 struct listnode
*node
;
1253 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1254 struct nexthop nhop
;
1257 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1260 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1265 if (nh
->vrf_id
!= vrf
->vrf_id
)
1268 _nexthop_del(&nhgc
->nhg
, nh
);
1270 if (nhg_hooks
.del_nexthop
)
1271 nhg_hooks
.del_nexthop(nhgc
, nh
);
1278 void nexthop_group_interface_state_change(struct interface
*ifp
,
1279 ifindex_t oldifindex
)
1281 struct nexthop_group_cmd
*nhgc
;
1282 struct nexthop_hold
*nhh
;
1284 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1285 struct listnode
*node
;
1288 if (if_is_up(ifp
)) {
1289 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1290 struct nexthop nhop
;
1292 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1295 switch (nhop
.type
) {
1296 case NEXTHOP_TYPE_IPV4
:
1297 case NEXTHOP_TYPE_IPV6
:
1298 case NEXTHOP_TYPE_BLACKHOLE
:
1300 case NEXTHOP_TYPE_IFINDEX
:
1301 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1302 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1305 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1310 if (ifp
->ifindex
!= nhop
.ifindex
)
1315 memcpy(nh
, &nhop
, sizeof(nhop
));
1316 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1318 if (nhg_hooks
.add_nexthop
)
1319 nhg_hooks
.add_nexthop(nhgc
, nh
);
1322 struct nexthop
*next_nh
;
1324 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1327 case NEXTHOP_TYPE_IPV4
:
1328 case NEXTHOP_TYPE_IPV6
:
1329 case NEXTHOP_TYPE_BLACKHOLE
:
1331 case NEXTHOP_TYPE_IFINDEX
:
1332 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1333 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1337 if (oldifindex
!= nh
->ifindex
)
1340 _nexthop_del(&nhgc
->nhg
, nh
);
1342 if (nhg_hooks
.del_nexthop
)
1343 nhg_hooks
.del_nexthop(nhgc
, nh
);
1351 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1353 struct nexthop_group_cmd
*nhgc
;
1355 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1356 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1360 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1361 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1362 {.completions
= NULL
}};
1364 void nexthop_group_init(void (*new)(const char *name
),
1365 void (*modify
)(const struct nexthop_group_cmd
*nhgc
),
1366 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1367 const struct nexthop
*nhop
),
1368 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1369 const struct nexthop
*nhop
),
1370 void (*delete)(const char *name
))
1372 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1374 cmd_variable_handler_register(nhg_name_handlers
);
1376 install_node(&nexthop_group_node
);
1377 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1378 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1380 install_default(NH_GROUP_NODE
);
1381 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1382 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1383 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1385 install_element(NH_GROUP_NODE
, &nexthop_group_resilience_cmd
);
1386 install_element(NH_GROUP_NODE
, &no_nexthop_group_resilience_cmd
);
1388 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1391 nhg_hooks
.new = new;
1393 nhg_hooks
.modify
= modify
;
1395 nhg_hooks
.add_nexthop
= add_nexthop
;
1397 nhg_hooks
.del_nexthop
= del_nexthop
;
1399 nhg_hooks
.delete = delete;