#include <stdlib.h>
#include "autopath.h"
+#include "bundle.h"
#include "byte-order.h"
#include "dynamic-string.h"
#include "netdev.h"
str_to_mac(addr, oada->dl_addr);
}
-
static bool
parse_port_name(const char *name, uint16_t *port)
{
return false;
}
+static void
+parse_output(struct ofpbuf *b, char *arg)
+{
+ if (strchr(arg, '[')) {
+ struct nx_action_output_reg *naor;
+ int ofs, n_bits;
+ uint32_t src;
+
+ nxm_parse_field_bits(arg, &src, &ofs, &n_bits);
+
+ naor = put_action(b, sizeof *naor, OFPAT_VENDOR);
+ naor->vendor = htonl(NX_VENDOR_ID);
+ naor->subtype = htons(NXAST_OUTPUT_REG);
+ naor->ofs_nbits = nxm_encode_ofs_nbits(ofs, n_bits);
+ naor->src = htonl(src);
+ naor->max_len = htons(UINT16_MAX);
+ } else {
+ put_output_action(b, str_to_u32(arg));
+ }
+}
+
+static void
+parse_resubmit(struct nx_action_resubmit *nar, char *arg)
+{
+ char *in_port_s, *table_s;
+ uint16_t in_port;
+ uint8_t table;
+
+ in_port_s = strsep(&arg, ",");
+ if (in_port_s && in_port_s[0]) {
+ if (!parse_port_name(in_port_s, &in_port)) {
+ in_port = str_to_u32(in_port_s);
+ }
+ } else {
+ in_port = OFPP_IN_PORT;
+ }
+
+ table_s = strsep(&arg, ",");
+ table = table_s && table_s[0] ? str_to_u32(table_s) : 255;
+
+ if (in_port == OFPP_IN_PORT && table == 255) {
+ ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified "
+ " on resubmit");
+ }
+
+ nar->vendor = htonl(NX_VENDOR_ID);
+ nar->in_port = htons(in_port);
+ if (in_port != OFPP_IN_PORT && table == 255) {
+ nar->subtype = htons(NXAST_RESUBMIT);
+ } else {
+ nar->subtype = htons(NXAST_RESUBMIT_TABLE);
+ nar->table = table;
+ }
+}
+
static void
str_to_action(char *str, struct ofpbuf *b)
{
} else if (!strcasecmp(act, "resubmit")) {
struct nx_action_resubmit *nar;
nar = put_action(b, sizeof *nar, OFPAT_VENDOR);
- nar->vendor = htonl(NX_VENDOR_ID);
- nar->subtype = htons(NXAST_RESUBMIT);
- nar->in_port = htons(str_to_u32(arg));
+ parse_resubmit(nar, arg);
} else if (!strcasecmp(act, "set_tunnel")
|| !strcasecmp(act, "set_tunnel64")) {
uint64_t tun_id = str_to_u64(arg);
if (remainder) {
ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder);
}
+ nan = (struct nx_action_note *)((char *)b->data + start_ofs);
nan->len = htons(b->size - start_ofs);
} else if (!strcasecmp(act, "move")) {
struct nx_action_reg_move *move;
struct nx_action_autopath *naa;
naa = ofpbuf_put_uninit(b, sizeof *naa);
autopath_parse(naa, arg);
+ } else if (!strcasecmp(act, "bundle")) {
+ bundle_parse(b, arg);
+ } else if (!strcasecmp(act, "bundle_load")) {
+ bundle_parse_load(b, arg);
} else if (!strcasecmp(act, "output")) {
- put_output_action(b, str_to_u32(arg));
+ parse_output(b, arg);
} else if (!strcasecmp(act, "enqueue")) {
char *sp = NULL;
char *port_s = strtok_r(arg, ":q", &sp);
return false;
}
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
#define FIELDS \
FIELD(F_TUN_ID, "tun_id", 0) \
FIELD(F_IN_PORT, "in_port", FWW_IN_PORT) \
flow_wildcards_t wildcard; /* FWW_* bit. */
};
+static void
+ofp_fatal(const char *flow, bool verbose, const char *format, ...)
+{
+ va_list args;
+
+ if (verbose) {
+ fprintf(stderr, "%s:\n", flow);
+ }
+
+ va_start(args, format);
+ ovs_fatal_valist(0, format, args);
+}
+
static bool
parse_field_name(const char *name, const struct field **f_out)
{
static void
parse_reg_value(struct cls_rule *rule, int reg_idx, const char *value)
{
- uint32_t reg_value, reg_mask;
+ /* This uses an oversized destination field (64 bits when 32 bits would do)
+ * 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. */
+ unsigned long long int reg_value, reg_mask;
if (!strcmp(value, "ANY") || !strcmp(value, "*")) {
cls_rule_set_reg_masked(rule, reg_idx, 0, 0);
- } else if (sscanf(value, "%"SCNi32"/%"SCNi32,
+ } else if (sscanf(value, "%lli/%lli",
®_value, ®_mask) == 2) {
cls_rule_set_reg_masked(rule, reg_idx, reg_value, reg_mask);
- } else if (sscanf(value, "%"SCNi32, ®_value)) {
+ } else if (sscanf(value, "%lli", ®_value)) {
cls_rule_set_reg(rule, reg_idx, reg_value);
} else {
ovs_fatal(0, "register fields must take the form <value> "
}
}
-/* Convert 'string' (as described in the Flow Syntax section of the ovs-ofctl
- * man page) into 'pf'. If 'actions' is specified, an action must be in
- * 'string' and may be expanded or reallocated. */
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'fm' for sending the specified flow_mod 'command' to a switch.
+ * If 'actions' is specified, an action must be in 'string' and may be expanded
+ * or reallocated.
+ *
+ * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_*
+ * constant for 'command'. To parse syntax for an OFPST_FLOW or
+ * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */
void
-parse_ofp_str(struct flow_mod *fm, struct ofpbuf *actions, char *string)
+parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
+ bool verbose)
{
+ enum {
+ F_OUT_PORT = 1 << 0,
+ F_ACTIONS = 1 << 1,
+ F_COOKIE = 1 << 2,
+ F_TIMEOUT = 1 << 3,
+ F_PRIORITY = 1 << 4
+ } fields;
+ char *string = xstrdup(str_);
char *save_ptr = NULL;
char *name;
+ switch (command) {
+ case -1:
+ fields = F_OUT_PORT;
+ break;
+
+ case OFPFC_ADD:
+ fields = F_ACTIONS | F_COOKIE | F_TIMEOUT | F_PRIORITY;
+ break;
+
+ case OFPFC_DELETE:
+ fields = F_OUT_PORT;
+ break;
+
+ case OFPFC_DELETE_STRICT:
+ fields = F_OUT_PORT | F_PRIORITY;
+ break;
+
+ case OFPFC_MODIFY:
+ fields = F_ACTIONS | F_COOKIE;
+ break;
+
+ case OFPFC_MODIFY_STRICT:
+ fields = F_ACTIONS | F_COOKIE | F_PRIORITY;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
fm->cookie = htonll(0);
fm->table_id = 0xff;
- fm->command = UINT16_MAX;
+ fm->command = command;
fm->idle_timeout = OFP_FLOW_PERMANENT;
fm->hard_timeout = OFP_FLOW_PERMANENT;
fm->buffer_id = UINT32_MAX;
fm->out_port = OFPP_NONE;
fm->flags = 0;
- if (actions) {
- char *act_str = strstr(string, "action");
+ if (fields & F_ACTIONS) {
+ struct ofpbuf actions;
+ char *act_str;
+
+ act_str = strstr(string, "action");
if (!act_str) {
- ovs_fatal(0, "must specify an action");
+ ofp_fatal(str_, verbose, "must specify an action");
}
*act_str = '\0';
act_str = strchr(act_str + 1, '=');
if (!act_str) {
- ovs_fatal(0, "must specify an action");
+ ofp_fatal(str_, verbose, "must specify an action");
}
act_str++;
- str_to_action(act_str, actions);
- fm->actions = actions->data;
- fm->n_actions = actions->size / sizeof(union ofp_action);
+ ofpbuf_init(&actions, sizeof(union ofp_action));
+ str_to_action(act_str, &actions);
+ fm->actions = ofpbuf_steal_data(&actions);
+ fm->n_actions = actions.size / sizeof(union ofp_action);
} else {
fm->actions = NULL;
fm->n_actions = 0;
value = strtok_r(NULL, ", \t\r\n", &save_ptr);
if (!value) {
- ovs_fatal(0, "field %s missing value", name);
+ ofp_fatal(str_, verbose, "field %s missing value", name);
}
if (!strcmp(name, "table")) {
fm->table_id = atoi(value);
} else if (!strcmp(name, "out_port")) {
fm->out_port = atoi(value);
- } else if (!strcmp(name, "priority")) {
+ } else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
fm->cr.priority = atoi(value);
- } else if (!strcmp(name, "idle_timeout")) {
+ } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
fm->idle_timeout = atoi(value);
- } else if (!strcmp(name, "hard_timeout")) {
+ } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
fm->hard_timeout = atoi(value);
- } else if (!strcmp(name, "cookie")) {
+ } else if (fields & F_COOKIE && !strcmp(name, "cookie")) {
fm->cookie = htonll(str_to_u64(value));
} else if (parse_field_name(name, &f)) {
if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
&& isdigit((unsigned char) name[3])) {
unsigned int reg_idx = atoi(name + 3);
if (reg_idx >= FLOW_N_REGS) {
- ovs_fatal(0, "only %d registers supported", FLOW_N_REGS);
+ if (verbose) {
+ fprintf(stderr, "%s:\n", str_);
+ }
+ ofp_fatal(str_, verbose, "only %d registers supported", FLOW_N_REGS);
}
parse_reg_value(&fm->cr, reg_idx, value);
} else if (!strcmp(name, "duration")
* "ovs-ofctl dump-flows" back into commands that parse
* flows. */
} else {
- ovs_fatal(0, "unknown keyword %s", name);
+ ofp_fatal(str_, verbose, "unknown keyword %s", name);
}
}
}
+
+ free(string);
}
/* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
* flow. */
void
parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
- bool *flow_mod_table_id, char *string, uint16_t command)
+ bool *flow_mod_table_id, char *string, uint16_t command,
+ bool verbose)
{
- bool is_del = command == OFPFC_DELETE || command == OFPFC_DELETE_STRICT;
enum nx_flow_format min_format, next_format;
struct cls_rule rule_copy;
struct ofpbuf actions;
struct ofpbuf *ofm;
- struct flow_mod fm;
+ struct ofputil_flow_mod fm;
ofpbuf_init(&actions, 64);
- parse_ofp_str(&fm, is_del ? NULL : &actions, string);
- fm.command = command;
+ parse_ofp_str(&fm, command, string, verbose);
min_format = ofputil_min_flow_format(&fm.cr);
next_format = MAX(*cur_format, min_format);
ok = ds_get_preprocessed_line(&s, stream) == 0;
if (ok) {
parse_ofp_flow_mod_str(packets, cur, flow_mod_table_id,
- ds_cstr(&s), command);
+ ds_cstr(&s), command, true);
}
ds_destroy(&s);
}
void
-parse_ofp_flow_stats_request_str(struct flow_stats_request *fsr,
+parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
bool aggregate, char *string)
{
- struct flow_mod fm;
+ struct ofputil_flow_mod fm;
- parse_ofp_str(&fm, NULL, string);
+ parse_ofp_str(&fm, -1, string, false);
fsr->aggregate = aggregate;
fsr->match = fm.cr;
fsr->out_port = fm.out_port;