diff options
author | Donald Sharp <sharpd@cumulusnetworks.com> | 2018-03-10 21:12:52 +0100 |
---|---|---|
committer | Donald Sharp <sharpd@cumulusnetworks.com> | 2018-03-14 13:32:39 +0100 |
commit | 31919191561fa9b978f8c3cf713e30ed6fb20889 (patch) | |
tree | 59c41374f64717c5301f8dc1087892886e892494 | |
parent | Merge pull request #1880 from pguibert6WIND/enforce_vrf_netns_enable (diff) | |
download | frr-31919191561fa9b978f8c3cf713e30ed6fb20889.tar.xz frr-31919191561fa9b978f8c3cf713e30ed6fb20889.zip |
lib: Add nexthop-group cli
Add a nexthop-group cli:
nexthop-group NAME
nexthop A
nexthop B
nexthop C
!
This will allow interested parties to hook into the cli for
nexthops. Users can add callback functions for add/delete
of a nexthop group as well as add/delete of each individual
nexthop.
Future work( PBR and static routes ) will take advantage
of this.
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
-rw-r--r-- | lib/nexthop.c | 51 | ||||
-rw-r--r-- | lib/nexthop.h | 2 | ||||
-rw-r--r-- | lib/nexthop_group.c | 315 | ||||
-rw-r--r-- | lib/nexthop_group.h | 36 |
4 files changed, 396 insertions, 8 deletions
diff --git a/lib/nexthop.c b/lib/nexthop.c index cee34e85c..fb7ccc169 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -163,6 +163,57 @@ void nexthops_free(struct nexthop *nexthop) } } +bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2) +{ + if (nh1 && !nh2) + return false; + + if (!nh1 && nh2) + return false; + + if (nh1 == nh2) + return true; + + if (nh1->vrf_id != nh2->vrf_id) + return false; + + if (nh1->type != nh2->type) + return false; + + switch (nh1->type) { + case NEXTHOP_TYPE_IFINDEX: + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_IPV4: + if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) + return false; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) + return false; + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_IPV6: + if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) + return false; + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) + return false; + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (nh1->bh_type != nh2->bh_type) + return false; + break; + } + + return true; +} + /* Update nexthop with label information. */ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, u_int8_t num_labels, mpls_label_t *label) diff --git a/lib/nexthop.h b/lib/nexthop.h index 0ca8a0063..568243d3a 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -118,6 +118,8 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, u_int8_t, mpls_label_t *); void nexthop_del_labels(struct nexthop *); +extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2); + extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); extern int nexthop_same_no_recurse(const struct nexthop *next1, const struct nexthop *next2); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index e7f10487d..2012fbdbe 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -19,6 +19,7 @@ */ #include <zebra.h> +#include <vrf.h> #include <nexthop.h> #include <nexthop_group.h> #include <vty.h> @@ -28,6 +29,56 @@ #include "lib/nexthop_group_clippy.c" #endif +DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") + +struct nexthop_group_hooks { + void (*new)(const char *name); + void (*add_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); + void (*del_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); + void (*delete)(const char *name); +}; + +static struct nexthop_group_hooks nhg_hooks; + +static inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2); +RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) + +struct nhgc_entry_head nhgc_entries; + +static inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2) +{ + return strcmp(nhgc1->name, nhgc2->name); +} + +struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh) +{ + struct nexthop *nexthop; + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop_same(nh, nexthop)) + return nexthop; + } + + return NULL; +} + +struct nexthop_group *nexthop_group_new(void) +{ + return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group)); +} + +void nexthop_group_delete(struct nexthop_group **nhg) +{ + XFREE(MTYPE_NEXTHOP_GROUP, *nhg); +} + /* Add nexthop to the end of a nexthop list. */ void nexthop_add(struct nexthop **target, struct nexthop *nexthop) { @@ -42,6 +93,27 @@ void nexthop_add(struct nexthop **target, struct nexthop *nexthop) nexthop->prev = last; } +/* Delete nexthop from a nexthop list. */ +void nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) +{ + struct nexthop *nexthop; + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop_same(nh, nexthop)) + break; + } + + assert(nexthop); + + if (nexthop->prev) + nexthop->prev->next = nexthop->next; + else + nhg->nexthop = nexthop->next; + + if (nexthop->next) + nexthop->next->prev = nexthop->prev; +} + void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, struct nexthop *rparent) { @@ -71,12 +143,169 @@ void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, } } -DEFPY (nexthop_group, - nexthop_group_cmd, - "nexthop-group NAME", - "Enter into the nexthop-group submode\n" - "Specify the NAME of the nexthop-group\n") +static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) +{ + struct nexthop *nexthop; + + nexthop = nhgc->nhg.nexthop; + while (nexthop) { + struct nexthop *next = nexthop_next(nexthop); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nexthop); + + nexthop_free(nexthop); + + nexthop = next; + } +} + +static struct nexthop_group_cmd *nhgc_find(const char *name) +{ + struct nexthop_group_cmd find; + + strlcpy(find.name, name, sizeof(find.name)); + + return RB_FIND(nhgc_entry_head, &nhgc_entries, &find); +} + +static struct nexthop_group_cmd *nhgc_get(const char *name) +{ + struct nexthop_group_cmd *nhgc; + + nhgc = nhgc_find(name); + if (!nhgc) { + nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc)); + strlcpy(nhgc->name, name, sizeof(nhgc->name)); + + QOBJ_REG(nhgc, nexthop_group_cmd); + RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc); + + if (nhg_hooks.new) + nhg_hooks.new(name); + } + + return nhgc; +} + +static void nhgc_delete(struct nexthop_group_cmd *nhgc) { + nhgc_delete_nexthops(nhgc); + + if (nhg_hooks.delete) + nhg_hooks.delete(nhgc->name); + + RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc); +} + +DEFINE_QOBJ_TYPE(nexthop_group_cmd) + +DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", + "Enter into the nexthop-group submode\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[1]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_get(nhg_name); + VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc); + + return CMD_SUCCESS; +} + +DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", + NO_STR + "Delete the nexthop-group\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[2]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_find(nhg_name); + if (nhgc) + nhgc_delete(nhgc); + + return CMD_SUCCESS; +} + +DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, + "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]", + NO_STR + "Specify one of the nexthops in this ECMP group\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + struct vrf *vrf; + struct nexthop nhop; + struct nexthop *nh; + + if (name) + vrf = vrf_lookup_by_name(name); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + + if (!vrf) { + vty_out(vty, "Specified: %s is non-existent\n", name); + return CMD_WARNING; + } + + memset(&nhop, 0, sizeof(nhop)); + nhop.vrf_id = vrf->vrf_id; + + if (addr->sa.sa_family == AF_INET) { + nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV4; + } else { + memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16); + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV6; + } + + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (no) { + if (nh) { + nexthop_del(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + } else if (!nh) { + /* must be adding new nexthop since !no and !nexthop_exists */ + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + + if (nhg_hooks.add_nexthop) + nhg_hooks.add_nexthop(nhgc, nh); + } + return CMD_SUCCESS; } @@ -88,13 +317,85 @@ struct cmd_node nexthop_group_node = { static int nexthop_group_write(struct vty *vty) { - vty_out(vty, "!\n"); + struct nexthop_group_cmd *nhgc; + struct nexthop *nh; + struct vrf *vrf; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + char buf[100]; + + vty_out(vty, "nexthop-group %s\n", nhgc->name); + + for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) { + + vty_out(vty, " nexthop "); + + switch (nh->type) { + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, "%s", + ifindex2ifname(nh->ifindex, + nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), + ifindex2ifname(nh->ifindex, + nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + vty_out(vty, "%s", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, + sizeof(buf))); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, "%s %s", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, + sizeof(buf)), + ifindex2ifname(nh->ifindex, + nh->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + if (nh->vrf_id) { + vrf = vrf_lookup_by_id(nh->vrf_id); + vty_out(vty, " nexthop-vrf %s", vrf->name); + } + vty_out(vty, "\n"); + } + vty_out(vty, "!\n"); + } return 1; } -void nexthop_group_init(void) +void nexthop_group_init(void (*new)(const char *name), + void (*add_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop), + void (*del_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop), + void (*delete)(const char *name)) { + RB_INIT(nhgc_entry_head, &nhgc_entries); + install_node(&nexthop_group_node, nexthop_group_write); install_element(CONFIG_NODE, &nexthop_group_cmd); + install_element(CONFIG_NODE, &no_nexthop_group_cmd); + + install_default(NH_GROUP_NODE); + install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd); + + memset(&nhg_hooks, 0, sizeof(nhg_hooks)); + + if (new) + nhg_hooks.new = new; + if (add_nexthop) + nhg_hooks.add_nexthop = add_nexthop; + if (del_nexthop) + nhg_hooks.del_nexthop = del_nexthop; + if (delete) + nhg_hooks.delete = delete; } diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 561fe9642..563799dc8 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -33,9 +33,11 @@ struct nexthop_group { struct nexthop *nexthop; }; -void nexthop_group_init(void); +struct nexthop_group *nexthop_group_new(void); +void nexthop_group_delete(struct nexthop_group **nhg); void nexthop_add(struct nexthop **target, struct nexthop *nexthop); +void nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop); void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, struct nexthop *rparent); @@ -51,4 +53,36 @@ void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, (nhop) = (head.nexthop); \ (nhop); \ (nhop) = nexthop_next(nhop) + +struct nexthop_group_cmd { + + RB_ENTRY(nexthop_group_cmd) nhgc_entry; + + char name[80]; + + struct nexthop_group nhg; + + QOBJ_FIELDS +}; +RB_HEAD(nhgc_entry_head, nexthp_group_cmd); +RB_PROTOTYPE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) +DECLARE_QOBJ_TYPE(nexthop_group_cmd) + +/* + * Initialize nexthop_groups. If you are interested in when + * a nexthop_group is added/deleted/modified, then set the + * appropriate callback functions to handle it in your + * code + */ +void nexthop_group_init( + void (*new)(const char *name), + void (*add_nexthop)(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop), + void (*del_nexthop)(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop), + void (*delete)(const char *name)); + +extern struct nexthop *nexthop_exists(struct nexthop_group *nhg, + struct nexthop *nh); #endif |