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>
29 #ifndef VTYSH_EXTRACT_PL
30 #include "lib/nexthop_group_clippy.c"
33 DEFINE_MTYPE_STATIC(LIB
, NEXTHOP_GROUP
, "Nexthop Group")
35 struct nexthop_group_hooks
{
36 void (*new)(const char *name
);
37 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
38 const struct nexthop
*nhop
);
39 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
40 const struct nexthop
*nhop
);
41 void (*delete)(const char *name
);
44 static struct nexthop_group_hooks nhg_hooks
;
47 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
48 const struct nexthop_group_cmd
*nhgc2
);
49 RB_GENERATE(nhgc_entry_head
, nexthop_group_cmd
, nhgc_entry
,
50 nexthop_group_cmd_compare
)
52 struct nhgc_entry_head nhgc_entries
;
55 nexthop_group_cmd_compare(const struct nexthop_group_cmd
*nhgc1
,
56 const struct nexthop_group_cmd
*nhgc2
)
58 return strcmp(nhgc1
->name
, nhgc2
->name
);
61 struct nexthop
*nexthop_exists(struct nexthop_group
*nhg
, struct nexthop
*nh
)
63 struct nexthop
*nexthop
;
65 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
66 if (nexthop_same(nh
, nexthop
))
73 struct nexthop_group
*nexthop_group_new(void)
75 return XCALLOC(MTYPE_NEXTHOP_GROUP
, sizeof(struct nexthop_group
));
78 void nexthop_group_delete(struct nexthop_group
**nhg
)
80 XFREE(MTYPE_NEXTHOP_GROUP
, *nhg
);
83 /* Add nexthop to the end of a nexthop list. */
84 void nexthop_add(struct nexthop
**target
, struct nexthop
*nexthop
)
88 for (last
= *target
; last
&& last
->next
; last
= last
->next
)
97 /* Delete nexthop from a nexthop list. */
98 void nexthop_del(struct nexthop_group
*nhg
, struct nexthop
*nh
)
100 struct nexthop
*nexthop
;
102 for (nexthop
= nhg
->nexthop
; nexthop
; nexthop
= nexthop
->next
) {
103 if (nexthop_same(nh
, nexthop
))
110 nexthop
->prev
->next
= nexthop
->next
;
112 nhg
->nexthop
= nexthop
->next
;
115 nexthop
->next
->prev
= nexthop
->prev
;
121 void copy_nexthops(struct nexthop
**tnh
, struct nexthop
*nh
,
122 struct nexthop
*rparent
)
124 struct nexthop
*nexthop
;
127 for (nh1
= nh
; nh1
; nh1
= nh1
->next
) {
128 nexthop
= nexthop_new();
129 nexthop
->vrf_id
= nh1
->vrf_id
;
130 nexthop
->ifindex
= nh1
->ifindex
;
131 nexthop
->type
= nh1
->type
;
132 nexthop
->flags
= nh1
->flags
;
133 memcpy(&nexthop
->gate
, &nh1
->gate
, sizeof(nh1
->gate
));
134 memcpy(&nexthop
->src
, &nh1
->src
, sizeof(nh1
->src
));
135 memcpy(&nexthop
->rmap_src
, &nh1
->rmap_src
,
136 sizeof(nh1
->rmap_src
));
137 nexthop
->rparent
= rparent
;
139 nexthop_add_labels(nexthop
, nh1
->nh_label_type
,
140 nh1
->nh_label
->num_labels
,
141 &nh1
->nh_label
->label
[0]);
142 nexthop_add(tnh
, nexthop
);
144 if (CHECK_FLAG(nh1
->flags
, NEXTHOP_FLAG_RECURSIVE
))
145 copy_nexthops(&nexthop
->resolved
, nh1
->resolved
,
150 static void nhgc_delete_nexthops(struct nexthop_group_cmd
*nhgc
)
152 struct nexthop
*nexthop
;
154 nexthop
= nhgc
->nhg
.nexthop
;
156 struct nexthop
*next
= nexthop_next(nexthop
);
158 nexthop_del(&nhgc
->nhg
, nexthop
);
159 if (nhg_hooks
.del_nexthop
)
160 nhg_hooks
.del_nexthop(nhgc
, nexthop
);
162 nexthop_free(nexthop
);
168 struct nexthop_group_cmd
*nhgc_find(const char *name
)
170 struct nexthop_group_cmd find
;
172 strlcpy(find
.name
, name
, sizeof(find
.name
));
174 return RB_FIND(nhgc_entry_head
, &nhgc_entries
, &find
);
177 static int nhgc_cmp_helper(const char *a
, const char *b
)
191 static int nhgl_cmp(struct nexthop_hold
*nh1
, struct nexthop_hold
*nh2
)
195 ret
= sockunion_cmp(&nh1
->addr
, &nh2
->addr
);
199 ret
= nhgc_cmp_helper(nh1
->intf
, nh2
->intf
);
203 return nhgc_cmp_helper(nh1
->nhvrf_name
, nh2
->nhvrf_name
);
206 static void nhgl_delete(struct nexthop_hold
*nh
)
209 XFREE(MTYPE_TMP
, nh
->intf
);
212 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
214 XFREE(MTYPE_TMP
, nh
);
217 static struct nexthop_group_cmd
*nhgc_get(const char *name
)
219 struct nexthop_group_cmd
*nhgc
;
221 nhgc
= nhgc_find(name
);
223 nhgc
= XCALLOC(MTYPE_TMP
, sizeof(*nhgc
));
224 strlcpy(nhgc
->name
, name
, sizeof(nhgc
->name
));
226 QOBJ_REG(nhgc
, nexthop_group_cmd
);
227 RB_INSERT(nhgc_entry_head
, &nhgc_entries
, nhgc
);
229 nhgc
->nhg_list
= list_new();
230 nhgc
->nhg_list
->cmp
= (int (*)(void *, void *))nhgl_cmp
;
231 nhgc
->nhg_list
->del
= (void (*)(void *))nhgl_delete
;
240 static void nhgc_delete(struct nexthop_group_cmd
*nhgc
)
242 nhgc_delete_nexthops(nhgc
);
244 if (nhg_hooks
.delete)
245 nhg_hooks
.delete(nhgc
->name
);
247 RB_REMOVE(nhgc_entry_head
, &nhgc_entries
, nhgc
);
249 list_delete_and_null(&nhgc
->nhg_list
);
251 XFREE(MTYPE_TMP
, nhgc
);
254 DEFINE_QOBJ_TYPE(nexthop_group_cmd
)
256 DEFUN_NOSH(nexthop_group
, nexthop_group_cmd
, "nexthop-group NAME",
257 "Enter into the nexthop-group submode\n"
258 "Specify the NAME of the nexthop-group\n")
260 const char *nhg_name
= argv
[1]->arg
;
261 struct nexthop_group_cmd
*nhgc
= NULL
;
263 nhgc
= nhgc_get(nhg_name
);
264 VTY_PUSH_CONTEXT(NH_GROUP_NODE
, nhgc
);
269 DEFUN_NOSH(no_nexthop_group
, no_nexthop_group_cmd
, "no nexthop-group NAME",
271 "Delete the nexthop-group\n"
272 "Specify the NAME of the nexthop-group\n")
274 const char *nhg_name
= argv
[2]->arg
;
275 struct nexthop_group_cmd
*nhgc
= NULL
;
277 nhgc
= nhgc_find(nhg_name
);
284 static void nexthop_group_save_nhop(struct nexthop_group_cmd
*nhgc
,
285 const char *nhvrf_name
,
286 const union sockunion
*addr
,
289 struct nexthop_hold
*nh
;
291 nh
= XCALLOC(MTYPE_TMP
, sizeof(*nh
));
294 nh
->nhvrf_name
= XSTRDUP(MTYPE_TMP
, nhvrf_name
);
296 nh
->intf
= XSTRDUP(MTYPE_TMP
, intf
);
300 listnode_add_sort(nhgc
->nhg_list
, nh
);
303 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd
*nhgc
,
304 const char *nhvrf_name
,
305 const union sockunion
*addr
,
308 struct nexthop_hold
*nh
;
309 struct listnode
*node
;
311 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
312 if (nhgc_cmp_helper(nhvrf_name
, nh
->nhvrf_name
) == 0 &&
313 sockunion_cmp(addr
, &nh
->addr
) == 0 &&
314 nhgc_cmp_helper(intf
, nh
->intf
) == 0)
319 * Something has gone seriously wrong, fail gracefully
324 list_delete_node(nhgc
->nhg_list
, node
);
327 XFREE(MTYPE_TMP
, nh
->nhvrf_name
);
329 XFREE(MTYPE_TMP
, nh
->intf
);
331 XFREE(MTYPE_TMP
, nh
);
334 static bool nexthop_group_parse_nexthop(struct nexthop
*nhop
,
335 const union sockunion
*addr
,
336 const char *intf
, const char *name
)
340 memset(nhop
, 0, sizeof(*nhop
));
343 vrf
= vrf_lookup_by_name(name
);
345 vrf
= vrf_lookup_by_id(VRF_DEFAULT
);
350 nhop
->vrf_id
= vrf
->vrf_id
;
352 if (addr
->sa
.sa_family
== AF_INET
) {
353 nhop
->gate
.ipv4
.s_addr
= addr
->sin
.sin_addr
.s_addr
;
355 nhop
->type
= NEXTHOP_TYPE_IPV4_IFINDEX
;
356 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
357 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
360 nhop
->type
= NEXTHOP_TYPE_IPV4
;
362 memcpy(&nhop
->gate
.ipv6
, &addr
->sin6
.sin6_addr
, 16);
364 nhop
->type
= NEXTHOP_TYPE_IPV6_IFINDEX
;
365 nhop
->ifindex
= ifname2ifindex(intf
, vrf
->vrf_id
);
366 if (nhop
->ifindex
== IFINDEX_INTERNAL
)
369 nhop
->type
= NEXTHOP_TYPE_IPV6
;
375 DEFPY(ecmp_nexthops
, ecmp_nexthops_cmd
,
376 "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
378 "Specify one of the nexthops in this ECMP group\n"
382 "If the nexthop is in a different vrf tell us\n"
383 "The nexthop-vrf Name\n")
385 VTY_DECLVAR_CONTEXT(nexthop_group_cmd
, nhgc
);
391 * This is impossible to happen as that the cli parser refuses
392 * to let you get here without an addr, but the SA system
393 * does not understand this intricacy
397 legal
= nexthop_group_parse_nexthop(&nhop
, addr
, intf
, name
);
399 if (nhop
.type
== NEXTHOP_TYPE_IPV6
400 && IN6_IS_ADDR_LINKLOCAL(&nhop
.gate
.ipv6
)) {
402 "Specified a v6 LL with no interface, rejecting\n");
403 return CMD_WARNING_CONFIG_FAILED
;
406 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
409 nexthop_group_unsave_nhop(nhgc
, name
, addr
, intf
);
411 nexthop_del(&nhgc
->nhg
, nh
);
413 if (nhg_hooks
.del_nexthop
)
414 nhg_hooks
.del_nexthop(nhgc
, nh
);
419 /* must be adding new nexthop since !no and !nexthop_exists */
423 memcpy(nh
, &nhop
, sizeof(nhop
));
424 nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
427 nexthop_group_save_nhop(nhgc
, name
, addr
, intf
);
429 if (legal
&& nhg_hooks
.add_nexthop
)
430 nhg_hooks
.add_nexthop(nhgc
, nh
);
436 struct cmd_node nexthop_group_node
= {
438 "%s(config-nh-group)# ",
442 void nexthop_group_write_nexthop(struct vty
*vty
, struct nexthop
*nh
)
447 vty_out(vty
, "nexthop ");
450 case NEXTHOP_TYPE_IFINDEX
:
451 vty_out(vty
, "%s", ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
453 case NEXTHOP_TYPE_IPV4
:
454 vty_out(vty
, "%s", inet_ntoa(nh
->gate
.ipv4
));
456 case NEXTHOP_TYPE_IPV4_IFINDEX
:
457 vty_out(vty
, "%s %s", inet_ntoa(nh
->gate
.ipv4
),
458 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
460 case NEXTHOP_TYPE_IPV6
:
462 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)));
464 case NEXTHOP_TYPE_IPV6_IFINDEX
:
465 vty_out(vty
, "%s %s",
466 inet_ntop(AF_INET6
, &nh
->gate
.ipv6
, buf
, sizeof(buf
)),
467 ifindex2ifname(nh
->ifindex
, nh
->vrf_id
));
469 case NEXTHOP_TYPE_BLACKHOLE
:
473 if (nh
->vrf_id
!= VRF_DEFAULT
) {
474 vrf
= vrf_lookup_by_id(nh
->vrf_id
);
475 vty_out(vty
, " nexthop-vrf %s", vrf
->name
);
480 static void nexthop_group_write_nexthop_internal(struct vty
*vty
,
481 struct nexthop_hold
*nh
)
485 vty_out(vty
, "nexthop ");
487 vty_out(vty
, "%s", sockunion2str(&nh
->addr
, buf
, sizeof(buf
)));
490 vty_out(vty
, " %s", nh
->intf
);
493 vty_out(vty
, " nexthop-vrf %s", nh
->nhvrf_name
);
498 static int nexthop_group_write(struct vty
*vty
)
500 struct nexthop_group_cmd
*nhgc
;
501 struct nexthop_hold
*nh
;
503 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
504 struct listnode
*node
;
506 vty_out(vty
, "nexthop-group %s\n", nhgc
->name
);
508 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nh
)) {
510 nexthop_group_write_nexthop_internal(vty
, nh
);
519 void nexthop_group_enable_vrf(struct vrf
*vrf
)
521 struct nexthop_group_cmd
*nhgc
;
522 struct nexthop_hold
*nhh
;
524 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
525 struct listnode
*node
;
527 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
531 if (!nexthop_group_parse_nexthop(&nhop
, &nhh
->addr
,
536 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
541 if (nhop
.vrf_id
!= vrf
->vrf_id
)
546 memcpy(nh
, &nhop
, sizeof(nhop
));
547 nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
549 if (nhg_hooks
.add_nexthop
)
550 nhg_hooks
.add_nexthop(nhgc
, nh
);
555 void nexthop_group_disable_vrf(struct vrf
*vrf
)
557 struct nexthop_group_cmd
*nhgc
;
558 struct nexthop_hold
*nhh
;
560 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
561 struct listnode
*node
;
563 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
567 if (!nexthop_group_parse_nexthop(&nhop
, &nhh
->addr
,
572 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
577 if (nh
->vrf_id
!= vrf
->vrf_id
)
580 nexthop_del(&nhgc
->nhg
, nh
);
582 if (nhg_hooks
.del_nexthop
)
583 nhg_hooks
.del_nexthop(nhgc
, nh
);
590 void nexthop_group_interface_state_change(struct interface
*ifp
,
591 ifindex_t oldifindex
)
593 struct nexthop_group_cmd
*nhgc
;
594 struct nexthop_hold
*nhh
;
596 RB_FOREACH (nhgc
, nhgc_entry_head
, &nhgc_entries
) {
597 struct listnode
*node
;
601 for (ALL_LIST_ELEMENTS_RO(nhgc
->nhg_list
, node
, nhh
)) {
604 if (!nexthop_group_parse_nexthop(
605 &nhop
, &nhh
->addr
, nhh
->intf
,
610 case NEXTHOP_TYPE_IPV4
:
611 case NEXTHOP_TYPE_IPV6
:
612 case NEXTHOP_TYPE_BLACKHOLE
:
614 case NEXTHOP_TYPE_IFINDEX
:
615 case NEXTHOP_TYPE_IPV4_IFINDEX
:
616 case NEXTHOP_TYPE_IPV6_IFINDEX
:
619 nh
= nexthop_exists(&nhgc
->nhg
, &nhop
);
624 if (ifp
->ifindex
!= nhop
.ifindex
)
629 memcpy(nh
, &nhop
, sizeof(nhop
));
630 nexthop_add(&nhgc
->nhg
.nexthop
, nh
);
632 if (nhg_hooks
.add_nexthop
)
633 nhg_hooks
.add_nexthop(nhgc
, nh
);
636 struct nexthop
*next_nh
;
638 for (nh
= nhgc
->nhg
.nexthop
; nh
; nh
= next_nh
) {
641 case NEXTHOP_TYPE_IPV4
:
642 case NEXTHOP_TYPE_IPV6
:
643 case NEXTHOP_TYPE_BLACKHOLE
:
645 case NEXTHOP_TYPE_IFINDEX
:
646 case NEXTHOP_TYPE_IPV4_IFINDEX
:
647 case NEXTHOP_TYPE_IPV6_IFINDEX
:
651 if (oldifindex
!= nh
->ifindex
)
654 nexthop_del(&nhgc
->nhg
, nh
);
656 if (nhg_hooks
.del_nexthop
)
657 nhg_hooks
.del_nexthop(nhgc
, nh
);
665 void nexthop_group_init(void (*new)(const char *name
),
666 void (*add_nexthop
)(const struct nexthop_group_cmd
*nhg
,
667 const struct nexthop
*nhop
),
668 void (*del_nexthop
)(const struct nexthop_group_cmd
*nhg
,
669 const struct nexthop
*nhop
),
670 void (*delete)(const char *name
))
672 RB_INIT(nhgc_entry_head
, &nhgc_entries
);
674 install_node(&nexthop_group_node
, nexthop_group_write
);
675 install_element(CONFIG_NODE
, &nexthop_group_cmd
);
676 install_element(CONFIG_NODE
, &no_nexthop_group_cmd
);
678 install_default(NH_GROUP_NODE
);
679 install_element(NH_GROUP_NODE
, &ecmp_nexthops_cmd
);
681 memset(&nhg_hooks
, 0, sizeof(nhg_hooks
));
686 nhg_hooks
.add_nexthop
= add_nexthop
;
688 nhg_hooks
.del_nexthop
= del_nexthop
;
690 nhg_hooks
.delete = delete;