[ovs-dev] [mirror 11/13] odp-util: New function odp_actions_from_string().

Ben Pfaff blp at nicira.com
Wed Oct 26 17:09:44 UTC 2011


An upcoming commit will add a user.  The only planned users for now are
part of unit tests themselves, so it doesn't seem important to unit test
it.
---
 lib/odp-util.c        |  255 ++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/odp-util.h        |    3 +
 utilities/ovs-dpctl.c |   29 ++++++
 3 files changed, 285 insertions(+), 2 deletions(-)

diff --git a/lib/odp-util.c b/lib/odp-util.c
index 79fe4ca..a966238 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -19,6 +19,7 @@
 #include "odp-util.h"
 #include <errno.h>
 #include <inttypes.h>
+#include <math.h>
 #include <netinet/icmp6.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +35,10 @@
 #include "timeval.h"
 #include "util.h"
 
+/* The set of characters that may separate one action or one key attribute
+ * from another. */
+static const char *delimiters = ", \t\r\n";
+
 static void format_odp_key_attr(const struct nlattr *, struct ds *);
 static int parse_odp_key_attr(const char *, const struct shash *port_names,
                               struct ofpbuf *);
@@ -103,6 +108,21 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
     }
 }
 
+static enum ovs_key_attr
+ovs_key_attr_from_string(const char *s, size_t len)
+{
+    enum ovs_key_attr attr;
+
+    for (attr = 0; attr <= OVS_KEY_ATTR_MAX; attr++) {
+        const char *attr_name = ovs_key_attr_to_string(attr);
+        if (strlen(attr_name) == len && !memcmp(s, attr_name, len)) {
+            return attr;
+        }
+    }
+
+    return OVS_KEY_ATTR_UNSPEC;
+}
+
 static void
 format_generic_odp_action(struct ds *ds, const struct nlattr *a)
 {
@@ -216,7 +236,6 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     }
 
     switch (type) {
-
     case OVS_ACTION_ATTR_OUTPUT:
         ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
         break;
@@ -277,6 +296,238 @@ format_odp_actions(struct ds *ds, const struct nlattr *actions,
         ds_put_cstr(ds, "drop");
     }
 }
