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")
37 struct nexthop_group_hooks
{
38 void (*new)(const char *name
);
39 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
40 const struct nexthop
*nhop
);
41 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
42 const struct nexthop
*nhop
);
43 void (*delete)(const char *name
);
46 static struct nexthop_group_hooks nhg_hooks
;
49 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
50 const struct nexthop_group_cmd
*nhgc2
);
51 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
52 nexthop_group_cmd_compare
)
54 static struct nhgc_entry_head nhgc_entries
;
57 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
58 const struct nexthop_group_cmd
*nhgc2
)
60 return strcmp(nhgc1
->name
, nhgc2
->name
);
63 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
68 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
74 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
79 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
80 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
87 struct nexthop
*nexthop_exists(struct nexthop_group
*nhg
, struct nexthop
*nh
)
89 struct nexthop
*nexthop
;
91 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
92 if (nexthop_same(nh
, nexthop
))
99 struct nexthop_group
*nexthop_group_new(void)
101 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
104 void nexthop_group_copy(struct nexthop_group
*to
, struct nexthop_group
*from
)
106 /* Copy everything, including recursive info */
107 copy_nexthops(&to
->nexthop
, from
->nexthop
, NULL
);
110 void nexthop_group_delete(struct nexthop_group
**nhg
)
112 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
115 /* Add nexthop to the end of a nexthop list. */
116 void _nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
118 struct nexthop
*last
;
120 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
123 last
->next
= nexthop
;
126 nexthop
->prev
= last
;
129 void _nexthop_group_add_sorted(struct nexthop_group
*nhg
,
130 struct nexthop
*nexthop
)
132 struct nexthop
*position
, *prev
;
134 for (position
= nhg
->nexthop
, prev
= NULL
; position
;
135 prev
= position
, position
= position
->next
) {
136 if (nexthop_cmp(position
, nexthop
) > 0) {
137 nexthop
->next
= position
;
138 nexthop
->prev
= prev
;
141 nexthop
->prev
->next
= nexthop
;
143 nhg
->nexthop
= nexthop
;
145 position
->prev
= nexthop
;
150 nexthop
->prev
= prev
;
152 prev
->next
= nexthop
;
154 nhg
->nexthop
= nexthop
;
157 /* Delete nexthop from a nexthop list. */
158 void _nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
160 struct nexthop
*nexthop
;
162 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
163 if (nexthop_same(nh
, nexthop
))
170 nexthop
->prev
->next
= nexthop
->next
;
172 nhg
->nexthop
= nexthop
->next
;
175 nexthop
->next
->prev
= nexthop
->prev
;
181 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
182 struct nexthop
*rparent
)
184 struct nexthop
*nexthop
;
185 const struct nexthop
*nh1
;
187 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
188 nexthop
= nexthop_dup(nh1
, rparent
);
189 _nexthop_add(tnh
, nexthop
);
191 if (CHECK_FLAG(nh1
->flags
, NEXTHOP_FLAG_RECURSIVE
))
192 copy_nexthops(&nexthop
->resolved
, nh1
->resolved
,
197 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
203 * We are not interested in hashing over any recursively
206 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
207 key
= jhash_1word(nexthop_hash(nh
), key
);
212 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
214 struct nexthop
*nexthop
;
216 nexthop
= nhgc
->nhg
.nexthop
;
218 struct nexthop
*next
= nexthop_next(nexthop
);
220 _nexthop_del(&nhgc
->nhg
, nexthop
);
221 if (nhg_hooks
.del_nexthop
)
222 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
224 nexthop_free(nexthop
);
230 struct nexthop_group_cmd
*nhgc_find(const char *name
)
232 struct nexthop_group_cmd find
;
234 strlcpy(find
.name
, name
, sizeof(find
.name
));
236 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
239 static int nhgc_cmp_helper(const char *a
, const char *b
)
253 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
264 return sockunion_cmp(a
, b
);
267 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
271 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
275 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
279 return nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
282 static void nhgl_delete(struct nexthop_hold
*nh
)
284 XFREE(MTYPE_TMP
, nh
->intf
);
286 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
289 sockunion_free(nh
->addr
);
291 XFREE(MTYPE_TMP
, nh
);
294 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
296 struct nexthop_group_cmd
*nhgc
;
298 nhgc
= nhgc_find(name
);
300 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
301 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
303 QOBJ_REG(nhgc
, nexthop_group_cmd
);
304 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
306 nhgc
->nhg_list
= list_new();
307 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
308 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
317 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
319 nhgc_delete_nexthops(nhgc
);
321 if (nhg_hooks
.delete)
322 nhg_hooks
.delete(nhgc
->name
);
324 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
326 list_delete(&nhgc
->nhg_list
);
328 XFREE(MTYPE_TMP
, nhgc
);
331 DEFINE_QOBJ_TYPE(nexthop_group_cmd
)
333 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NHGNAME",
334 "Enter into the nexthop-group submode\n"
335 "Specify the NAME of the nexthop-group\n")
337 const char *nhg_name
= argv
[1]->arg
;
338 struct nexthop_group_cmd
*nhgc
= NULL
;
340 nhgc
= nhgc_get(nhg_name
);
341 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
346 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NHGNAME",
348 "Delete the nexthop-group\n"
349 "Specify the NAME of the nexthop-group\n")
351 const char *nhg_name
= argv
[2]->arg
;
352 struct nexthop_group_cmd
*nhgc
= NULL
;
354 nhgc
= nhgc_find(nhg_name
);
361 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
362 const char *nhvrf_name
,
363 const union sockunion
*addr
,
366 struct nexthop_hold
*nh
;
368 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
371 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
373 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
375 nh
->addr
= sockunion_dup(addr
);
377 listnode_add_sort(nhgc
->nhg_list
, nh
);
380 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
381 const char *nhvrf_name
,
382 const union sockunion
*addr
,
385 struct nexthop_hold
*nh
;
386 struct listnode
*node
;
388 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
389 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0 &&
390 nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0 &&
391 nhgc_cmp_helper(intf
, nh
->intf
) == 0)
396 * Something has gone seriously wrong, fail gracefully
401 list_delete_node(nhgc
->nhg_list
, node
);
405 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
406 const union sockunion
*addr
,
407 const char *intf
, const char *name
)
411 memset(nhop
, 0, sizeof(*nhop
));
414 vrf
= vrf_lookup_by_name(name
);
416 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
421 nhop
->vrf_id
= vrf
->vrf_id
;
424 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
425 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
430 if (addr
->sa
.sa_family
== AF_INET
) {
431 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
433 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
435 nhop
->type
= NEXTHOP_TYPE_IPV4
;
437 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
439 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
441 nhop
->type
= NEXTHOP_TYPE_IPV6
;
444 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
449 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
452 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
455 [nexthop-vrf NAME$name]",
457 "Specify one of the nexthops in this ECMP group\n"
462 "If the nexthop is in a different vrf tell us\n"
463 "The nexthop-vrf Name\n")
465 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
470 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, name
);
472 if (nhop
.type
== NEXTHOP_TYPE_IPV6
473 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
475 "Specified a v6 LL with no interface, rejecting\n");
476 return CMD_WARNING_CONFIG_FAILED
;
479 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
482 nexthop_group_unsave_nhop(nhgc
, name
, addr
, intf
);
484 _nexthop_del(&nhgc
->nhg
, nh
);
486 if (nhg_hooks
.del_nexthop
)
487 nhg_hooks
.del_nexthop(nhgc
, nh
);
492 /* must be adding new nexthop since !no and !nexthop_exists */
496 memcpy(nh
, &nhop
, sizeof(nhop
));
497 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
500 nexthop_group_save_nhop(nhgc
, name
, addr
, intf
);
502 if (legal
&& nhg_hooks
.add_nexthop
)
503 nhg_hooks
.add_nexthop(nhgc
, nh
);
509 struct cmd_node nexthop_group_node
= {
511 "%s(config-nh-group)# ",
515 void nexthop_group_write_nexthop(struct vty
*vty
, struct nexthop
*nh
)
520 vty_out(vty
, "nexthop ");
523 case NEXTHOP_TYPE_IFINDEX
:
524 vty_out(vty
, "%s", ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
526 case NEXTHOP_TYPE_IPV4
:
527 vty_out(vty
, "%s", inet_ntoa(nh
->gate
.ipv4
));
529 case NEXTHOP_TYPE_IPV4_IFINDEX
:
530 vty_out(vty
, "%s %s", inet_ntoa(nh
->gate
.ipv4
),
531 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
533 case NEXTHOP_TYPE_IPV6
:
535 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)));
537 case NEXTHOP_TYPE_IPV6_IFINDEX
:
538 vty_out(vty
, "%s %s",
539 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)),
540 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
542 case NEXTHOP_TYPE_BLACKHOLE
:
546 if (nh
->vrf_id
!= VRF_DEFAULT
) {
547 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
548 vty_out(vty
, " nexthop-vrf %s", vrf
->name
);
553 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
554 struct nexthop_hold
*nh
)
558 vty_out(vty
, "nexthop");
561 vty_out(vty
, " %s", sockunion2str(nh
->addr
, buf
, sizeof(buf
)));
564 vty_out(vty
, " %s", nh
->intf
);
567 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
572 static int nexthop_group_write(struct vty
*vty
)
574 struct nexthop_group_cmd
*nhgc
;
575 struct nexthop_hold
*nh
;
577 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
578 struct listnode
*node
;
580 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
582 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
584 nexthop_group_write_nexthop_internal(vty
, nh
);
593 void nexthop_group_enable_vrf(struct vrf
*vrf
)
595 struct nexthop_group_cmd
*nhgc
;
596 struct nexthop_hold
*nhh
;
598 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
599 struct listnode
*node
;
601 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
605 if (!nexthop_group_parse_nexthop(&nhop
, nhh
->addr
,
610 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
615 if (nhop
.vrf_id
!= vrf
->vrf_id
)
620 memcpy(nh
, &nhop
, sizeof(nhop
));
621 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
623 if (nhg_hooks
.add_nexthop
)
624 nhg_hooks
.add_nexthop(nhgc
, nh
);
629 void nexthop_group_disable_vrf(struct vrf
*vrf
)
631 struct nexthop_group_cmd
*nhgc
;
632 struct nexthop_hold
*nhh
;
634 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
635 struct listnode
*node
;
637 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
641 if (!nexthop_group_parse_nexthop(&nhop
, nhh
->addr
,
646 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
651 if (nh
->vrf_id
!= vrf
->vrf_id
)
654 _nexthop_del(&nhgc
->nhg
, nh
);
656 if (nhg_hooks
.del_nexthop
)
657 nhg_hooks
.del_nexthop(nhgc
, nh
);
664 void nexthop_group_interface_state_change(struct interface
*ifp
,
665 ifindex_t oldifindex
)
667 struct nexthop_group_cmd
*nhgc
;
668 struct nexthop_hold
*nhh
;
670 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
671 struct listnode
*node
;
675 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
678 if (!nexthop_group_parse_nexthop(
679 &nhop
, nhh
->addr
, nhh
->intf
,
684 case NEXTHOP_TYPE_IPV4
:
685 case NEXTHOP_TYPE_IPV6
:
686 case NEXTHOP_TYPE_BLACKHOLE
:
688 case NEXTHOP_TYPE_IFINDEX
:
689 case NEXTHOP_TYPE_IPV4_IFINDEX
:
690 case NEXTHOP_TYPE_IPV6_IFINDEX
:
693 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
698 if (ifp
->ifindex
!= nhop
.ifindex
)
703 memcpy(nh
, &nhop
, sizeof(nhop
));
704 _nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
706 if (nhg_hooks
.add_nexthop
)
707 nhg_hooks
.add_nexthop(nhgc
, nh
);
710 struct nexthop
*next_nh
;
712 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
715 case NEXTHOP_TYPE_IPV4
:
716 case NEXTHOP_TYPE_IPV6
:
717 case NEXTHOP_TYPE_BLACKHOLE
:
719 case NEXTHOP_TYPE_IFINDEX
:
720 case NEXTHOP_TYPE_IPV4_IFINDEX
:
721 case NEXTHOP_TYPE_IPV6_IFINDEX
:
725 if (oldifindex
!= nh
->ifindex
)
728 _nexthop_del(&nhgc
->nhg
, nh
);
730 if (nhg_hooks
.del_nexthop
)
731 nhg_hooks
.del_nexthop(nhgc
, nh
);
739 static void nhg_name_autocomplete(vector comps
, struct cmd_token
*token
)
741 struct nexthop_group_cmd
*nhgc
;
743 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
744 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, nhgc
->name
));
748 static const struct cmd_variable_handler nhg_name_handlers
[] = {
749 {.tokenname
= "NHGNAME", .completions
= nhg_name_autocomplete
},
750 {.completions
= NULL
}};
752 void nexthop_group_init(void (*new)(const char *name
),
753 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
754 const struct nexthop
*nhop
),
755 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
756 const struct nexthop
*nhop
),
757 void (*delete)(const char *name
))
759 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
761 cmd_variable_handler_register(nhg_name_handlers
);
763 install_node(&nexthop_group_node
, nexthop_group_write
);
764 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
765 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
767 install_default(NH_GROUP_NODE
);
768 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
770 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
775 nhg_hooks
.add_nexthop
= add_nexthop
;
777 nhg_hooks
.del_nexthop
= del_nexthop
;
779 nhg_hooks
.delete = delete;