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>
30 #ifndef VTYSH_EXTRACT_PL
31 #include "lib/nexthop_group_clippy.c"
34 DEFINE_MTYPE_STATIC(LIB
, NEXTHOP_GROUP
, "Nexthop Group")
36 struct nexthop_group_hooks
{
37 void (*new)(const char *name
);
38 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
39 const struct nexthop
*nhop
);
40 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
41 const struct nexthop
*nhop
);
42 void (*delete)(const char *name
);
45 static struct nexthop_group_hooks nhg_hooks
;
48 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
49 const struct nexthop_group_cmd
*nhgc2
);
50 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
51 nexthop_group_cmd_compare
)
53 static struct nhgc_entry_head nhgc_entries
;
56 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
57 const struct nexthop_group_cmd
*nhgc2
)
59 return strcmp(nhgc1
->name
, nhgc2
->name
);
62 uint8_t nexthop_group_nexthop_num(const struct nexthop_group
*nhg
)
67 for (ALL_NEXTHOPS_PTR(nhg
, nhop
))
73 uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group
*nhg
)
78 for (ALL_NEXTHOPS_PTR(nhg
, nhop
)) {
79 if (CHECK_FLAG(nhop
->flags
, NEXTHOP_FLAG_ACTIVE
))
86 struct nexthop
*nexthop_exists(struct nexthop_group
*nhg
, struct nexthop
*nh
)
88 struct nexthop
*nexthop
;
90 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
91 if (nexthop_same(nh
, nexthop
))
98 struct nexthop_group
*nexthop_group_new(void)
100 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
103 void nexthop_group_delete(struct nexthop_group
**nhg
)
105 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
108 /* Add nexthop to the end of a nexthop list. */
109 void nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
111 struct nexthop
*last
;
113 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
116 last
->next
= nexthop
;
119 nexthop
->prev
= last
;
122 /* Delete nexthop from a nexthop list. */
123 void nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
125 struct nexthop
*nexthop
;
127 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
128 if (nexthop_same(nh
, nexthop
))
135 nexthop
->prev
->next
= nexthop
->next
;
137 nhg
->nexthop
= nexthop
->next
;
140 nexthop
->next
->prev
= nexthop
->prev
;
146 void copy_nexthops(struct nexthop
**tnh
, const struct nexthop
*nh
,
147 struct nexthop
*rparent
)
149 struct nexthop
*nexthop
;
150 const struct nexthop
*nh1
;
152 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
153 nexthop
= nexthop_new();
154 nexthop
->vrf_id
= nh1
->vrf_id
;
155 nexthop
->ifindex
= nh1
->ifindex
;
156 nexthop
->type
= nh1
->type
;
157 nexthop
->flags
= nh1
->flags
;
158 memcpy(&nexthop
->gate
, &nh1
->gate
, sizeof(nh1
->gate
));
159 memcpy(&nexthop
->src
, &nh1
->src
, sizeof(nh1
->src
));
160 memcpy(&nexthop
->rmap_src
, &nh1
->rmap_src
,
161 sizeof(nh1
->rmap_src
));
162 nexthop
->rparent
= rparent
;
164 nexthop_add_labels(nexthop
, nh1
->nh_label_type
,
165 nh1
->nh_label
->num_labels
,
166 &nh1
->nh_label
->label
[0]);
167 nexthop_add(tnh
, nexthop
);
169 if (CHECK_FLAG(nh1
->flags
, NEXTHOP_FLAG_RECURSIVE
))
170 copy_nexthops(&nexthop
->resolved
, nh1
->resolved
,
175 uint32_t nexthop_group_hash(const struct nexthop_group
*nhg
)
181 * We are not interested in hashing over any recursively
184 for (nh
= nhg
->nexthop
; nh
; nh
= nh
->next
)
185 key
= jhash_1word(nexthop_hash(nh
), key
);
190 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
192 struct nexthop
*nexthop
;
194 nexthop
= nhgc
->nhg
.nexthop
;
196 struct nexthop
*next
= nexthop_next(nexthop
);
198 nexthop_del(&nhgc
->nhg
, nexthop
);
199 if (nhg_hooks
.del_nexthop
)
200 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
202 nexthop_free(nexthop
);
208 struct nexthop_group_cmd
*nhgc_find(const char *name
)
210 struct nexthop_group_cmd find
;
212 strlcpy(find
.name
, name
, sizeof(find
.name
));
214 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
217 static int nhgc_cmp_helper(const char *a
, const char *b
)
231 static int nhgc_addr_cmp_helper(const union sockunion
*a
, const union sockunion
*b
)
242 return sockunion_cmp(a
, b
);
245 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
249 ret
= nhgc_addr_cmp_helper(nh1
->addr
, nh2
->addr
);
253 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
257 return nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
260 static void nhgl_delete(struct nexthop_hold
*nh
)
262 XFREE(MTYPE_TMP
, nh
->intf
);
264 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
267 sockunion_free(nh
->addr
);
269 XFREE(MTYPE_TMP
, nh
);
272 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
274 struct nexthop_group_cmd
*nhgc
;
276 nhgc
= nhgc_find(name
);
278 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
279 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
281 QOBJ_REG(nhgc
, nexthop_group_cmd
);
282 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
284 nhgc
->nhg_list
= list_new();
285 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
286 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
295 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
297 nhgc_delete_nexthops(nhgc
);
299 if (nhg_hooks
.delete)
300 nhg_hooks
.delete(nhgc
->name
);
302 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
304 list_delete(&nhgc
->nhg_list
);
306 XFREE(MTYPE_TMP
, nhgc
);
309 DEFINE_QOBJ_TYPE(nexthop_group_cmd
)
311 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NAME",
312 "Enter into the nexthop-group submode\n"
313 "Specify the NAME of the nexthop-group\n")
315 const char *nhg_name
= argv
[1]->arg
;
316 struct nexthop_group_cmd
*nhgc
= NULL
;
318 nhgc
= nhgc_get(nhg_name
);
319 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
324 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NAME",
326 "Delete the nexthop-group\n"
327 "Specify the NAME of the nexthop-group\n")
329 const char *nhg_name
= argv
[2]->arg
;
330 struct nexthop_group_cmd
*nhgc
= NULL
;
332 nhgc
= nhgc_find(nhg_name
);
339 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
340 const char *nhvrf_name
,
341 const union sockunion
*addr
,
344 struct nexthop_hold
*nh
;
346 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
349 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
351 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
353 nh
->addr
= sockunion_dup(addr
);
355 listnode_add_sort(nhgc
->nhg_list
, nh
);
358 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
359 const char *nhvrf_name
,
360 const union sockunion
*addr
,
363 struct nexthop_hold
*nh
;
364 struct listnode
*node
;
366 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
367 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0 &&
368 nhgc_addr_cmp_helper(addr
, nh
->addr
) == 0 &&
369 nhgc_cmp_helper(intf
, nh
->intf
) == 0)
374 * Something has gone seriously wrong, fail gracefully
379 list_delete_node(nhgc
->nhg_list
, node
);
383 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
384 const union sockunion
*addr
,
385 const char *intf
, const char *name
)
389 memset(nhop
, 0, sizeof(*nhop
));
392 vrf
= vrf_lookup_by_name(name
);
394 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
399 nhop
->vrf_id
= vrf
->vrf_id
;
402 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
403 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
408 if (addr
->sa
.sa_family
== AF_INET
) {
409 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
411 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
413 nhop
->type
= NEXTHOP_TYPE_IPV4
;
415 nhop
->gate
.ipv6
= addr
->sin6
.sin6_addr
;
417 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
419 nhop
->type
= NEXTHOP_TYPE_IPV6
;
422 nhop
->type
= NEXTHOP_TYPE_IFINDEX
;
427 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
430 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
433 [nexthop-vrf NAME$name]",
435 "Specify one of the nexthops in this ECMP group\n"
440 "If the nexthop is in a different vrf tell us\n"
441 "The nexthop-vrf Name\n")
443 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
448 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, name
);
450 if (nhop
.type
== NEXTHOP_TYPE_IPV6
451 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
453 "Specified a v6 LL with no interface, rejecting\n");
454 return CMD_WARNING_CONFIG_FAILED
;
457 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
460 nexthop_group_unsave_nhop(nhgc
, name
, addr
, intf
);
462 nexthop_del(&nhgc
->nhg
, nh
);
464 if (nhg_hooks
.del_nexthop
)
465 nhg_hooks
.del_nexthop(nhgc
, nh
);
470 /* must be adding new nexthop since !no and !nexthop_exists */
474 memcpy(nh
, &nhop
, sizeof(nhop
));
475 nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
478 nexthop_group_save_nhop(nhgc
, name
, addr
, intf
);
480 if (legal
&& nhg_hooks
.add_nexthop
)
481 nhg_hooks
.add_nexthop(nhgc
, nh
);
487 struct cmd_node nexthop_group_node
= {
489 "%s(config-nh-group)# ",
493 void nexthop_group_write_nexthop(struct vty
*vty
, struct nexthop
*nh
)
498 vty_out(vty
, "nexthop ");
501 case NEXTHOP_TYPE_IFINDEX
:
502 vty_out(vty
, "%s", ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
504 case NEXTHOP_TYPE_IPV4
:
505 vty_out(vty
, "%s", inet_ntoa(nh
->gate
.ipv4
));
507 case NEXTHOP_TYPE_IPV4_IFINDEX
:
508 vty_out(vty
, "%s %s", inet_ntoa(nh
->gate
.ipv4
),
509 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
511 case NEXTHOP_TYPE_IPV6
:
513 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)));
515 case NEXTHOP_TYPE_IPV6_IFINDEX
:
516 vty_out(vty
, "%s %s",
517 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)),
518 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
520 case NEXTHOP_TYPE_BLACKHOLE
:
524 if (nh
->vrf_id
!= VRF_DEFAULT
) {
525 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
526 vty_out(vty
, " nexthop-vrf %s", vrf
->name
);
531 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
532 struct nexthop_hold
*nh
)
536 vty_out(vty
, "nexthop");
539 vty_out(vty
, " %s", sockunion2str(nh
->addr
, buf
, sizeof(buf
)));
542 vty_out(vty
, " %s", nh
->intf
);
545 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
550 static int nexthop_group_write(struct vty
*vty
)
552 struct nexthop_group_cmd
*nhgc
;
553 struct nexthop_hold
*nh
;
555 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
556 struct listnode
*node
;
558 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
560 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
562 nexthop_group_write_nexthop_internal(vty
, nh
);
571 void nexthop_group_enable_vrf(struct vrf
*vrf
)
573 struct nexthop_group_cmd
*nhgc
;
574 struct nexthop_hold
*nhh
;
576 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
577 struct listnode
*node
;
579 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
583 if (!nexthop_group_parse_nexthop(&nhop
, nhh
->addr
,
588 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
593 if (nhop
.vrf_id
!= vrf
->vrf_id
)
598 memcpy(nh
, &nhop
, sizeof(nhop
));
599 nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
601 if (nhg_hooks
.add_nexthop
)
602 nhg_hooks
.add_nexthop(nhgc
, nh
);
607 void nexthop_group_disable_vrf(struct vrf
*vrf
)
609 struct nexthop_group_cmd
*nhgc
;
610 struct nexthop_hold
*nhh
;
612 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
613 struct listnode
*node
;
615 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
619 if (!nexthop_group_parse_nexthop(&nhop
, nhh
->addr
,
624 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
629 if (nh
->vrf_id
!= vrf
->vrf_id
)
632 nexthop_del(&nhgc
->nhg
, nh
);
634 if (nhg_hooks
.del_nexthop
)
635 nhg_hooks
.del_nexthop(nhgc
, nh
);
642 void nexthop_group_interface_state_change(struct interface
*ifp
,
643 ifindex_t oldifindex
)
645 struct nexthop_group_cmd
*nhgc
;
646 struct nexthop_hold
*nhh
;
648 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
649 struct listnode
*node
;
653 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
656 if (!nexthop_group_parse_nexthop(
657 &nhop
, nhh
->addr
, nhh
->intf
,
662 case NEXTHOP_TYPE_IPV4
:
663 case NEXTHOP_TYPE_IPV6
:
664 case NEXTHOP_TYPE_BLACKHOLE
:
666 case NEXTHOP_TYPE_IFINDEX
:
667 case NEXTHOP_TYPE_IPV4_IFINDEX
:
668 case NEXTHOP_TYPE_IPV6_IFINDEX
:
671 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
676 if (ifp
->ifindex
!= nhop
.ifindex
)
681 memcpy(nh
, &nhop
, sizeof(nhop
));
682 nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
684 if (nhg_hooks
.add_nexthop
)
685 nhg_hooks
.add_nexthop(nhgc
, nh
);
688 struct nexthop
*next_nh
;
690 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
693 case NEXTHOP_TYPE_IPV4
:
694 case NEXTHOP_TYPE_IPV6
:
695 case NEXTHOP_TYPE_BLACKHOLE
:
697 case NEXTHOP_TYPE_IFINDEX
:
698 case NEXTHOP_TYPE_IPV4_IFINDEX
:
699 case NEXTHOP_TYPE_IPV6_IFINDEX
:
703 if (oldifindex
!= nh
->ifindex
)
706 nexthop_del(&nhgc
->nhg
, nh
);
708 if (nhg_hooks
.del_nexthop
)
709 nhg_hooks
.del_nexthop(nhgc
, nh
);
717 void nexthop_group_init(void (*new)(const char *name
),
718 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
719 const struct nexthop
*nhop
),
720 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
721 const struct nexthop
*nhop
),
722 void (*delete)(const char *name
))
724 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
726 install_node(&nexthop_group_node
, nexthop_group_write
);
727 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
728 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
730 install_default(NH_GROUP_NODE
);
731 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
733 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
738 nhg_hooks
.add_nexthop
= add_nexthop
;
740 nhg_hooks
.del_nexthop
= del_nexthop
;
742 nhg_hooks
.delete = delete;