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 #ifndef VTYSH_EXTRACT_PL
32 #include "lib/nexthop_group_clippy.c"
35 DEFINE_MTYPE_STATIC(LIB
, NEXTHOP_GROUP
, "Nexthop Group")
38 * Internal struct used to hold nhg config strings
42 union sockunion
*addr
;
50 struct nexthop_group_hooks
{
51 void (*new)(const char *name
);
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 struct nexthop
*nexthop_exists(const struct nexthop_group
*nhg
,
136 const struct nexthop
*nh
)
138 struct nexthop
*nexthop
;
140 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
141 if (nexthop_same(nh
, nexthop
))
149 * Helper that locates a nexthop in an nhg config list. Note that
150 * this uses a specific matching / equality rule that's different from
151 * the complete match performed by 'nexthop_same()'.
153 static struct nexthop
*nhg_nh_find(const struct nexthop_group
*nhg
,
154 const struct nexthop
*nh
)
156 struct nexthop
*nexthop
;
159 /* We compare: vrf, gateway, and interface */
161 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
163 /* Compare vrf and type */
164 if (nexthop
->vrf_id
!= nh
->vrf_id
)
166 if (nexthop
->type
!= nh
->type
)
169 /* Compare gateway */
170 switch (nexthop
->type
) {
171 case NEXTHOP_TYPE_IPV4
:
172 case NEXTHOP_TYPE_IPV6
:
173 ret
= nexthop_g_addr_cmp(nexthop
->type
,
174 &nexthop
->gate
, &nh
->gate
);
178 case NEXTHOP_TYPE_IPV4_IFINDEX
:
179 case NEXTHOP_TYPE_IPV6_IFINDEX
:
180 ret
= nexthop_g_addr_cmp(nexthop
->type
,
181 &nexthop
->gate
, &nh
->gate
);
184 /* Intentional Fall-Through */
185 case NEXTHOP_TYPE_IFINDEX
:
186 if (nexthop
->ifindex
!= nh
->ifindex
)
189 case NEXTHOP_TYPE_BLACKHOLE
:
190 if (nexthop
->bh_type
!= nh
->bh_type
)
202 nexthop_group_equal_common(const struct nexthop_group
*nhg1
,
203 const struct nexthop_group
*nhg2
,
204 uint8_t (*nexthop_group_nexthop_num_func
)(
205 const struct nexthop_group
*nhg
))
216 if (nexthop_group_nexthop_num_func(nhg1
)
217 != nexthop_group_nexthop_num_func(nhg2
))
223 /* This assumes ordered */
224 bool nexthop_group_equal_no_recurse(const struct nexthop_group
*nhg1
,
225 const struct nexthop_group
*nhg2
)
227 struct nexthop
*nh1
= NULL
;
228 struct nexthop
*nh2
= NULL
;
230 if (!nexthop_group_equal_common(nhg1
, nhg2
,
231 &nexthop_group_nexthop_num_no_recurse
))
234 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
235 nh1
= nh1
->next
, nh2
= nh2
->next
) {
240 if (!nexthop_same(nh1
, nh2
))
247 /* This assumes ordered */
248 bool nexthop_group_equal(const struct nexthop_group
*nhg1
,
249 const struct nexthop_group
*nhg2
)
251 struct nexthop
*nh1
= NULL
;
252 struct nexthop
*nh2
= NULL
;
254 if (!nexthop_group_equal_common(nhg1
, nhg2
, &nexthop_group_nexthop_num
))
257 for (nh1
= nhg1
->nexthop
, nh2
= nhg2
->nexthop
; nh1
|| nh2
;
258 nh1
= nexthop_next(nh1
), nh2
= nexthop_next(nh2
)) {
263 if (!nexthop_same(nh1
, nh2
))
269 struct nexthop_group
*nexthop_group_new(void)
271 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
274 void nexthop_group_copy(struct nexthop_group
*to
,
275 const struct nexthop_group
*from
)
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_firsthop(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 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
679 const char *nhvrf_name
,
680 const union sockunion
*addr
,
681 const char *intf
, bool onlink
,
682 const char *labels
, const uint32_t weight
,
683 const char *backup_str
)
685 struct nexthop_hold
*nh
;
687 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
690 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
692 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
694 nh
->addr
= sockunion_dup(addr
);
696 nh
->labels
= XSTRDUP(MTYPE_TMP
, labels
);
703 nh
->backup_str
= XSTRDUP(MTYPE_TMP
, backup_str
);
705 listnode_add_sort(nhgc
->nhg_list
, nh
);
709 * Remove config info about a nexthop from group 'nhgc'. Note that we
710 * use only a subset of the available attributes here to determine
712 * Note that this doesn't change the list of nexthops, only the config
715 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
716 const char *nhvrf_name
,
717 const union sockunion
*addr
,
720 struct nexthop_hold
*nh
;
721 struct listnode
*node
;
723 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
724 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0
725 && nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0
726 && nhgc_cmp_helper(intf
, nh
->intf
) == 0)
731 * Something has gone seriously wrong, fail gracefully
736 list_delete_node(nhgc
->nhg_list
, node
);
741 * Parse the config strings we support for a single nexthop. This gets used
742 * in a couple of different ways, and we distinguish between transient
743 * failures - such as a still-unprocessed interface - and fatal errors
744 * from label-string parsing.
746 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
747 const union sockunion
*addr
,
748 const char *intf
, bool onlink
,
749 const char *name
, const char *labels
,
750 int *lbl_ret
, uint32_t weight
,
751 const char *backup_str
)
757 memset(nhop
, 0, sizeof(*nhop
));
760 vrf
= vrf_lookup_by_name(name
);
762 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
767 nhop
->vrf_id
= vrf
->vrf_id
;
770 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
771 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
776 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_ONLINK
);
779 if (addr
->sa
.sa_family
== AF_INET
) {
780 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
782 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
784 nhop
->type
= NEXTHOP_TYPE_IPV4
;
786 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
788 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
790 nhop
->type
= NEXTHOP_TYPE_IPV6
;
793 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
797 mpls_label_t larray
[MPLS_MAX_LABELS
];
799 ret
= mpls_str2label(labels
, &num
, larray
);
801 /* Return label parse result */
808 nexthop_add_labels(nhop
, ZEBRA_LSP_NONE
,
812 nhop
->weight
= weight
;
815 /* Parse backup indexes */
816 ret
= nexthop_str2backups(backup_str
,
817 &num
, nhop
->backup_idx
);
819 SET_FLAG(nhop
->flags
, NEXTHOP_FLAG_HAS_BACKUP
);
820 nhop
->backup_num
= num
;
829 * Wrapper to parse the strings in a 'nexthop_hold'
831 static bool nexthop_group_parse_nhh(struct nexthop
*nhop
,
832 const struct nexthop_hold
*nhh
)
834 return (nexthop_group_parse_nexthop(
835 nhop
, nhh
->addr
, nhh
->intf
, nhh
->onlink
, nhh
->nhvrf_name
,
836 nhh
->labels
, NULL
, nhh
->weight
, nhh
->backup_str
));
839 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
842 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
846 nexthop-vrf NAME$vrf_name \
852 "Specify one of the nexthops in this ECMP group\n"
856 "Treat nexthop as directly attached to the interface\n"
858 "If the nexthop is in a different vrf tell us\n"
859 "The nexthop-vrf Name\n"
860 "Specify label(s) for this nexthop\n"
861 "One or more labels in the range (16-1048575) separated by '/'\n"
862 "Weight to be used by the nexthop for purposes of ECMP\n"
863 "Weight value to be used\n"
864 "Specify backup nexthop indexes in another group\n"
865 "One or more indexes in the range (0-254) separated by ','\n")
867 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
873 uint8_t backups
[NEXTHOP_MAX_BACKUPS
];
876 /* Pre-parse backup string to validate */
878 lbl_ret
= nexthop_str2backups(backup_idx
, &num
, backups
);
880 vty_out(vty
, "%% Invalid backups\n");
881 return CMD_WARNING_CONFIG_FAILED
;
885 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, !!onlink
,
886 vrf_name
, label
, &lbl_ret
, weight
,
889 if (nhop
.type
== NEXTHOP_TYPE_IPV6
890 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
892 "Specified a v6 LL with no interface, rejecting\n");
893 return CMD_WARNING_CONFIG_FAILED
;
896 /* Handle label-string errors */
897 if (!legal
&& lbl_ret
< 0) {
900 vty_out(vty
, "%% Malformed label(s)\n");
904 "%% Cannot use reserved label(s) (%d-%d)\n",
905 MPLS_LABEL_RESERVED_MIN
,
906 MPLS_LABEL_RESERVED_MAX
);
910 "%% Too many labels. Enter %d or fewer\n",
914 return CMD_WARNING_CONFIG_FAILED
;
917 /* Look for an existing nexthop in the config. Note that the test
918 * here tests only some attributes - it's not a complete comparison.
919 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
920 * that represent config that may or may not be valid (yet), and
921 * actual nexthops that have been validated and parsed.
923 nh
= nhg_nh_find(&nhgc
->nhg
, &nhop
);
925 /* Always attempt to remove old config info. */
926 nexthop_group_unsave_nhop(nhgc
, vrf_name
, addr
, intf
);
928 /* Remove any existing nexthop, for delete and replace cases. */
930 nexthop_unlink(&nhgc
->nhg
, nh
);
932 if (nhg_hooks
.del_nexthop
)
933 nhg_hooks
.del_nexthop(nhgc
, nh
);
938 /* Add/replace case: capture nexthop if valid, and capture
939 * config info always.
944 memcpy(nh
, &nhop
, sizeof(nhop
));
945 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
948 /* Save config always */
949 nexthop_group_save_nhop(nhgc
, vrf_name
, addr
, intf
, !!onlink
,
950 label
, weight
, backup_idx
);
952 if (legal
&& nhg_hooks
.add_nexthop
)
953 nhg_hooks
.add_nexthop(nhgc
, nh
);
957 struct interface
*ifp
= if_lookup_by_name_all_vrf(intf
);
960 ifp
->configured
= true;
965 static int nexthop_group_write(struct vty
*vty
);
966 static struct cmd_node nexthop_group_node
= {
967 .name
= "nexthop-group",
968 .node
= NH_GROUP_NODE
,
969 .parent_node
= CONFIG_NODE
,
970 .prompt
= "%s(config-nh-group)# ",
971 .config_write
= nexthop_group_write
,
974 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
975 const struct nexthop
*nh
,
981 vty_out(vty
, "nexthop ");
986 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
989 case NEXTHOP_TYPE_IFINDEX
:
990 vty_out(vty
, "%s", ifname
);
992 case NEXTHOP_TYPE_IPV4
:
993 vty_out(vty
, "%s", inet_ntoa(nh
->gate
.ipv4
));
995 case NEXTHOP_TYPE_IPV4_IFINDEX
:
996 vty_out(vty
, "%s %s", inet_ntoa(nh
->gate
.ipv4
), ifname
);
998 case NEXTHOP_TYPE_IPV6
:
1000 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)));
1002 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1003 vty_out(vty
, "%s %s",
1004 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)),
1007 case NEXTHOP_TYPE_BLACKHOLE
:
1012 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1017 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1019 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1020 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1021 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1024 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1027 mpls_label2str(nh
->nh_label
->num_labels
,
1028 nh
->nh_label
->label
,
1029 buf
, sizeof(buf
), 0);
1030 vty_out(vty
, " label %s", buf
);
1034 vty_out(vty
, " weight %u", nh
->weight
);
1036 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1037 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1039 for (i
= 1; i
< nh
->backup_num
; i
++)
1040 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1046 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1050 json_object
*json_backups
= NULL
;
1054 case NEXTHOP_TYPE_IFINDEX
:
1055 json_object_string_add(j
, "nexthop",
1056 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1058 case NEXTHOP_TYPE_IPV4
:
1059 json_object_string_add(j
, "nexthop", inet_ntoa(nh
->gate
.ipv4
));
1061 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1062 json_object_string_add(j
, "nexthop", inet_ntoa(nh
->gate
.ipv4
));
1063 json_object_string_add(j
, "vrfId",
1064 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1066 case NEXTHOP_TYPE_IPV6
:
1067 json_object_string_add(
1069 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)));
1071 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1072 json_object_string_add(
1074 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)));
1075 json_object_string_add(j
, "vrfId",
1076 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1078 case NEXTHOP_TYPE_BLACKHOLE
:
1082 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1083 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1084 json_object_string_add(j
, "targetVrf", vrf
->name
);
1087 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1090 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1091 buf
, sizeof(buf
), 0);
1092 json_object_string_add(j
, "label", buf
);
1096 json_object_int_add(j
, "weight", nh
->weight
);
1098 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1099 json_backups
= json_object_new_array();
1100 for (i
= 0; i
< nh
->backup_num
; i
++)
1101 json_object_array_add(
1103 json_object_new_int(nh
->backup_idx
[i
]));
1105 json_object_object_add(j
, "backupIdx", json_backups
);
1109 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1110 const struct nexthop_hold
*nh
)
1114 vty_out(vty
, "nexthop");
1117 vty_out(vty
, " %s", sockunion2str(nh
->addr
, buf
, sizeof(buf
)));
1120 vty_out(vty
, " %s", nh
->intf
);
1123 vty_out(vty
, " onlink");
1126 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1129 vty_out(vty
, " label %s", nh
->labels
);
1132 vty_out(vty
, " weight %u", nh
->weight
);
1135 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1140 static int nexthop_group_write(struct vty
*vty
)
1142 struct nexthop_group_cmd
*nhgc
;
1143 struct nexthop_hold
*nh
;
1145 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1146 struct listnode
*node
;
1148 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1150 if (nhgc
->backup_list_name
[0])
1151 vty_out(vty
, " backup-group %s\n",
1152 nhgc
->backup_list_name
);
1154 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1156 nexthop_group_write_nexthop_internal(vty
, nh
);
1159 vty_out(vty
, "!\n");
1165 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1167 struct nexthop_group_cmd
*nhgc
;
1168 struct nexthop_hold
*nhh
;
1170 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1171 struct listnode
*node
;
1173 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1174 struct nexthop nhop
;
1177 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1180 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1185 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1190 memcpy(nh
, &nhop
, sizeof(nhop
));
1191 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1193 if (nhg_hooks
.add_nexthop
)
1194 nhg_hooks
.add_nexthop(nhgc
, nh
);
1199 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1201 struct nexthop_group_cmd
*nhgc
;
1202 struct nexthop_hold
*nhh
;
1204 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1205 struct listnode
*node
;
1207 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1208 struct nexthop nhop
;
1211 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1214 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1219 if (nh
->vrf_id
!= vrf
->vrf_id
)
1222 _nexthop_del(&nhgc
->nhg
, nh
);
1224 if (nhg_hooks
.del_nexthop
)
1225 nhg_hooks
.del_nexthop(nhgc
, nh
);
1232 void nexthop_group_interface_state_change(struct interface
*ifp
,
1233 ifindex_t oldifindex
)
1235 struct nexthop_group_cmd
*nhgc
;
1236 struct nexthop_hold
*nhh
;
1238 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1239 struct listnode
*node
;
1242 if (if_is_up(ifp
)) {
1243 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1244 struct nexthop nhop
;
1246 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1249 switch (nhop
.type
) {
1250 case NEXTHOP_TYPE_IPV4
:
1251 case NEXTHOP_TYPE_IPV6
:
1252 case NEXTHOP_TYPE_BLACKHOLE
:
1254 case NEXTHOP_TYPE_IFINDEX
:
1255 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1256 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1259 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1264 if (ifp
->ifindex
!= nhop
.ifindex
)
1267 ifp
->configured
= true;
1270 memcpy(nh
, &nhop
, sizeof(nhop
));
1271 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1273 if (nhg_hooks
.add_nexthop
)
1274 nhg_hooks
.add_nexthop(nhgc
, nh
);
1277 struct nexthop
*next_nh
;
1279 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1282 case NEXTHOP_TYPE_IPV4
:
1283 case NEXTHOP_TYPE_IPV6
:
1284 case NEXTHOP_TYPE_BLACKHOLE
:
1286 case NEXTHOP_TYPE_IFINDEX
:
1287 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1288 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1292 if (oldifindex
!= nh
->ifindex
)
1295 _nexthop_del(&nhgc
->nhg
, nh
);
1297 if (nhg_hooks
.del_nexthop
)
1298 nhg_hooks
.del_nexthop(nhgc
, nh
);
1306 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1308 struct nexthop_group_cmd
*nhgc
;
1310 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1311 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1315 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1316 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1317 {.completions
= NULL
}};
1319 void nexthop_group_init(void (*new)(const char *name
),
1320 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1321 const struct nexthop
*nhop
),
1322 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1323 const struct nexthop
*nhop
),
1324 void (*delete)(const char *name
))
1326 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1328 cmd_variable_handler_register(nhg_name_handlers
);
1330 install_node(&nexthop_group_node
);
1331 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1332 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1334 install_default(NH_GROUP_NODE
);
1335 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1336 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1337 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1339 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1342 nhg_hooks
.new = new;
1344 nhg_hooks
.add_nexthop
= add_nexthop
;
1346 nhg_hooks
.del_nexthop
= del_nexthop
;
1348 nhg_hooks
.delete = delete;