]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: Add nexthop-group cli
authorDonald Sharp <sharpd@cumulusnetworks.com>
Sat, 10 Mar 2018 20:12:52 +0000 (15:12 -0500)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 14 Mar 2018 12:32:39 +0000 (08:32 -0400)
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>
lib/nexthop.c
lib/nexthop.h
lib/nexthop_group.c
lib/nexthop_group.h

index cee34e85c72ff17b3caf1095bd2b88ad53ef625d..fb7ccc169e70f973114152c83170096fa8b70566 100644 (file)
@@ -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)
index 0ca8a0063aebb7b843e359627e91e4c70956239f..568243d3a9b71f4ea53936a1676793df16305b15 100644 (file)
@@ -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);
index e7f10487d10339e6e310b5546e9854153d52a90e..2012fbdbed4de70db222060ad2f27942ff2d8929 100644 (file)
@@ -19,6 +19,7 @@
  */
 #include <zebra.h>
 
+#include <vrf.h>
 #include <nexthop.h>
 #include <nexthop_group.h>
 #include <vty.h>
 #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;
 }
index 561fe9642592e7964fa40ce1dc74b886f992b9fd..563799dc8ffa97784c2b4743a23204f9ed704305 100644 (file)
@@ -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