+
+static int
+parse_odp_action(const char *s, const struct shash *port_names,
+                 struct ofpbuf *actions)
+{
+    /* Many of the sscanf calls in this function use oversized destination
+     * fields because some sscanf() implementations truncate the range of %i
+     * directives, so that e.g. "%"SCNi16 interprets input of "0xfedc" as a
+     * value of 0x7fff.  The other alternatives are to allow only a single
+     * radix (e.g. decimal or hexadecimal) or to write more sophisticated
+     * parsers.
+     *
+     * The tun_id parser has to use an alternative approach because there is no
+     * type larger than 64 bits. */
+
+    {
+        unsigned long long int port;
+        int n = -1;
+
+        if (sscanf(s, "%lli%n", &port, &n) > 0 && n > 0) {
+            nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, port);
+            return n;
+        }
+    }
+
+    if (port_names) {
+        int len = strcspn(s, delimiters);
+        struct shash_node *node;
+
+        node = shash_find_len(port_names, s, len);
+        if (node) {
+            nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT,
+                           (uintptr_t) node->data);
+            return len;
+        }
+    }
+
+    {
+        unsigned long long int pid;
+        unsigned long long int length;
+        unsigned long long int ifindex;
+        char userdata_s[32];
+        int n_output;
+        int vid, pcp;
+        int n = -1;
+
+        if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) {
+            odp_put_userspace_action(pid, NULL, actions);
+            return n;
+        } else if (sscanf(s, "userspace(pid=%lli,controller,length=%lli)%n",
+                          &pid, &length, &n) > 0 && n > 0) {
+            struct user_action_cookie cookie;
+
+            cookie.type = USER_ACTION_COOKIE_CONTROLLER;
+            cookie.n_output = 0;
+            cookie.vlan_tci = htons(0);
+            cookie.data = length;
+            odp_put_userspace_action(pid, &cookie, actions);
+            return n;
+        } else if (sscanf(s, "userspace(pid=%lli,sFlow,n_output=%i,vid=%i,"
+                          "pcp=%i,ifindex=%lli)%n", &pid, &n_output,
+                          &vid, &pcp, &ifindex, &n) > 0 && n > 0) {
+            struct user_action_cookie cookie;
+            uint16_t tci;
+
+            tci = vid | (pcp << VLAN_PCP_SHIFT);
+            if (tci) {
+                tci |= VLAN_CFI;
+            }
+
+            cookie.type = USER_ACTION_COOKIE_SFLOW;
+            cookie.n_output = n_output;
+            cookie.vlan_tci = htons(tci);
+            cookie.data = ifindex;
+            odp_put_userspace_action(pid, &cookie, actions);
+            return n;
+        } else if (sscanf(s, "userspace(pid=%lli,userdata="
+                          "%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
+                          &n) > 0 && n > 0) {
+            struct user_action_cookie cookie;
+            uint64_t userdata;
+
+            userdata = strtoull(userdata_s, NULL, 0);
+            memcpy(&cookie, &userdata, sizeof cookie);
+            odp_put_userspace_action(pid, &cookie, actions);
+            return n;
+        }
+    }
+
+    if (!strncmp(s, "set(", 4)) {
+        size_t start_ofs;
+        int retval;
+
+        start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET);
+        retval = parse_odp_key_attr(s + 4, port_names, actions);
+        if (retval < 0) {
+            return retval;
+        }
+        if (s[retval + 4] != ')') {
+            return -EINVAL;
+        }
+        nl_msg_end_nested(actions, start_ofs);
+        return retval + 5;
+    }
+
+    if (!strncmp(s, "push(", 5)) {
+        size_t start_ofs;
+        int retval;
+
+        start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_PUSH);
+        retval = parse_odp_key_attr(s + 5, port_names, actions);
+        if (retval < 0) {
+            return retval;
+        }
+        if (s[retval + 5] != ')') {
+            return -EINVAL;
+        }
+        nl_msg_end_nested(actions, start_ofs);
+        return retval + 6;
+    }
+
+    if (!strncmp(s, "pop(", 4)) {
+        enum ovs_key_attr key;
+        size_t len;
+
+        len = strcspn(s + 4, ")");
+        key = ovs_key_attr_from_string(s + 4, len);
+        if (key == OVS_KEY_ATTR_UNSPEC || s[4 + len] != ')') {
+            return -EINVAL;
+        }
+        nl_msg_put_u16(actions, OVS_ACTION_ATTR_POP, key);
+        return len + 5;
+    }
+
+    {
+        unsigned long long int priority;
+        int n = -1;
+
+        if (sscanf(s, "set_priority(%lli)%n", &priority, &n) > 0 && n > 0) {
+            nl_msg_put_u32(actions, OVS_ACTION_ATTR_SET_PRIORITY, priority);
+            return n;
+        }
+    }
+
+    {
+        int n = -1;
+
+        sscanf(s, "pop_priority%n", &n);
+        if (n > 0) {
+            nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_PRIORITY);
+            return n;
+        }
+    }
+
+    {
+        double percentage;
+        int n = -1;
+
+        if (sscanf(s, "sample(sample=%lf%%,actions(%n", &percentage, &n) > 0
+            && percentage >= 0. && percentage <= 100.0
+            && n > 0) {
+            size_t sample_ofs, actions_ofs;
+            double probability;
+
+            probability = floor(UINT32_MAX * (percentage / 100.0) + .5);
+            sample_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SAMPLE);
+            nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PROBABILITY,
+                           (probability <= 0 ? 0
+                            : probability >= UINT32_MAX ? UINT32_MAX
+                            : probability));
+
+            actions_ofs = nl_msg_start_nested(actions,
+                                              OVS_SAMPLE_ATTR_ACTIONS);
+            for (;;) {
+                int retval;
+
+                s += strspn(s, delimiters);
+                if (s[n] == ')') {
+                    break;
+                }
+
+                retval = parse_odp_action(s + n, port_names, actions);
+                if (retval < 0) {
+                    return retval;
+                }
+                n += retval;
+
+            }
+            nl_msg_end_nested(actions, actions_ofs);
+            nl_msg_end_nested(actions, sample_ofs);
+
+            return s[n + 1] == ')' ? n + 2 : -EINVAL;
+        }
+    }
+
+    return -EINVAL;
+}
+
+/* Parses the string representation of datapath actions, in the format output
+ * by format_odp_action().  Returns 0 if successful, otherwise a positive errno
+ * value.  On success, the ODP actions are appended to 'actions' as a series of
+ * Netlink attributes.  On failure, no data is appended to 'actions'.  Either
+ * way, 'actions''s data might be reallocated. */
+int
+odp_actions_from_string(const char *s, const struct shash *port_names,
+                        struct ofpbuf *actions)
+{
+    size_t old_size;
+
+    if (!strcasecmp(s, "drop")) {
+        return 0;
+    }
+
+    old_size = actions->size;
+    for (;;) {
+        int retval;
+
+        s += strspn(s, delimiters);
+        if (!*s) {
+            return 0;
+        }
+
+        retval = parse_odp_action(s, port_names, actions);
+        if (retval < 0 || !strchr(delimiters, s[retval])) {
+            actions->size = old_size;
+            return -retval;
+        }
+        s += retval;
+    }
+
+    return 0;
+}
 
 /* Returns the correct length of the payload for a flow key attribute of the
  * specified 'type', or -1 if 'type' is unknown. */
