/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "bundle.h"
#include "byte-order.h"
#include "dynamic-string.h"
+#include "learn.h"
#include "meta-flow.h"
#include "netdev.h"
#include "multipath.h"
VLOG_DEFINE_THIS_MODULE(ofp_parse);
+static uint8_t
+str_to_table_id(const char *str)
+{
+ int table_id;
+
+ if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) {
+ ovs_fatal(0, "invalid table \"%s\"", str);
+ }
+ return table_id;
+}
+
+static uint16_t
+str_to_u16(const char *str, const char *name)
+{
+ int value;
+
+ if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) {
+ ovs_fatal(0, "invalid %s \"%s\"", name, str);
+ }
+ return value;
+}
+
static uint32_t
str_to_u32(const char *str)
{
{
if (strchr(arg, '[')) {
struct nx_action_output_reg *naor;
- int ofs, n_bits;
- uint32_t src;
+ struct mf_subfield src;
- nxm_parse_field_bits(arg, &src, &ofs, &n_bits);
+ mf_parse_subfield(&src, arg);
naor = ofputil_put_NXAST_OUTPUT_REG(b);
- naor->ofs_nbits = nxm_encode_ofs_nbits(ofs, n_bits);
- naor->src = htonl(src);
+ naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits);
+ naor->src = htonl(src.field->nxm_header);
naor->max_len = htons(UINT16_MAX);
} else {
put_output_action(b, str_to_u32(arg));
}
static void
-parse_named_action(enum ofputil_action_code code, struct ofpbuf *b, char *arg)
+parse_fin_timeout(struct ofpbuf *b, char *arg)
+{
+ struct nx_action_fin_timeout *naft;
+ char *key, *value;
+
+ naft = ofputil_put_NXAST_FIN_TIMEOUT(b);
+ while (ofputil_parse_key_value(&arg, &key, &value)) {
+ if (!strcmp(key, "idle_timeout")) {
+ naft->fin_idle_timeout = htons(str_to_u16(value, key));
+ } else if (!strcmp(key, "hard_timeout")) {
+ naft->fin_hard_timeout = htons(str_to_u16(value, key));
+ } else {
+ ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key);
+ }
+ }
+}
+
+static void
+parse_named_action(enum ofputil_action_code code, const struct flow *flow,
+ struct ofpbuf *b, char *arg)
{
struct ofp_action_dl_addr *oada;
struct ofp_action_vlan_pcp *oavp;
case OFPUTIL_NXAST_RESUBMIT_TABLE:
case OFPUTIL_NXAST_OUTPUT_REG:
NOT_REACHED();
+
+ case OFPUTIL_NXAST_LEARN:
+ learn_parse(b, arg, flow);
+ break;
+
+ case OFPUTIL_NXAST_EXIT:
+ ofputil_put_NXAST_EXIT(b);
+ break;
+
+ case OFPUTIL_NXAST_DEC_TTL:
+ ofputil_put_NXAST_DEC_TTL(b);
+ break;
+
+ case OFPUTIL_NXAST_FIN_TIMEOUT:
+ parse_fin_timeout(b, arg);
+ break;
}
}
static void
-str_to_action(char *str, struct ofpbuf *b)
+str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
{
char *pos, *act, *arg;
int n_actions;
code = ofputil_action_code_from_name(act);
if (code >= 0) {
- parse_named_action(code, b, arg);
+ parse_named_action(code, flow, b, arg);
} else if (!strcasecmp(act, "drop")) {
/* A drop action in OpenFlow occurs by just not setting
* an action. */
enum {
F_OUT_PORT = 1 << 0,
F_ACTIONS = 1 << 1,
- F_COOKIE = 1 << 2,
F_TIMEOUT = 1 << 3,
- F_PRIORITY = 1 << 4
+ F_PRIORITY = 1 << 4,
+ F_FLAGS = 1 << 5,
} fields;
char *string = xstrdup(str_);
char *save_ptr = NULL;
+ char *act_str = NULL;
char *name;
switch (command) {
break;
case OFPFC_ADD:
- fields = F_ACTIONS | F_COOKIE | F_TIMEOUT | F_PRIORITY;
+ fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS;
break;
case OFPFC_DELETE:
break;
case OFPFC_MODIFY:
- fields = F_ACTIONS | F_COOKIE;
+ fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS;
break;
case OFPFC_MODIFY_STRICT:
- fields = F_ACTIONS | F_COOKIE | F_PRIORITY;
+ fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS;
break;
default:
cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
fm->table_id = 0xff;
fm->command = command;
fm->idle_timeout = OFP_FLOW_PERMANENT;
fm->out_port = OFPP_NONE;
fm->flags = 0;
if (fields & F_ACTIONS) {
- struct ofpbuf actions;
- char *act_str;
-
act_str = strstr(string, "action");
if (!act_str) {
ofp_fatal(str_, verbose, "must specify an action");
}
act_str++;
-
- 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;
}
for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
if (p->nw_proto) {
cls_rule_set_nw_proto(&fm->cr, p->nw_proto);
}
+ } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) {
+ fm->flags |= OFPFF_SEND_FLOW_REM;
+ } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) {
+ fm->flags |= OFPFF_CHECK_OVERLAP;
} else {
char *value;
}
if (!strcmp(name, "table")) {
- fm->table_id = atoi(value);
+ fm->table_id = str_to_table_id(value);
} else if (!strcmp(name, "out_port")) {
fm->out_port = atoi(value);
} else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
- fm->cr.priority = atoi(value);
+ fm->cr.priority = str_to_u16(value, name);
} else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
- fm->idle_timeout = atoi(value);
+ fm->idle_timeout = str_to_u16(value, name);
} else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
- fm->hard_timeout = atoi(value);
- } else if (fields & F_COOKIE && !strcmp(name, "cookie")) {
+ fm->hard_timeout = str_to_u16(value, name);
+ } else if (!strcmp(name, "cookie")) {
+ char *mask = strchr(value, '/');
+ if (mask) {
+ if (command == OFPFC_ADD) {
+ ofp_fatal(str_, verbose, "flow additions cannot use "
+ "a cookie mask");
+ }
+ *mask = '\0';
+ fm->cookie_mask = htonll(str_to_u64(mask+1));
+ } else {
+ fm->cookie_mask = htonll(UINT64_MAX);
+ }
fm->cookie = htonll(str_to_u64(value));
} else if (mf_from_name(name)) {
parse_field(mf_from_name(name), value, &fm->cr);
}
}
}
+ if (fields & F_ACTIONS) {
+ struct ofpbuf actions;
+
+ ofpbuf_init(&actions, sizeof(union ofp_action));
+ str_to_action(&fm->cr.flow, 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;
+ }
free(string);
}
+/* Parses 's' as a set of OpenFlow actions and appends the actions to
+ * 'actions'.
+ *
+ * Prints an error on stderr and aborts the program if 's' syntax is
+ * invalid. */
+void
+parse_ofp_actions(const char *s_, struct ofpbuf *actions)
+{
+ char *s = xstrdup(s_);
+ str_to_action(NULL, s, actions);
+ free(s);
+}
+
/* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
* (one of OFPFC_*) and appends the parsed OpenFlow message to 'packets'.
* '*cur_format' should initially contain the flow format currently configured
* 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 verbose)
+ bool *flow_mod_table_id, const char *string,
+ uint16_t command, bool verbose)
{
enum nx_flow_format min_format, next_format;
struct cls_rule rule_copy;
- struct ofpbuf actions;
struct ofpbuf *ofm;
struct ofputil_flow_mod fm;
- ofpbuf_init(&actions, 64);
parse_ofp_str(&fm, command, string, verbose);
min_format = ofputil_min_flow_format(&fm.cr);
+ if (command != OFPFC_ADD && fm.cookie_mask != htonll(0)) {
+ min_format = NXFF_NXM;
+ }
next_format = MAX(*cur_format, min_format);
if (next_format != *cur_format) {
struct ofpbuf *sff = ofputil_make_set_flow_format(next_format);
ofm = ofputil_encode_flow_mod(&fm, *cur_format, *flow_mod_table_id);
list_push_back(packets, &ofm->list_node);
-
- ofpbuf_uninit(&actions);
}
/* Similar to parse_ofp_flow_mod_str(), except that the string is read from
void
parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
- bool aggregate, char *string)
+ bool aggregate, const char *string)
{
struct ofputil_flow_mod fm;
parse_ofp_str(&fm, -1, string, false);
fsr->aggregate = aggregate;
+ fsr->cookie = fm.cookie;
+ fsr->cookie_mask = fm.cookie_mask;
fsr->match = fm.cr;
fsr->out_port = fm.out_port;
fsr->table_id = fm.table_id;