send_features_request(sw, rconn);
if (cfg->default_flows) {
- const struct ofpbuf *b;
-
- LIST_FOR_EACH (b, list_node, cfg->default_flows) {
- struct ofpbuf *copy = ofpbuf_clone(b);
- int error = rconn_send(rconn, copy, NULL);
- if (error) {
- VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)",
- rconn_get_name(rconn), strerror(error));
- ofpbuf_delete(copy);
- break;
+ enum ofputil_protocol usable_protocols;
+ enum ofputil_protocol protocol;
+ struct ofpbuf *msg = NULL;
+ int ofp_version;
+ int error = 0;
+ size_t i;
+
+ /* Figure out the initial protocol on the connection. */
+ ofp_version = rconn_get_version(rconn);
+ protocol = ofputil_protocol_from_ofp_version(ofp_version);
+
+ /* If the initial protocol isn't good enough for default_flows, then
+ * pick one that will work and encode messages to set up that
+ * protocol.
+ *
+ * This could be improved by actually negotiating a mutually acceptable
+ * flow format with the switch, but that would require an asynchronous
+ * state machine. This version ought to work fine in practice. */
+ usable_protocols = ofputil_flow_mod_usable_protocols(
+ cfg->default_flows, cfg->n_default_flows);
+ if (!(protocol & usable_protocols)) {
+ enum ofputil_protocol want = rightmost_1bit(usable_protocols);
+ while (!error) {
+ msg = ofputil_encode_set_protocol(protocol, want, &protocol);
+ if (!msg) {
+ break;
+ }
+ error = rconn_send(rconn, msg, NULL);
}
}
+
+ for (i = 0; !error && i < cfg->n_default_flows; i++) {
+ msg = ofputil_encode_flow_mod(&cfg->default_flows[i], protocol);
+ error = rconn_send(rconn, msg, NULL);
+ }
+
+ if (error) {
+ VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)",
+ rconn_get_name(rconn), strerror(error));
+ ofpbuf_delete(msg);
+ }
}
return sw;
* OFP_FLOW_PERMANENT: Set up permanent flows. */
int max_idle;
- /* Optionally, a list of one or more "struct ofpbuf"s containing OpenFlow
- * messages to send to the switch at time of connection. Presumably these
- * will be OFPT_FLOW_MOD requests to set up the flow table. */
- const struct list *default_flows;
+ /* Optional "flow mod" requests to send to the switch at connection time,
+ * to set up the flow table. */
+ const struct ofputil_flow_mod *default_flows;
+ size_t n_default_flows;
/* The OpenFlow queue to use by default. Use UINT32_MAX to avoid
* specifying a particular queue. */
}
/* 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
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
-#include "openflow/nicira-ext.h"
-struct list;
struct ofpbuf;
struct ofputil_flow_mod;
struct ofputil_flow_stats_request;
void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
bool verbose);
-void parse_ofp_flow_mod_str(struct list *packets,
- enum nx_flow_format *cur, bool *flow_mod_table_id,
- const char *string, uint16_t command, bool verbose);
-bool parse_ofp_flow_mod_file(struct list *packets,
- enum nx_flow_format *cur, bool *flow_mod_table_id,
- FILE *, uint16_t command);
+void parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
+ uint16_t command, bool verbose);
+void parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
+ struct ofputil_flow_mod **fms, size_t *n_fms);
void parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
bool aggregate, const char *string);
bool need_priority;
enum ofperr error;
- error = ofputil_decode_flow_mod(&fm, oh, true);
+ error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID);
if (error) {
ofp_print_error(s, error);
return;
uint32_t format = ntohl(nsff->format);
ds_put_cstr(string, " format=");
- if (ofputil_flow_format_is_valid(format)) {
- ds_put_cstr(string, ofputil_flow_format_to_string(format));
+ if (ofputil_nx_flow_format_is_valid(format)) {
+ ds_put_cstr(string, ofputil_nx_flow_format_to_string(format));
} else {
ds_put_format(string, "%"PRIu32, format);
}
return type->code;
}
\f
-/* Flow formats. */
+/* Protocols. */
+struct proto_abbrev {
+ enum ofputil_protocol protocol;
+ const char *name;
+};
+
+/* Most users really don't care about some of the differences between
+ * protocols. These abbreviations help with that. */
+static const struct proto_abbrev proto_abbrevs[] = {
+ { OFPUTIL_P_ANY, "any" },
+ { OFPUTIL_P_OF10_ANY, "OpenFlow10" },
+ { OFPUTIL_P_NXM_ANY, "NXM" },
+};
+#define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs)
+
+enum ofputil_protocol ofputil_flow_dump_protocols[] = {
+ OFPUTIL_P_NXM,
+ OFPUTIL_P_OF10,
+};
+size_t ofputil_n_flow_dump_protocols = ARRAY_SIZE(ofputil_flow_dump_protocols);
+
+/* Returns the ofputil_protocol that is initially in effect on an OpenFlow
+ * connection that has negotiated the given 'version'. 'version' should
+ * normally be an 8-bit OpenFlow version identifier (e.g. 0x01 for OpenFlow
+ * 1.0, 0x02 for OpenFlow 1.1). Returns 0 if 'version' is not supported or
+ * outside the valid range. */
+enum ofputil_protocol
+ofputil_protocol_from_ofp_version(int version)
+{
+ switch (version) {
+ case OFP_VERSION: return OFPUTIL_P_OF10;
+ default: return 0;
+ }
+}
+
+/* Returns true if 'protocol' is a single OFPUTIL_P_* value, false
+ * otherwise. */
bool
-ofputil_flow_format_is_valid(enum nx_flow_format flow_format)
+ofputil_protocol_is_valid(enum ofputil_protocol protocol)
{
- switch (flow_format) {
- case NXFF_OPENFLOW10:
- case NXFF_NXM:
- return true;
+ return protocol & OFPUTIL_P_ANY && is_pow2(protocol);
+}
+
+/* Returns the equivalent of 'protocol' with the Nicira flow_mod_table_id
+ * extension turned on or off if 'enable' is true or false, respectively.
+ *
+ * This extension is only useful for protocols whose "standard" version does
+ * not allow specific tables to be modified. In particular, this is true of
+ * OpenFlow 1.0. In later versions of OpenFlow, a flow_mod request always
+ * specifies a table ID and so there is no need for such an extension. When
+ * 'protocol' is such a protocol that doesn't need a flow_mod_table_id
+ * extension, this function just returns its 'protocol' argument unchanged
+ * regardless of the value of 'enable'. */
+enum ofputil_protocol
+ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable)
+{
+ switch (protocol) {
+ case OFPUTIL_P_OF10:
+ case OFPUTIL_P_OF10_TID:
+ return enable ? OFPUTIL_P_OF10_TID : OFPUTIL_P_OF10;
+
+ case OFPUTIL_P_NXM:
+ case OFPUTIL_P_NXM_TID:
+ return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM;
+
+ default:
+ NOT_REACHED();
}
+}
- return false;
+/* Returns the "base" version of 'protocol'. That is, if 'protocol' includes
+ * some extension to a standard protocol version, the return value is the
+ * standard version of that protocol without any extension. If 'protocol' is a
+ * standard protocol version, returns 'protocol' unchanged. */
+enum ofputil_protocol
+ofputil_protocol_to_base(enum ofputil_protocol protocol)
+{
+ return ofputil_protocol_set_tid(protocol, false);
}
-const char *
-ofputil_flow_format_to_string(enum nx_flow_format flow_format)
+/* Returns 'new_base' with any extensions taken from 'cur'. */
+enum ofputil_protocol
+ofputil_protocol_set_base(enum ofputil_protocol cur,
+ enum ofputil_protocol new_base)
{
- switch (flow_format) {
- case NXFF_OPENFLOW10:
- return "openflow10";
- case NXFF_NXM:
- return "nxm";
+ bool tid = (cur & OFPUTIL_P_TID) != 0;
+
+ switch (new_base) {
+ case OFPUTIL_P_OF10:
+ case OFPUTIL_P_OF10_TID:
+ return ofputil_protocol_set_tid(OFPUTIL_P_OF10, tid);
+
+ case OFPUTIL_P_NXM:
+ case OFPUTIL_P_NXM_TID:
+ return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid);
+
default:
NOT_REACHED();
}
}
-int
-ofputil_flow_format_from_string(const char *s)
+/* Returns a string form of 'protocol', if a simple form exists (that is, if
+ * 'protocol' is either a single protocol or it is a combination of protocols
+ * that have a single abbreviation). Otherwise, returns NULL. */
+const char *
+ofputil_protocol_to_string(enum ofputil_protocol protocol)
{
- return (!strcmp(s, "openflow10") ? NXFF_OPENFLOW10
- : !strcmp(s, "nxm") ? NXFF_NXM
- : -1);
+ const struct proto_abbrev *p;
+
+ /* Use a "switch" statement for single-bit names so that we get a compiler
+ * warning if we forget any. */
+ switch (protocol) {
+ case OFPUTIL_P_NXM:
+ return "NXM-table_id";
+
+ case OFPUTIL_P_NXM_TID:
+ return "NXM+table_id";
+
+ case OFPUTIL_P_OF10:
+ return "OpenFlow10-table_id";
+
+ case OFPUTIL_P_OF10_TID:
+ return "OpenFlow10+table_id";
+ }
+
+ /* Check abbreviations. */
+ for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
+ if (protocol == p->protocol) {
+ return p->name;
+ }
+ }
+
+ return NULL;
+}
+
+/* Returns a string that represents 'protocols'. The return value might be a
+ * comma-separated list if 'protocols' doesn't have a simple name. The return
+ * value is "none" if 'protocols' is 0.
+ *
+ * The caller must free the returned string (with free()). */
+char *
+ofputil_protocols_to_string(enum ofputil_protocol protocols)
+{
+ struct ds s;
+
+ assert(!(protocols & ~OFPUTIL_P_ANY));
+ if (protocols == 0) {
+ return xstrdup("none");
+ }
+
+ ds_init(&s);
+ while (protocols) {
+ const struct proto_abbrev *p;
+ int i;
+
+ if (s.length) {
+ ds_put_char(&s, ',');
+ }
+
+ for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
+ if ((protocols & p->protocol) == p->protocol) {
+ ds_put_cstr(&s, p->name);
+ protocols &= ~p->protocol;
+ goto match;
+ }
+ }
+
+ for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) {
+ enum ofputil_protocol bit = 1u << i;
+
+ if (protocols & bit) {
+ ds_put_cstr(&s, ofputil_protocol_to_string(bit));
+ protocols &= ~bit;
+ goto match;
+ }
+ }
+ NOT_REACHED();
+
+ match: ;
+ }
+ return ds_steal_cstr(&s);
+}
+
+static enum ofputil_protocol
+ofputil_protocol_from_string__(const char *s, size_t n)
+{
+ const struct proto_abbrev *p;
+ int i;
+
+ for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) {
+ enum ofputil_protocol bit = 1u << i;
+ const char *name = ofputil_protocol_to_string(bit);
+
+ if (name && n == strlen(name) && !strncasecmp(s, name, n)) {
+ return bit;
+ }
+ }
+
+ for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
+ if (n == strlen(p->name) && !strncasecmp(s, p->name, n)) {
+ return p->protocol;
+ }
+ }
+
+ return 0;
+}
+
+/* Returns the nonempty set of protocols represented by 's', which can be a
+ * single protocol name or abbreviation or a comma-separated list of them.
+ *
+ * Aborts the program with an error message if 's' is invalid. */
+enum ofputil_protocol
+ofputil_protocols_from_string(const char *s)
+{
+ const char *orig_s = s;
+ enum ofputil_protocol protocols;
+
+ protocols = 0;
+ while (*s) {
+ enum ofputil_protocol p;
+ size_t n;
+
+ n = strcspn(s, ",");
+ if (n == 0) {
+ s++;
+ continue;
+ }
+
+ p = ofputil_protocol_from_string__(s, n);
+ if (!p) {
+ ovs_fatal(0, "%.*s: unknown flow protocol", (int) n, s);
+ }
+ protocols |= p;
+
+ s += n;
+ }
+
+ if (!protocols) {
+ ovs_fatal(0, "%s: no flow protocol specified", orig_s);
+ }
+ return protocols;
}
bool
return true;
}
-/* Returns the minimum nx_flow_format to use for sending 'rule' to a switch
- * (e.g. to add or remove a flow). Only NXM can handle tunnel IDs, registers,
- * or fixing the Ethernet multicast bit. Otherwise, it's better to use
- * NXFF_OPENFLOW10 for backward compatibility. */
-enum nx_flow_format
-ofputil_min_flow_format(const struct cls_rule *rule)
+/* Returns a bit-mask of ofputil_protocols that can be used for sending 'rule'
+ * to a switch (e.g. to add or remove a flow). Only NXM can handle tunnel IDs,
+ * registers, or fixing the Ethernet multicast bit. Otherwise, it's better to
+ * use OpenFlow 1.0 protocol for backward compatibility. */
+enum ofputil_protocol
+ofputil_usable_protocols(const struct cls_rule *rule)
{
const struct flow_wildcards *wc = &rule->wc;
/* Only NXM supports separately wildcards the Ethernet multicast bit. */
if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching ARP hardware addresses. */
if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching IPv6 traffic. */
if (!(wc->wildcards & FWW_DL_TYPE)
&& (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching registers. */
if (!regs_fully_wildcarded(wc)) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching tun_id. */
if (wc->tun_id_mask != htonll(0)) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching fragments. */
if (wc->nw_frag_mask) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching IPv6 flow label. */
if (!(wc->wildcards & FWW_IPV6_LABEL)) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching IP ECN bits. */
if (!(wc->wildcards & FWW_NW_ECN)) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching IP TTL/hop limit. */
if (!(wc->wildcards & FWW_NW_TTL)) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports bitwise matching on transport port. */
if ((wc->tp_src_mask && wc->tp_src_mask != htons(UINT16_MAX)) ||
(wc->tp_dst_mask && wc->tp_dst_mask != htons(UINT16_MAX))) {
- return NXFF_NXM;
+ return OFPUTIL_P_NXM_ANY;
}
/* Other formats can express this rule. */
- return NXFF_OPENFLOW10;
+ return OFPUTIL_P_ANY;
+}
+
+/* Returns an OpenFlow message that, sent on an OpenFlow connection whose
+ * protocol is 'current', at least partly transitions the protocol to 'want'.
+ * Stores in '*next' the protocol that will be in effect on the OpenFlow
+ * connection if the switch processes the returned message correctly. (If
+ * '*next != want' then the caller will have to iterate.)
+ *
+ * If 'current == want', returns NULL and stores 'current' in '*next'. */
+struct ofpbuf *
+ofputil_encode_set_protocol(enum ofputil_protocol current,
+ enum ofputil_protocol want,
+ enum ofputil_protocol *next)
+{
+ enum ofputil_protocol cur_base, want_base;
+ bool cur_tid, want_tid;
+
+ cur_base = ofputil_protocol_to_base(current);
+ want_base = ofputil_protocol_to_base(want);
+ if (cur_base != want_base) {
+ *next = ofputil_protocol_set_base(current, want_base);
+
+ switch (want_base) {
+ case OFPUTIL_P_NXM:
+ return ofputil_encode_nx_set_flow_format(NXFF_NXM);
+
+ case OFPUTIL_P_OF10:
+ return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
+
+ case OFPUTIL_P_OF10_TID:
+ case OFPUTIL_P_NXM_TID:
+ NOT_REACHED();
+ }
+ }
+
+ cur_tid = (current & OFPUTIL_P_TID) != 0;
+ want_tid = (want & OFPUTIL_P_TID) != 0;
+ if (cur_tid != want_tid) {
+ *next = ofputil_protocol_set_tid(current, want_tid);
+ return ofputil_make_flow_mod_table_id(want_tid);
+ }
+
+ assert(current == want);
+
+ *next = current;
+ return NULL;
}
-/* Returns an OpenFlow message that can be used to set the flow format to
- * 'flow_format'. */
+/* Returns an NXT_SET_FLOW_FORMAT message that can be used to set the flow
+ * format to 'nxff'. */
struct ofpbuf *
-ofputil_make_set_flow_format(enum nx_flow_format flow_format)
+ofputil_encode_nx_set_flow_format(enum nx_flow_format nxff)
{
struct nx_set_flow_format *sff;
struct ofpbuf *msg;
+ assert(ofputil_nx_flow_format_is_valid(nxff));
+
sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
- sff->format = htonl(flow_format);
+ sff->format = htonl(nxff);
return msg;
}
+/* Returns the base protocol if 'flow_format' is a valid NXFF_* value, false
+ * otherwise. */
+enum ofputil_protocol
+ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format)
+{
+ switch (flow_format) {
+ case NXFF_OPENFLOW10:
+ return OFPUTIL_P_OF10;
+
+ case NXFF_NXM:
+ return OFPUTIL_P_NXM;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns true if 'flow_format' is a valid NXFF_* value, false otherwise. */
+bool
+ofputil_nx_flow_format_is_valid(enum nx_flow_format flow_format)
+{
+ return ofputil_nx_flow_format_to_protocol(flow_format) != 0;
+}
+
+/* Returns a string version of 'flow_format', which must be a valid NXFF_*
+ * value. */
+const char *
+ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format)
+{
+ switch (flow_format) {
+ case NXFF_OPENFLOW10:
+ return "openflow10";
+ case NXFF_NXM:
+ return "nxm";
+ default:
+ NOT_REACHED();
+ }
+}
+
struct ofpbuf *
ofputil_make_set_packet_in_format(enum nx_packet_in_format packet_in_format)
{
* flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
* code.
*
- * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
- * enabled, false otherwise.
- *
* Does not validate the flow_mod actions. */
enum ofperr
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
- const struct ofp_header *oh, bool flow_mod_table_id)
+ const struct ofp_header *oh,
+ enum ofputil_protocol protocol)
{
const struct ofputil_msg_type *type;
uint16_t command;
/* Translate the rule. */
ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
- ofputil_normalize_rule(&fm->cr, NXFF_OPENFLOW10);
+ ofputil_normalize_rule(&fm->cr);
/* Translate the message. */
fm->cookie = ofm->cookie;
NOT_REACHED();
}
- if (flow_mod_table_id) {
+ if (protocol & OFPUTIL_P_TID) {
fm->command = command & 0xff;
fm->table_id = command >> 8;
} else {
}
/* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
- * 'flow_format' and returns the message.
+ * 'protocol' and returns the message.
*
* 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
* enabled, false otherwise. */
struct ofpbuf *
ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
- enum nx_flow_format flow_format,
- bool flow_mod_table_id)
+ enum ofputil_protocol protocol)
{
size_t actions_len = fm->n_actions * sizeof *fm->actions;
+ struct ofp_flow_mod *ofm;
+ struct nx_flow_mod *nfm;
struct ofpbuf *msg;
uint16_t command;
+ int match_len;
- command = (flow_mod_table_id
+ command = (protocol & OFPUTIL_P_TID
? (fm->command & 0xff) | (fm->table_id << 8)
: fm->command);
- if (flow_format == NXFF_OPENFLOW10) {
- struct ofp_flow_mod *ofm;
-
+ switch (protocol) {
+ case OFPUTIL_P_OF10:
+ case OFPUTIL_P_OF10_TID:
msg = ofpbuf_new(sizeof *ofm + actions_len);
ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg);
ofputil_cls_rule_to_match(&fm->cr, &ofm->match);
ofm->buffer_id = htonl(fm->buffer_id);
ofm->out_port = htons(fm->out_port);
ofm->flags = htons(fm->flags);
- } else if (flow_format == NXFF_NXM) {
- struct nx_flow_mod *nfm;
- int match_len;
+ break;
+ case OFPUTIL_P_NXM:
+ case OFPUTIL_P_NXM_TID:
msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
nfm = msg->data;
nfm->out_port = htons(fm->out_port);
nfm->flags = htons(fm->flags);
nfm->match_len = htons(match_len);
- } else {
+ break;
+
+ default:
NOT_REACHED();
}
return msg;
}
+/* Returns a bitmask with a 1-bit for each protocol that could be used to
+ * send all of the 'n_fm's flow table modification requests in 'fms', and a
+ * 0-bit for each protocol that is inadequate.
+ *
+ * (The return value will have at least one 1-bit.) */
+enum ofputil_protocol
+ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
+ size_t n_fms)
+{
+ enum ofputil_protocol usable_protocols;
+ size_t i;
+
+ usable_protocols = OFPUTIL_P_ANY;
+ for (i = 0; i < n_fms; i++) {
+ const struct ofputil_flow_mod *fm = &fms[i];
+
+ usable_protocols &= ofputil_usable_protocols(&fm->cr);
+ if (fm->table_id != 0xff) {
+ usable_protocols &= OFPUTIL_P_TID;
+ }
+ if (fm->command != OFPFC_ADD && fm->cookie_mask != htonll(0)) {
+ usable_protocols &= OFPUTIL_P_NXM_ANY;
+ }
+ }
+ assert(usable_protocols);
+
+ return usable_protocols;
+}
+
static enum ofperr
ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
const struct ofp_header *oh,
/* Converts abstract flow_stats_request 'fsr' into an OFPST_FLOW,
* OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE request 'oh' according to
- * 'flow_format', and returns the message. */
+ * 'protocol', and returns the message. */
struct ofpbuf *
ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
- enum nx_flow_format flow_format)
+ enum ofputil_protocol protocol)
{
struct ofpbuf *msg;
- if (flow_format == NXFF_OPENFLOW10) {
+ switch (protocol) {
+ case OFPUTIL_P_OF10:
+ case OFPUTIL_P_OF10_TID: {
struct ofp_flow_stats_request *ofsr;
int type;
ofputil_cls_rule_to_match(&fsr->match, &ofsr->match);
ofsr->table_id = fsr->table_id;
ofsr->out_port = htons(fsr->out_port);
- } else if (flow_format == NXFF_NXM) {
+ break;
+ }
+
+ case OFPUTIL_P_NXM:
+ case OFPUTIL_P_NXM_TID: {
struct nx_flow_stats_request *nfsr;
int match_len;
int subtype;
nfsr->out_port = htons(fsr->out_port);
nfsr->match_len = htons(match_len);
nfsr->table_id = fsr->table_id;
- } else {
+ break;
+ }
+
+ default:
NOT_REACHED();
}
return msg;
}
+/* Returns a bitmask with a 1-bit for each protocol that could be used to
+ * accurately encode 'fsr', and a 0-bit for each protocol that is inadequate.
+ *
+ * (The return value will have at least one 1-bit.) */
+enum ofputil_protocol
+ofputil_flow_stats_request_usable_protocols(
+ const struct ofputil_flow_stats_request *fsr)
+{
+ enum ofputil_protocol usable_protocols;
+
+ usable_protocols = ofputil_usable_protocols(&fsr->match);
+ if (fsr->cookie_mask != htonll(0)) {
+ usable_protocols &= OFPUTIL_P_NXM_ANY;
+ }
+ return usable_protocols;
+}
+
/* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract
* ofputil_flow_stats in 'fs'.
*
}
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
- * NXST_AGGREGATE reply according to 'flow_format', and returns the message. */
+ * NXST_AGGREGATE reply according to 'protocol', and returns the message. */
struct ofpbuf *
ofputil_encode_aggregate_stats_reply(
const struct ofputil_aggregate_stats *stats,
}
/* Converts abstract ofputil_flow_removed 'fr' into an OFPT_FLOW_REMOVED or
- * NXT_FLOW_REMOVED message 'oh' according to 'flow_format', and returns the
+ * NXT_FLOW_REMOVED message 'oh' according to 'protocol', and returns the
* message. */
struct ofpbuf *
ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
- enum nx_flow_format flow_format)
+ enum ofputil_protocol protocol)
{
struct ofpbuf *msg;
- if (flow_format == NXFF_OPENFLOW10) {
+ switch (protocol) {
+ case OFPUTIL_P_OF10:
+ case OFPUTIL_P_OF10_TID: {
struct ofp_flow_removed *ofr;
ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0),
ofr->idle_timeout = htons(fr->idle_timeout);
ofr->packet_count = htonll(unknown_to_zero(fr->packet_count));
ofr->byte_count = htonll(unknown_to_zero(fr->byte_count));
- } else if (flow_format == NXFF_NXM) {
+ break;
+ }
+
+ case OFPUTIL_P_NXM:
+ case OFPUTIL_P_NXM_TID: {
struct nx_flow_removed *nfr;
int match_len;
nfr->match_len = htons(match_len);
nfr->packet_count = htonll(fr->packet_count);
nfr->byte_count = htonll(fr->byte_count);
- } else {
+ break;
+ }
+
+ default:
NOT_REACHED();
}
* example, Open vSwitch does not understand SCTP, an L4 protocol, so the
* L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
* SCTP flow.
- *
- * 'flow_format' specifies the format of the flow as received or as intended to
- * be sent. This is important for IPv6 and ARP, for which NXM supports more
- * detailed matching. */
+ */
void
-ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format)
+ofputil_normalize_rule(struct cls_rule *rule)
{
enum {
MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */
rule->flow.nw_proto == IPPROTO_ICMP) {
may_match |= MAY_TP_ADDR;
}
- } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6)
- && flow_format == NXFF_NXM) {
+ } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6)) {
may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
if (rule->flow.nw_proto == IPPROTO_TCP ||
rule->flow.nw_proto == IPPROTO_UDP) {
}
}
} else if (rule->flow.dl_type == htons(ETH_TYPE_ARP)) {
- may_match = MAY_NW_PROTO | MAY_NW_ADDR;
- if (flow_format == NXFF_NXM) {
- may_match |= MAY_ARP_SHA | MAY_ARP_THA;
- }
+ may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
} else {
may_match = 0;
}
ovs_be32 ofputil_wcbits_to_netmask(int wcbits);
int ofputil_netmask_to_wcbits(ovs_be32 netmask);
+/* Protocols.
+ *
+ * These are arranged from most portable to least portable, or alternatively
+ * from least powerful to most powerful. Formats earlier on the list are more
+ * likely to be understood for the purpose of making requests, but formats
+ * later on the list are more likely to accurately describe a flow within a
+ * switch.
+ *
+ * On any given OpenFlow connection, a single protocol is in effect at any
+ * given time. These values use separate bits only because that makes it easy
+ * to test whether a particular protocol is within a given set of protocols and
+ * to implement set union and intersection.
+ */
+enum ofputil_protocol {
+ /* OpenFlow 1.0-based protocols. */
+ OFPUTIL_P_OF10 = 1 << 0, /* OpenFlow 1.0 flow format. */
+ OFPUTIL_P_OF10_TID = 1 << 1, /* OF1.0 + flow_mod_table_id extension. */
+#define OFPUTIL_P_OF10_ANY (OFPUTIL_P_OF10 | OFPUTIL_P_OF10_TID)
+
+ /* OpenFlow 1.0 with NXM-based flow formats. */
+ OFPUTIL_P_NXM = 1 << 2, /* Nicira extended match. */
+ OFPUTIL_P_NXM_TID = 1 << 3, /* NXM + flow_mod_table_id extension. */
+#define OFPUTIL_P_NXM_ANY (OFPUTIL_P_NXM | OFPUTIL_P_NXM_TID)
+
+ /* All protocols. */
+#define OFPUTIL_P_ANY (OFPUTIL_P_OF10_ANY | OFPUTIL_P_NXM_ANY)
+
+ /* Protocols in which a specific table may be specified in flow_mods. */
+#define OFPUTIL_P_TID (OFPUTIL_P_OF10_TID | OFPUTIL_P_NXM_TID)
+};
+
+/* Protocols to use for flow dumps, from most to least preferred. */
+extern enum ofputil_protocol ofputil_flow_dump_protocols[];
+extern size_t ofputil_n_flow_dump_protocols;
+
+enum ofputil_protocol ofputil_protocol_from_ofp_version(int version);
+bool ofputil_protocol_is_valid(enum ofputil_protocol);
+enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol,
+ bool enable);
+enum ofputil_protocol ofputil_protocol_to_base(enum ofputil_protocol);
+enum ofputil_protocol ofputil_protocol_set_base(
+ enum ofputil_protocol cur, enum ofputil_protocol new_base);
+
+const char *ofputil_protocol_to_string(enum ofputil_protocol);
+char *ofputil_protocols_to_string(enum ofputil_protocol);
+enum ofputil_protocol ofputil_protocols_from_string(const char *);
+enum ofputil_protocol ofputil_usable_protocols(const struct cls_rule *);
+
+struct ofpbuf *ofputil_encode_set_protocol(enum ofputil_protocol current,
+ enum ofputil_protocol want,
+ enum ofputil_protocol *next);
+
+/* nx_flow_format */
+struct ofpbuf *ofputil_encode_nx_set_flow_format(enum nx_flow_format);
+enum ofputil_protocol ofputil_nx_flow_format_to_protocol(enum nx_flow_format);
+bool ofputil_nx_flow_format_is_valid(enum nx_flow_format);
+const char *ofputil_nx_flow_format_to_string(enum nx_flow_format);
+
/* Work with OpenFlow 1.0 ofp_match. */
void ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *);
void ofputil_cls_rule_from_match(const struct ofp_match *,
unsigned int priority, struct cls_rule *);
-void ofputil_normalize_rule(struct cls_rule *, enum nx_flow_format);
+void ofputil_normalize_rule(struct cls_rule *);
void ofputil_cls_rule_to_match(const struct cls_rule *, struct ofp_match *);
/* dl_type translation between OpenFlow and 'struct flow' format. */
ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type);
ovs_be16 ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type);
-/* Flow formats. */
-bool ofputil_flow_format_is_valid(enum nx_flow_format);
-const char *ofputil_flow_format_to_string(enum nx_flow_format);
-int ofputil_flow_format_from_string(const char *);
-enum nx_flow_format ofputil_min_flow_format(const struct cls_rule *);
-
-struct ofpbuf *ofputil_make_set_flow_format(enum nx_flow_format);
-
/* PACKET_IN. */
bool ofputil_packet_in_format_is_valid(enum nx_packet_in_format);
int ofputil_packet_in_format_from_string(const char *);
/* NXT_FLOW_MOD_TABLE_ID extension. */
struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
-/* Flow format independent flow_mod. */
+/* Protocol-independent flow_mod. */
struct ofputil_flow_mod {
struct cls_rule cr;
ovs_be64 cookie;
enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
const struct ofp_header *,
- bool flow_mod_table_id);
+ enum ofputil_protocol);
struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
- enum nx_flow_format,
- bool flow_mod_table_id);
+ enum ofputil_protocol);
+
+enum ofputil_protocol ofputil_flow_mod_usable_protocols(
+ const struct ofputil_flow_mod *fms, size_t n_fms);
-/* Flow stats or aggregate stats request, independent of flow format. */
+/* Flow stats or aggregate stats request, independent of protocol. */
struct ofputil_flow_stats_request {
bool aggregate; /* Aggregate results? */
struct cls_rule match;
enum ofperr ofputil_decode_flow_stats_request(
struct ofputil_flow_stats_request *, const struct ofp_header *);
struct ofpbuf *ofputil_encode_flow_stats_request(
- const struct ofputil_flow_stats_request *, enum nx_flow_format);
+ const struct ofputil_flow_stats_request *, enum ofputil_protocol);
+enum ofputil_protocol ofputil_flow_stats_request_usable_protocols(
+ const struct ofputil_flow_stats_request *);
-/* Flow stats reply, independent of flow format. */
+/* Flow stats reply, independent of protocol. */
struct ofputil_flow_stats {
struct cls_rule rule;
ovs_be64 cookie;
void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
struct list *replies);
-/* Aggregate stats reply, independent of flow format. */
+/* Aggregate stats reply, independent of protocol. */
struct ofputil_aggregate_stats {
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */
const struct ofputil_aggregate_stats *stats,
const struct ofp_stats_msg *request);
-/* Flow removed message, independent of flow format. */
+/* Flow removed message, independent of protocol. */
struct ofputil_flow_removed {
struct cls_rule rule;
ovs_be64 cookie;
enum ofperr ofputil_decode_flow_removed(struct ofputil_flow_removed *,
const struct ofp_header *);
struct ofpbuf *ofputil_encode_flow_removed(const struct ofputil_flow_removed *,
- enum nx_flow_format);
+ enum ofputil_protocol);
/* Abstract packet-in message. */
struct ofputil_packet_in {
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 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.
return rconn->vconn ? vconn_get_local_port(rconn->vconn) : 0;
}
+/* Returns the OpenFlow version negotiated with the peer, or -1 if there is
+ * currently no connection or if version negotiation is not yet complete. */
+int
+rconn_get_version(const struct rconn *rconn)
+{
+ return rconn->vconn ? vconn_get_version(rconn->vconn) : -1;
+}
+
/* Returns the total number of packets successfully received by the underlying
* vconn. */
unsigned int
ovs_be16 rconn_get_remote_port(const struct rconn *);
ovs_be32 rconn_get_local_ip(const struct rconn *);
ovs_be16 rconn_get_local_port(const struct rconn *);
+int rconn_get_version(const struct rconn *);
const char *rconn_get_state(const struct rconn *);
unsigned int rconn_get_attempted_connections(const struct rconn *);
/* Returns true if X is a power of 2, otherwise false. */
#define IS_POW2(X) ((X) && !((X) & ((X) - 1)))
+static inline bool
+is_pow2(uintmax_t x)
+{
+ return IS_POW2(x);
+}
+
+/* Returns the rightmost 1-bit in 'x' (e.g. 01011000 => 00001000), or 0 if 'x'
+ * is 0. */
+static inline uintmax_t
+rightmost_1bit(uintmax_t x)
+{
+ return x & -x;
+}
+
#ifndef MIN
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#endif
return vconn->local_port;
}
+/* Returns the OpenFlow version negotiated with the peer, or -1 if version
+ * negotiation is not yet complete.
+ *
+ * A vconn that has successfully connected (that is, vconn_connect() or
+ * vconn_send() or vconn_recv() has returned 0) always negotiated a version. */
+int
+vconn_get_version(const struct vconn *vconn)
+{
+ return vconn->version;
+}
+
static void
vcs_connecting(struct vconn *vconn)
{
{
enum vconn_state last_state;
- assert(vconn->min_version >= 0);
+ assert(vconn->min_version > 0);
do {
last_state = vconn->state;
switch (vconn->state) {
}
oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh);
- if (oh->version != vconn->version
+ if ((oh->version != vconn->version || oh->version == 0)
&& oh->type != OFPT_HELLO
&& oh->type != OFPT_ERROR
&& oh->type != OFPT_ECHO_REQUEST
&& oh->type != OFPT_ECHO_REPLY
&& oh->type != OFPT_VENDOR)
{
- if (vconn->version < 0) {
+ if (vconn->version == 0) {
VLOG_ERR_RL(&bad_ofmsg_rl,
"%s: received OpenFlow message type %"PRIu8" "
"before version negotiation complete",
: !connect_status ? VCS_SEND_HELLO
: VCS_DISCONNECTED);
vconn->error = connect_status;
- vconn->version = -1;
- vconn->min_version = -1;
+ vconn->version = 0;
+ vconn->min_version = 0;
vconn->remote_ip = 0;
vconn->remote_port = 0;
vconn->local_ip = 0;
ovs_be16 vconn_get_remote_port(const struct vconn *);
ovs_be32 vconn_get_local_ip(const struct vconn *);
ovs_be16 vconn_get_local_port(const struct vconn *);
+int vconn_get_version(const struct vconn *);
int vconn_connect(struct vconn *);
int vconn_recv(struct vconn *, struct ofpbuf **);
int vconn_send(struct vconn *, struct ofpbuf *);
/* OpenFlow state. */
enum nx_role role; /* Role. */
- enum nx_flow_format flow_format; /* Currently selected flow format. */
+ enum ofputil_protocol protocol; /* Current protocol variant. */
enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */
- bool flow_mod_table_id; /* NXT_FLOW_MOD_TABLE_ID enabled? */
/* Asynchronous flow table operation support. */
struct list opgroups; /* Contains pending "ofopgroups", if any. */
return (ofconn->master_async_config[OAM_PACKET_IN] & bit) != 0;
}
-/* Returns the currently configured flow format for 'ofconn', one of NXFF_*.
+/* Returns the currently configured protocol for 'ofconn', one of OFPUTIL_P_*.
*
- * The default, if no other format has been set, is NXFF_OPENFLOW10. */
-enum nx_flow_format
-ofconn_get_flow_format(struct ofconn *ofconn)
+ * The default, if no other format has been set, is OFPUTIL_P_OPENFLOW10. */
+enum ofputil_protocol
+ofconn_get_protocol(struct ofconn *ofconn)
{
- return ofconn->flow_format;
+ return ofconn->protocol;
}
-/* Sets the flow format for 'ofconn' to 'flow_format' (one of NXFF_*). */
+/* Sets the protocol for 'ofconn' to 'protocol' (one of OFPUTIL_P_*).
+ *
+ * (This doesn't actually send anything to accomplish this. Presumably the
+ * caller already did that.) */
void
-ofconn_set_flow_format(struct ofconn *ofconn, enum nx_flow_format flow_format)
+ofconn_set_protocol(struct ofconn *ofconn, enum ofputil_protocol protocol)
{
- ofconn->flow_format = flow_format;
+ ofconn->protocol = protocol;
}
/* Returns the currently configured packet in format for 'ofconn', one of
ofconn->controller_id = controller_id;
}
-/* Returns true if the NXT_FLOW_MOD_TABLE_ID extension is enabled, false
- * otherwise.
- *
- * By default the extension is not enabled. */
-bool
-ofconn_get_flow_mod_table_id(const struct ofconn *ofconn)
-{
- return ofconn->flow_mod_table_id;
-}
-
-/* Enables or disables (according to 'enable') the NXT_FLOW_MOD_TABLE_ID
- * extension on 'ofconn'. */
-void
-ofconn_set_flow_mod_table_id(struct ofconn *ofconn, bool enable)
-{
- ofconn->flow_mod_table_id = enable;
-}
-
/* Returns the default miss send length for 'ofconn'. */
int
ofconn_get_miss_send_len(const struct ofconn *ofconn)
int i;
ofconn->role = NX_ROLE_OTHER;
- ofconn->flow_format = NXFF_OPENFLOW10;
+ ofconn->protocol = OFPUTIL_P_OF10;
ofconn->packet_in_format = NXPIF_OPENFLOW10;
- ofconn->flow_mod_table_id = false;
/* Disassociate 'ofconn' from all of the ofopgroups that it initiated that
* have not yet completed. (Those ofopgroups will still run to completion
* also prevents new flows from being added (and expiring). (It
* also prevents processing OpenFlow requests that would not add
* new flows, so it is imperfect.) */
- msg = ofputil_encode_flow_removed(fr, ofconn->flow_format);
+ msg = ofputil_encode_flow_removed(fr, ofconn->protocol);
ofconn_send_reply(ofconn, msg);
}
}
enum nx_role ofconn_get_role(const struct ofconn *);
void ofconn_set_role(struct ofconn *, enum nx_role);
-enum nx_flow_format ofconn_get_flow_format(struct ofconn *);
-void ofconn_set_flow_format(struct ofconn *, enum nx_flow_format);
+enum ofputil_protocol ofconn_get_protocol(struct ofconn *);
+void ofconn_set_protocol(struct ofconn *, enum ofputil_protocol);
enum nx_packet_in_format ofconn_get_packet_in_format(struct ofconn *);
void ofconn_set_packet_in_format(struct ofconn *, enum nx_packet_in_format);
void ofconn_set_controller_id(struct ofconn *, uint16_t controller_id);
-bool ofconn_get_flow_mod_table_id(const struct ofconn *);
-void ofconn_set_flow_mod_table_id(struct ofconn *, bool enable);
-
void ofconn_set_invalid_ttl_to_controller(struct ofconn *, bool);
bool ofconn_get_invalid_ttl_to_controller(struct ofconn *);
return error;
}
- error = ofputil_decode_flow_mod(&fm, oh,
- ofconn_get_flow_mod_table_id(ofconn));
+ error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn));
if (error) {
return error;
}
{
const struct nx_flow_mod_table_id *msg
= (const struct nx_flow_mod_table_id *) oh;
+ enum ofputil_protocol cur, next;
+
+ cur = ofconn_get_protocol(ofconn);
+ next = ofputil_protocol_set_tid(cur, msg->set != 0);
+ ofconn_set_protocol(ofconn, next);
- ofconn_set_flow_mod_table_id(ofconn, msg->set != 0);
return 0;
}
{
const struct nx_set_flow_format *msg
= (const struct nx_set_flow_format *) oh;
- uint32_t format;
+ enum ofputil_protocol cur, next;
+ enum ofputil_protocol next_base;
- format = ntohl(msg->format);
- if (format != NXFF_OPENFLOW10 && format != NXFF_NXM) {
+ next_base = ofputil_nx_flow_format_to_protocol(ntohl(msg->format));
+ if (!next_base) {
return OFPERR_OFPBRC_EPERM;
}
- if (format != ofconn_get_flow_format(ofconn)
- && ofconn_has_pending_opgroups(ofconn)) {
- /* Avoid sending async messages in surprising flow format. */
+ cur = ofconn_get_protocol(ofconn);
+ next = ofputil_protocol_set_base(cur, next_base);
+ if (cur != next && ofconn_has_pending_opgroups(ofconn)) {
+ /* Avoid sending async messages in surprising protocol. */
return OFPROTO_POSTPONE;
}
- ofconn_set_flow_format(ofconn, format);
+ ofconn_set_protocol(ofconn, next);
return 0;
}
AT_SETUP([autopath basic])
AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(1, NXM_NX_REG0[[]])'], [0],
- [OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(1,NXM_NX_REG0[[]])
+ [usable protocols: any
+chosen protocol: OpenFlow10-table_id
+OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(1,NXM_NX_REG0[[]])
])
AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(2, NXM_NX_REG0[[2..30]])'], [0],
- [OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(2,NXM_NX_REG0[[2..30]])
+ [usable protocols: any
+chosen protocol: OpenFlow10-table_id
+OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(2,NXM_NX_REG0[[2..30]])
])
AT_CLEANUP
actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
-[[OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
+[[usable protocols: any
+chosen protocol: OpenFlow10-table_id
+OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10])
OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
]])
table=1 priority=0 actions=flood
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
-[[OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
-OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
-NXT_FLOW_MOD_TABLE_ID (xid=0x3): enable
-OFPT_FLOW_MOD (xid=0x4): ADD actions=learn(table=1,hard_timeout=10,NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[]),resubmit(,1)
-OFPT_FLOW_MOD (xid=0x5): ADD table:1 priority=0 actions=FLOOD
+[[usable protocols: OpenFlow10+table_id,NXM+table_id
+chosen protocol: OpenFlow10+table_id
+OFPT_FLOW_MOD (xid=0x1): ADD table:255 actions=learn(table=1,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
+OFPT_FLOW_MOD (xid=0x2): ADD table:255 actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,hard_timeout=10,NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[]),resubmit(,1)
+OFPT_FLOW_MOD (xid=0x4): ADD table:1 priority=0 actions=FLOOD
]])
AT_CLEANUP
ip,actions=learn(eth_type=0x800,NXM_OF_IP_DST[])
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
-[[OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x00000005->NXM_OF_IP_DST[])
+[[usable protocols: any
+chosen protocol: OpenFlow10-table_id
+OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x00000005->NXM_OF_IP_DST[])
OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[])
]])
])
# Adding another flow will be refused.
AT_CHECK([ovs-ofctl add-flow br0 in_port=5,actions=drop], [1], [], [stderr])
-AT_CHECK([head -n 1 stderr], [0],
- [OFPT_ERROR (xid=0x1): OFPFMFC_ALL_TABLES_FULL
+AT_CHECK([head -n 1 stderr | ofctl_strip], [0],
+ [OFPT_ERROR: OFPFMFC_ALL_TABLES_FULL
])
# Also a mod-flow that would add a flow will be refused.
AT_CHECK([ovs-ofctl mod-flows br0 in_port=5,actions=drop], [1], [], [stderr])
-AT_CHECK([head -n 1 stderr], [0],
- [OFPT_ERROR (xid=0x1): OFPFMFC_ALL_TABLES_FULL
+AT_CHECK([head -n 1 stderr | ofctl_strip], [0],
+ [OFPT_ERROR: OFPFMFC_ALL_TABLES_FULL
])
# Replacing or modifying an existing flow is allowed.
AT_CHECK([ovs-ofctl add-flow br0 in_port=4,actions=normal])
# Flows with no timeouts at all cannot be evicted.
AT_CHECK([ovs-ofctl add-flow br0 in_port=7,actions=normal])
AT_CHECK([ovs-ofctl add-flow br0 in_port=8,actions=drop], [1], [], [stderr])
-AT_CHECK([head -n 1 stderr], [0],
- [OFPT_ERROR (xid=0x1): OFPFMFC_ALL_TABLES_FULL
+AT_CHECK([head -n 1 stderr | ofctl_strip], [0],
+ [OFPT_ERROR: OFPFMFC_ALL_TABLES_FULL
])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
in_port=4 actions=NORMAL
AT_BANNER([ovs-ofctl])
-AT_SETUP([ovs-ofctl parse-flows])
+AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.0)])
+AT_DATA([flows.txt], [[
+# comment
+tcp,tp_src=123,actions=flood
+in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop
+udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
+tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
+udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
+cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
+actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
+]])
+
+AT_CHECK([ovs-ofctl parse-flows flows.txt
+], [0], [stdout])
+AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
+[[usable protocols: any
+chosen protocol: OpenFlow10-table_id
+OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
+OFPT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
+OFPT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
+OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
+OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
+OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
+OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
+]])
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl parse-flows (NXM)])
AT_DATA([flows.txt], [[
# comment
tcp,tp_src=123,actions=flood
AT_CHECK([ovs-ofctl parse-flows flows.txt
], [0], [stdout])
-AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
-[[OFPT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
-OFPT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
-OFPT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
-OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
-OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
-OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
-OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
-NXT_SET_FLOW_FORMAT: format=nxm
-NXT_FLOW_MOD: ADD tcp,tun_id=0x1234,tp_src=0x1230/0xfff0 cookie:0x5678 actions=FLOOD
-NXT_FLOW_MOD: ADD actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789
-NXT_FLOW_MOD: ADD actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
-NXT_FLOW_MOD_TABLE_ID: enable
+AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
+[[usable protocols: NXM+table_id
+chosen protocol: NXM+table_id
+NXT_FLOW_MOD: ADD table:255 tcp,tp_src=123 actions=FLOOD
+NXT_FLOW_MOD: ADD table:255 in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
+NXT_FLOW_MOD: ADD table:255 udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
+NXT_FLOW_MOD: ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
+NXT_FLOW_MOD: ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
+NXT_FLOW_MOD: ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
+NXT_FLOW_MOD: ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
+NXT_FLOW_MOD: ADD table:255 tcp,tun_id=0x1234,tp_src=0x1230/0xfff0 cookie:0x5678 actions=FLOOD
+NXT_FLOW_MOD: ADD table:255 actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789
+NXT_FLOW_MOD: ADD table:255 actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
NXT_FLOW_MOD: ADD table:1 actions=drop
NXT_FLOW_MOD: ADD table:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
NXT_FLOW_MOD: ADD table:255 actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
])
AT_CHECK([ovs-ofctl -F nxm parse-flows flows.txt], [0], [stdout])
AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl
+usable protocols: NXM
+chosen protocol: NXM-table_id
NXT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
NXT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
NXT_FLOW_MOD: ADD arp,dl_src=00:0a:e4:25:6b:b0,arp_sha=00:0a:e4:25:6b:b0 actions=drop
]])
AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout])
AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
-[[NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(007b) actions=FLOOD
+[[usable protocols: NXM
+chosen protocol: NXM-table_id
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(007b) actions=FLOOD
NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(fffe), NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_VLAN_TCI_W(1009/1fff) actions=drop
NXT_FLOW_MOD: ADD NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(000ae4256bb0) actions=drop
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_LABEL(00012345) actions=output:2
dnl OpenFlow 1.0 doesn't support tunnels.
AT_SETUP([ovs-ofctl -F option and tun_id])
AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy tun_id=123,actions=drop],
- [1], [], [ovs-ofctl: flow cannot be expressed in flow format openflow10 (flow format nxm or better is required)
+ [1], [], [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
])
AT_CLEANUP
AT_SETUP([ovs-ofctl dump-flows rejects bad -F option])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl -F openflow10 dump-flows unix:br0.mgmt reg0=0xabcdef], [1], [],
- [ovs-ofctl: unix:br0.mgmt: cannot use requested flow format nxm for specified flow
+ [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
])
OVS_VSWITCHD_STOP
AT_CLEANUP
/* -Q, --port-queue: map from port name to port number (cast to void *). */
static struct shash port_queues = SHASH_INITIALIZER(&port_queues);
-/* --with-flows: Flows to send to switch, or an empty list not to send any
- * default flows. */
-static struct list default_flows = LIST_INITIALIZER(&default_flows);
+/* --with-flows: Flows to send to switch. */
+static struct ofputil_flow_mod *default_flows;
+static size_t n_default_flows;
/* --unixctl: Name of unixctl socket, or null to use the default. */
static char *unixctl_path = NULL;
: LSW_FLOOD);
cfg.wildcards = wildcards;
cfg.max_idle = set_up_flows ? max_idle : -1;
- cfg.default_flows = &default_flows;
+ cfg.default_flows = default_flows;
+ cfg.n_default_flows = n_default_flows;
cfg.default_queue = default_queue;
cfg.port_queues = &port_queues;
sw->lswitch = lswitch_create(sw->rconn, &cfg);
: EAGAIN);
}
-static void
-read_flow_file(const char *name)
-{
- enum nx_flow_format flow_format;
- bool flow_mod_table_id;
- FILE *stream;
-
- stream = fopen(optarg, "r");
- if (!stream) {
- ovs_fatal(errno, "%s: open", name);
- }
-
- flow_format = NXFF_OPENFLOW10;
- flow_mod_table_id = false;
- while (parse_ofp_flow_mod_file(&default_flows,
- &flow_format, &flow_mod_table_id,
- stream, OFPFC_ADD)) {
- continue;
- }
-
- fclose(stream);
-}
-
static void
add_port_queue(char *s)
{
break;
case OPT_WITH_FLOWS:
- read_flow_file(optarg);
+ parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows,
+ &n_default_flows);
break;
case OPT_UNIXCTL:
\fB\-\-strict\fR
Uses strict matching when running flow modification commands.
.
-.IP "\fB\-F \fIformat\fR"
-.IQ "\fB\-\-flow\-format=\fIformat\fR"
-\fBovs\-ofctl\fR supports the following flow formats, in order of
-increasing capability:
+.IP "\fB\-F \fIformat\fR[\fB,\fIformat\fR...]"
+.IQ "\fB\-\-flow\-format=\fIformat\fR[\fB,\fIformat\fR...]"
+\fBovs\-ofctl\fR supports the following individual flow formats, any
+number of which may be listed as \fIformat\fR:
.RS
-.IP "\fBopenflow10\fR"
-This is the standard OpenFlow 1.0 flow format. It should be supported
-by all OpenFlow switches.
+.IP "\fBOpenFlow10\-table_id\fR"
+This is the standard OpenFlow 1.0 flow format. All OpenFlow switches
+and all versions of Open vSwitch support this flow format.
.
-.IP "\fBnxm\fR (Nicira Extended Match)"
+.IP "\fBOpenFlow10+table_id\fR"
+This is the standard OpenFlow 1.0 flow format plus a Nicira extension
+that allows \fBovs\-ofctl\fR to specify the flow table in which a
+particular flow should be placed. Open vSwitch 1.2 and later supports
+this flow format.
+.
+.IP "\fBNXM\-table_id\fR (Nicira Extended Match)"
This Nicira extension to OpenFlow is flexible and extensible. It
supports all of the Nicira flow extensions, such as \fBtun_id\fR and
-registers.
+registers. Open vSwitch 1.1 and later supports this flow format.
+.
+.IP "\fBNXM+table_id\fR (Nicira Extended Match)"
+This combines Nicira Extended match with the ability to place a flow
+in a specific table. Open vSwitch 1.2 and later supports this flow
+format.
.RE
+.
.IP
-Usually, \fBovs\-ofctl\fR picks the correct format automatically. For
-commands that modify the flow table, \fBovs\-ofctl\fR by default uses
-the most widely supported flow format that supports the flows being
-added. For commands that query the flow table, \fBovs\-ofctl\fR by
-default queries and uses the most advanced format supported by the
-switch.
-.IP
-This option, where \fIformat\fR is one of the formats listed in the
-above table, overrides \fBovs\-ofctl\fR's default choice of flow
-format. If a command cannot work as requested using the requested
-flow format, \fBovs\-ofctl\fR will report a fatal error.
+\fBovs\-ofctl\fR also supports the following abbreviations for
+collections of flow formats:
+.RS
+.IP "\fBany\fR"
+Any supported flow format.
+.IP "\fBOpenFlow10\fR"
+\fBOpenFlow10\-table_id\fR or \fBOpenFlow10+table_id\fR.
+.IP "\fBNXM\fR"
+\fBNXM\-table_id\fR or \fBNXM+table_id\fR.
+.RE
.
+.IP
+For commands that modify the flow table, \fBovs\-ofctl\fR by default
+negotiates the most widely supported flow format that supports the
+flows being added. For commands that query the flow table,
+\fBovs\-ofctl\fR by default uses the most advanced format supported by
+the switch.
+.IP
+This option, where \fIformat\fR is a comma-separated list of one or
+more of the formats listed above, limits \fBovs\-ofctl\fR's choice of
+flow format. If a command cannot work as requested using one of the
+specified flow formats, \fBovs\-ofctl\fR will report a fatal error.
.
.IP "\fB\-P \fIformat\fR"
.IQ "\fB\-\-packet\-in\-format=\fIformat\fR"
* (to reset flow counters). */
static bool readd;
-/* -F, --flow-format: Flow format to use. Either one of NXFF_* to force a
- * particular flow format or -1 to let ovs-ofctl choose intelligently. */
-static int preferred_flow_format = -1;
+/* -F, --flow-format: Allowed protocols. By default, any protocol is
+ * allowed. */
+static enum ofputil_protocol allowed_protocols = OFPUTIL_P_ANY;
/* -P, --packet-in-format: Packet IN format to use in monitor and snoop
* commands. Either one of NXPIF_* to force a particular packet_in format, or
break;
case 'F':
- preferred_flow_format = ofputil_flow_format_from_string(optarg);
- if (preferred_flow_format < 0) {
- ovs_fatal(0, "unknown flow format `%s'", optarg);
+ allowed_protocols = ofputil_protocols_from_string(optarg);
+ if (!allowed_protocols) {
+ ovs_fatal(0, "%s: invalid flow format(s)", optarg);
}
break;
free(vconn_name);
}
-static void
+static enum ofputil_protocol
open_vconn__(const char *name, const char *default_suffix,
struct vconn **vconnp)
{
char *datapath_name, *datapath_type, *socket_name;
+ enum ofputil_protocol protocol;
char *bridge_path;
+ int ofp_version;
struct stat s;
bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
free(bridge_path);
free(socket_name);
+
+ ofp_version = vconn_get_version(*vconnp);
+ protocol = ofputil_protocol_from_ofp_version(ofp_version);
+ if (!protocol) {
+ ovs_fatal(0, "%s: unsupported OpenFlow version 0x%02x",
+ name, ofp_version);
+ }
+ return protocol;
}
-static void
+static enum ofputil_protocol
open_vconn(const char *name, struct vconn **vconnp)
{
return open_vconn__(name, "mgmt", vconnp);
}
static bool
-try_set_flow_format(struct vconn *vconn, enum nx_flow_format flow_format)
+try_set_protocol(struct vconn *vconn, enum ofputil_protocol want,
+ enum ofputil_protocol *cur)
{
- struct ofpbuf *sff, *reply;
+ for (;;) {
+ struct ofpbuf *request, *reply;
+ enum ofputil_protocol next;
- sff = ofputil_make_set_flow_format(flow_format);
- run(vconn_transact_noreply(vconn, sff, &reply),
- "talking to %s", vconn_get_name(vconn));
- if (reply) {
- char *s = ofp_to_string(reply->data, reply->size, 2);
- VLOG_DBG("%s: failed to set flow format %s, controller replied: %s",
- vconn_get_name(vconn),
- ofputil_flow_format_to_string(flow_format),
- s);
- free(s);
- ofpbuf_delete(reply);
- return false;
+ request = ofputil_encode_set_protocol(*cur, want, &next);
+ if (!request) {
+ return true;
+ }
+
+ run(vconn_transact_noreply(vconn, request, &reply),
+ "talking to %s", vconn_get_name(vconn));
+ if (reply) {
+ char *s = ofp_to_string(reply->data, reply->size, 2);
+ VLOG_DBG("%s: failed to set protocol, switch replied: %s",
+ vconn_get_name(vconn), s);
+ free(s);
+ ofpbuf_delete(reply);
+ return false;
+ }
+
+ *cur = next;
}
- return true;
}
-static void
-set_flow_format(struct vconn *vconn, enum nx_flow_format flow_format)
+static enum ofputil_protocol
+set_protocol_for_flow_dump(struct vconn *vconn,
+ enum ofputil_protocol cur_protocol,
+ enum ofputil_protocol usable_protocols)
{
- struct ofpbuf *sff = ofputil_make_set_flow_format(flow_format);
- transact_noreply(vconn, sff);
- VLOG_DBG("%s: using user-specified flow format %s",
- vconn_get_name(vconn),
- ofputil_flow_format_to_string(flow_format));
-}
+ char *usable_s;
+ int i;
-static enum nx_flow_format
-negotiate_highest_flow_format(struct vconn *vconn,
- enum nx_flow_format min_format)
-{
- if (preferred_flow_format != -1) {
- if (preferred_flow_format < min_format) {
- ovs_fatal(0, "%s: cannot use requested flow format %s for "
- "specified flow", vconn_get_name(vconn),
- ofputil_flow_format_to_string(min_format));
+ for (i = 0; i < ofputil_n_flow_dump_protocols; i++) {
+ enum ofputil_protocol f = ofputil_flow_dump_protocols[i];
+ if (f & usable_protocols & allowed_protocols
+ && try_set_protocol(vconn, f, &cur_protocol)) {
+ return f;
}
+ }
- set_flow_format(vconn, preferred_flow_format);
- return preferred_flow_format;
+ usable_s = ofputil_protocols_to_string(usable_protocols);
+ if (usable_protocols & allowed_protocols) {
+ ovs_fatal(0, "switch does not support any of the usable flow "
+ "formats (%s)", usable_s);
} else {
- enum nx_flow_format flow_format;
-
- if (try_set_flow_format(vconn, NXFF_NXM)) {
- flow_format = NXFF_NXM;
- } else {
- flow_format = NXFF_OPENFLOW10;
- }
-
- if (flow_format < min_format) {
- ovs_fatal(0, "%s: cannot use switch's most advanced flow format "
- "%s for specified flow", vconn_get_name(vconn),
- ofputil_flow_format_to_string(min_format));
- }
-
- VLOG_DBG("%s: negotiated flow format %s", vconn_get_name(vconn),
- ofputil_flow_format_to_string(flow_format));
- return flow_format;
+ char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
+ ovs_fatal(0, "none of the usable flow formats (%s) is among the "
+ "allowed flow formats (%s)", usable_s, allowed_s);
}
}
static void
do_dump_flows__(int argc, char *argv[], bool aggregate)
{
- enum nx_flow_format min_flow_format, flow_format;
+ enum ofputil_protocol usable_protocols, protocol;
struct ofputil_flow_stats_request fsr;
struct ofpbuf *request;
struct vconn *vconn;
parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
+ usable_protocols = ofputil_flow_stats_request_usable_protocols(&fsr);
- open_vconn(argv[1], &vconn);
- min_flow_format = ofputil_min_flow_format(&fsr.match);
- if (fsr.cookie_mask != htonll(0)) {
- min_flow_format = NXFF_NXM;
- }
- flow_format = negotiate_highest_flow_format(vconn, min_flow_format);
- request = ofputil_encode_flow_stats_request(&fsr, flow_format);
+ protocol = open_vconn(argv[1], &vconn);
+ protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
+ request = ofputil_encode_flow_stats_request(&fsr, protocol);
dump_stats_transaction(argv[1], request);
vconn_close(vconn);
}
dump_stats_transaction(argv[1], request);
}
-/* Sets up the flow format for a vconn that will be used to modify the flow
- * table. Returns the flow format used, after possibly adding an OpenFlow
- * request to 'requests'.
- *
- * If 'preferred_flow_format' is -1, returns NXFF_OPENFLOW10 without modifying
- * 'requests', since NXFF_OPENFLOW10 is the default flow format for any
- * OpenFlow connection.
- *
- * If 'preferred_flow_format' is a specific format, adds a request to set that
- * format to 'requests' and returns the format. */
-static enum nx_flow_format
-set_initial_format_for_flow_mod(struct list *requests)
+static enum ofputil_protocol
+open_vconn_for_flow_mod(const char *remote,
+ const struct ofputil_flow_mod *fms, size_t n_fms,
+ struct vconn **vconnp)
{
- if (preferred_flow_format < 0) {
- return NXFF_OPENFLOW10;
- } else {
- struct ofpbuf *sff;
+ enum ofputil_protocol usable_protocols;
+ enum ofputil_protocol cur_protocol;
+ char *usable_s;
+ int i;
- sff = ofputil_make_set_flow_format(preferred_flow_format);
- list_push_back(requests, &sff->list_node);
- return preferred_flow_format;
+ /* Figure out what flow formats will work. */
+ usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
+ if (!(usable_protocols & allowed_protocols)) {
+ char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
+ usable_s = ofputil_protocols_to_string(usable_protocols);
+ ovs_fatal(0, "none of the usable flow formats (%s) is among the "
+ "allowed flow formats (%s)", usable_s, allowed_s);
}
-}
-/* Checks that 'flow_format' is acceptable as a flow format after a flow_mod
- * operation, given the global 'preferred_flow_format'. */
-static void
-check_final_format_for_flow_mod(enum nx_flow_format flow_format)
-{
- if (preferred_flow_format >= 0 && flow_format > preferred_flow_format) {
- ovs_fatal(0, "flow cannot be expressed in flow format %s "
- "(flow format %s or better is required)",
- ofputil_flow_format_to_string(preferred_flow_format),
- ofputil_flow_format_to_string(flow_format));
+ /* If the initial flow format is allowed and usable, keep it. */
+ cur_protocol = open_vconn(remote, vconnp);
+ if (usable_protocols & allowed_protocols & cur_protocol) {
+ return cur_protocol;
+ }
+
+ /* Otherwise try each flow format in turn. */
+ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+ enum ofputil_protocol f = 1 << i;
+
+ if (f != cur_protocol
+ && f & usable_protocols & allowed_protocols
+ && try_set_protocol(*vconnp, f, &cur_protocol)) {
+ return f;
+ }
}
+
+ usable_s = ofputil_protocols_to_string(usable_protocols);
+ ovs_fatal(0, "switch does not support any of the usable flow "
+ "formats (%s)", usable_s);
}
static void
-do_flow_mod_file__(int argc OVS_UNUSED, char *argv[], uint16_t command)
+do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms)
{
- enum nx_flow_format flow_format;
- bool flow_mod_table_id;
- struct list requests;
+ enum ofputil_protocol protocol;
struct vconn *vconn;
- FILE *file;
+ size_t i;
- file = !strcmp(argv[2], "-") ? stdin : fopen(argv[2], "r");
- if (file == NULL) {
- ovs_fatal(errno, "%s: open", argv[2]);
- }
+ protocol = open_vconn_for_flow_mod(remote, fms, n_fms, &vconn);
- list_init(&requests);
- flow_format = set_initial_format_for_flow_mod(&requests);
- flow_mod_table_id = false;
+ for (i = 0; i < n_fms; i++) {
+ struct ofputil_flow_mod *fm = &fms[i];
- open_vconn(argv[1], &vconn);
- while (parse_ofp_flow_mod_file(&requests, &flow_format, &flow_mod_table_id,
- file, command)) {
- check_final_format_for_flow_mod(flow_format);
- transact_multiple_noreply(vconn, &requests);
+ transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
+ free(fm->actions);
}
vconn_close(vconn);
-
- if (file != stdin) {
- fclose(file);
- }
}
static void
-do_flow_mod__(int argc, char *argv[], uint16_t command)
+do_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
{
- enum nx_flow_format flow_format;
- bool flow_mod_table_id;
- struct list requests;
- struct vconn *vconn;
+ struct ofputil_flow_mod *fms = NULL;
+ size_t n_fms = 0;
+ parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+ do_flow_mod__(argv[1], fms, n_fms);
+ free(fms);
+}
+
+static void
+do_flow_mod(int argc, char *argv[], uint16_t command)
+{
if (argc > 2 && !strcmp(argv[2], "-")) {
- do_flow_mod_file__(argc, argv, command);
- return;
+ do_flow_mod_file(argc, argv, command);
+ } else {
+ struct ofputil_flow_mod fm;
+ parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
+ do_flow_mod__(argv[1], &fm, 1);
}
-
- list_init(&requests);
- flow_format = set_initial_format_for_flow_mod(&requests);
- flow_mod_table_id = false;
-
- parse_ofp_flow_mod_str(&requests, &flow_format, &flow_mod_table_id,
- argc > 2 ? argv[2] : "", command, false);
- check_final_format_for_flow_mod(flow_format);
-
- open_vconn(argv[1], &vconn);
- transact_multiple_noreply(vconn, &requests);
- vconn_close(vconn);
}
static void
do_add_flow(int argc, char *argv[])
{
- do_flow_mod__(argc, argv, OFPFC_ADD);
+ do_flow_mod(argc, argv, OFPFC_ADD);
}
static void
do_add_flows(int argc, char *argv[])
{
- do_flow_mod_file__(argc, argv, OFPFC_ADD);
+ do_flow_mod_file(argc, argv, OFPFC_ADD);
}
static void
do_mod_flows(int argc, char *argv[])
{
- do_flow_mod__(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
+ do_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
}
static void
do_del_flows(int argc, char *argv[])
{
- do_flow_mod__(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
+ do_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
}
static void
}
/* Reads the flows in 'filename' as flow table entries in 'cls' for the version
- * with the specified 'index'. Returns the minimum flow format required to
- * represent the flows that were read. */
-static enum nx_flow_format
+ * with the specified 'index'. Returns the flow formats able to represent the
+ * flows that were read. */
+static enum ofputil_protocol
read_flows_from_file(const char *filename, struct classifier *cls, int index)
{
- enum nx_flow_format min_flow_format;
+ enum ofputil_protocol usable_protocols;
struct ds s;
FILE *file;
}
ds_init(&s);
- min_flow_format = NXFF_OPENFLOW10;
+ usable_protocols = OFPUTIL_P_ANY;
while (!ds_get_preprocessed_line(&s, file)) {
struct fte_version *version;
struct ofputil_flow_mod fm;
- enum nx_flow_format min_ff;
parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
version->actions = fm.actions;
version->n_actions = fm.n_actions;
- min_ff = ofputil_min_flow_format(&fm.cr);
- min_flow_format = MAX(min_flow_format, min_ff);
- check_final_format_for_flow_mod(min_flow_format);
+ usable_protocols &= ofputil_usable_protocols(&fm.cr);
fte_insert(cls, &fm.cr, version, index);
}
fclose(file);
}
- return min_flow_format;
+ return usable_protocols;
}
/* Reads the OpenFlow flow table from 'vconn', which has currently active flow
- * format 'flow_format', and adds them as flow table entries in 'cls' for the
+ * format 'protocol', and adds them as flow table entries in 'cls' for the
* version with the specified 'index'. */
static void
-read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format,
+read_flows_from_switch(struct vconn *vconn,
+ enum ofputil_protocol protocol,
struct classifier *cls, int index)
{
struct ofputil_flow_stats_request fsr;
fsr.out_port = OFPP_NONE;
fsr.table_id = 0xff;
fsr.cookie = fsr.cookie_mask = htonll(0);
- request = ofputil_encode_flow_stats_request(&fsr, flow_format);
+ request = ofputil_encode_flow_stats_request(&fsr, protocol);
send_xid = ((struct ofp_header *) request->data)->xid;
send_openflow_buffer(vconn, request);
static void
fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
- enum nx_flow_format flow_format, struct list *packets)
+ enum ofputil_protocol protocol, struct list *packets)
{
const struct fte_version *version = fte->versions[index];
struct ofputil_flow_mod fm;
fm.n_actions = 0;
}
- ofm = ofputil_encode_flow_mod(&fm, flow_format, false);
+ ofm = ofputil_encode_flow_mod(&fm, protocol);
list_push_back(packets, &ofm->list_node);
}
do_replace_flows(int argc OVS_UNUSED, char *argv[])
{
enum { FILE_IDX = 0, SWITCH_IDX = 1 };
- enum nx_flow_format min_flow_format, flow_format;
+ enum ofputil_protocol usable_protocols, protocol;
struct cls_cursor cursor;
struct classifier cls;
struct list requests;
struct fte *fte;
classifier_init(&cls);
- min_flow_format = read_flows_from_file(argv[2], &cls, FILE_IDX);
+ usable_protocols = read_flows_from_file(argv[2], &cls, FILE_IDX);
- open_vconn(argv[1], &vconn);
- flow_format = negotiate_highest_flow_format(vconn, min_flow_format);
- read_flows_from_switch(vconn, flow_format, &cls, SWITCH_IDX);
+ protocol = open_vconn(argv[1], &vconn);
+ protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
+
+ read_flows_from_switch(vconn, protocol, &cls, SWITCH_IDX);
list_init(&requests);
if (sw_ver && !file_ver) {
fte_make_flow_mod(fte, SWITCH_IDX, OFPFC_DELETE_STRICT,
- flow_format, &requests);
+ protocol, &requests);
}
}
if (file_ver
&& (readd || !sw_ver || !fte_version_equals(sw_ver, file_ver))) {
- fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, flow_format,
- &requests);
+ fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
}
}
transact_multiple_noreply(vconn, &requests);
|| (!strchr(source, ':') && !stat(source, &s))) {
read_flows_from_file(source, cls, index);
} else {
- enum nx_flow_format flow_format;
+ enum ofputil_protocol protocol;
struct vconn *vconn;
- open_vconn(source, &vconn);
- flow_format = negotiate_highest_flow_format(vconn, NXFF_OPENFLOW10);
- read_flows_from_switch(vconn, flow_format, cls, index);
+ protocol = open_vconn(source, &vconn);
+ protocol = set_protocol_for_flow_dump(vconn, protocol, OFPUTIL_P_ANY);
+ read_flows_from_switch(vconn, protocol, cls, index);
vconn_close(vconn);
}
}
/* Undocumented commands for unit testing. */
static void
-print_packet_list(struct list *packets)
+do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
{
- struct ofpbuf *packet, *next;
+ enum ofputil_protocol usable_protocols;
+ enum ofputil_protocol protocol = 0;
+ char *usable_s;
+ size_t i;
- LIST_FOR_EACH_SAFE (packet, next, list_node, packets) {
- ofp_print(stdout, packet->data, packet->size, verbosity);
- list_remove(&packet->list_node);
- ofpbuf_delete(packet);
+ usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
+ usable_s = ofputil_protocols_to_string(usable_protocols);
+ printf("usable protocols: %s\n", usable_s);
+ free(usable_s);
+
+ if (!(usable_protocols & allowed_protocols)) {
+ ovs_fatal(0, "no usable protocol");
+ }
+ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+ protocol = 1 << i;
+ if (protocol & usable_protocols & allowed_protocols) {
+ break;
+ }
+ }
+ assert(IS_POW2(protocol));
+
+ printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol));
+
+ for (i = 0; i < n_fms; i++) {
+ struct ofputil_flow_mod *fm = &fms[i];
+ struct ofpbuf *msg;
+
+ msg = ofputil_encode_flow_mod(fm, protocol);
+ ofp_print(stdout, msg->data, msg->size, verbosity);
+ ofpbuf_delete(msg);
+
+ free(fm->actions);
}
}
static void
do_parse_flow(int argc OVS_UNUSED, char *argv[])
{
- enum nx_flow_format flow_format;
- bool flow_mod_table_id;
- struct list packets;
-
- flow_format = NXFF_OPENFLOW10;
- if (preferred_flow_format > 0) {
- flow_format = preferred_flow_format;
- }
- flow_mod_table_id = false;
+ struct ofputil_flow_mod fm;
- list_init(&packets);
- parse_ofp_flow_mod_str(&packets, &flow_format, &flow_mod_table_id,
- argv[1], OFPFC_ADD, false);
- print_packet_list(&packets);
+ parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
+ do_parse_flows__(&fm, 1);
}
/* "parse-flows FILENAME": reads the named file as a sequence of flows (like
static void
do_parse_flows(int argc OVS_UNUSED, char *argv[])
{
- enum nx_flow_format flow_format;
- bool flow_mod_table_id;
- struct list packets;
- FILE *file;
+ struct ofputil_flow_mod *fms = NULL;
+ size_t n_fms = 0;
- file = fopen(argv[1], "r");
- if (file == NULL) {
- ovs_fatal(errno, "%s: open", argv[1]);
- }
-
- flow_format = NXFF_OPENFLOW10;
- if (preferred_flow_format > 0) {
- flow_format = preferred_flow_format;
- }
- flow_mod_table_id = false;
-
- list_init(&packets);
- while (parse_ofp_flow_mod_file(&packets, &flow_format, &flow_mod_table_id,
- file, OFPFC_ADD)) {
- print_packet_list(&packets);
- }
- fclose(file);
+ parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+ do_parse_flows__(fms, n_fms);
+ free(fms);
}
/* "parse-nx-match": reads a series of nx_match specifications as strings from