[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