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
)
1099 vty_out(vty
, "nexthop");
1102 vty_out(vty
, " %s", sockunion2str(nh
->addr
, buf
, sizeof(buf
)));
1105 vty_out(vty
, " %s", nh
->intf
);
1108 vty_out(vty
, " onlink");
1111 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
1114 vty_out(vty
, " label %s", nh
->labels
);
1117 vty_out(vty
, " weight %u", nh
->weight
);
1120 vty_out(vty
, " backup-idx %s", nh
->backup_str
);
1125 static int nexthop_group_write(struct vty
*vty
)
1127 struct nexthop_group_cmd
*nhgc
;
1128 struct nexthop_hold
*nh
;
1130 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1131 struct listnode
*node
;
1133 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
1135 if (nhgc
->backup_list_name
[0])
1136 vty_out(vty
, " backup-group %s\n",
1137 nhgc
->backup_list_name
);
1139 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
1141 nexthop_group_write_nexthop_internal(vty
, nh
);
1144 vty_out(vty
, "exit\n");
1145 vty_out(vty
, "!\n");
1151 void nexthop_group_enable_vrf(struct vrf
*vrf
)
1153 struct nexthop_group_cmd
*nhgc
;
1154 struct nexthop_hold
*nhh
;
1156 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1157 struct listnode
*node
;
1159 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1160 struct nexthop nhop
;
1163 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1166 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1171 if (nhop
.vrf_id
!= vrf
->vrf_id
)
1176 memcpy(nh
, &nhop
, sizeof(nhop
));
1177 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1179 if (nhg_hooks
.add_nexthop
)
1180 nhg_hooks
.add_nexthop(nhgc
, nh
);
1185 void nexthop_group_disable_vrf(struct vrf
*vrf
)
1187 struct nexthop_group_cmd
*nhgc
;
1188 struct nexthop_hold
*nhh
;
1190 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1191 struct listnode
*node
;
1193 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1194 struct nexthop nhop
;
1197 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1200 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1205 if (nh
->vrf_id
!= vrf
->vrf_id
)
1208 _nexthop_del(&nhgc
->nhg
, nh
);
1210 if (nhg_hooks
.del_nexthop
)
1211 nhg_hooks
.del_nexthop(nhgc
, nh
);
1218 void nexthop_group_interface_state_change(struct interface
*ifp
,
1219 ifindex_t oldifindex
)
1221 struct nexthop_group_cmd
*nhgc
;
1222 struct nexthop_hold
*nhh
;
1224 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1225 struct listnode
*node
;
1228 if (if_is_up(ifp
)) {
1229 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
1230 struct nexthop nhop
;
1232 if (!nexthop_group_parse_nhh(&nhop
, nhh
))
1235 switch (nhop
.type
) {
1236 case NEXTHOP_TYPE_IPV4
:
1237 case NEXTHOP_TYPE_IPV6
:
1238 case NEXTHOP_TYPE_BLACKHOLE
:
1240 case NEXTHOP_TYPE_IFINDEX
:
1241 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1242 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1245 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
1250 if (ifp
->ifindex
!= nhop
.ifindex
)
1255 memcpy(nh
, &nhop
, sizeof(nhop
));
1256 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
1258 if (nhg_hooks
.add_nexthop
)
1259 nhg_hooks
.add_nexthop(nhgc
, nh
);
1262 struct nexthop
*next_nh
;
1264 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
1267 case NEXTHOP_TYPE_IPV4
:
1268 case NEXTHOP_TYPE_IPV6
:
1269 case NEXTHOP_TYPE_BLACKHOLE
:
1271 case NEXTHOP_TYPE_IFINDEX
:
1272 case NEXTHOP_TYPE_IPV4_IFINDEX
:
1273 case NEXTHOP_TYPE_IPV6_IFINDEX
:
1277 if (oldifindex
!= nh
->ifindex
)
1280 _nexthop_del(&nhgc
->nhg
, nh
);
1282 if (nhg_hooks
.del_nexthop
)
1283 nhg_hooks
.del_nexthop(nhgc
, nh
);
1291 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
1293 struct nexthop_group_cmd
*nhgc
;
1295 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
1296 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
1300 static const struct cmd_variable_handler nhg_name_handlers
[] = {
1301 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
1302 {.completions
= NULL
}};
1304 void nexthop_group_init(void (*new)(const char *name
),
1305 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1306 const struct nexthop
*nhop
),
1307 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
1308 const struct nexthop
*nhop
),
1309 void (*delete)(const char *name
))
1311 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
1313 cmd_variable_handler_register(nhg_name_handlers
);
1315 install_node(&nexthop_group_node
);
1316 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
1317 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
1319 install_default(NH_GROUP_NODE
);
1320 install_element(NH_GROUP_NODE
, &nexthop_group_backup_cmd
);
1321 install_element(NH_GROUP_NODE
, &no_nexthop_group_backup_cmd
);
1322 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
1324 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
1327 nhg_hooks
.new = new;
1329 nhg_hooks
.add_nexthop
= add_nexthop
;
1331 nhg_hooks
.del_nexthop
= del_nexthop
;
1333 nhg_hooks
.delete = delete;