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
;
36 struct nexthop_group_hooks
{
37 void (*new)(const char *name
);
38 void (*modify
)(const struct nexthop_group_cmd
*nhgc
);
39 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
40 const struct nexthop
*nhop
);
41 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
42 const struct nexthop
*nhop
);
43 void (*delete)(const char *name
);
46 static struct nexthop_group_hooks nhg_hooks
;
49 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
50 const struct nexthop_group_cmd
*nhgc2
);
51 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
52 nexthop_group_cmd_compare
)
54 static struct nhgc_entry_head nhgc_entries
;
57 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
58 const struct nexthop_group_cmd
*nhgc2
)
60 return strcmp(nhgc1
->name
, nhgc2
->name
);
63 static struct nexthop
*nexthop_group_tail(const struct nexthop_group
*nhg
)
65 struct nexthop
*nexthop
= nhg
->nexthop
;
67 while (nexthop
&& nexthop
->next
)
68 nexthop
= nexthop
->next
;
73 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
78 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
84 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
89 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
)
95 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
100 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
101 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
109 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
111 struct nexthop
*nhop
;
114 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
) {
115 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
122 bool nexthop_group_has_label(const struct nexthop_group
*nhg
)
124 struct nexthop
*nhop
;
126 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
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 vni_t vni
, int *lbl_ret
,
795 uint32_t weight
, const char *backup_str
)
800 uint8_t labelnum
= 0;
802 memset(nhop
, 0, sizeof(*nhop
));
805 vrf
= vrf_lookup_by_name(name
);
807 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
812 nhop
->vrf_id
= vrf
->vrf_id
;
815 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
816 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
821 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_ONLINK
);
824 if (addr
->sa
.sa_family
== AF_INET
) {
825 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
827 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
829 nhop
->type
= NEXTHOP_TYPE_IPV4
;
831 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
833 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
835 nhop
->type
= NEXTHOP_TYPE_IPV6
;
838 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
841 mpls_label_t larray
[MPLS_MAX_LABELS
];
843 ret
= mpls_str2label(labels
, &labelnum
, larray
);
845 /* Return label parse result */
851 else if (labelnum
> 0)
852 nexthop_add_labels(nhop
, ZEBRA_LSP_NONE
, labelnum
,
855 mpls_label_t label
= MPLS_INVALID_LABEL
;
857 vni2label(vni
, &label
);
858 nexthop_add_labels(nhop
, ZEBRA_LSP_EVPN
, 1, &label
);
861 nhop
->weight
= weight
;
864 /* Parse backup indexes */
865 ret
= nexthop_str2backups(backup_str
,
866 &num
, nhop
->backup_idx
);
868 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
);
869 nhop
->backup_num
= num
;
878 * Wrapper to parse the strings in a 'nexthop_hold'
880 static bool nexthop_group_parse_nhh(struct nexthop
*nhop
,
881 const struct nexthop_hold
*nhh
)
883 return (nexthop_group_parse_nexthop(
884 nhop
, nhh
->addr
, nhh
->intf
, nhh
->onlink
, nhh
->nhvrf_name
,
885 nhh
->labels
, nhh
->vni
, NULL
, nhh
->weight
, nhh
->backup_str
));
888 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
891 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
895 nexthop-vrf NAME$vrf_name \
902 "Specify one of the nexthops in this ECMP group\n"
906 "Treat nexthop as directly attached to the interface\n"
908 "If the nexthop is in a different vrf tell us\n"
909 "The nexthop-vrf Name\n"
910 "Specify label(s) for this nexthop\n"
911 "One or more labels in the range (16-1048575) separated by '/'\n"
912 "Specify VNI(s) for this nexthop\n"
913 "VNI in the range (1-16777215)\n"
914 "Weight to be used by the nexthop for purposes of ECMP\n"
915 "Weight value to be used\n"
916 "Specify backup nexthop indexes in another group\n"
917 "One or more indexes in the range (0-254) separated by ','\n")
919 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
925 uint8_t backups
[NEXTHOP_MAX_BACKUPS
];
928 /* Pre-parse backup string to validate */
930 lbl_ret
= nexthop_str2backups(backup_idx
, &num
, backups
);
932 vty_out(vty
, "%% Invalid backups\n");
933 return CMD_WARNING_CONFIG_FAILED
;
937 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, !!onlink
,
938 vrf_name
, label
, vni
, &lbl_ret
,
941 if (nhop
.type
== NEXTHOP_TYPE_IPV6
942 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
944 "Specified a v6 LL with no interface, rejecting\n");
945 return CMD_WARNING_CONFIG_FAILED
;
948 /* Handle label-string errors */
949 if (!legal
&& lbl_ret
< 0) {
952 vty_out(vty
, "%% Malformed label(s)\n");
956 "%% Cannot use reserved label(s) (%d-%d)\n",
957 MPLS_LABEL_RESERVED_MIN
,
958 MPLS_LABEL_RESERVED_MAX
);
962 "%% Too many labels. Enter %d or fewer\n",
966 return CMD_WARNING_CONFIG_FAILED
;
969 /* Look for an existing nexthop in the config. Note that the test
970 * here tests only some attributes - it's not a complete comparison.
971 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
972 * that represent config that may or may not be valid (yet), and
973 * actual nexthops that have been validated and parsed.
975 nh
= nhg_nh_find(&nhgc
->nhg
, &nhop
);
977 /* Always attempt to remove old config info. */
978 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
980 /* Remove any existing nexthop, for delete and replace cases. */
982 nexthop_unlink(&nhgc
->nhg
, nh
);
984 if (nhg_hooks
.del_nexthop
)
985 nhg_hooks
.del_nexthop(nhgc
, nh
);
990 /* Add/replace case: capture nexthop if valid, and capture
991 * config info always.
996 memcpy(nh
, &nhop
, sizeof(nhop
));
997 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1000 /* Save config always */
1001 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
, !!onlink
,
1002 label
, weight
, backup_idx
);
1004 if (legal
&& nhg_hooks
.add_nexthop
)
1005 nhg_hooks
.add_nexthop(nhgc
, nh
);
1011 static int nexthop_group_write(struct vty
*vty
);
1012 static struct cmd_node nexthop_group_node
= {
1013 .name
= "nexthop-group",
1014 .node
= NH_GROUP_NODE
,
1015 .parent_node
= CONFIG_NODE
,
1016 .prompt
= "%s(config-nh-group)# ",
1017 .config_write
= nexthop_group_write
,
1020 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
1021 const struct nexthop
*nh
,
1026 vty_out(vty
, "nexthop ");
1031 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
1034 case NEXTHOP_TYPE_IFINDEX
:
1035 vty_out(vty
, "%s", ifname
);
1037 case NEXTHOP_TYPE_IPV4
:
1038 vty_out(vty
, "%pI4", &nh
->gate
.ipv4
);
1040 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1041 vty_out(vty
, "%pI4 %s", &nh
->gate
.ipv4
, ifname
);
1043 case NEXTHOP_TYPE_IPV6
:
1044 vty_out(vty
, "%pI6", &nh
->gate
.ipv6
);
1046 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1047 vty_out(vty
, "%pI6 %s", &nh
->gate
.ipv6
, ifname
);
1049 case NEXTHOP_TYPE_BLACKHOLE
:
1054 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1059 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1061 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1062 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1063 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1066 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1069 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1070 buf
, sizeof(buf
), nh
->nh_label_type
, 0);
1071 vty_out(vty
, " label %s", buf
);
1075 vty_out(vty
, " weight %u", nh
->weight
);
1077 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1078 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1080 for (i
= 1; i
< nh
->backup_num
; i
++)
1081 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1087 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1090 json_object
*json_backups
= NULL
;
1094 case NEXTHOP_TYPE_IFINDEX
:
1095 json_object_string_add(j
, "nexthop",
1096 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1098 case NEXTHOP_TYPE_IPV4
:
1099 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1101 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1102 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1103 json_object_string_add(j
, "vrfId",
1104 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1106 case NEXTHOP_TYPE_IPV6
:
1107 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1109 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1110 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1111 json_object_string_add(j
, "vrfId",
1112 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1114 case NEXTHOP_TYPE_BLACKHOLE
:
1118 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1119 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1120 json_object_string_add(j
, "targetVrf", vrf
->name
);
1123 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1126 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1127 buf
, sizeof(buf
), nh
->nh_label_type
, 0);
1128 json_object_string_add(j
, "label", buf
);
1132 json_object_int_add(j
, "weight", nh
->weight
);
1134 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1135 json_backups
= json_object_new_array();
1136 for (i
= 0; i
< nh
->backup_num
; i
++)
1137 json_object_array_add(
1139 json_object_new_int(nh
->backup_idx
[i
]));
1141 json_object_object_add(j
, "backupIdx", json_backups
);
1145 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1146 const struct nexthop_hold
*nh
)
1148 vty_out(vty
, "nexthop");
1151 vty_out(vty
, " %pSU", nh
->addr
);
1154 vty_out(vty
, " %s", nh
->intf
);
1157 vty_out(vty
, " onlink");
1160 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1163 vty_out(vty
, " label %s", nh
->labels
);
1166 vty_out(vty
, " vni %u", nh
->vni
);
1169 vty_out(vty
, " weight %u", nh
->weight
);
1172 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1177 static int nexthop_group_write(struct vty
*vty
)
1179 struct nexthop_group_cmd
*nhgc
;
1180 struct nexthop_hold
*nh
;
1182 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1183 struct listnode
*node
;
1185 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1187 if (nhgc
->nhg
.nhgr
.buckets
)
1189 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
1190 nhgc
->nhg
.nhgr
.buckets
,
1191 nhgc
->nhg
.nhgr
.idle_timer
,
1192 nhgc
->nhg
.nhgr
.unbalanced_timer
);
1194 if (nhgc
->backup_list_name
[0])
1195 vty_out(vty
, " backup-group %s\n",
1196 nhgc
->backup_list_name
);
1198 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1200 nexthop_group_write_nexthop_internal(vty
, nh
);
1203 vty_out(vty
, "exit\n");
1204 vty_out(vty
, "!\n");
1210 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1212 struct nexthop_group_cmd
*nhgc
;
1213 struct nexthop_hold
*nhh
;
1215 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1216 struct listnode
*node
;
1218 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1219 struct nexthop nhop
;
1222 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1225 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1230 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1235 memcpy(nh
, &nhop
, sizeof(nhop
));
1236 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1238 if (nhg_hooks
.add_nexthop
)
1239 nhg_hooks
.add_nexthop(nhgc
, nh
);
1244 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1246 struct nexthop_group_cmd
*nhgc
;
1247 struct nexthop_hold
*nhh
;
1249 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1250 struct listnode
*node
;
1252 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1253 struct nexthop nhop
;
1256 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1259 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1264 if (nh
->vrf_id
!= vrf
->vrf_id
)
1267 _nexthop_del(&nhgc
->nhg
, nh
);
1269 if (nhg_hooks
.del_nexthop
)
1270 nhg_hooks
.del_nexthop(nhgc
, nh
);
1277 void nexthop_group_interface_state_change(struct interface
*ifp
,
1278 ifindex_t oldifindex
)
1280 struct nexthop_group_cmd
*nhgc
;
1281 struct nexthop_hold
*nhh
;
1283 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1284 struct listnode
*node
;
1287 if (if_is_up(ifp
)) {
1288 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1289 struct nexthop nhop
;
1291 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1294 switch (nhop
.type
) {
1295 case NEXTHOP_TYPE_IPV4
:
1296 case NEXTHOP_TYPE_IPV6
:
1297 case NEXTHOP_TYPE_BLACKHOLE
:
1299 case NEXTHOP_TYPE_IFINDEX
:
1300 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1301 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1304 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1309 if (ifp
->ifindex
!= nhop
.ifindex
)
1314 memcpy(nh
, &nhop
, sizeof(nhop
));
1315 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1317 if (nhg_hooks
.add_nexthop
)
1318 nhg_hooks
.add_nexthop(nhgc
, nh
);
1321 struct nexthop
*next_nh
;
1323 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1326 case NEXTHOP_TYPE_IPV4
:
1327 case NEXTHOP_TYPE_IPV6
:
1328 case NEXTHOP_TYPE_BLACKHOLE
:
1330 case NEXTHOP_TYPE_IFINDEX
:
1331 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1332 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1336 if (oldifindex
!= nh
->ifindex
)
1339 _nexthop_del(&nhgc
->nhg
, nh
);
1341 if (nhg_hooks
.del_nexthop
)
1342 nhg_hooks
.del_nexthop(nhgc
, nh
);
1350 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1352 struct nexthop_group_cmd
*nhgc
;
1354 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1355 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1359 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1360 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1361 {.completions
= NULL
}};
1363 void nexthop_group_init(void (*new)(const char *name
),
1364 void (*modify
)(const struct nexthop_group_cmd
*nhgc
),
1365 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1366 const struct nexthop
*nhop
),
1367 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1368 const struct nexthop
*nhop
),
1369 void (*delete)(const char *name
))
1371 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1373 cmd_variable_handler_register(nhg_name_handlers
);
1375 install_node(&nexthop_group_node
);
1376 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1377 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1379 install_default(NH_GROUP_NODE
);
1380 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1381 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1382 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1384 install_element(NH_GROUP_NODE
, &nexthop_group_resilience_cmd
);
1385 install_element(NH_GROUP_NODE
, &no_nexthop_group_resilience_cmd
);
1387 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1390 nhg_hooks
.new = new;
1392 nhg_hooks
.modify
= modify
;
1394 nhg_hooks
.add_nexthop
= add_nexthop
;
1396 nhg_hooks
.del_nexthop
= del_nexthop
;
1398 nhg_hooks
.delete = delete;