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
;
49 struct nexthop_group_hooks
{
50 void (*new)(const char *name
);
51 void (*modify
)(const struct nexthop_group_cmd
*nhgc
);
52 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
53 const struct nexthop
*nhop
);
54 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
55 const struct nexthop
*nhop
);
56 void (*delete)(const char *name
);
59 static struct nexthop_group_hooks nhg_hooks
;
62 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
63 const struct nexthop_group_cmd
*nhgc2
);
64 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
65 nexthop_group_cmd_compare
)
67 static struct nhgc_entry_head nhgc_entries
;
70 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
71 const struct nexthop_group_cmd
*nhgc2
)
73 return strcmp(nhgc1
->name
, nhgc2
->name
);
76 static struct nexthop
*nexthop_group_tail(const struct nexthop_group
*nhg
)
78 struct nexthop
*nexthop
= nhg
->nexthop
;
80 while (nexthop
&& nexthop
->next
)
81 nexthop
= nexthop
->next
;
86 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
91 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
97 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
102 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
)
108 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
110 struct nexthop
*nhop
;
113 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
114 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
122 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
124 struct nexthop
*nhop
;
127 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
) {
128 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
135 bool nexthop_group_has_label(const struct nexthop_group
*nhg
)
137 struct nexthop
*nhop
;
139 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
147 struct nexthop
*nexthop_exists(const struct nexthop_group
*nhg
,
148 const struct nexthop
*nh
)
150 struct nexthop
*nexthop
;
152 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
153 if (nexthop_same(nh
, nexthop
))
161 * Helper that locates a nexthop in an nhg config list. Note that
162 * this uses a specific matching / equality rule that's different from
163 * the complete match performed by 'nexthop_same()'.
165 static struct nexthop
*nhg_nh_find(const struct nexthop_group
*nhg
,
166 const struct nexthop
*nh
)
168 struct nexthop
*nexthop
;
171 /* We compare: vrf, gateway, and interface */
173 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
175 /* Compare vrf and type */
176 if (nexthop
->vrf_id
!= nh
->vrf_id
)
178 if (nexthop
->type
!= nh
->type
)
181 /* Compare gateway */
182 switch (nexthop
->type
) {
183 case NEXTHOP_TYPE_IPV4
:
184 case NEXTHOP_TYPE_IPV6
:
185 ret
= nexthop_g_addr_cmp(nexthop
->type
,
186 &nexthop
->gate
, &nh
->gate
);
190 case NEXTHOP_TYPE_IPV4_IFINDEX
:
191 case NEXTHOP_TYPE_IPV6_IFINDEX
:
192 ret
= nexthop_g_addr_cmp(nexthop
->type
,
193 &nexthop
->gate
, &nh
->gate
);
196 /* Intentional Fall-Through */
197 case NEXTHOP_TYPE_IFINDEX
:
198 if (nexthop
->ifindex
!= nh
->ifindex
)
201 case NEXTHOP_TYPE_BLACKHOLE
:
202 if (nexthop
->bh_type
!= nh
->bh_type
)
214 nexthop_group_equal_common(const struct nexthop_group
*nhg1
,
215 const struct nexthop_group
*nhg2
,
216 uint8_t (*nexthop_group_nexthop_num_func
)(
217 const struct nexthop_group
*nhg
))
228 if (nexthop_group_nexthop_num_func(nhg1
)
229 != nexthop_group_nexthop_num_func(nhg2
))
235 /* This assumes ordered */
236 bool nexthop_group_equal_no_recurse(const struct nexthop_group
*nhg1
,
237 const struct nexthop_group
*nhg2
)
239 struct nexthop
*nh1
= NULL
;
240 struct nexthop
*nh2
= NULL
;
242 if (!nexthop_group_equal_common(nhg1
, nhg2
,
243 &nexthop_group_nexthop_num_no_recurse
))
246 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
247 nh1
= nh1
->next
, nh2
= nh2
->next
) {
252 if (!nexthop_same(nh1
, nh2
))
259 /* This assumes ordered */
260 bool nexthop_group_equal(const struct nexthop_group
*nhg1
,
261 const struct nexthop_group
*nhg2
)
263 struct nexthop
*nh1
= NULL
;
264 struct nexthop
*nh2
= NULL
;
266 if (!nexthop_group_equal_common(nhg1
, nhg2
, &nexthop_group_nexthop_num
))
269 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
270 nh1
= nexthop_next(nh1
), nh2
= nexthop_next(nh2
)) {
275 if (!nexthop_same(nh1
, nh2
))
281 struct nexthop_group
*nexthop_group_new(void)
283 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
286 void nexthop_group_copy(struct nexthop_group
*to
,
287 const struct nexthop_group
*from
)
289 to
->nhgr
= from
->nhgr
;
290 /* Copy everything, including recursive info */
291 copy_nexthops(&to
->nexthop
, from
->nexthop
, NULL
);
294 void nexthop_group_delete(struct nexthop_group
**nhg
)
296 /* OK to call with NULL group */
301 nexthops_free((*nhg
)->nexthop
);
303 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
306 /* Add nexthop to the end of a nexthop list. */
307 void _nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
309 struct nexthop
*last
;
311 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
314 last
->next
= nexthop
;
317 nexthop
->prev
= last
;
320 /* Add nexthop to sorted list of nexthops */
321 static void _nexthop_add_sorted(struct nexthop
**head
,
322 struct nexthop
*nexthop
)
324 struct nexthop
*position
, *prev
;
326 assert(!nexthop
->next
);
328 for (position
= *head
, prev
= NULL
; position
;
329 prev
= position
, position
= position
->next
) {
330 if (nexthop_cmp(position
, nexthop
) > 0) {
331 nexthop
->next
= position
;
332 nexthop
->prev
= prev
;
335 nexthop
->prev
->next
= nexthop
;
339 position
->prev
= nexthop
;
344 nexthop
->prev
= prev
;
346 prev
->next
= nexthop
;
351 void nexthop_group_add_sorted(struct nexthop_group
*nhg
,
352 struct nexthop
*nexthop
)
354 struct nexthop
*tail
;
356 assert(!nexthop
->next
);
358 /* Try to just append to the end first;
359 * trust the list is already sorted
361 tail
= nexthop_group_tail(nhg
);
363 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
364 tail
->next
= nexthop
;
365 nexthop
->prev
= tail
;
370 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
373 /* Delete nexthop from a nexthop list. */
374 void _nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
376 struct nexthop
*nexthop
;
378 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
379 if (nexthop_same(nh
, nexthop
))
386 nexthop
->prev
->next
= nexthop
->next
;
388 nhg
->nexthop
= nexthop
->next
;
391 nexthop
->next
->prev
= nexthop
->prev
;
397 /* Unlink a nexthop from the list it's on, unconditionally */
398 static void nexthop_unlink(struct nexthop_group
*nhg
, struct nexthop
*nexthop
)
402 nexthop
->prev
->next
= nexthop
->next
;
404 assert(nhg
->nexthop
== nexthop
);
405 assert(nexthop
->prev
== NULL
);
406 nhg
->nexthop
= nexthop
->next
;
410 nexthop
->next
->prev
= nexthop
->prev
;
412 nexthop
->prev
= NULL
;
413 nexthop
->next
= NULL
;
417 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
419 void nexthop_group_copy_nh_sorted(struct nexthop_group
*nhg
,
420 const struct nexthop
*nh
)
422 struct nexthop
*nexthop
, *tail
;
423 const struct nexthop
*nh1
;
425 /* We'll try to append to the end of the new list;
426 * if the original list in nh is already sorted, this eliminates
427 * lots of comparison operations.
429 tail
= nexthop_group_tail(nhg
);
431 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
432 nexthop
= nexthop_dup(nh1
, NULL
);
434 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
435 tail
->next
= nexthop
;
436 nexthop
->prev
= tail
;
442 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
449 /* Copy a list of nexthops, no effort made to sort or order them. */
450 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
451 struct nexthop
*rparent
)
453 struct nexthop
*nexthop
;
454 const struct nexthop
*nh1
;
456 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
457 nexthop
= nexthop_dup(nh1
, rparent
);
458 _nexthop_add(tnh
, nexthop
);
462 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group
*nhg
)
468 * We are not interested in hashing over any recursively
471 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
472 key
= jhash_1word(nexthop_hash(nh
), key
);
477 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
482 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
483 key
= jhash_1word(nexthop_hash(nh
), key
);
488 void nexthop_group_mark_duplicates(struct nexthop_group
*nhg
)
490 struct nexthop
*nexthop
, *prev
;
492 for (ALL_NEXTHOPS_PTR(nhg
, nexthop
)) {
493 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
);
494 for (ALL_NEXTHOPS_PTR(nhg
, prev
)) {
497 if (nexthop_same(nexthop
, prev
)) {
498 SET_FLAG(nexthop
->flags
,
499 NEXTHOP_FLAG_DUPLICATE
);
506 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
508 struct nexthop
*nexthop
;
510 nexthop
= nhgc
->nhg
.nexthop
;
512 struct nexthop
*next
= nexthop_next(nexthop
);
514 _nexthop_del(&nhgc
->nhg
, nexthop
);
515 if (nhg_hooks
.del_nexthop
)
516 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
518 nexthop_free(nexthop
);
524 struct nexthop_group_cmd
*nhgc_find(const char *name
)
526 struct nexthop_group_cmd find
;
528 strlcpy(find
.name
, name
, sizeof(find
.name
));
530 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
533 static int nhgc_cmp_helper(const char *a
, const char *b
)
547 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
558 return sockunion_cmp(a
, b
);
561 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
565 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
569 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
573 ret
= nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
577 ret
= ((int)nh2
->onlink
) - ((int)nh1
->onlink
);
581 return nhgc_cmp_helper(nh1
->labels
, nh2
->labels
);
584 static void nhgl_delete(struct nexthop_hold
*nh
)
586 XFREE(MTYPE_TMP
, nh
->intf
);
588 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
591 sockunion_free(nh
->addr
);
593 XFREE(MTYPE_TMP
, nh
->labels
);
595 XFREE(MTYPE_TMP
, nh
);
598 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
600 struct nexthop_group_cmd
*nhgc
;
602 nhgc
= nhgc_find(name
);
604 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
605 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
607 QOBJ_REG(nhgc
, nexthop_group_cmd
);
608 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
610 nhgc
->nhg_list
= list_new();
611 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
612 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
621 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
623 nhgc_delete_nexthops(nhgc
);
625 if (nhg_hooks
.delete)
626 nhg_hooks
.delete(nhgc
->name
);
628 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
630 list_delete(&nhgc
->nhg_list
);
633 XFREE(MTYPE_TMP
, nhgc
);
636 DEFINE_QOBJ_TYPE(nexthop_group_cmd
);
638 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NHGNAME",
639 "Enter into the nexthop-group submode\n"
640 "Specify the NAME of the nexthop-group\n")
642 const char *nhg_name
= argv
[1]->arg
;
643 struct nexthop_group_cmd
*nhgc
= NULL
;
645 nhgc
= nhgc_get(nhg_name
);
646 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
651 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NHGNAME",
653 "Delete the nexthop-group\n"
654 "Specify the NAME of the nexthop-group\n")
656 const char *nhg_name
= argv
[2]->arg
;
657 struct nexthop_group_cmd
*nhgc
= NULL
;
659 nhgc
= nhgc_find(nhg_name
);
666 DEFPY(nexthop_group_backup
, nexthop_group_backup_cmd
,
667 "backup-group WORD$name",
668 "Specify a group name containing backup nexthops\n"
669 "The name of the backup group\n")
671 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
673 strlcpy(nhgc
->backup_list_name
, name
, sizeof(nhgc
->backup_list_name
));
678 DEFPY(no_nexthop_group_backup
, no_nexthop_group_backup_cmd
,
679 "no backup-group [WORD$name]",
681 "Clear group name containing backup nexthops\n"
682 "The name of the backup group\n")
684 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
686 nhgc
->backup_list_name
[0] = 0;
691 DEFPY(nexthop_group_resilience
,
692 nexthop_group_resilience_cmd
,
693 "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
694 "A resilient Nexthop Group\n"
695 "Buckets in the Hash for this Group\n"
696 "Number of buckets\n"
697 "The Idle timer for this Resilient Nexthop Group in seconds\n"
698 "Number of seconds of Idle time\n"
699 "The length of time that the Nexthop Group can be unbalanced\n"
700 "Number of seconds of Unbalanced time\n")
702 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
704 nhgc
->nhg
.nhgr
.buckets
= buckets
;
705 nhgc
->nhg
.nhgr
.idle_timer
= idle_timer
;
706 nhgc
->nhg
.nhgr
.unbalanced_timer
= unbalanced_timer
;
708 if (nhg_hooks
.modify
)
709 nhg_hooks
.modify(nhgc
);
714 DEFPY(no_nexthop_group_resilience
,
715 no_nexthop_group_resilience_cmd
,
716 "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
718 "A resilient Nexthop Group\n"
719 "Buckets in the Hash for this Group\n"
720 "Number of buckets\n"
721 "The Idle timer for this Resilient Nexthop Group in seconds\n"
722 "Number of seconds of Idle time\n"
723 "The length of time that the Nexthop Group can be unbalanced\n"
724 "Number of seconds of Unbalanced time\n")
726 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
728 nhgc
->nhg
.nhgr
.buckets
= 0;
729 nhgc
->nhg
.nhgr
.idle_timer
= 0;
730 nhgc
->nhg
.nhgr
.unbalanced_timer
= 0;
735 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
736 const char *nhvrf_name
,
737 const union sockunion
*addr
,
738 const char *intf
, bool onlink
,
739 const char *labels
, const uint32_t weight
,
740 const char *backup_str
)
742 struct nexthop_hold
*nh
;
744 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
747 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
749 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
751 nh
->addr
= sockunion_dup(addr
);
753 nh
->labels
= XSTRDUP(MTYPE_TMP
, labels
);
760 nh
->backup_str
= XSTRDUP(MTYPE_TMP
, backup_str
);
762 listnode_add_sort(nhgc
->nhg_list
, nh
);
766 * Remove config info about a nexthop from group 'nhgc'. Note that we
767 * use only a subset of the available attributes here to determine
769 * Note that this doesn't change the list of nexthops, only the config
772 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
773 const char *nhvrf_name
,
774 const union sockunion
*addr
,
777 struct nexthop_hold
*nh
;
778 struct listnode
*node
;
780 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
781 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0
782 && nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0
783 && nhgc_cmp_helper(intf
, nh
->intf
) == 0)
788 * Something has gone seriously wrong, fail gracefully
793 list_delete_node(nhgc
->nhg_list
, node
);
798 * Parse the config strings we support for a single nexthop. This gets used
799 * in a couple of different ways, and we distinguish between transient
800 * failures - such as a still-unprocessed interface - and fatal errors
801 * from label-string parsing.
803 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
804 const union sockunion
*addr
,
805 const char *intf
, bool onlink
,
806 const char *name
, const char *labels
,
807 vni_t vni
, int *lbl_ret
,
808 uint32_t weight
, const char *backup_str
)
813 uint8_t labelnum
= 0;
815 memset(nhop
, 0, sizeof(*nhop
));
818 vrf
= vrf_lookup_by_name(name
);
820 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
825 nhop
->vrf_id
= vrf
->vrf_id
;
828 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
829 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
834 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_ONLINK
);
837 if (addr
->sa
.sa_family
== AF_INET
) {
838 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
840 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
842 nhop
->type
= NEXTHOP_TYPE_IPV4
;
844 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
846 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
848 nhop
->type
= NEXTHOP_TYPE_IPV6
;
851 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
854 mpls_label_t larray
[MPLS_MAX_LABELS
];
856 ret
= mpls_str2label(labels
, &labelnum
, larray
);
858 /* Return label parse result */
864 else if (labelnum
> 0)
865 nexthop_add_labels(nhop
, ZEBRA_LSP_NONE
, labelnum
,
868 mpls_label_t label
= MPLS_INVALID_LABEL
;
870 vni2label(vni
, &label
);
871 nexthop_add_labels(nhop
, ZEBRA_LSP_EVPN
, 1, &label
);
874 nhop
->weight
= weight
;
877 /* Parse backup indexes */
878 ret
= nexthop_str2backups(backup_str
,
879 &num
, nhop
->backup_idx
);
881 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
);
882 nhop
->backup_num
= num
;
891 * Wrapper to parse the strings in a 'nexthop_hold'
893 static bool nexthop_group_parse_nhh(struct nexthop
*nhop
,
894 const struct nexthop_hold
*nhh
)
896 return (nexthop_group_parse_nexthop(
897 nhop
, nhh
->addr
, nhh
->intf
, nhh
->onlink
, nhh
->nhvrf_name
,
898 nhh
->labels
, nhh
->vni
, NULL
, nhh
->weight
, nhh
->backup_str
));
901 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
904 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
908 nexthop-vrf NAME$vrf_name \
915 "Specify one of the nexthops in this ECMP group\n"
919 "Treat nexthop as directly attached to the interface\n"
921 "If the nexthop is in a different vrf tell us\n"
922 "The nexthop-vrf Name\n"
923 "Specify label(s) for this nexthop\n"
924 "One or more labels in the range (16-1048575) separated by '/'\n"
925 "Specify VNI(s) for this nexthop\n"
926 "VNI in the range (1-16777215)\n"
927 "Weight to be used by the nexthop for purposes of ECMP\n"
928 "Weight value to be used\n"
929 "Specify backup nexthop indexes in another group\n"
930 "One or more indexes in the range (0-254) separated by ','\n")
932 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
938 uint8_t backups
[NEXTHOP_MAX_BACKUPS
];
941 /* Pre-parse backup string to validate */
943 lbl_ret
= nexthop_str2backups(backup_idx
, &num
, backups
);
945 vty_out(vty
, "%% Invalid backups\n");
946 return CMD_WARNING_CONFIG_FAILED
;
950 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, !!onlink
,
951 vrf_name
, label
, vni
, &lbl_ret
,
954 if (nhop
.type
== NEXTHOP_TYPE_IPV6
955 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
957 "Specified a v6 LL with no interface, rejecting\n");
958 return CMD_WARNING_CONFIG_FAILED
;
961 /* Handle label-string errors */
962 if (!legal
&& lbl_ret
< 0) {
965 vty_out(vty
, "%% Malformed label(s)\n");
969 "%% Cannot use reserved label(s) (%d-%d)\n",
970 MPLS_LABEL_RESERVED_MIN
,
971 MPLS_LABEL_RESERVED_MAX
);
975 "%% Too many labels. Enter %d or fewer\n",
979 return CMD_WARNING_CONFIG_FAILED
;
982 /* Look for an existing nexthop in the config. Note that the test
983 * here tests only some attributes - it's not a complete comparison.
984 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
985 * that represent config that may or may not be valid (yet), and
986 * actual nexthops that have been validated and parsed.
988 nh
= nhg_nh_find(&nhgc
->nhg
, &nhop
);
990 /* Always attempt to remove old config info. */
991 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
993 /* Remove any existing nexthop, for delete and replace cases. */
995 nexthop_unlink(&nhgc
->nhg
, nh
);
997 if (nhg_hooks
.del_nexthop
)
998 nhg_hooks
.del_nexthop(nhgc
, nh
);
1003 /* Add/replace case: capture nexthop if valid, and capture
1004 * config info always.
1009 memcpy(nh
, &nhop
, sizeof(nhop
));
1010 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1013 /* Save config always */
1014 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
, !!onlink
,
1015 label
, weight
, backup_idx
);
1017 if (legal
&& nhg_hooks
.add_nexthop
)
1018 nhg_hooks
.add_nexthop(nhgc
, nh
);
1024 static int nexthop_group_write(struct vty
*vty
);
1025 static struct cmd_node nexthop_group_node
= {
1026 .name
= "nexthop-group",
1027 .node
= NH_GROUP_NODE
,
1028 .parent_node
= CONFIG_NODE
,
1029 .prompt
= "%s(config-nh-group)# ",
1030 .config_write
= nexthop_group_write
,
1033 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
1034 const struct nexthop
*nh
,
1039 vty_out(vty
, "nexthop ");
1044 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
1047 case NEXTHOP_TYPE_IFINDEX
:
1048 vty_out(vty
, "%s", ifname
);
1050 case NEXTHOP_TYPE_IPV4
:
1051 vty_out(vty
, "%pI4", &nh
->gate
.ipv4
);
1053 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1054 vty_out(vty
, "%pI4 %s", &nh
->gate
.ipv4
, ifname
);
1056 case NEXTHOP_TYPE_IPV6
:
1057 vty_out(vty
, "%pI6", &nh
->gate
.ipv6
);
1059 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1060 vty_out(vty
, "%pI6 %s", &nh
->gate
.ipv6
, ifname
);
1062 case NEXTHOP_TYPE_BLACKHOLE
:
1067 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1072 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1074 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1075 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1076 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1079 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1082 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1083 buf
, sizeof(buf
), nh
->nh_label_type
, 0);
1084 vty_out(vty
, " label %s", buf
);
1088 vty_out(vty
, " weight %u", nh
->weight
);
1090 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1091 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1093 for (i
= 1; i
< nh
->backup_num
; i
++)
1094 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1100 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1103 json_object
*json_backups
= NULL
;
1107 case NEXTHOP_TYPE_IFINDEX
:
1108 json_object_string_add(j
, "nexthop",
1109 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1111 case NEXTHOP_TYPE_IPV4
:
1112 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1114 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1115 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1116 json_object_string_add(j
, "vrfId",
1117 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1119 case NEXTHOP_TYPE_IPV6
:
1120 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1122 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1123 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1124 json_object_string_add(j
, "vrfId",
1125 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1127 case NEXTHOP_TYPE_BLACKHOLE
:
1131 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1132 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1133 json_object_string_add(j
, "targetVrf", vrf
->name
);
1136 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1139 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1140 buf
, sizeof(buf
), nh
->nh_label_type
, 0);
1141 json_object_string_add(j
, "label", buf
);
1145 json_object_int_add(j
, "weight", nh
->weight
);
1147 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1148 json_backups
= json_object_new_array();
1149 for (i
= 0; i
< nh
->backup_num
; i
++)
1150 json_object_array_add(
1152 json_object_new_int(nh
->backup_idx
[i
]));
1154 json_object_object_add(j
, "backupIdx", json_backups
);
1158 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1159 const struct nexthop_hold
*nh
)
1161 vty_out(vty
, "nexthop");
1164 vty_out(vty
, " %pSU", nh
->addr
);
1167 vty_out(vty
, " %s", nh
->intf
);
1170 vty_out(vty
, " onlink");
1173 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1176 vty_out(vty
, " label %s", nh
->labels
);
1179 vty_out(vty
, " vni %u", nh
->vni
);
1182 vty_out(vty
, " weight %u", nh
->weight
);
1185 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1190 static int nexthop_group_write(struct vty
*vty
)
1192 struct nexthop_group_cmd
*nhgc
;
1193 struct nexthop_hold
*nh
;
1195 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1196 struct listnode
*node
;
1198 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1200 if (nhgc
->nhg
.nhgr
.buckets
)
1202 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1203 nhgc
->nhg
.nhgr
.buckets
,
1204 nhgc
->nhg
.nhgr
.idle_timer
,
1205 nhgc
->nhg
.nhgr
.unbalanced_timer
);
1207 if (nhgc
->backup_list_name
[0])
1208 vty_out(vty
, " backup-group %s\n",
1209 nhgc
->backup_list_name
);
1211 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1213 nexthop_group_write_nexthop_internal(vty
, nh
);
1216 vty_out(vty
, "exit\n");
1217 vty_out(vty
, "!\n");
1223 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1225 struct nexthop_group_cmd
*nhgc
;
1226 struct nexthop_hold
*nhh
;
1228 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1229 struct listnode
*node
;
1231 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1232 struct nexthop nhop
;
1235 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1238 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1243 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1248 memcpy(nh
, &nhop
, sizeof(nhop
));
1249 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1251 if (nhg_hooks
.add_nexthop
)
1252 nhg_hooks
.add_nexthop(nhgc
, nh
);
1257 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1259 struct nexthop_group_cmd
*nhgc
;
1260 struct nexthop_hold
*nhh
;
1262 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1263 struct listnode
*node
;
1265 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1266 struct nexthop nhop
;
1269 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1272 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1277 if (nh
->vrf_id
!= vrf
->vrf_id
)
1280 _nexthop_del(&nhgc
->nhg
, nh
);
1282 if (nhg_hooks
.del_nexthop
)
1283 nhg_hooks
.del_nexthop(nhgc
, nh
);
1290 void nexthop_group_interface_state_change(struct interface
*ifp
,
1291 ifindex_t oldifindex
)
1293 struct nexthop_group_cmd
*nhgc
;
1294 struct nexthop_hold
*nhh
;
1296 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1297 struct listnode
*node
;
1300 if (if_is_up(ifp
)) {
1301 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1302 struct nexthop nhop
;
1304 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1307 switch (nhop
.type
) {
1308 case NEXTHOP_TYPE_IPV4
:
1309 case NEXTHOP_TYPE_IPV6
:
1310 case NEXTHOP_TYPE_BLACKHOLE
:
1312 case NEXTHOP_TYPE_IFINDEX
:
1313 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1314 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1317 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1322 if (ifp
->ifindex
!= nhop
.ifindex
)
1327 memcpy(nh
, &nhop
, sizeof(nhop
));
1328 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1330 if (nhg_hooks
.add_nexthop
)
1331 nhg_hooks
.add_nexthop(nhgc
, nh
);
1334 struct nexthop
*next_nh
;
1336 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1339 case NEXTHOP_TYPE_IPV4
:
1340 case NEXTHOP_TYPE_IPV6
:
1341 case NEXTHOP_TYPE_BLACKHOLE
:
1343 case NEXTHOP_TYPE_IFINDEX
:
1344 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1345 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1349 if (oldifindex
!= nh
->ifindex
)
1352 _nexthop_del(&nhgc
->nhg
, nh
);
1354 if (nhg_hooks
.del_nexthop
)
1355 nhg_hooks
.del_nexthop(nhgc
, nh
);
1363 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1365 struct nexthop_group_cmd
*nhgc
;
1367 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1368 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1372 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1373 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1374 {.completions
= NULL
}};
1376 void nexthop_group_init(void (*new)(const char *name
),
1377 void (*modify
)(const struct nexthop_group_cmd
*nhgc
),
1378 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1379 const struct nexthop
*nhop
),
1380 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1381 const struct nexthop
*nhop
),
1382 void (*delete)(const char *name
))
1384 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1386 cmd_variable_handler_register(nhg_name_handlers
);
1388 install_node(&nexthop_group_node
);
1389 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1390 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1392 install_default(NH_GROUP_NODE
);
1393 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1394 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1395 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1397 install_element(NH_GROUP_NODE
, &nexthop_group_resilience_cmd
);
1398 install_element(NH_GROUP_NODE
, &no_nexthop_group_resilience_cmd
);
1400 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1403 nhg_hooks
.new = new;
1405 nhg_hooks
.modify
= modify
;
1407 nhg_hooks
.add_nexthop
= add_nexthop
;
1409 nhg_hooks
.del_nexthop
= del_nexthop
;
1411 nhg_hooks
.delete = delete;