/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
VLOG_DEFINE_THIS_MODULE(ofp_parse);
+static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
+ NO_RETURN;
+
static uint8_t
str_to_table_id(const char *str)
{
{
struct ofp_action_output *oao;
- oao = ofputil_put_OFPAT_OUTPUT(b);
+ oao = ofputil_put_OFPAT10_OUTPUT(b);
oao->port = htons(port);
return oao;
}
ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
}
- oae = ofputil_put_OFPAT_ENQUEUE(b);
+ oae = ofputil_put_OFPAT10_ENQUEUE(b);
oae->port = htons(str_to_u32(port));
oae->queue_id = htonl(str_to_u32(queue));
}
struct ofp_action_tp_port *oata;
switch (code) {
- case OFPUTIL_OFPAT_OUTPUT:
+ case OFPUTIL_ACTION_INVALID:
+ NOT_REACHED();
+
+ case OFPUTIL_OFPAT10_OUTPUT:
parse_output(b, arg);
break;
- case OFPUTIL_OFPAT_SET_VLAN_VID:
- oavv = ofputil_put_OFPAT_SET_VLAN_VID(b);
+ case OFPUTIL_OFPAT10_SET_VLAN_VID:
+ oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b);
oavv->vlan_vid = htons(str_to_u32(arg));
break;
- case OFPUTIL_OFPAT_SET_VLAN_PCP:
- oavp = ofputil_put_OFPAT_SET_VLAN_PCP(b);
+ case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+ oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b);
oavp->vlan_pcp = str_to_u32(arg);
break;
- case OFPUTIL_OFPAT_STRIP_VLAN:
- ofputil_put_OFPAT_STRIP_VLAN(b);
+ case OFPUTIL_OFPAT10_STRIP_VLAN:
+ ofputil_put_OFPAT10_STRIP_VLAN(b);
break;
- case OFPUTIL_OFPAT_SET_DL_SRC:
- case OFPUTIL_OFPAT_SET_DL_DST:
+ case OFPUTIL_OFPAT10_SET_DL_SRC:
+ case OFPUTIL_OFPAT10_SET_DL_DST:
oada = ofputil_put_action(code, b);
str_to_mac(arg, oada->dl_addr);
break;
- case OFPUTIL_OFPAT_SET_NW_SRC:
- case OFPUTIL_OFPAT_SET_NW_DST:
+ case OFPUTIL_OFPAT10_SET_NW_SRC:
+ case OFPUTIL_OFPAT10_SET_NW_DST:
oana = ofputil_put_action(code, b);
str_to_ip(arg, &oana->nw_addr);
break;
- case OFPUTIL_OFPAT_SET_NW_TOS:
- ofputil_put_OFPAT_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
+ case OFPUTIL_OFPAT10_SET_NW_TOS:
+ ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
break;
- case OFPUTIL_OFPAT_SET_TP_SRC:
- case OFPUTIL_OFPAT_SET_TP_DST:
+ case OFPUTIL_OFPAT10_SET_TP_SRC:
+ case OFPUTIL_OFPAT10_SET_TP_DST:
oata = ofputil_put_action(code, b);
oata->tp_port = htons(str_to_u32(arg));
break;
- case OFPUTIL_OFPAT_ENQUEUE:
+ case OFPUTIL_OFPAT10_ENQUEUE:
parse_enqueue(b, arg);
break;
cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
fm->cookie = htonll(0);
fm->cookie_mask = htonll(0);
+ if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
+ /* For modify, by default, don't update the cookie. */
+ fm->new_cookie = htonll(UINT64_MAX);
+ } else{
+ fm->new_cookie = htonll(0);
+ }
fm->table_id = 0xff;
fm->command = command;
fm->idle_timeout = OFP_FLOW_PERMANENT;
fm->hard_timeout = str_to_u16(value, name);
} else if (!strcmp(name, "cookie")) {
char *mask = strchr(value, '/');
+
if (mask) {
+ /* A mask means we're searching for a cookie. */
if (command == OFPFC_ADD) {
ofp_fatal(str_, verbose, "flow additions cannot use "
"a cookie mask");
}
*mask = '\0';
+ fm->cookie = htonll(str_to_u64(value));
fm->cookie_mask = htonll(str_to_u64(mask+1));
} else {
- fm->cookie_mask = htonll(UINT64_MAX);
+ /* No mask means that the cookie is being set. */
+ if (command != OFPFC_ADD && command != OFPFC_MODIFY
+ && command != OFPFC_MODIFY_STRICT) {
+ ofp_fatal(str_, verbose, "cannot set cookie");
+ }
+ fm->new_cookie = htonll(str_to_u64(value));
}
- fm->cookie = htonll(str_to_u64(value));
} else if (mf_from_name(name)) {
parse_field(mf_from_name(name), value, &fm->cr);
} else if (!strcmp(name, "duration")
}
}
}
+ if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
+ && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
+ /* On modifies without a mask, we are supposed to add a flow if
+ * one does not exist. If a cookie wasn't been specified, use a
+ * default of zero. */
+ fm->new_cookie = htonll(0);
+ }
if (fields & F_ACTIONS) {
struct ofpbuf actions;
}
/* 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
- * on the connection; this function will add a message to change the flow
- * format and update '*cur_format', if this is necessary to add the parsed
- * flow. */
+ * (one of OFPFC_*) into 'fm'. */
void
-parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
- bool *flow_mod_table_id, const char *string,
+parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
uint16_t command, bool verbose)
{
- enum nx_flow_format min_format, next_format;
struct cls_rule rule_copy;
- struct ofpbuf *ofm;
- struct ofputil_flow_mod fm;
-
- 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);
- list_push_back(packets, &sff->list_node);
- *cur_format = next_format;
- }
+ parse_ofp_str(fm, command, string, verbose);
/* Normalize a copy of the rule. This ensures that non-normalized flows
* get logged but doesn't affect what gets sent to the switch, so that the
* switch can do whatever it likes with the flow. */
- rule_copy = fm.cr;
- ofputil_normalize_rule(&rule_copy, next_format);
-
- if (fm.table_id != 0xff && !*flow_mod_table_id) {
- struct ofpbuf *sff = ofputil_make_flow_mod_table_id(true);
- list_push_back(packets, &sff->list_node);
- *flow_mod_table_id = true;
- }
-
- ofm = ofputil_encode_flow_mod(&fm, *cur_format, *flow_mod_table_id);
- list_push_back(packets, &ofm->list_node);
+ rule_copy = fm->cr;
+ ofputil_normalize_rule(&rule_copy);
}
-/* Similar to parse_ofp_flow_mod_str(), except that the string is read from
- * 'stream' and the command is always OFPFC_ADD. Returns false if end-of-file
- * is reached before reading a flow, otherwise true. */
-bool
-parse_ofp_flow_mod_file(struct list *packets,
- enum nx_flow_format *cur, bool *flow_mod_table_id,
- FILE *stream, uint16_t command)
+void
+parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
+ struct ofputil_flow_mod **fms, size_t *n_fms)
{
+ size_t allocated_fms;
+ FILE *stream;
struct ds s;
- bool ok;
+ stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
+ if (stream == NULL) {
+ ovs_fatal(errno, "%s: open", file_name);
+ }
+
+ allocated_fms = *n_fms;
ds_init(&s);
- 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, true);
+ while (!ds_get_preprocessed_line(&s, stream)) {
+ if (*n_fms >= allocated_fms) {
+ *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
+ }
+ parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, false);
+ *n_fms += 1;
}
ds_destroy(&s);
- return ok;
+ if (stream != stdin) {
+ fclose(stream);
+ }
}
void
fsr->out_port = fm.out_port;
fsr->table_id = fm.table_id;
}
+
+/* Parses a specification of a flow from 's' into 'flow'. 's' must take the
+ * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a
+ * mf_field. Fields must be specified in a natural order for satisfying
+ * prerequisites.
+ *
+ * Returns NULL on success, otherwise a malloc()'d string that explains the
+ * problem. */
+char *
+parse_ofp_exact_flow(struct flow *flow, const char *s)
+{
+ char *pos, *key, *value_s;
+ char *error = NULL;
+ char *copy;
+
+ memset(flow, 0, sizeof *flow);
+
+ pos = copy = xstrdup(s);
+ while (ofputil_parse_key_value(&pos, &key, &value_s)) {
+ const struct protocol *p;
+ if (parse_protocol(key, &p)) {
+ if (flow->dl_type) {
+ error = xasprintf("%s: Ethernet type set multiple times", s);
+ goto exit;
+ }
+ flow->dl_type = htons(p->dl_type);
+
+ if (p->nw_proto) {
+ if (flow->nw_proto) {
+ error = xasprintf("%s: network protocol set "
+ "multiple times", s);
+ goto exit;
+ }
+ flow->nw_proto = p->nw_proto;
+ }
+ } else {
+ const struct mf_field *mf;
+ union mf_value value;
+ char *field_error;
+
+ mf = mf_from_name(key);
+ if (!mf) {
+ error = xasprintf("%s: unknown field %s", s, key);
+ goto exit;
+ }
+
+ if (!mf_are_prereqs_ok(mf, flow)) {
+ error = xasprintf("%s: prerequisites not met for setting %s",
+ s, key);
+ goto exit;
+ }
+
+ if (!mf_is_zero(mf, flow)) {
+ error = xasprintf("%s: field %s set multiple times", s, key);
+ goto exit;
+ }
+
+ field_error = mf_parse_value(mf, value_s, &value);
+ if (field_error) {
+ error = xasprintf("%s: bad value for %s (%s)",
+ s, key, field_error);
+ free(field_error);
+ goto exit;
+ }
+
+ mf_set_flow_value(mf, &value, flow);
+ }
+ }
+
+exit:
+ free(copy);
+
+ if (error) {
+ memset(flow, 0, sizeof *flow);
+ }
+ return error;
+}