From 27527aa09ce456796eeea728cef9528aa5612b70 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 10 Feb 2012 13:30:23 -0800 Subject: [PATCH] Introduce ofputil_protocol, to abstract the protocol in use on a connection. Open vSwitch already handles a few different protocol variations, but it does so in a nonuniform manner: - OpenFlow 1.0 and NXM flow formats are distinguished using the NXFF_* constant values from nicira-ext.h. - The "flow_mod_table_id" feature setting is maintained in ofproto as part of an OpenFlow connection's (ofconn's) state. There's no way to easily communicate this state among components. It's not much of a problem yet, but as more protocol support is added it seems better to have an abstract, uniform way to represent protocol versions and variants. This commit implements that by introducing a new type "enum ofputil_protocol". Each ofputil_protocol value represents a variant of a protocol version. Each value is a separate bit, so a single enum can also represent a set of protocols, which is often useful as well. Reviewed-by: Simon Horman Signed-off-by: Ben Pfaff --- lib/learning-switch.c | 49 +++- lib/learning-switch.h | 8 +- lib/ofp-parse.c | 71 ++--- lib/ofp-parse.h | 12 +- lib/ofp-print.c | 6 +- lib/ofp-util.c | 513 +++++++++++++++++++++++++++++++------ lib/ofp-util.h | 92 +++++-- lib/rconn.c | 10 +- lib/rconn.h | 1 + lib/util.h | 14 + lib/vconn.c | 21 +- lib/vconn.h | 1 + ofproto/connmgr.c | 45 +--- ofproto/connmgr.h | 7 +- ofproto/ofproto.c | 25 +- tests/autopath.at | 8 +- tests/learn.at | 19 +- tests/ofproto.at | 12 +- tests/ovs-ofctl.at | 65 +++-- utilities/ovs-controller.c | 35 +-- utilities/ovs-ofctl.8.in | 62 +++-- utilities/ovs-ofctl.c | 399 ++++++++++++++--------------- 22 files changed, 966 insertions(+), 509 deletions(-) diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 4ce307e4b..125bc37e2 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -139,18 +139,47 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) 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; diff --git a/lib/learning-switch.h b/lib/learning-switch.h index c6f347e19..833b94da6 100644 --- a/lib/learning-switch.h +++ b/lib/learning-switch.h @@ -46,10 +46,10 @@ struct lswitch_config { * 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. */ diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 58bf49d10..c630ccd1a 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -696,70 +696,49 @@ parse_ofp_actions(const char *s_, 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 diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index 1812a450a..c552caa51 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -22,9 +22,7 @@ #include #include #include -#include "openflow/nicira-ext.h" -struct list; struct ofpbuf; struct ofputil_flow_mod; struct ofputil_flow_stats_request; @@ -32,12 +30,10 @@ 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); diff --git a/lib/ofp-print.c b/lib/ofp-print.c index be2558e2c..b1f46df83 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -784,7 +784,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, 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; @@ -1319,8 +1319,8 @@ ofp_print_nxt_set_flow_format(struct ds *string, 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); } diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 0f3fdb214..acddf3cc2 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -838,39 +838,248 @@ ofputil_msg_type_code(const struct ofputil_msg_type *type) return type->code; } -/* 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 @@ -919,12 +1128,12 @@ regs_fully_wildcarded(const struct flow_wildcards *wc) 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; @@ -932,74 +1141,161 @@ ofputil_min_flow_format(const struct cls_rule *rule) /* 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) { @@ -1029,13 +1325,11 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id) * 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; @@ -1068,7 +1362,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, /* 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; @@ -1118,7 +1412,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, NOT_REACHED(); } - if (flow_mod_table_id) { + if (protocol & OFPUTIL_P_TID) { fm->command = command & 0xff; fm->table_id = command >> 8; } else { @@ -1130,26 +1424,28 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, } /* 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); @@ -1161,10 +1457,10 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, 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; @@ -1184,7 +1480,9 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, nfm->out_port = htons(fm->out_port); nfm->flags = htons(fm->flags); nfm->match_len = htons(match_len); - } else { + break; + + default: NOT_REACHED(); } @@ -1193,6 +1491,35 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, 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, @@ -1274,14 +1601,16 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr, /* 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; @@ -1290,7 +1619,11 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, 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; @@ -1304,13 +1637,33 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, 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'. * @@ -1516,7 +1869,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *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, @@ -1605,15 +1958,17 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, } /* 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), @@ -1627,7 +1982,11 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, 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; @@ -1644,7 +2003,10 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, 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(); } @@ -2793,12 +3155,9 @@ action_outputs_to_port(const union ofp_action *action, ovs_be16 port) * 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 */ @@ -2821,8 +3180,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) 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) { @@ -2836,10 +3194,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) } } } 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; } diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 5cc63272f..c2b0e2a00 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -111,25 +111,75 @@ void ofputil_format_port(uint16_t port, struct ds *); 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 *); @@ -139,7 +189,7 @@ struct ofpbuf *ofputil_make_set_packet_in_format(enum nx_packet_in_format); /* 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; @@ -157,12 +207,14 @@ struct ofputil_flow_mod { 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; @@ -175,9 +227,11 @@ struct ofputil_flow_stats_request { 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; @@ -200,7 +254,7 @@ int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *, 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. */ @@ -211,7 +265,7 @@ struct ofpbuf *ofputil_encode_aggregate_stats_reply( 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; @@ -226,7 +280,7 @@ struct ofputil_flow_removed { 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 { diff --git a/lib/rconn.c b/lib/rconn.c index 1b69b8f60..072e1ad82 100644 --- a/lib/rconn.c +++ b/lib/rconn.c @@ -1,5 +1,5 @@ /* - * 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. @@ -731,6 +731,14 @@ rconn_get_local_port(const struct rconn *rconn) 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 diff --git a/lib/rconn.h b/lib/rconn.h index a6c2fa7f4..d0326e65a 100644 --- a/lib/rconn.h +++ b/lib/rconn.h @@ -76,6 +76,7 @@ ovs_be32 rconn_get_remote_ip(const struct rconn *); 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 *); diff --git a/lib/util.h b/lib/util.h index d504f2fe9..9318fa727 100644 --- a/lib/util.h +++ b/lib/util.h @@ -78,6 +78,20 @@ extern const char *program_name; /* 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 diff --git a/lib/vconn.c b/lib/vconn.c index 5da502627..93d6388cf 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -353,6 +353,17 @@ vconn_get_local_port(const struct vconn *vconn) 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) { @@ -471,7 +482,7 @@ vconn_connect(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) { @@ -538,14 +549,14 @@ do_recv(struct vconn *vconn, struct ofpbuf **msgp) } 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", @@ -995,8 +1006,8 @@ vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status, : !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; diff --git a/lib/vconn.h b/lib/vconn.h index 74b6b49cb..516e2d3a8 100644 --- a/lib/vconn.h +++ b/lib/vconn.h @@ -40,6 +40,7 @@ ovs_be32 vconn_get_remote_ip(const struct vconn *); 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 *); diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index c820ad5d3..a94f2916e 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -58,9 +58,8 @@ struct ofconn { /* 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. */ @@ -786,20 +785,23 @@ ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn) 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 @@ -831,24 +833,6 @@ ofconn_set_controller_id(struct ofconn *ofconn, uint16_t controller_id) 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) @@ -994,9 +978,8 @@ ofconn_flush(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 @@ -1289,7 +1272,7 @@ connmgr_send_flow_removed(struct connmgr *mgr, * 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); } } diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h index e326482a1..14698aba9 100644 --- a/ofproto/connmgr.h +++ b/ofproto/connmgr.h @@ -91,17 +91,14 @@ enum ofconn_type ofconn_get_type(const struct ofconn *); 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 *); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index ce36d9569..25303adf7 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -2981,8 +2981,7 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) 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; } @@ -3067,8 +3066,12 @@ handle_nxt_flow_mod_table_id(struct ofconn *ofconn, { 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; } @@ -3077,20 +3080,22 @@ handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh) { 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; } diff --git a/tests/autopath.at b/tests/autopath.at index 6b837f84b..c3e0b346b 100644 --- a/tests/autopath.at +++ b/tests/autopath.at @@ -2,10 +2,14 @@ AT_BANNER([autopath link selection]) 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 diff --git a/tests/learn.at b/tests/learn.at index a97017181..861c2f9a9 100644 --- a/tests/learn.at +++ b/tests/learn.at @@ -7,7 +7,9 @@ actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output: 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]) ]]) @@ -22,11 +24,12 @@ table=0 actions=learn(table=1,hard_timeout=10, NXM_OF_VLAN_TCI[0..11],output:NXM 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 @@ -37,7 +40,9 @@ ip,actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[]) 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[]) ]]) diff --git a/tests/ofproto.at b/tests/ofproto.at index 6602816ae..0bc997cfe 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -262,13 +262,13 @@ NXST_FLOW reply: ]) # 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]) @@ -328,8 +328,8 @@ NXST_FLOW reply: # 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 diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 39625d118..039592570 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -1,6 +1,33 @@ 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 @@ -33,19 +60,19 @@ actions=controller(max_len=123,reason=invalid_ttl,id=555) 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) @@ -96,6 +123,8 @@ dl_dst=aa:bb:cc:dd:ee:ff/00:00:00:00:00:00,actions=drop ]) 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 @@ -153,7 +182,9 @@ vlan_tci=0x1123/0x1fff,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 @@ -596,7 +627,7 @@ dnl Check that "-F openflow10" rejects a flow_mod with a tun_id, since 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 @@ -631,7 +662,7 @@ dnl can't be represented in OpenFlow 1.0. 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 diff --git a/utilities/ovs-controller.c b/utilities/ovs-controller.c index cb70e4f77..25deaffd9 100644 --- a/utilities/ovs-controller.c +++ b/utilities/ovs-controller.c @@ -78,9 +78,9 @@ static uint32_t default_queue = UINT32_MAX; /* -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; @@ -230,7 +230,8 @@ new_switch(struct switch_ *sw, struct vconn *vconn) : 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); @@ -258,29 +259,6 @@ do_switching(struct switch_ *sw) : 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) { @@ -386,7 +364,8 @@ parse_options(int argc, char *argv[]) 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: diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 0755e9cb1..d1d82c50b 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1177,33 +1177,55 @@ passing through the flow. \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" diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 607ec256a..7936c1182 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -67,9 +67,9 @@ static bool strict; * (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 @@ -146,9 +146,9 @@ parse_options(int argc, char *argv[]) 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; @@ -282,12 +282,14 @@ open_vconn_socket(const char *name, struct vconn **vconnp) 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); @@ -317,9 +319,17 @@ open_vconn__(const char *name, const char *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); @@ -564,87 +574,74 @@ str_to_port_no(const char *vconn_name, const char *port_name) } 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); } @@ -685,122 +682,110 @@ do_queue_stats(int argc, char *argv[]) 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 @@ -1456,12 +1441,12 @@ fte_insert(struct classifier *cls, const struct cls_rule *rule, } /* 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; @@ -1471,11 +1456,10 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index) } 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); @@ -1487,9 +1471,7 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index) 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); } @@ -1499,14 +1481,15 @@ read_flows_from_file(const char *filename, struct classifier *cls, int 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; @@ -1519,7 +1502,7 @@ read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format, 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); @@ -1583,7 +1566,7 @@ read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format, 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; @@ -1607,7 +1590,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command, 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); } @@ -1615,7 +1598,7 @@ static void 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; @@ -1623,11 +1606,12 @@ do_replace_flows(int argc OVS_UNUSED, char *argv[]) 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); @@ -1639,7 +1623,7 @@ do_replace_flows(int argc OVS_UNUSED, char *argv[]) if (sw_ver && !file_ver) { fte_make_flow_mod(fte, SWITCH_IDX, OFPFC_DELETE_STRICT, - flow_format, &requests); + protocol, &requests); } } @@ -1652,8 +1636,7 @@ do_replace_flows(int argc OVS_UNUSED, char *argv[]) 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); @@ -1671,12 +1654,12 @@ read_flows_from_source(const char *source, struct classifier *cls, int index) || (!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); } } @@ -1724,14 +1707,40 @@ do_diff_flows(int argc OVS_UNUSED, char *argv[]) /* 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); } } @@ -1740,20 +1749,10 @@ print_packet_list(struct list *packets) 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 @@ -1761,28 +1760,12 @@ do_parse_flow(int argc OVS_UNUSED, char *argv[]) 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 -- 2.43.0