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 struct nexthop
*nexthop_exists(const struct nexthop_group
*nhg
,
135 const struct nexthop
*nh
)
137 struct nexthop
*nexthop
;
139 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
140 if (nexthop_same(nh
, nexthop
))
148 * Helper that locates a nexthop in an nhg config list. Note that
149 * this uses a specific matching / equality rule that's different from
150 * the complete match performed by 'nexthop_same()'.
152 static struct nexthop
*nhg_nh_find(const struct nexthop_group
*nhg
,
153 const struct nexthop
*nh
)
155 struct nexthop
*nexthop
;
158 /* We compare: vrf, gateway, and interface */
160 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
162 /* Compare vrf and type */
163 if (nexthop
->vrf_id
!= nh
->vrf_id
)
165 if (nexthop
->type
!= nh
->type
)
168 /* Compare gateway */
169 switch (nexthop
->type
) {
170 case NEXTHOP_TYPE_IPV4
:
171 case NEXTHOP_TYPE_IPV6
:
172 ret
= nexthop_g_addr_cmp(nexthop
->type
,
173 &nexthop
->gate
, &nh
->gate
);
177 case NEXTHOP_TYPE_IPV4_IFINDEX
:
178 case NEXTHOP_TYPE_IPV6_IFINDEX
:
179 ret
= nexthop_g_addr_cmp(nexthop
->type
,
180 &nexthop
->gate
, &nh
->gate
);
183 /* Intentional Fall-Through */
184 case NEXTHOP_TYPE_IFINDEX
:
185 if (nexthop
->ifindex
!= nh
->ifindex
)
188 case NEXTHOP_TYPE_BLACKHOLE
:
189 if (nexthop
->bh_type
!= nh
->bh_type
)
201 nexthop_group_equal_common(const struct nexthop_group
*nhg1
,
202 const struct nexthop_group
*nhg2
,
203 uint8_t (*nexthop_group_nexthop_num_func
)(
204 const struct nexthop_group
*nhg
))
215 if (nexthop_group_nexthop_num_func(nhg1
)
216 != nexthop_group_nexthop_num_func(nhg2
))
222 /* This assumes ordered */
223 bool nexthop_group_equal_no_recurse(const struct nexthop_group
*nhg1
,
224 const struct nexthop_group
*nhg2
)
226 struct nexthop
*nh1
= NULL
;
227 struct nexthop
*nh2
= NULL
;
229 if (!nexthop_group_equal_common(nhg1
, nhg2
,
230 &nexthop_group_nexthop_num_no_recurse
))
233 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
234 nh1
= nh1
->next
, nh2
= nh2
->next
) {
239 if (!nexthop_same(nh1
, nh2
))
246 /* This assumes ordered */
247 bool nexthop_group_equal(const struct nexthop_group
*nhg1
,
248 const struct nexthop_group
*nhg2
)
250 struct nexthop
*nh1
= NULL
;
251 struct nexthop
*nh2
= NULL
;
253 if (!nexthop_group_equal_common(nhg1
, nhg2
, &nexthop_group_nexthop_num
))
256 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
257 nh1
= nexthop_next(nh1
), nh2
= nexthop_next(nh2
)) {
262 if (!nexthop_same(nh1
, nh2
))
268 struct nexthop_group
*nexthop_group_new(void)
270 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
273 void nexthop_group_copy(struct nexthop_group
*to
,
274 const struct nexthop_group
*from
)
276 to
->nhgr
= from
->nhgr
;
277 /* Copy everything, including recursive info */
278 copy_nexthops(&to
->nexthop
, from
->nexthop
, NULL
);
281 void nexthop_group_delete(struct nexthop_group
**nhg
)
283 /* OK to call with NULL group */
288 nexthops_free((*nhg
)->nexthop
);
290 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
293 /* Add nexthop to the end of a nexthop list. */
294 void _nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
296 struct nexthop
*last
;
298 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
301 last
->next
= nexthop
;
304 nexthop
->prev
= last
;
307 /* Add nexthop to sorted list of nexthops */
308 static void _nexthop_add_sorted(struct nexthop
**head
,
309 struct nexthop
*nexthop
)
311 struct nexthop
*position
, *prev
;
313 assert(!nexthop
->next
);
315 for (position
= *head
, prev
= NULL
; position
;
316 prev
= position
, position
= position
->next
) {
317 if (nexthop_cmp(position
, nexthop
) > 0) {
318 nexthop
->next
= position
;
319 nexthop
->prev
= prev
;
322 nexthop
->prev
->next
= nexthop
;
326 position
->prev
= nexthop
;
331 nexthop
->prev
= prev
;
333 prev
->next
= nexthop
;
338 void nexthop_group_add_sorted(struct nexthop_group
*nhg
,
339 struct nexthop
*nexthop
)
341 struct nexthop
*tail
;
343 assert(!nexthop
->next
);
345 /* Try to just append to the end first;
346 * trust the list is already sorted
348 tail
= nexthop_group_tail(nhg
);
350 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
351 tail
->next
= nexthop
;
352 nexthop
->prev
= tail
;
357 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
360 /* Delete nexthop from a nexthop list. */
361 void _nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
363 struct nexthop
*nexthop
;
365 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
366 if (nexthop_same(nh
, nexthop
))
373 nexthop
->prev
->next
= nexthop
->next
;
375 nhg
->nexthop
= nexthop
->next
;
378 nexthop
->next
->prev
= nexthop
->prev
;
384 /* Unlink a nexthop from the list it's on, unconditionally */
385 static void nexthop_unlink(struct nexthop_group
*nhg
, struct nexthop
*nexthop
)
389 nexthop
->prev
->next
= nexthop
->next
;
391 assert(nhg
->nexthop
== nexthop
);
392 assert(nexthop
->prev
== NULL
);
393 nhg
->nexthop
= nexthop
->next
;
397 nexthop
->next
->prev
= nexthop
->prev
;
399 nexthop
->prev
= NULL
;
400 nexthop
->next
= NULL
;
404 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
406 void nexthop_group_copy_nh_sorted(struct nexthop_group
*nhg
,
407 const struct nexthop
*nh
)
409 struct nexthop
*nexthop
, *tail
;
410 const struct nexthop
*nh1
;
412 /* We'll try to append to the end of the new list;
413 * if the original list in nh is already sorted, this eliminates
414 * lots of comparison operations.
416 tail
= nexthop_group_tail(nhg
);
418 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
419 nexthop
= nexthop_dup(nh1
, NULL
);
421 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
422 tail
->next
= nexthop
;
423 nexthop
->prev
= tail
;
429 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
436 /* Copy a list of nexthops, no effort made to sort or order them. */
437 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
438 struct nexthop
*rparent
)
440 struct nexthop
*nexthop
;
441 const struct nexthop
*nh1
;
443 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
444 nexthop
= nexthop_dup(nh1
, rparent
);
445 _nexthop_add(tnh
, nexthop
);
449 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group
*nhg
)
455 * We are not interested in hashing over any recursively
458 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
459 key
= jhash_1word(nexthop_hash(nh
), key
);
464 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
469 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
470 key
= jhash_1word(nexthop_hash(nh
), key
);
475 void nexthop_group_mark_duplicates(struct nexthop_group
*nhg
)
477 struct nexthop
*nexthop
, *prev
;
479 for (ALL_NEXTHOPS_PTR(nhg
, nexthop
)) {
480 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
);
481 for (ALL_NEXTHOPS_PTR(nhg
, prev
)) {
484 if (nexthop_same(nexthop
, prev
)) {
485 SET_FLAG(nexthop
->flags
,
486 NEXTHOP_FLAG_DUPLICATE
);
493 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
495 struct nexthop
*nexthop
;
497 nexthop
= nhgc
->nhg
.nexthop
;
499 struct nexthop
*next
= nexthop_next(nexthop
);
501 _nexthop_del(&nhgc
->nhg
, nexthop
);
502 if (nhg_hooks
.del_nexthop
)
503 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
505 nexthop_free(nexthop
);
511 struct nexthop_group_cmd
*nhgc_find(const char *name
)
513 struct nexthop_group_cmd find
;
515 strlcpy(find
.name
, name
, sizeof(find
.name
));
517 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
520 static int nhgc_cmp_helper(const char *a
, const char *b
)
534 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
545 return sockunion_cmp(a
, b
);
548 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
552 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
556 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
560 ret
= nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
564 ret
= ((int)nh2
->onlink
) - ((int)nh1
->onlink
);
568 return nhgc_cmp_helper(nh1
->labels
, nh2
->labels
);
571 static void nhgl_delete(struct nexthop_hold
*nh
)
573 XFREE(MTYPE_TMP
, nh
->intf
);
575 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
578 sockunion_free(nh
->addr
);
580 XFREE(MTYPE_TMP
, nh
->labels
);
582 XFREE(MTYPE_TMP
, nh
);
585 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
587 struct nexthop_group_cmd
*nhgc
;
589 nhgc
= nhgc_find(name
);
591 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
592 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
594 QOBJ_REG(nhgc
, nexthop_group_cmd
);
595 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
597 nhgc
->nhg_list
= list_new();
598 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
599 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
608 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
610 nhgc_delete_nexthops(nhgc
);
612 if (nhg_hooks
.delete)
613 nhg_hooks
.delete(nhgc
->name
);
615 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
617 list_delete(&nhgc
->nhg_list
);
620 XFREE(MTYPE_TMP
, nhgc
);
623 DEFINE_QOBJ_TYPE(nexthop_group_cmd
);
625 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NHGNAME",
626 "Enter into the nexthop-group submode\n"
627 "Specify the NAME of the nexthop-group\n")
629 const char *nhg_name
= argv
[1]->arg
;
630 struct nexthop_group_cmd
*nhgc
= NULL
;
632 nhgc
= nhgc_get(nhg_name
);
633 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
638 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NHGNAME",
640 "Delete the nexthop-group\n"
641 "Specify the NAME of the nexthop-group\n")
643 const char *nhg_name
= argv
[2]->arg
;
644 struct nexthop_group_cmd
*nhgc
= NULL
;
646 nhgc
= nhgc_find(nhg_name
);
653 DEFPY(nexthop_group_backup
, nexthop_group_backup_cmd
,
654 "backup-group WORD$name",
655 "Specify a group name containing backup nexthops\n"
656 "The name of the backup group\n")
658 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
660 strlcpy(nhgc
->backup_list_name
, name
, sizeof(nhgc
->backup_list_name
));
665 DEFPY(no_nexthop_group_backup
, no_nexthop_group_backup_cmd
,
666 "no backup-group [WORD$name]",
668 "Clear group name containing backup nexthops\n"
669 "The name of the backup group\n")
671 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
673 nhgc
->backup_list_name
[0] = 0;
678 DEFPY(nexthop_group_resilience
,
679 nexthop_group_resilience_cmd
,
680 "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
681 "A resilient Nexthop Group\n"
682 "Buckets in the Hash for this Group\n"
683 "Number of buckets\n"
684 "The Idle timer for this Resilient Nexthop Group in seconds\n"
685 "Number of seconds of Idle time\n"
686 "The length of time that the Nexthop Group can be unbalanced\n"
687 "Number of seconds of Unbalanced time\n")
689 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
691 nhgc
->nhg
.nhgr
.buckets
= buckets
;
692 nhgc
->nhg
.nhgr
.idle_timer
= idle_timer
;
693 nhgc
->nhg
.nhgr
.unbalanced_timer
= unbalanced_timer
;
695 if (nhg_hooks
.modify
)
696 nhg_hooks
.modify(nhgc
);
701 DEFPY(no_nexthop_group_resilience
,
702 no_nexthop_group_resilience_cmd
,
703 "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
705 "A resilient Nexthop Group\n"
706 "Buckets in the Hash for this Group\n"
707 "Number of buckets\n"
708 "The Idle timer for this Resilient Nexthop Group in seconds\n"
709 "Number of seconds of Idle time\n"
710 "The length of time that the Nexthop Group can be unbalanced\n"
711 "Number of seconds of Unbalanced time\n")
713 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
715 nhgc
->nhg
.nhgr
.buckets
= 0;
716 nhgc
->nhg
.nhgr
.idle_timer
= 0;
717 nhgc
->nhg
.nhgr
.unbalanced_timer
= 0;
722 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
723 const char *nhvrf_name
,
724 const union sockunion
*addr
,
725 const char *intf
, bool onlink
,
726 const char *labels
, const uint32_t weight
,
727 const char *backup_str
)
729 struct nexthop_hold
*nh
;
731 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
734 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
736 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
738 nh
->addr
= sockunion_dup(addr
);
740 nh
->labels
= XSTRDUP(MTYPE_TMP
, labels
);
747 nh
->backup_str
= XSTRDUP(MTYPE_TMP
, backup_str
);
749 listnode_add_sort(nhgc
->nhg_list
, nh
);
753 * Remove config info about a nexthop from group 'nhgc'. Note that we
754 * use only a subset of the available attributes here to determine
756 * Note that this doesn't change the list of nexthops, only the config
759 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
760 const char *nhvrf_name
,
761 const union sockunion
*addr
,
764 struct nexthop_hold
*nh
;
765 struct listnode
*node
;
767 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
768 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0
769 && nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0
770 && nhgc_cmp_helper(intf
, nh
->intf
) == 0)
775 * Something has gone seriously wrong, fail gracefully
780 list_delete_node(nhgc
->nhg_list
, node
);
785 * Parse the config strings we support for a single nexthop. This gets used
786 * in a couple of different ways, and we distinguish between transient
787 * failures - such as a still-unprocessed interface - and fatal errors
788 * from label-string parsing.
790 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
791 const union sockunion
*addr
,
792 const char *intf
, bool onlink
,
793 const char *name
, const char *labels
,
794 int *lbl_ret
, uint32_t weight
,
795 const char *backup_str
)
801 memset(nhop
, 0, sizeof(*nhop
));
804 vrf
= vrf_lookup_by_name(name
);
806 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
811 nhop
->vrf_id
= vrf
->vrf_id
;
814 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
815 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
820 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_ONLINK
);
823 if (addr
->sa
.sa_family
== AF_INET
) {
824 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
826 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
828 nhop
->type
= NEXTHOP_TYPE_IPV4
;
830 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
832 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
834 nhop
->type
= NEXTHOP_TYPE_IPV6
;
837 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
841 mpls_label_t larray
[MPLS_MAX_LABELS
];
843 ret
= mpls_str2label(labels
, &num
, larray
);
845 /* Return label parse result */
852 nexthop_add_labels(nhop
, ZEBRA_LSP_NONE
,
856 nhop
->weight
= weight
;
859 /* Parse backup indexes */
860 ret
= nexthop_str2backups(backup_str
,
861 &num
, nhop
->backup_idx
);
863 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
);
864 nhop
->backup_num
= num
;
873 * Wrapper to parse the strings in a 'nexthop_hold'
875 static bool nexthop_group_parse_nhh(struct nexthop
*nhop
,
876 const struct nexthop_hold
*nhh
)
878 return (nexthop_group_parse_nexthop(
879 nhop
, nhh
->addr
, nhh
->intf
, nhh
->onlink
, nhh
->nhvrf_name
,
880 nhh
->labels
, NULL
, nhh
->weight
, nhh
->backup_str
));
883 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
886 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
890 nexthop-vrf NAME$vrf_name \
896 "Specify one of the nexthops in this ECMP group\n"
900 "Treat nexthop as directly attached to the interface\n"
902 "If the nexthop is in a different vrf tell us\n"
903 "The nexthop-vrf Name\n"
904 "Specify label(s) for this nexthop\n"
905 "One or more labels in the range (16-1048575) separated by '/'\n"
906 "Weight to be used by the nexthop for purposes of ECMP\n"
907 "Weight value to be used\n"
908 "Specify backup nexthop indexes in another group\n"
909 "One or more indexes in the range (0-254) separated by ','\n")
911 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
917 uint8_t backups
[NEXTHOP_MAX_BACKUPS
];
920 /* Pre-parse backup string to validate */
922 lbl_ret
= nexthop_str2backups(backup_idx
, &num
, backups
);
924 vty_out(vty
, "%% Invalid backups\n");
925 return CMD_WARNING_CONFIG_FAILED
;
929 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, !!onlink
,
930 vrf_name
, label
, &lbl_ret
, weight
,
933 if (nhop
.type
== NEXTHOP_TYPE_IPV6
934 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
936 "Specified a v6 LL with no interface, rejecting\n");
937 return CMD_WARNING_CONFIG_FAILED
;
940 /* Handle label-string errors */
941 if (!legal
&& lbl_ret
< 0) {
944 vty_out(vty
, "%% Malformed label(s)\n");
948 "%% Cannot use reserved label(s) (%d-%d)\n",
949 MPLS_LABEL_RESERVED_MIN
,
950 MPLS_LABEL_RESERVED_MAX
);
954 "%% Too many labels. Enter %d or fewer\n",
958 return CMD_WARNING_CONFIG_FAILED
;
961 /* Look for an existing nexthop in the config. Note that the test
962 * here tests only some attributes - it's not a complete comparison.
963 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
964 * that represent config that may or may not be valid (yet), and
965 * actual nexthops that have been validated and parsed.
967 nh
= nhg_nh_find(&nhgc
->nhg
, &nhop
);
969 /* Always attempt to remove old config info. */
970 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
972 /* Remove any existing nexthop, for delete and replace cases. */
974 nexthop_unlink(&nhgc
->nhg
, nh
);
976 if (nhg_hooks
.del_nexthop
)
977 nhg_hooks
.del_nexthop(nhgc
, nh
);
982 /* Add/replace case: capture nexthop if valid, and capture
983 * config info always.
988 memcpy(nh
, &nhop
, sizeof(nhop
));
989 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
992 /* Save config always */
993 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
, !!onlink
,
994 label
, weight
, backup_idx
);
996 if (legal
&& nhg_hooks
.add_nexthop
)
997 nhg_hooks
.add_nexthop(nhgc
, nh
);
1003 static int nexthop_group_write(struct vty
*vty
);
1004 static struct cmd_node nexthop_group_node
= {
1005 .name
= "nexthop-group",
1006 .node
= NH_GROUP_NODE
,
1007 .parent_node
= CONFIG_NODE
,
1008 .prompt
= "%s(config-nh-group)# ",
1009 .config_write
= nexthop_group_write
,
1012 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
1013 const struct nexthop
*nh
,
1018 vty_out(vty
, "nexthop ");
1023 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
1026 case NEXTHOP_TYPE_IFINDEX
:
1027 vty_out(vty
, "%s", ifname
);
1029 case NEXTHOP_TYPE_IPV4
:
1030 vty_out(vty
, "%pI4", &nh
->gate
.ipv4
);
1032 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1033 vty_out(vty
, "%pI4 %s", &nh
->gate
.ipv4
, ifname
);
1035 case NEXTHOP_TYPE_IPV6
:
1036 vty_out(vty
, "%pI6", &nh
->gate
.ipv6
);
1038 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1039 vty_out(vty
, "%pI6 %s", &nh
->gate
.ipv6
, ifname
);
1041 case NEXTHOP_TYPE_BLACKHOLE
:
1046 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1051 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1053 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1054 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1055 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1058 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1061 mpls_label2str(nh
->nh_label
->num_labels
,
1062 nh
->nh_label
->label
,
1063 buf
, sizeof(buf
), 0);
1064 vty_out(vty
, " label %s", buf
);
1068 vty_out(vty
, " weight %u", nh
->weight
);
1070 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1071 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1073 for (i
= 1; i
< nh
->backup_num
; i
++)
1074 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1080 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1083 json_object
*json_backups
= NULL
;
1087 case NEXTHOP_TYPE_IFINDEX
:
1088 json_object_string_add(j
, "nexthop",
1089 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1091 case NEXTHOP_TYPE_IPV4
:
1092 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1094 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1095 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1096 json_object_string_add(j
, "vrfId",
1097 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1099 case NEXTHOP_TYPE_IPV6
:
1100 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1102 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1103 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1104 json_object_string_add(j
, "vrfId",
1105 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1107 case NEXTHOP_TYPE_BLACKHOLE
:
1111 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1112 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1113 json_object_string_add(j
, "targetVrf", vrf
->name
);
1116 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1119 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1120 buf
, sizeof(buf
), 0);
1121 json_object_string_add(j
, "label", buf
);
1125 json_object_int_add(j
, "weight", nh
->weight
);
1127 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1128 json_backups
= json_object_new_array();
1129 for (i
= 0; i
< nh
->backup_num
; i
++)
1130 json_object_array_add(
1132 json_object_new_int(nh
->backup_idx
[i
]));
1134 json_object_object_add(j
, "backupIdx", json_backups
);
1138 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1139 const struct nexthop_hold
*nh
)
1141 vty_out(vty
, "nexthop");
1144 vty_out(vty
, " %pSU", nh
->addr
);
1147 vty_out(vty
, " %s", nh
->intf
);
1150 vty_out(vty
, " onlink");
1153 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1156 vty_out(vty
, " label %s", nh
->labels
);
1159 vty_out(vty
, " weight %u", nh
->weight
);
1162 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1167 static int nexthop_group_write(struct vty
*vty
)
1169 struct nexthop_group_cmd
*nhgc
;
1170 struct nexthop_hold
*nh
;
1172 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1173 struct listnode
*node
;
1175 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1177 if (nhgc
->nhg
.nhgr
.buckets
)
1179 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1180 nhgc
->nhg
.nhgr
.buckets
,
1181 nhgc
->nhg
.nhgr
.idle_timer
,
1182 nhgc
->nhg
.nhgr
.unbalanced_timer
);
1184 if (nhgc
->backup_list_name
[0])
1185 vty_out(vty
, " backup-group %s\n",
1186 nhgc
->backup_list_name
);
1188 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1190 nexthop_group_write_nexthop_internal(vty
, nh
);
1193 vty_out(vty
, "exit\n");
1194 vty_out(vty
, "!\n");
1200 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1202 struct nexthop_group_cmd
*nhgc
;
1203 struct nexthop_hold
*nhh
;
1205 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1206 struct listnode
*node
;
1208 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1209 struct nexthop nhop
;
1212 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1215 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1220 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1225 memcpy(nh
, &nhop
, sizeof(nhop
));
1226 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1228 if (nhg_hooks
.add_nexthop
)
1229 nhg_hooks
.add_nexthop(nhgc
, nh
);
1234 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1236 struct nexthop_group_cmd
*nhgc
;
1237 struct nexthop_hold
*nhh
;
1239 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1240 struct listnode
*node
;
1242 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1243 struct nexthop nhop
;
1246 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1249 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1254 if (nh
->vrf_id
!= vrf
->vrf_id
)
1257 _nexthop_del(&nhgc
->nhg
, nh
);
1259 if (nhg_hooks
.del_nexthop
)
1260 nhg_hooks
.del_nexthop(nhgc
, nh
);
1267 void nexthop_group_interface_state_change(struct interface
*ifp
,
1268 ifindex_t oldifindex
)
1270 struct nexthop_group_cmd
*nhgc
;
1271 struct nexthop_hold
*nhh
;
1273 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1274 struct listnode
*node
;
1277 if (if_is_up(ifp
)) {
1278 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1279 struct nexthop nhop
;
1281 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1284 switch (nhop
.type
) {
1285 case NEXTHOP_TYPE_IPV4
:
1286 case NEXTHOP_TYPE_IPV6
:
1287 case NEXTHOP_TYPE_BLACKHOLE
:
1289 case NEXTHOP_TYPE_IFINDEX
:
1290 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1291 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1294 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1299 if (ifp
->ifindex
!= nhop
.ifindex
)
1304 memcpy(nh
, &nhop
, sizeof(nhop
));
1305 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1307 if (nhg_hooks
.add_nexthop
)
1308 nhg_hooks
.add_nexthop(nhgc
, nh
);
1311 struct nexthop
*next_nh
;
1313 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1316 case NEXTHOP_TYPE_IPV4
:
1317 case NEXTHOP_TYPE_IPV6
:
1318 case NEXTHOP_TYPE_BLACKHOLE
:
1320 case NEXTHOP_TYPE_IFINDEX
:
1321 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1322 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1326 if (oldifindex
!= nh
->ifindex
)
1329 _nexthop_del(&nhgc
->nhg
, nh
);
1331 if (nhg_hooks
.del_nexthop
)
1332 nhg_hooks
.del_nexthop(nhgc
, nh
);
1340 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1342 struct nexthop_group_cmd
*nhgc
;
1344 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1345 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1349 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1350 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1351 {.completions
= NULL
}};
1353 void nexthop_group_init(void (*new)(const char *name
),
1354 void (*modify
)(const struct nexthop_group_cmd
*nhgc
),
1355 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1356 const struct nexthop
*nhop
),
1357 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1358 const struct nexthop
*nhop
),
1359 void (*delete)(const char *name
))
1361 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1363 cmd_variable_handler_register(nhg_name_handlers
);
1365 install_node(&nexthop_group_node
);
1366 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1367 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1369 install_default(NH_GROUP_NODE
);
1370 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1371 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1372 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1374 install_element(NH_GROUP_NODE
, &nexthop_group_resilience_cmd
);
1375 install_element(NH_GROUP_NODE
, &no_nexthop_group_resilience_cmd
);
1377 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1380 nhg_hooks
.new = new;
1382 nhg_hooks
.modify
= modify
;
1384 nhg_hooks
.add_nexthop
= add_nexthop
;
1386 nhg_hooks
.del_nexthop
= del_nexthop
;
1388 nhg_hooks
.delete = delete;