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 (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
51 const struct nexthop
*nhop
);
52 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
53 const struct nexthop
*nhop
);
54 void (*delete)(const char *name
);
57 static struct nexthop_group_hooks nhg_hooks
;
60 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
61 const struct nexthop_group_cmd
*nhgc2
);
62 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
63 nexthop_group_cmd_compare
)
65 static struct nhgc_entry_head nhgc_entries
;
68 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
69 const struct nexthop_group_cmd
*nhgc2
)
71 return strcmp(nhgc1
->name
, nhgc2
->name
);
74 static struct nexthop
*nexthop_group_tail(const struct nexthop_group
*nhg
)
76 struct nexthop
*nexthop
= nhg
->nexthop
;
78 while (nexthop
&& nexthop
->next
)
79 nexthop
= nexthop
->next
;
84 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
89 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
95 uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
100 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
)
106 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
108 struct nexthop
*nhop
;
111 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
112 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
120 nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group
*nhg
)
122 struct nexthop
*nhop
;
125 for (nhop
= nhg
->nexthop
; nhop
; nhop
= nhop
->next
) {
126 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
133 struct nexthop
*nexthop_exists(const struct nexthop_group
*nhg
,
134 const struct nexthop
*nh
)
136 struct nexthop
*nexthop
;
138 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
139 if (nexthop_same(nh
, nexthop
))
147 * Helper that locates a nexthop in an nhg config list. Note that
148 * this uses a specific matching / equality rule that's different from
149 * the complete match performed by 'nexthop_same()'.
151 static struct nexthop
*nhg_nh_find(const struct nexthop_group
*nhg
,
152 const struct nexthop
*nh
)
154 struct nexthop
*nexthop
;
157 /* We compare: vrf, gateway, and interface */
159 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
161 /* Compare vrf and type */
162 if (nexthop
->vrf_id
!= nh
->vrf_id
)
164 if (nexthop
->type
!= nh
->type
)
167 /* Compare gateway */
168 switch (nexthop
->type
) {
169 case NEXTHOP_TYPE_IPV4
:
170 case NEXTHOP_TYPE_IPV6
:
171 ret
= nexthop_g_addr_cmp(nexthop
->type
,
172 &nexthop
->gate
, &nh
->gate
);
176 case NEXTHOP_TYPE_IPV4_IFINDEX
:
177 case NEXTHOP_TYPE_IPV6_IFINDEX
:
178 ret
= nexthop_g_addr_cmp(nexthop
->type
,
179 &nexthop
->gate
, &nh
->gate
);
182 /* Intentional Fall-Through */
183 case NEXTHOP_TYPE_IFINDEX
:
184 if (nexthop
->ifindex
!= nh
->ifindex
)
187 case NEXTHOP_TYPE_BLACKHOLE
:
188 if (nexthop
->bh_type
!= nh
->bh_type
)
200 nexthop_group_equal_common(const struct nexthop_group
*nhg1
,
201 const struct nexthop_group
*nhg2
,
202 uint8_t (*nexthop_group_nexthop_num_func
)(
203 const struct nexthop_group
*nhg
))
214 if (nexthop_group_nexthop_num_func(nhg1
)
215 != nexthop_group_nexthop_num_func(nhg2
))
221 /* This assumes ordered */
222 bool nexthop_group_equal_no_recurse(const struct nexthop_group
*nhg1
,
223 const struct nexthop_group
*nhg2
)
225 struct nexthop
*nh1
= NULL
;
226 struct nexthop
*nh2
= NULL
;
228 if (!nexthop_group_equal_common(nhg1
, nhg2
,
229 &nexthop_group_nexthop_num_no_recurse
))
232 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
233 nh1
= nh1
->next
, nh2
= nh2
->next
) {
238 if (!nexthop_same(nh1
, nh2
))
245 /* This assumes ordered */
246 bool nexthop_group_equal(const struct nexthop_group
*nhg1
,
247 const struct nexthop_group
*nhg2
)
249 struct nexthop
*nh1
= NULL
;
250 struct nexthop
*nh2
= NULL
;
252 if (!nexthop_group_equal_common(nhg1
, nhg2
, &nexthop_group_nexthop_num
))
255 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
256 nh1
= nexthop_next(nh1
), nh2
= nexthop_next(nh2
)) {
261 if (!nexthop_same(nh1
, nh2
))
267 struct nexthop_group
*nexthop_group_new(void)
269 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
272 void nexthop_group_copy(struct nexthop_group
*to
,
273 const struct nexthop_group
*from
)
275 to
->nhgr
= from
->nhgr
;
276 /* Copy everything, including recursive info */
277 copy_nexthops(&to
->nexthop
, from
->nexthop
, NULL
);
280 void nexthop_group_delete(struct nexthop_group
**nhg
)
282 /* OK to call with NULL group */
287 nexthops_free((*nhg
)->nexthop
);
289 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
292 /* Add nexthop to the end of a nexthop list. */
293 void _nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
295 struct nexthop
*last
;
297 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
300 last
->next
= nexthop
;
303 nexthop
->prev
= last
;
306 /* Add nexthop to sorted list of nexthops */
307 static void _nexthop_add_sorted(struct nexthop
**head
,
308 struct nexthop
*nexthop
)
310 struct nexthop
*position
, *prev
;
312 assert(!nexthop
->next
);
314 for (position
= *head
, prev
= NULL
; position
;
315 prev
= position
, position
= position
->next
) {
316 if (nexthop_cmp(position
, nexthop
) > 0) {
317 nexthop
->next
= position
;
318 nexthop
->prev
= prev
;
321 nexthop
->prev
->next
= nexthop
;
325 position
->prev
= nexthop
;
330 nexthop
->prev
= prev
;
332 prev
->next
= nexthop
;
337 void nexthop_group_add_sorted(struct nexthop_group
*nhg
,
338 struct nexthop
*nexthop
)
340 struct nexthop
*tail
;
342 assert(!nexthop
->next
);
344 /* Try to just append to the end first;
345 * trust the list is already sorted
347 tail
= nexthop_group_tail(nhg
);
349 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
350 tail
->next
= nexthop
;
351 nexthop
->prev
= tail
;
356 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
359 /* Delete nexthop from a nexthop list. */
360 void _nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
362 struct nexthop
*nexthop
;
364 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
365 if (nexthop_same(nh
, nexthop
))
372 nexthop
->prev
->next
= nexthop
->next
;
374 nhg
->nexthop
= nexthop
->next
;
377 nexthop
->next
->prev
= nexthop
->prev
;
383 /* Unlink a nexthop from the list it's on, unconditionally */
384 static void nexthop_unlink(struct nexthop_group
*nhg
, struct nexthop
*nexthop
)
388 nexthop
->prev
->next
= nexthop
->next
;
390 assert(nhg
->nexthop
== nexthop
);
391 assert(nexthop
->prev
== NULL
);
392 nhg
->nexthop
= nexthop
->next
;
396 nexthop
->next
->prev
= nexthop
->prev
;
398 nexthop
->prev
= NULL
;
399 nexthop
->next
= NULL
;
403 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
405 void nexthop_group_copy_nh_sorted(struct nexthop_group
*nhg
,
406 const struct nexthop
*nh
)
408 struct nexthop
*nexthop
, *tail
;
409 const struct nexthop
*nh1
;
411 /* We'll try to append to the end of the new list;
412 * if the original list in nh is already sorted, this eliminates
413 * lots of comparison operations.
415 tail
= nexthop_group_tail(nhg
);
417 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
418 nexthop
= nexthop_dup(nh1
, NULL
);
420 if (tail
&& (nexthop_cmp(tail
, nexthop
) < 0)) {
421 tail
->next
= nexthop
;
422 nexthop
->prev
= tail
;
428 _nexthop_add_sorted(&nhg
->nexthop
, nexthop
);
435 /* Copy a list of nexthops, no effort made to sort or order them. */
436 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
437 struct nexthop
*rparent
)
439 struct nexthop
*nexthop
;
440 const struct nexthop
*nh1
;
442 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
443 nexthop
= nexthop_dup(nh1
, rparent
);
444 _nexthop_add(tnh
, nexthop
);
448 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group
*nhg
)
454 * We are not interested in hashing over any recursively
457 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
458 key
= jhash_1word(nexthop_hash(nh
), key
);
463 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
468 for (ALL_NEXTHOPS_PTR(nhg
, nh
))
469 key
= jhash_1word(nexthop_hash(nh
), key
);
474 void nexthop_group_mark_duplicates(struct nexthop_group
*nhg
)
476 struct nexthop
*nexthop
, *prev
;
478 for (ALL_NEXTHOPS_PTR(nhg
, nexthop
)) {
479 UNSET_FLAG(nexthop
->flags
, NEXTHOP_FLAG_DUPLICATE
);
480 for (ALL_NEXTHOPS_PTR(nhg
, prev
)) {
483 if (nexthop_same(nexthop
, prev
)) {
484 SET_FLAG(nexthop
->flags
,
485 NEXTHOP_FLAG_DUPLICATE
);
492 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
494 struct nexthop
*nexthop
;
496 nexthop
= nhgc
->nhg
.nexthop
;
498 struct nexthop
*next
= nexthop_next(nexthop
);
500 _nexthop_del(&nhgc
->nhg
, nexthop
);
501 if (nhg_hooks
.del_nexthop
)
502 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
504 nexthop_free(nexthop
);
510 struct nexthop_group_cmd
*nhgc_find(const char *name
)
512 struct nexthop_group_cmd find
;
514 strlcpy(find
.name
, name
, sizeof(find
.name
));
516 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
519 static int nhgc_cmp_helper(const char *a
, const char *b
)
533 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
544 return sockunion_cmp(a
, b
);
547 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
551 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
555 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
559 ret
= nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
563 ret
= ((int)nh2
->onlink
) - ((int)nh1
->onlink
);
567 return nhgc_cmp_helper(nh1
->labels
, nh2
->labels
);
570 static void nhgl_delete(struct nexthop_hold
*nh
)
572 XFREE(MTYPE_TMP
, nh
->intf
);
574 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
577 sockunion_free(nh
->addr
);
579 XFREE(MTYPE_TMP
, nh
->labels
);
581 XFREE(MTYPE_TMP
, nh
);
584 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
586 struct nexthop_group_cmd
*nhgc
;
588 nhgc
= nhgc_find(name
);
590 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
591 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
593 QOBJ_REG(nhgc
, nexthop_group_cmd
);
594 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
596 nhgc
->nhg_list
= list_new();
597 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
598 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
607 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
609 nhgc_delete_nexthops(nhgc
);
611 if (nhg_hooks
.delete)
612 nhg_hooks
.delete(nhgc
->name
);
614 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
616 list_delete(&nhgc
->nhg_list
);
619 XFREE(MTYPE_TMP
, nhgc
);
622 DEFINE_QOBJ_TYPE(nexthop_group_cmd
);
624 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NHGNAME",
625 "Enter into the nexthop-group submode\n"
626 "Specify the NAME of the nexthop-group\n")
628 const char *nhg_name
= argv
[1]->arg
;
629 struct nexthop_group_cmd
*nhgc
= NULL
;
631 nhgc
= nhgc_get(nhg_name
);
632 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
637 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NHGNAME",
639 "Delete the nexthop-group\n"
640 "Specify the NAME of the nexthop-group\n")
642 const char *nhg_name
= argv
[2]->arg
;
643 struct nexthop_group_cmd
*nhgc
= NULL
;
645 nhgc
= nhgc_find(nhg_name
);
652 DEFPY(nexthop_group_backup
, nexthop_group_backup_cmd
,
653 "backup-group WORD$name",
654 "Specify a group name containing backup nexthops\n"
655 "The name of the backup group\n")
657 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
659 strlcpy(nhgc
->backup_list_name
, name
, sizeof(nhgc
->backup_list_name
));
664 DEFPY(no_nexthop_group_backup
, no_nexthop_group_backup_cmd
,
665 "no backup-group [WORD$name]",
667 "Clear group name containing backup nexthops\n"
668 "The name of the backup group\n")
670 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
672 nhgc
->backup_list_name
[0] = 0;
677 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
678 const char *nhvrf_name
,
679 const union sockunion
*addr
,
680 const char *intf
, bool onlink
,
681 const char *labels
, const uint32_t weight
,
682 const char *backup_str
)
684 struct nexthop_hold
*nh
;
686 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
689 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
691 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
693 nh
->addr
= sockunion_dup(addr
);
695 nh
->labels
= XSTRDUP(MTYPE_TMP
, labels
);
702 nh
->backup_str
= XSTRDUP(MTYPE_TMP
, backup_str
);
704 listnode_add_sort(nhgc
->nhg_list
, nh
);
708 * Remove config info about a nexthop from group 'nhgc'. Note that we
709 * use only a subset of the available attributes here to determine
711 * Note that this doesn't change the list of nexthops, only the config
714 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
715 const char *nhvrf_name
,
716 const union sockunion
*addr
,
719 struct nexthop_hold
*nh
;
720 struct listnode
*node
;
722 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
723 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0
724 && nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0
725 && nhgc_cmp_helper(intf
, nh
->intf
) == 0)
730 * Something has gone seriously wrong, fail gracefully
735 list_delete_node(nhgc
->nhg_list
, node
);
740 * Parse the config strings we support for a single nexthop. This gets used
741 * in a couple of different ways, and we distinguish between transient
742 * failures - such as a still-unprocessed interface - and fatal errors
743 * from label-string parsing.
745 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
746 const union sockunion
*addr
,
747 const char *intf
, bool onlink
,
748 const char *name
, const char *labels
,
749 int *lbl_ret
, uint32_t weight
,
750 const char *backup_str
)
756 memset(nhop
, 0, sizeof(*nhop
));
759 vrf
= vrf_lookup_by_name(name
);
761 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
766 nhop
->vrf_id
= vrf
->vrf_id
;
769 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
770 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
775 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_ONLINK
);
778 if (addr
->sa
.sa_family
== AF_INET
) {
779 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
781 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
783 nhop
->type
= NEXTHOP_TYPE_IPV4
;
785 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
787 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
789 nhop
->type
= NEXTHOP_TYPE_IPV6
;
792 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
796 mpls_label_t larray
[MPLS_MAX_LABELS
];
798 ret
= mpls_str2label(labels
, &num
, larray
);
800 /* Return label parse result */
807 nexthop_add_labels(nhop
, ZEBRA_LSP_NONE
,
811 nhop
->weight
= weight
;
814 /* Parse backup indexes */
815 ret
= nexthop_str2backups(backup_str
,
816 &num
, nhop
->backup_idx
);
818 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
);
819 nhop
->backup_num
= num
;
828 * Wrapper to parse the strings in a 'nexthop_hold'
830 static bool nexthop_group_parse_nhh(struct nexthop
*nhop
,
831 const struct nexthop_hold
*nhh
)
833 return (nexthop_group_parse_nexthop(
834 nhop
, nhh
->addr
, nhh
->intf
, nhh
->onlink
, nhh
->nhvrf_name
,
835 nhh
->labels
, NULL
, nhh
->weight
, nhh
->backup_str
));
838 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
841 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
845 nexthop-vrf NAME$vrf_name \
851 "Specify one of the nexthops in this ECMP group\n"
855 "Treat nexthop as directly attached to the interface\n"
857 "If the nexthop is in a different vrf tell us\n"
858 "The nexthop-vrf Name\n"
859 "Specify label(s) for this nexthop\n"
860 "One or more labels in the range (16-1048575) separated by '/'\n"
861 "Weight to be used by the nexthop for purposes of ECMP\n"
862 "Weight value to be used\n"
863 "Specify backup nexthop indexes in another group\n"
864 "One or more indexes in the range (0-254) separated by ','\n")
866 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
872 uint8_t backups
[NEXTHOP_MAX_BACKUPS
];
875 /* Pre-parse backup string to validate */
877 lbl_ret
= nexthop_str2backups(backup_idx
, &num
, backups
);
879 vty_out(vty
, "%% Invalid backups\n");
880 return CMD_WARNING_CONFIG_FAILED
;
884 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, !!onlink
,
885 vrf_name
, label
, &lbl_ret
, weight
,
888 if (nhop
.type
== NEXTHOP_TYPE_IPV6
889 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
891 "Specified a v6 LL with no interface, rejecting\n");
892 return CMD_WARNING_CONFIG_FAILED
;
895 /* Handle label-string errors */
896 if (!legal
&& lbl_ret
< 0) {
899 vty_out(vty
, "%% Malformed label(s)\n");
903 "%% Cannot use reserved label(s) (%d-%d)\n",
904 MPLS_LABEL_RESERVED_MIN
,
905 MPLS_LABEL_RESERVED_MAX
);
909 "%% Too many labels. Enter %d or fewer\n",
913 return CMD_WARNING_CONFIG_FAILED
;
916 /* Look for an existing nexthop in the config. Note that the test
917 * here tests only some attributes - it's not a complete comparison.
918 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
919 * that represent config that may or may not be valid (yet), and
920 * actual nexthops that have been validated and parsed.
922 nh
= nhg_nh_find(&nhgc
->nhg
, &nhop
);
924 /* Always attempt to remove old config info. */
925 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
927 /* Remove any existing nexthop, for delete and replace cases. */
929 nexthop_unlink(&nhgc
->nhg
, nh
);
931 if (nhg_hooks
.del_nexthop
)
932 nhg_hooks
.del_nexthop(nhgc
, nh
);
937 /* Add/replace case: capture nexthop if valid, and capture
938 * config info always.
943 memcpy(nh
, &nhop
, sizeof(nhop
));
944 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
947 /* Save config always */
948 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
, !!onlink
,
949 label
, weight
, backup_idx
);
951 if (legal
&& nhg_hooks
.add_nexthop
)
952 nhg_hooks
.add_nexthop(nhgc
, nh
);
958 static int nexthop_group_write(struct vty
*vty
);
959 static struct cmd_node nexthop_group_node
= {
960 .name
= "nexthop-group",
961 .node
= NH_GROUP_NODE
,
962 .parent_node
= CONFIG_NODE
,
963 .prompt
= "%s(config-nh-group)# ",
964 .config_write
= nexthop_group_write
,
967 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
968 const struct nexthop
*nh
,
973 vty_out(vty
, "nexthop ");
978 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
981 case NEXTHOP_TYPE_IFINDEX
:
982 vty_out(vty
, "%s", ifname
);
984 case NEXTHOP_TYPE_IPV4
:
985 vty_out(vty
, "%pI4", &nh
->gate
.ipv4
);
987 case NEXTHOP_TYPE_IPV4_IFINDEX
:
988 vty_out(vty
, "%pI4 %s", &nh
->gate
.ipv4
, ifname
);
990 case NEXTHOP_TYPE_IPV6
:
991 vty_out(vty
, "%pI6", &nh
->gate
.ipv6
);
993 case NEXTHOP_TYPE_IPV6_IFINDEX
:
994 vty_out(vty
, "%pI6 %s", &nh
->gate
.ipv6
, ifname
);
996 case NEXTHOP_TYPE_BLACKHOLE
:
1001 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1006 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1008 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1009 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1010 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1013 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1016 mpls_label2str(nh
->nh_label
->num_labels
,
1017 nh
->nh_label
->label
,
1018 buf
, sizeof(buf
), 0);
1019 vty_out(vty
, " label %s", buf
);
1023 vty_out(vty
, " weight %u", nh
->weight
);
1025 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1026 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1028 for (i
= 1; i
< nh
->backup_num
; i
++)
1029 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1035 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1038 json_object
*json_backups
= NULL
;
1042 case NEXTHOP_TYPE_IFINDEX
:
1043 json_object_string_add(j
, "nexthop",
1044 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1046 case NEXTHOP_TYPE_IPV4
:
1047 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1049 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1050 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1051 json_object_string_add(j
, "vrfId",
1052 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1054 case NEXTHOP_TYPE_IPV6
:
1055 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1057 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1058 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1059 json_object_string_add(j
, "vrfId",
1060 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1062 case NEXTHOP_TYPE_BLACKHOLE
:
1066 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1067 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1068 json_object_string_add(j
, "targetVrf", vrf
->name
);
1071 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1074 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1075 buf
, sizeof(buf
), 0);
1076 json_object_string_add(j
, "label", buf
);
1080 json_object_int_add(j
, "weight", nh
->weight
);
1082 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1083 json_backups
= json_object_new_array();
1084 for (i
= 0; i
< nh
->backup_num
; i
++)
1085 json_object_array_add(
1087 json_object_new_int(nh
->backup_idx
[i
]));
1089 json_object_object_add(j
, "backupIdx", json_backups
);
1093 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1094 const struct nexthop_hold
*nh
)
1096 vty_out(vty
, "nexthop");
1099 vty_out(vty
, " %pSU", nh
->addr
);
1102 vty_out(vty
, " %s", nh
->intf
);
1105 vty_out(vty
, " onlink");
1108 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1111 vty_out(vty
, " label %s", nh
->labels
);
1114 vty_out(vty
, " weight %u", nh
->weight
);
1117 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1122 static int nexthop_group_write(struct vty
*vty
)
1124 struct nexthop_group_cmd
*nhgc
;
1125 struct nexthop_hold
*nh
;
1127 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1128 struct listnode
*node
;
1130 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1132 if (nhgc
->backup_list_name
[0])
1133 vty_out(vty
, " backup-group %s\n",
1134 nhgc
->backup_list_name
);
1136 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1138 nexthop_group_write_nexthop_internal(vty
, nh
);
1141 vty_out(vty
, "exit\n");
1142 vty_out(vty
, "!\n");
1148 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1150 struct nexthop_group_cmd
*nhgc
;
1151 struct nexthop_hold
*nhh
;
1153 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1154 struct listnode
*node
;
1156 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1157 struct nexthop nhop
;
1160 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1163 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1168 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1173 memcpy(nh
, &nhop
, sizeof(nhop
));
1174 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1176 if (nhg_hooks
.add_nexthop
)
1177 nhg_hooks
.add_nexthop(nhgc
, nh
);
1182 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1184 struct nexthop_group_cmd
*nhgc
;
1185 struct nexthop_hold
*nhh
;
1187 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1188 struct listnode
*node
;
1190 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1191 struct nexthop nhop
;
1194 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1197 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1202 if (nh
->vrf_id
!= vrf
->vrf_id
)
1205 _nexthop_del(&nhgc
->nhg
, nh
);
1207 if (nhg_hooks
.del_nexthop
)
1208 nhg_hooks
.del_nexthop(nhgc
, nh
);
1215 void nexthop_group_interface_state_change(struct interface
*ifp
,
1216 ifindex_t oldifindex
)
1218 struct nexthop_group_cmd
*nhgc
;
1219 struct nexthop_hold
*nhh
;
1221 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1222 struct listnode
*node
;
1225 if (if_is_up(ifp
)) {
1226 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1227 struct nexthop nhop
;
1229 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1232 switch (nhop
.type
) {
1233 case NEXTHOP_TYPE_IPV4
:
1234 case NEXTHOP_TYPE_IPV6
:
1235 case NEXTHOP_TYPE_BLACKHOLE
:
1237 case NEXTHOP_TYPE_IFINDEX
:
1238 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1239 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1242 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1247 if (ifp
->ifindex
!= nhop
.ifindex
)
1252 memcpy(nh
, &nhop
, sizeof(nhop
));
1253 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1255 if (nhg_hooks
.add_nexthop
)
1256 nhg_hooks
.add_nexthop(nhgc
, nh
);
1259 struct nexthop
*next_nh
;
1261 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1264 case NEXTHOP_TYPE_IPV4
:
1265 case NEXTHOP_TYPE_IPV6
:
1266 case NEXTHOP_TYPE_BLACKHOLE
:
1268 case NEXTHOP_TYPE_IFINDEX
:
1269 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1270 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1274 if (oldifindex
!= nh
->ifindex
)
1277 _nexthop_del(&nhgc
->nhg
, nh
);
1279 if (nhg_hooks
.del_nexthop
)
1280 nhg_hooks
.del_nexthop(nhgc
, nh
);
1288 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1290 struct nexthop_group_cmd
*nhgc
;
1292 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1293 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1297 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1298 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1299 {.completions
= NULL
}};
1301 void nexthop_group_init(void (*new)(const char *name
),
1302 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1303 const struct nexthop
*nhop
),
1304 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1305 const struct nexthop
*nhop
),
1306 void (*delete)(const char *name
))
1308 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1310 cmd_variable_handler_register(nhg_name_handlers
);
1312 install_node(&nexthop_group_node
);
1313 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1314 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1316 install_default(NH_GROUP_NODE
);
1317 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1318 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1319 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1321 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1324 nhg_hooks
.new = new;
1326 nhg_hooks
.add_nexthop
= add_nexthop
;
1328 nhg_hooks
.del_nexthop
= del_nexthop
;
1330 nhg_hooks
.delete = delete;