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(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
);
959 static int nexthop_group_write(struct vty
*vty
);
960 static struct cmd_node nexthop_group_node
= {
961 .name
= "nexthop-group",
962 .node
= NH_GROUP_NODE
,
963 .parent_node
= CONFIG_NODE
,
964 .prompt
= "%s(config-nh-group)# ",
965 .config_write
= nexthop_group_write
,
968 void nexthop_group_write_nexthop_simple(struct vty
*vty
,
969 const struct nexthop
*nh
,
974 vty_out(vty
, "nexthop ");
979 ifname
= (char *)ifindex2ifname(nh
->ifindex
, nh
->vrf_id
);
982 case NEXTHOP_TYPE_IFINDEX
:
983 vty_out(vty
, "%s", ifname
);
985 case NEXTHOP_TYPE_IPV4
:
986 vty_out(vty
, "%pI4", &nh
->gate
.ipv4
);
988 case NEXTHOP_TYPE_IPV4_IFINDEX
:
989 vty_out(vty
, "%pI4 %s", &nh
->gate
.ipv4
, ifname
);
991 case NEXTHOP_TYPE_IPV6
:
992 vty_out(vty
, "%pI6", &nh
->gate
.ipv6
);
994 case NEXTHOP_TYPE_IPV6_IFINDEX
:
995 vty_out(vty
, "%pI6 %s", &nh
->gate
.ipv6
, ifname
);
997 case NEXTHOP_TYPE_BLACKHOLE
:
1002 void nexthop_group_write_nexthop(struct vty
*vty
, const struct nexthop
*nh
)
1007 nexthop_group_write_nexthop_simple(vty
, nh
, NULL
);
1009 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1010 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1011 vty_out(vty
, " nexthop-vrf %s", VRF_LOGNAME(vrf
));
1014 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1017 mpls_label2str(nh
->nh_label
->num_labels
,
1018 nh
->nh_label
->label
,
1019 buf
, sizeof(buf
), 0);
1020 vty_out(vty
, " label %s", buf
);
1024 vty_out(vty
, " weight %u", nh
->weight
);
1026 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1027 vty_out(vty
, " backup-idx %d", nh
->backup_idx
[0]);
1029 for (i
= 1; i
< nh
->backup_num
; i
++)
1030 vty_out(vty
, ",%d", nh
->backup_idx
[i
]);
1036 void nexthop_group_json_nexthop(json_object
*j
, const struct nexthop
*nh
)
1039 json_object
*json_backups
= NULL
;
1043 case NEXTHOP_TYPE_IFINDEX
:
1044 json_object_string_add(j
, "nexthop",
1045 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1047 case NEXTHOP_TYPE_IPV4
:
1048 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1050 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1051 json_object_string_addf(j
, "nexthop", "%pI4", &nh
->gate
.ipv4
);
1052 json_object_string_add(j
, "vrfId",
1053 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1055 case NEXTHOP_TYPE_IPV6
:
1056 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1058 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1059 json_object_string_addf(j
, "nexthop", "%pI6", &nh
->gate
.ipv6
);
1060 json_object_string_add(j
, "vrfId",
1061 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
1063 case NEXTHOP_TYPE_BLACKHOLE
:
1067 if (nh
->vrf_id
!= VRF_DEFAULT
) {
1068 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
1069 json_object_string_add(j
, "targetVrf", vrf
->name
);
1072 if (nh
->nh_label
&& nh
->nh_label
->num_labels
> 0) {
1075 mpls_label2str(nh
->nh_label
->num_labels
, nh
->nh_label
->label
,
1076 buf
, sizeof(buf
), 0);
1077 json_object_string_add(j
, "label", buf
);
1081 json_object_int_add(j
, "weight", nh
->weight
);
1083 if (CHECK_FLAG(nh
->flags
, NEXTHOP_FLAG_HAS_BACKUP
)) {
1084 json_backups
= json_object_new_array();
1085 for (i
= 0; i
< nh
->backup_num
; i
++)
1086 json_object_array_add(
1088 json_object_new_int(nh
->backup_idx
[i
]));
1090 json_object_object_add(j
, "backupIdx", json_backups
);
1094 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
1095 const struct nexthop_hold
*nh
)
1097 vty_out(vty
, "nexthop");
1100 vty_out(vty
, " %pSU", nh
->addr
);
1103 vty_out(vty
, " %s", nh
->intf
);
1106 vty_out(vty
, " onlink");
1109 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1112 vty_out(vty
, " label %s", nh
->labels
);
1115 vty_out(vty
, " weight %u", nh
->weight
);
1118 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1123 static int nexthop_group_write(struct vty
*vty
)
1125 struct nexthop_group_cmd
*nhgc
;
1126 struct nexthop_hold
*nh
;
1128 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1129 struct listnode
*node
;
1131 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1133 if (nhgc
->backup_list_name
[0])
1134 vty_out(vty
, " backup-group %s\n",
1135 nhgc
->backup_list_name
);
1137 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1139 nexthop_group_write_nexthop_internal(vty
, nh
);
1142 vty_out(vty
, "exit\n");
1143 vty_out(vty
, "!\n");
1149 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1151 struct nexthop_group_cmd
*nhgc
;
1152 struct nexthop_hold
*nhh
;
1154 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1155 struct listnode
*node
;
1157 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1158 struct nexthop nhop
;
1161 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1164 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1169 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1174 memcpy(nh
, &nhop
, sizeof(nhop
));
1175 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1177 if (nhg_hooks
.add_nexthop
)
1178 nhg_hooks
.add_nexthop(nhgc
, nh
);
1183 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1185 struct nexthop_group_cmd
*nhgc
;
1186 struct nexthop_hold
*nhh
;
1188 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1189 struct listnode
*node
;
1191 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1192 struct nexthop nhop
;
1195 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1198 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1203 if (nh
->vrf_id
!= vrf
->vrf_id
)
1206 _nexthop_del(&nhgc
->nhg
, nh
);
1208 if (nhg_hooks
.del_nexthop
)
1209 nhg_hooks
.del_nexthop(nhgc
, nh
);
1216 void nexthop_group_interface_state_change(struct interface
*ifp
,
1217 ifindex_t oldifindex
)
1219 struct nexthop_group_cmd
*nhgc
;
1220 struct nexthop_hold
*nhh
;
1222 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1223 struct listnode
*node
;
1226 if (if_is_up(ifp
)) {
1227 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1228 struct nexthop nhop
;
1230 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1233 switch (nhop
.type
) {
1234 case NEXTHOP_TYPE_IPV4
:
1235 case NEXTHOP_TYPE_IPV6
:
1236 case NEXTHOP_TYPE_BLACKHOLE
:
1238 case NEXTHOP_TYPE_IFINDEX
:
1239 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1240 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1243 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1248 if (ifp
->ifindex
!= nhop
.ifindex
)
1253 memcpy(nh
, &nhop
, sizeof(nhop
));
1254 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1256 if (nhg_hooks
.add_nexthop
)
1257 nhg_hooks
.add_nexthop(nhgc
, nh
);
1260 struct nexthop
*next_nh
;
1262 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1265 case NEXTHOP_TYPE_IPV4
:
1266 case NEXTHOP_TYPE_IPV6
:
1267 case NEXTHOP_TYPE_BLACKHOLE
:
1269 case NEXTHOP_TYPE_IFINDEX
:
1270 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1271 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1275 if (oldifindex
!= nh
->ifindex
)
1278 _nexthop_del(&nhgc
->nhg
, nh
);
1280 if (nhg_hooks
.del_nexthop
)
1281 nhg_hooks
.del_nexthop(nhgc
, nh
);
1289 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1291 struct nexthop_group_cmd
*nhgc
;
1293 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1294 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1298 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1299 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1300 {.completions
= NULL
}};
1302 void nexthop_group_init(void (*new)(const char *name
),
1303 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1304 const struct nexthop
*nhop
),
1305 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1306 const struct nexthop
*nhop
),
1307 void (*delete)(const char *name
))
1309 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1311 cmd_variable_handler_register(nhg_name_handlers
);
1313 install_node(&nexthop_group_node
);
1314 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1315 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1317 install_default(NH_GROUP_NODE
);
1318 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1319 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1320 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1322 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1325 nhg_hooks
.new = new;
1327 nhg_hooks
.add_nexthop
= add_nexthop
;
1329 nhg_hooks
.del_nexthop
= del_nexthop
;
1331 nhg_hooks
.delete = delete;