@@ -844,7 +1095,7 @@ odp_flow_key_from_string(const char *s, const struct shash *port_names,
     for (;;) {
         int retval;
 
-        s += strspn(s, ", \t\r\n");
+        s += strspn(s, delimiters);
         if (!*s) {
             return 0;
         }
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 455da69..20b7add 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -59,8 +59,11 @@ odp_port_to_ofp_port(uint16_t odp_port)
         return odp_port;
     }
 }
+
 void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
                         size_t actions_len);
+int odp_actions_from_string(const char *, const struct shash *port_names,
+                            struct ofpbuf *odp_actions);
 
 /* Upper bound on the length of a nlattr-formatted flow key.  The longest
  * nlattr-formatted flow key would be:
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index 499d494..69a66b6 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -37,6 +37,7 @@
 #include "dynamic-string.h"
 #include "netdev.h"
 #include "odp-util.h"
+#include "ofpbuf.h"
 #include "shash.h"
 #include "sset.h"
 #include "timeval.h"
@@ -692,6 +693,30 @@ do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     usage();
 }
+
+/* Undocumented commands for unit testing. */
+
+static void
+do_parse_actions(int argc, char *argv[])
+{
+    int i;
+
+    for (i = 1; i < argc; i++) {
+        struct ofpbuf actions;
+        struct ds s;
+
+        ofpbuf_init(&actions, 0);
+        run(odp_actions_from_string(argv[i], NULL, &actions),
+            "odp_actions_from_string");
+
+        ds_init(&s);
+        format_odp_actions(&s, actions.data, actions.size);
+        puts(ds_cstr(&s));
+        ds_destroy(&s);
+
+        ofpbuf_uninit(&actions);
+    }
+}
 
 static const struct command all_commands[] = {
     { "add-dp", 1, INT_MAX, do_add_dp },
@@ -704,5 +729,9 @@ static const struct command all_commands[] = {
     { "dump-flows", 1, 1, do_dump_flows },
     { "del-flows", 1, 1, do_del_flows },
     { "help", 0, INT_MAX, do_help },
+
+    /* Undocumented commands for testing. */
+    { "parse-actions", 1, INT_MAX, do_parse_actions },
+
     { NULL, 0, 0, NULL },
 };
-- 
1.7.2.5




More information about the dev mailing list