[ovs-dev] [PATCH 2/2] lib: Keep track of usable protocols while parsing.

Jarno Rajahalme jrajahalme at nicira.com
Wed Aug 21 01:41:45 UTC 2013


Keep track of usable protocols while parsing actions and matches,
rather than checking for them afterwards.  This fixes silently discarded
meter and goto table instructions when not explicitly specifying the
protocol to use.

Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
---
 lib/learning-switch.c      |   11 ++-
 lib/learning-switch.h      |    2 +
 lib/meta-flow.c            |  120 ++++++++++++++++++++++++--
 lib/meta-flow.h            |   23 ++++-
 lib/ofp-parse.c            |  152 +++++++++++++++++++++++++--------
 lib/ofp-parse.h            |   22 +++--
 lib/ofp-util.c             |  203 --------------------------------------------
 lib/ofp-util.h             |   17 ++--
 tests/learn.at             |    8 +-
 tests/ovs-ofctl.at         |   18 ++--
 utilities/ovs-controller.c |    5 +-
 utilities/ovs-ofctl.c      |   63 ++++++++------
 12 files changed, 333 insertions(+), 311 deletions(-)

diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 9c1aff7..336257c 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -89,6 +89,7 @@ struct lswitch {
      * to set up the flow table. */
     const struct ofputil_flow_mod *default_flows;
     size_t n_default_flows;
+    enum ofputil_protocol usable_protocols;
 };
 
 /* The log messages here could actually be useful in debugging, so keep the
@@ -161,6 +162,7 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
 
     sw->default_flows = cfg->default_flows;
     sw->n_default_flows = cfg->n_default_flows;
+    sw->usable_protocols = cfg->usable_protocols;
 
     sw->queued = rconn_packet_counter_create();
 
@@ -176,7 +178,6 @@ lswitch_handshake(struct lswitch *sw)
 
     protocol = ofputil_protocol_from_ofp_version(rconn_get_version(sw->rconn));
     if (sw->default_flows) {
-        enum ofputil_protocol usable_protocols;
         struct ofpbuf *msg = NULL;
         int error = 0;
         size_t i;
@@ -188,10 +189,8 @@ lswitch_handshake(struct lswitch *sw)
          * 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(
-            sw->default_flows, sw->n_default_flows);
-        if (!(protocol & usable_protocols)) {
-            enum ofputil_protocol want = rightmost_1bit(usable_protocols);
+        if (!(protocol & sw->usable_protocols)) {
+            enum ofputil_protocol want = rightmost_1bit(sw->usable_protocols);
             while (!error) {
                 msg = ofputil_encode_set_protocol(protocol, want, &protocol);
                 if (!msg) {
@@ -200,7 +199,7 @@ lswitch_handshake(struct lswitch *sw)
                 error = rconn_send(sw->rconn, msg, NULL);
             }
         }
-        if (protocol & usable_protocols) {
+        if (protocol & sw->usable_protocols) {
             for (i = 0; !error && i < sw->n_default_flows; i++) {
                 msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol);
                 error = rconn_send(sw->rconn, msg, NULL);
diff --git a/lib/learning-switch.h b/lib/learning-switch.h
index dcfb5a2..b3a5d13 100644
--- a/lib/learning-switch.h
+++ b/lib/learning-switch.h
@@ -20,6 +20,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
+#include "ofp-util.h"
 
 struct ofpbuf;
 struct rconn;
@@ -50,6 +51,7 @@ struct lswitch_config {
      * to set up the flow table. */
     const struct ofputil_flow_mod *default_flows;
     size_t n_default_flows;
+    enum ofputil_protocol usable_protocols;
 
     /* The OpenFlow queue to use by default.  Use UINT32_MAX to avoid
      * specifying a particular queue. */
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index ce061a3..58d80a3 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -55,6 +55,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
         OXM_OF_TUNNEL_ID, "OXM_OF_TUNNEL_ID",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_TUN_SRC, "tun_src", NULL,
         MF_FIELD_SIZES(be32),
@@ -64,6 +66,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_TUN_IPV4_SRC, "NXM_NX_TUN_IPV4_SRC",
         NXM_NX_TUN_IPV4_SRC, "NXM_NX_TUN_IPV4_SRC",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_TUN_DST, "tun_dst", NULL,
         MF_FIELD_SIZES(be32),
@@ -73,6 +77,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_TUN_IPV4_DST, "NXM_NX_TUN_IPV4_DST",
         NXM_NX_TUN_IPV4_DST, "NXM_NX_TUN_IPV4_DST",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_TUN_FLAGS, "tun_flags", NULL,
         MF_FIELD_SIZES(be16),
@@ -82,6 +88,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_NONE,
+        OFPUTIL_P_NONE,
     }, {
         MFF_TUN_TOS, "tun_tos", NULL,
         MF_FIELD_SIZES(u8),
@@ -91,6 +99,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_NONE,
+        OFPUTIL_P_NONE,
     }, {
         MFF_TUN_TTL, "tun_ttl", NULL,
         MF_FIELD_SIZES(u8),
@@ -100,6 +110,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_NONE,
+        OFPUTIL_P_NONE,
     }, {
         MFF_METADATA, "metadata", NULL,
         MF_FIELD_SIZES(be64),
@@ -109,6 +121,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         OXM_OF_METADATA, "OXM_OF_METADATA",
         OXM_OF_METADATA, "OXM_OF_METADATA",
+        OFPUTIL_P_NXM_OF11_UP,
+        OFPUTIL_P_NXM_OF11_UP,
     }, {
         MFF_IN_PORT, "in_port", NULL,
         MF_FIELD_SIZES(be16),
@@ -118,6 +132,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_IN_PORT, "NXM_OF_IN_PORT",
         NXM_OF_IN_PORT, "NXM_OF_IN_PORT",
+        OFPUTIL_P_ANY,   /* OF11+ via mapping to 32 bits. */
+        OFPUTIL_P_NONE,
     }, {
         MFF_IN_PORT_OXM, "in_port_oxm", NULL,
         MF_FIELD_SIZES(be32),
@@ -127,6 +143,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         OXM_OF_IN_PORT, "OXM_OF_IN_PORT",
         OXM_OF_IN_PORT, "OXM_OF_IN_PORT",
+        OFPUTIL_P_OF11_UP,
+        OFPUTIL_P_NONE,
     }, {
         MFF_SKB_PRIORITY, "skb_priority", NULL,
         MF_FIELD_SIZES(be32),
@@ -136,6 +154,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_NONE,
+        OFPUTIL_P_NONE,
     }, {
         MFF_PKT_MARK, "pkt_mark", NULL,
         MF_FIELD_SIZES(be32),
@@ -145,6 +165,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_PKT_MARK, "NXM_NX_PKT_MARK",
         NXM_NX_PKT_MARK, "NXM_NX_PKT_MARK",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
 
 #define REGISTER(IDX)                           \
@@ -157,6 +179,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,                                   \
         NXM_NX_REG(IDX), "NXM_NX_REG" #IDX,     \
         NXM_NX_REG(IDX), "NXM_NX_REG" #IDX,     \
+        OFPUTIL_P_NXM_OXM_ANY,                  \
+        OFPUTIL_P_NXM_OXM_ANY,                  \
     }
 #if FLOW_N_REGS > 0
     REGISTER(0),
@@ -199,6 +223,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_ETH_SRC, "NXM_OF_ETH_SRC",
         OXM_OF_ETH_SRC, "OXM_OF_ETH_SRC",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OF11_UP,   /* Bitwise masking only with NXM and OF11+! */
     }, {
         MFF_ETH_DST, "eth_dst", "dl_dst",
         MF_FIELD_SIZES(mac),
@@ -208,6 +234,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_ETH_DST, "NXM_OF_ETH_DST",
         OXM_OF_ETH_DST, "OXM_OF_ETH_DST",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OF11_UP,   /* Bitwise masking only with NXM and OF11+! */
     }, {
         MFF_ETH_TYPE, "eth_type", "dl_type",
         MF_FIELD_SIZES(be16),
@@ -217,6 +245,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_ETH_TYPE, "NXM_OF_ETH_TYPE",
         OXM_OF_ETH_TYPE, "OXM_OF_ETH_TYPE",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NONE,
     },
 
     {
@@ -228,6 +258,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_VLAN_TCI, "NXM_OF_VLAN_TCI",
         NXM_OF_VLAN_TCI, "NXM_OF_VLAN_TCI",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_DL_VLAN, "dl_vlan", NULL,
         sizeof(ovs_be16), 12,
@@ -237,6 +269,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_VLAN_VID, "vlan_vid", NULL,
         sizeof(ovs_be16), 12,
@@ -246,6 +280,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         OXM_OF_VLAN_VID, "OXM_OF_VLAN_VID",
         OXM_OF_VLAN_VID, "OXM_OF_VLAN_VID",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_DL_VLAN_PCP, "dl_vlan_pcp", NULL,
         1, 3,
@@ -255,6 +291,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_ANY,   /* Will be mapped to NXM and OXM. */
+        OFPUTIL_P_NONE,
     }, {
         MFF_VLAN_PCP, "vlan_pcp", NULL,
         1, 3,
@@ -264,6 +302,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
         OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
+        OFPUTIL_P_ANY,   /* Will be mapped to OF10 and NXM. */
+        OFPUTIL_P_NONE,
     },
 
     /* ## ---- ## */
@@ -278,6 +318,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         OXM_OF_MPLS_LABEL, "OXM_OF_MPLS_LABEL",
         OXM_OF_MPLS_LABEL, "OXM_OF_MPLS_LABEL",
+        OFPUTIL_P_NXM_OF11_UP,
+        OFPUTIL_P_NONE,
     }, {
         MFF_MPLS_TC, "mpls_tc", NULL,
         1, 3,
@@ -287,6 +329,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         OXM_OF_MPLS_TC, "OXM_OF_MPLS_TC",
         OXM_OF_MPLS_TC, "OXM_OF_MPLS_TC",
+        OFPUTIL_P_NXM_OF11_UP,
+        OFPUTIL_P_NONE,
     }, {
         MFF_MPLS_BOS, "mpls_bos", NULL,
         1, 1,
@@ -296,6 +340,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         OXM_OF_MPLS_BOS, "OXM_OF_MPLS_BOS",
         OXM_OF_MPLS_BOS, "OXM_OF_MPLS_BOS",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
     },
 
     /* ## -- ## */
@@ -311,6 +357,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_IP_SRC, "NXM_OF_IP_SRC",
         OXM_OF_IPV4_SRC, "OXM_OF_IPV4_SRC",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OF11_UP,
     }, {
         MFF_IPV4_DST, "ip_dst", "nw_dst",
         MF_FIELD_SIZES(be32),
@@ -320,6 +368,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_IP_DST, "NXM_OF_IP_DST",
         OXM_OF_IPV4_DST, "OXM_OF_IPV4_DST",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OF11_UP,
     },
 
     {
@@ -331,6 +381,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_IPV6_SRC, "NXM_NX_IPV6_SRC",
         OXM_OF_IPV6_SRC, "OXM_OF_IPV6_SRC",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_IPV6_DST, "ipv6_dst", NULL,
         MF_FIELD_SIZES(ipv6),
@@ -340,6 +392,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_IPV6_DST, "NXM_NX_IPV6_DST",
         OXM_OF_IPV6_DST, "OXM_OF_IPV6_DST",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
     {
         MFF_IPV6_LABEL, "ipv6_label", NULL,
@@ -350,6 +404,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_IPV6_LABEL, "NXM_NX_IPV6_LABEL",
         OXM_OF_IPV6_FLABEL, "OXM_OF_IPV6_FLABEL",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
 
     {
@@ -361,6 +417,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_IP_PROTO, "NXM_OF_IP_PROTO",
         OXM_OF_IP_PROTO, "OXM_OF_IP_PROTO",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NONE,
     }, {
         MFF_IP_DSCP, "nw_tos", NULL,
         MF_FIELD_SIZES(u8),
@@ -370,6 +428,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_IP_TOS, "NXM_OF_IP_TOS",
         NXM_OF_IP_TOS, "NXM_OF_IP_TOS",
+        OFPUTIL_P_ANY,   /* Will be shifted for OXM. */
+        OFPUTIL_P_NONE,
     }, {
         MFF_IP_DSCP_SHIFTED, "nw_tos_shifted", NULL,
         MF_FIELD_SIZES(u8),
@@ -379,6 +439,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         OXM_OF_IP_DSCP, "OXM_OF_IP_DSCP",
         OXM_OF_IP_DSCP, "OXM_OF_IP_DSCP",
+        OFPUTIL_P_ANY,   /* Will be shifted for non-OXM. */
+        OFPUTIL_P_NONE,
     }, {
         MFF_IP_ECN, "nw_ecn", NULL,
         1, 2,
@@ -388,6 +450,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_IP_ECN, "NXM_NX_IP_ECN",
         OXM_OF_IP_ECN, "OXM_OF_IP_ECN",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
     }, {
         MFF_IP_TTL, "nw_ttl", NULL,
         MF_FIELD_SIZES(u8),
@@ -397,6 +461,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_NX_IP_TTL, "NXM_NX_IP_TTL",
         NXM_NX_IP_TTL, "NXM_NX_IP_TTL",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
     }, {
         MFF_IP_FRAG, "ip_frag", NULL,
         1, 2,
@@ -406,6 +472,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_IP_FRAG, "NXM_NX_IP_FRAG",
         NXM_NX_IP_FRAG, "NXM_NX_IP_FRAG",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
 
     {
@@ -417,6 +485,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_ARP_OP, "NXM_OF_ARP_OP",
         OXM_OF_ARP_OP, "OXM_OF_ARP_OP",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NONE,
     }, {
         MFF_ARP_SPA, "arp_spa", NULL,
         MF_FIELD_SIZES(be32),
@@ -426,6 +496,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_ARP_SPA, "NXM_OF_ARP_SPA",
         OXM_OF_ARP_SPA, "OXM_OF_ARP_SPA",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OF11_UP,
     }, {
         MFF_ARP_TPA, "arp_tpa", NULL,
         MF_FIELD_SIZES(be32),
@@ -435,6 +507,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_ARP_TPA, "NXM_OF_ARP_TPA",
         OXM_OF_ARP_TPA, "OXM_OF_ARP_TPA",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OF11_UP,
     }, {
         MFF_ARP_SHA, "arp_sha", NULL,
         MF_FIELD_SIZES(mac),
@@ -444,6 +518,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_ARP_SHA, "NXM_NX_ARP_SHA",
         OXM_OF_ARP_SHA, "OXM_OF_ARP_SHA",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_ARP_THA, "arp_tha", NULL,
         MF_FIELD_SIZES(mac),
@@ -453,6 +529,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_ARP_THA, "NXM_NX_ARP_THA",
         OXM_OF_ARP_THA, "OXM_OF_ARP_THA",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
 
     /* ## -- ## */
@@ -468,6 +546,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_TCP_SRC, "NXM_OF_TCP_SRC",
         OXM_OF_TCP_SRC, "OXM_OF_TCP_SRC",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_TCP_DST, "tcp_dst", "tp_dst",
         MF_FIELD_SIZES(be16),
@@ -477,6 +557,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_TCP_DST, "NXM_OF_TCP_DST",
         OXM_OF_TCP_DST, "OXM_OF_TCP_DST",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
 
     {
@@ -488,6 +570,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_UDP_SRC, "NXM_OF_UDP_SRC",
         OXM_OF_UDP_SRC, "OXM_OF_UDP_SRC",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_UDP_DST, "udp_dst", NULL,
         MF_FIELD_SIZES(be16),
@@ -497,6 +581,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         true,
         NXM_OF_UDP_DST, "NXM_OF_UDP_DST",
         OXM_OF_UDP_DST, "OXM_OF_UDP_DST",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
 
     {
@@ -508,6 +594,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_ICMP_TYPE, "NXM_OF_ICMP_TYPE",
         OXM_OF_ICMPV4_TYPE, "OXM_OF_ICMPV4_TYPE",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NONE,
     }, {
         MFF_ICMPV4_CODE, "icmp_code", NULL,
         MF_FIELD_SIZES(u8),
@@ -517,6 +605,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_OF_ICMP_CODE, "NXM_OF_ICMP_CODE",
         OXM_OF_ICMPV4_CODE, "OXM_OF_ICMPV4_CODE",
+        OFPUTIL_P_ANY,
+        OFPUTIL_P_NONE,
     },
 
     {
@@ -528,6 +618,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_ICMPV6_TYPE, "NXM_NX_ICMPV6_TYPE",
         OXM_OF_ICMPV6_TYPE, "OXM_OF_ICMPV6_TYPE",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
     }, {
         MFF_ICMPV6_CODE, "icmpv6_code", NULL,
         MF_FIELD_SIZES(u8),
@@ -537,6 +629,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_ICMPV6_CODE, "NXM_NX_ICMPV6_CODE",
         OXM_OF_ICMPV6_CODE, "OXM_OF_ICMPV6_CODE",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
     },
 
     /* ## ---- ## */
@@ -552,6 +646,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_ND_TARGET, "NXM_NX_ND_TARGET",
         OXM_OF_IPV6_ND_TARGET, "OXM_OF_IPV6_ND_TARGET",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_ND_SLL, "nd_sll", NULL,
         MF_FIELD_SIZES(mac),
@@ -561,6 +657,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_ND_SLL, "NXM_NX_ND_SLL",
         OXM_OF_IPV6_ND_SLL, "OXM_OF_IPV6_ND_SLL",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }, {
         MFF_ND_TLL, "nd_tll", NULL,
         MF_FIELD_SIZES(mac),
@@ -570,6 +668,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         NXM_NX_ND_TLL, "NXM_NX_ND_TLL",
         OXM_OF_IPV6_ND_TLL, "OXM_OF_IPV6_ND_TLL",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     }
 };
 
@@ -1764,17 +1864,17 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
  *
  * 'mask' must be a valid mask for 'mf' (see mf_is_mask_valid()).  The caller
  * is responsible for ensuring that 'match' meets 'mf''s prerequisites. */
-void
+enum ofputil_protocol
 mf_set(const struct mf_field *mf,
        const union mf_value *value, const union mf_value *mask,
        struct match *match)
 {
     if (!mask || is_all_ones((const uint8_t *) mask, mf->n_bytes)) {
         mf_set_value(mf, value, match);
-        return;
+        return mf->usable_protocols;
     } else if (is_all_zeros((const uint8_t *) mask, mf->n_bytes)) {
         mf_set_wild(mf, match);
-        return;
+        return OFPUTIL_P_ANY;
     }
 
     switch (mf->id) {
@@ -1861,11 +1961,11 @@ mf_set(const struct mf_field *mf,
 
     case MFF_IPV4_SRC:
         match_set_nw_src_masked(match, value->be32, mask->be32);
-        break;
+        goto cidr_check;
 
     case MFF_IPV4_DST:
         match_set_nw_dst_masked(match, value->be32, mask->be32);
-        break;
+        goto cidr_check;
 
     case MFF_IPV6_SRC:
         match_set_ipv6_src_masked(match, &value->ipv6, &mask->ipv6);
@@ -1893,11 +1993,11 @@ mf_set(const struct mf_field *mf,
 
     case MFF_ARP_SPA:
         match_set_nw_src_masked(match, value->be32, mask->be32);
-        break;
+        goto cidr_check;
 
     case MFF_ARP_TPA:
         match_set_nw_dst_masked(match, value->be32, mask->be32);
-        break;
+        goto cidr_check;
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
@@ -1913,6 +2013,12 @@ mf_set(const struct mf_field *mf,
     default:
         NOT_REACHED();
     }
+
+    return mf->usable_protocols_bitwise;
+
+cidr_check:
+    return ip_is_cidr(mask->be32) ? mf->usable_protocols :
+            mf->usable_protocols_bitwise;
 }
 
 static enum ofperr
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index 93b894d..b5b014f 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -22,6 +22,7 @@
 #include <netinet/ip6.h>
 #include "flow.h"
 #include "ofp-errors.h"
+#include "ofp-util.h"
 #include "packets.h"
 
 struct ds;
@@ -276,7 +277,18 @@ struct mf_field {
     uint32_t nxm_header;        /* An NXM_* (or OXM_*) constant. */
     const char *nxm_name;       /* The nxm_header constant's name. */
     uint32_t oxm_header;        /* An OXM_* (or NXM_*) constant. */
-    const char *oxm_name;	    /* The oxm_header constant's name */
+    const char *oxm_name;       /* The oxm_header constant's name */
+
+    /* Usable protocols.
+     * NXM and OXM are extensible, allowing later extensions to be sent in
+     * earlier protocol versions, so this does not necessarily correspond to
+     * the OpenFlow protocol version the field was introduced in.
+     * Also, some field types are tranparently mapped to each other via the
+     * struct flow (like vlan and dscp/tos fields), so each variant supports
+     * all protocols. */
+    enum ofputil_protocol usable_protocols; /* If fully/cidr masked. */
+    /* If partially/non-cidr masked. */
+    enum ofputil_protocol usable_protocols_bitwise;
 };
 
 /* The representation of a field's value. */
@@ -340,9 +352,12 @@ bool mf_is_zero(const struct mf_field *, const struct flow *);
 
 void mf_get(const struct mf_field *, const struct match *,
             union mf_value *value, union mf_value *mask);
-void mf_set(const struct mf_field *,
-            const union mf_value *value, const union mf_value *mask,
-            struct match *);
+
+/* Returns the set of usable protocols. */
+enum ofputil_protocol mf_set(const struct mf_field *,
+                             const union mf_value *value,
+                             const union mf_value *mask,
+                             struct match *);
 
 void mf_set_wild(const struct mf_field *, struct match *);
 
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 5cb39f5..5f3f533 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -464,7 +464,8 @@ parse_set_mpls_ttl(struct ofpbuf *b, const char *arg)
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
-set_field_parse__(char *arg, struct ofpbuf *ofpacts)
+set_field_parse__(char *arg, struct ofpbuf *ofpacts,
+                  enum ofputil_protocol *usable_protocols)
 {
     struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts);
     char *value;
@@ -502,6 +503,7 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts)
     }
     ofpact_set_field_init(load, mf, &mf_value);
 
+    *usable_protocols &= mf->usable_protocols;
     return NULL;
 }
 
@@ -511,10 +513,11 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts)
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
-set_field_parse(const char *arg, struct ofpbuf *ofpacts)
+set_field_parse(const char *arg, struct ofpbuf *ofpacts,
+                enum ofputil_protocol *usable_protocols)
 {
     char *copy = xstrdup(arg);
-    char *error = set_field_parse__(copy, ofpacts);
+    char *error = set_field_parse__(copy, ofpacts, usable_protocols);
     free(copy);
     return error;
 }
@@ -593,7 +596,8 @@ parse_sample(struct ofpbuf *b, char *arg)
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
 parse_named_action(enum ofputil_action_code code,
-                   char *arg, struct ofpbuf *ofpacts)
+                   char *arg, struct ofpbuf *ofpacts,
+                   enum ofputil_protocol *usable_protocols)
 {
     size_t orig_size = ofpacts->size;
     struct ofpact_tunnel *tunnel;
@@ -639,7 +643,7 @@ parse_named_action(enum ofputil_action_code code,
         break;
 
     case OFPUTIL_OFPAT12_SET_FIELD:
-        return set_field_parse(arg, ofpacts);
+        return set_field_parse(arg, ofpacts, usable_protocols);
 
     case OFPUTIL_OFPAT10_STRIP_VLAN:
     case OFPUTIL_OFPAT11_POP_VLAN:
@@ -647,6 +651,7 @@ parse_named_action(enum ofputil_action_code code,
         break;
 
     case OFPUTIL_OFPAT11_PUSH_VLAN:
+        *usable_protocols &= OFPUTIL_P_OF11_UP;
         error = str_to_u16(arg, "ethertype", &ethertype);
         if (error) {
             return error;
@@ -842,11 +847,12 @@ parse_named_action(enum ofputil_action_code code,
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
 str_to_ofpact__(char *pos, char *act, char *arg,
-                struct ofpbuf *ofpacts, int n_actions)
+                struct ofpbuf *ofpacts, int n_actions,
+                enum ofputil_protocol *usable_protocols)
 {
     int code = ofputil_action_code_from_name(act);
     if (code >= 0) {
-        return parse_named_action(code, arg, ofpacts);
+        return parse_named_action(code, arg, ofpacts, usable_protocols);
     } else if (!strcasecmp(act, "drop")) {
         if (n_actions) {
             return xstrdup("Drop actions must not be preceded by other "
@@ -872,7 +878,8 @@ str_to_ofpact__(char *pos, char *act, char *arg,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
-str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
+str_to_ofpacts(char *str, struct ofpbuf *ofpacts,
+               enum ofputil_protocol *usable_protocols)
 {
     size_t orig_size = ofpacts->size;
     char *pos, *act, *arg;
@@ -882,7 +889,8 @@ str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
     pos = str;
     n_actions = 0;
     while (ofputil_parse_key_value(&pos, &act, &arg)) {
-        char *error = str_to_ofpact__(pos, act, arg, ofpacts, n_actions);
+        char *error = str_to_ofpact__(pos, act, arg, ofpacts, n_actions,
+                                      usable_protocols);
         if (error) {
             ofpacts->size = orig_size;
             return error;
@@ -907,11 +915,14 @@ str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
 parse_named_instruction(enum ovs_instruction_type type,
-                        char *arg, struct ofpbuf *ofpacts)
+                        char *arg, struct ofpbuf *ofpacts,
+                        enum ofputil_protocol *usable_protocols)
 {
     char *error_s = NULL;
     enum ofperr error;
 
+    *usable_protocols &= OFPUTIL_P_OF11_UP;
+
     switch (type) {
     case OVSINST_OFPIT11_APPLY_ACTIONS:
         NOT_REACHED();  /* This case is handled by str_to_inst_ofpacts() */
@@ -927,10 +938,12 @@ parse_named_instruction(enum ovs_instruction_type type,
         break;
 
     case OVSINST_OFPIT13_METER:
+        *usable_protocols &= OFPUTIL_P_OF13_UP;
         error_s = str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id);
         break;
 
     case OVSINST_OFPIT11_WRITE_METADATA:
+        *usable_protocols &= OFPUTIL_P_NXM_OF11_UP;
         error_s = parse_metadata(ofpacts, arg);
         break;
 
@@ -963,7 +976,8 @@ parse_named_instruction(enum ovs_instruction_type type,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
-str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
+str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts,
+                    enum ofputil_protocol *usable_protocols)
 {
     size_t orig_size = ofpacts->size;
     char *pos, *inst, *arg;
@@ -976,7 +990,8 @@ str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
     while (ofputil_parse_key_value(&pos, &inst, &arg)) {
         type = ovs_instruction_type_from_name(inst);
         if (type < 0) {
-            char *error = str_to_ofpact__(pos, inst, arg, ofpacts, n_actions);
+            char *error = str_to_ofpact__(pos, inst, arg, ofpacts, n_actions,
+                                          usable_protocols);
             if (error) {
                 ofpacts->size = orig_size;
                 return error;
@@ -992,7 +1007,8 @@ str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
             return xasprintf("%s isn't supported. Just write actions then "
                              "it is interpreted as apply_actions", inst);
         } else {
-            char *error = parse_named_instruction(type, arg, ofpacts);
+            char *error = parse_named_instruction(type, arg, ofpacts,
+                                                  usable_protocols);
             if (error) {
                 ofpacts->size = orig_size;
                 return error;
@@ -1056,25 +1072,28 @@ parse_protocol(const char *name, const struct protocol **p_out)
 }
 
 /* Parses 's' as the (possibly masked) value of field 'mf', and updates
- * 'match' appropriately.
+ * 'match' appropriately.  Restricts the set of usable protocols to ones
+ * supporting the parsed field.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * WARN_UNUSED_RESULT
-parse_field(const struct mf_field *mf, const char *s, struct match *match)
+parse_field(const struct mf_field *mf, const char *s, struct match *match,
+            enum ofputil_protocol *usable_protocols)
 {
     union mf_value value, mask;
     char *error;
 
     error = mf_parse(mf, s, &value, &mask);
     if (!error) {
-        mf_set(mf, &value, &mask, match);
+        *usable_protocols &= mf_set(mf, &value, &mask, match);
     }
     return error;
 }
 
 static char * WARN_UNUSED_RESULT
-parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
+parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
+                enum ofputil_protocol *usable_protocols)
 {
     enum {
         F_OUT_PORT = 1 << 0,
@@ -1087,6 +1106,8 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
     char *act_str = NULL;
     char *name;
 
+    *usable_protocols = OFPUTIL_P_ANY;
+
     switch (command) {
     case -1:
         fields = F_OUT_PORT;
@@ -1164,10 +1185,13 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
             fm->flags |= OFPFF_CHECK_OVERLAP;
         } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) {
             fm->flags |= OFPFF12_RESET_COUNTS;
+            *usable_protocols &= OFPUTIL_P_OF12_UP;
         } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) {
             fm->flags |= OFPFF13_NO_PKT_COUNTS;
+            *usable_protocols &= OFPUTIL_P_OF13_UP;
         } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) {
             fm->flags |= OFPFF13_NO_BYT_COUNTS;
+            *usable_protocols &= OFPUTIL_P_OF13_UP;
         } else {
             char *value;
 
@@ -1178,6 +1202,9 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
 
             if (!strcmp(name, "table")) {
                 error = str_to_u8(value, "table", &fm->table_id);
+                if (fm->table_id != 0xff) {
+                    *usable_protocols &= OFPUTIL_P_TID;
+                }
             } else if (!strcmp(name, "out_port")) {
                 if (!ofputil_port_from_string(value, &fm->out_port)) {
                     error = xasprintf("%s is not a valid OpenFlow port",
@@ -1207,6 +1234,12 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
                         return error;
                     }
                     error = str_to_be64(mask + 1, &fm->cookie_mask);
+
+                    /* Matching of the cookie is only supported through NXM or
+                     * OF1.1+. */
+                    if (fm->cookie_mask != htonll(0)) {
+                        *usable_protocols &= OFPUTIL_P_NXM_OF11_UP;
+                    }
                 } else {
                     /* No mask means that the cookie is being set. */
                     if (command != OFPFC_ADD && command != OFPFC_MODIFY
@@ -1217,7 +1250,8 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
                     fm->modify_cookie = true;
                 }
             } else if (mf_from_name(name)) {
-                error = parse_field(mf_from_name(name), value, &fm->match);
+                error = parse_field(mf_from_name(name), value, &fm->match,
+                                    usable_protocols);
             } else if (!strcmp(name, "duration")
                        || !strcmp(name, "n_packets")
                        || !strcmp(name, "n_bytes")
@@ -1235,6 +1269,20 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
             }
         }
     }
+    /* Check for usable protocol interdependencies between match fields. */
+    if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) {
+        const struct flow_wildcards *wc = &fm->match.wc;
+        /* Only NXM and OXM support matching L3 and L4 fields within IPv6.
+         *
+         * (IPv6 specific fields as well as arp_sha, arp_tha, nw_frag, and
+         *  nw_ttl are covered elsewhere so they don't need to be included in
+         *  this test too.)
+         */
+        if (wc->masks.nw_proto || wc->masks.nw_tos
+            || wc->masks.tp_src || wc->masks.tp_dst) {
+            *usable_protocols &= OFPUTIL_P_NXM_OXM_ANY;
+        }
+    }
     if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
         && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
         /* On modifies without a mask, we are supposed to add a flow if
@@ -1247,7 +1295,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
         char *error;
 
         ofpbuf_init(&ofpacts, 32);
-        error = str_to_inst_ofpacts(act_str, &ofpacts);
+        error = str_to_inst_ofpacts(act_str, &ofpacts, usable_protocols);
         if (!error) {
             enum ofperr err;
 
@@ -1275,6 +1323,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
 
 /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
  * page) into 'fm' for sending the specified flow_mod 'command' to a switch.
+ * Returns the set of usable protocols in '*usable_protocols'.
  *
  * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_*
  * constant for 'command'.  To parse syntax for an OFPST_FLOW or
@@ -1283,12 +1332,13 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * WARN_UNUSED_RESULT
-parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_)
+parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
+              enum ofputil_protocol *usable_protocols)
 {
     char *string = xstrdup(str_);
     char *error;
 
-    error = parse_ofp_str__(fm, command, string);
+    error = parse_ofp_str__(fm, command, string, usable_protocols);
     if (error) {
         fm->ofpacts = NULL;
         fm->ofpacts_len = 0;
@@ -1300,7 +1350,8 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_)
 
 static char * WARN_UNUSED_RESULT
 parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
-                          struct ofpbuf *bands, int command)
+                          struct ofpbuf *bands, int command,
+                          enum ofputil_protocol *usable_protocols)
 {
     enum {
         F_METER = 1 << 0,
@@ -1311,6 +1362,9 @@ parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
     char *band_str = NULL;
     char *name;
 
+    /* Meters require at least OF 1.3. */
+    *usable_protocols &= OFPUTIL_P_OF13_UP;
+
     switch (command) {
     case -1:
         fields = F_METER;
@@ -1499,7 +1553,7 @@ parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
  * error.  The caller is responsible for freeing the returned string. */
 char * WARN_UNUSED_RESULT
 parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
-                        int command)
+                        int command, enum ofputil_protocol *usable_protocols)
 {
     struct ofpbuf bands;
     char *string;
@@ -1508,7 +1562,8 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
     ofpbuf_init(&bands, 64);
     string = xstrdup(str_);
 
-    error = parse_ofp_meter_mod_str__(mm, string, &bands, command);
+    error = parse_ofp_meter_mod_str__(mm, string, &bands, command,
+                                      usable_protocols);
 
     free(string);
     ofpbuf_uninit(&bands);
@@ -1518,7 +1573,8 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
 
 static char * WARN_UNUSED_RESULT
 parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
-                             const char *str_, char *string)
+                             const char *str_, char *string,
+                             enum ofputil_protocol *usable_protocols)
 {
     static atomic_uint32_t id = ATOMIC_VAR_INIT(0);
     char *save_ptr = NULL;
@@ -1571,7 +1627,8 @@ parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
             } else if (mf_from_name(name)) {
                 char *error;
 
-                error = parse_field(mf_from_name(name), value, &fmr->match);
+                error = parse_field(mf_from_name(name), value, &fmr->match,
+                                    usable_protocols);
                 if (error) {
                     return error;
                 }
@@ -1590,10 +1647,12 @@ parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
  * error.  The caller is responsible for freeing the returned string. */
 char * WARN_UNUSED_RESULT
 parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
-                           const char *str_)
+                           const char *str_,
+                           enum ofputil_protocol *usable_protocols)
 {
     char *string = xstrdup(str_);
-    char *error = parse_flow_monitor_request__(fmr, str_, string);
+    char *error = parse_flow_monitor_request__(fmr, str_, string,
+                                               usable_protocols);
     free(string);
     return error;
 }
@@ -1604,10 +1663,15 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * WARN_UNUSED_RESULT
-parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
+parse_ofpacts(const char *s_, struct ofpbuf *ofpacts,
+              enum ofputil_protocol *usable_protocols)
 {
     char *s = xstrdup(s_);
-    char *error = str_to_ofpacts(s, ofpacts);
+    char *error;
+
+    *usable_protocols = OFPUTIL_P_ANY;
+
+    error = str_to_ofpacts(s, ofpacts, usable_protocols);
     free(s);
 
     return error;
@@ -1620,9 +1684,10 @@ parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
  * error.  The caller is responsible for freeing the returned string. */
 char * WARN_UNUSED_RESULT
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
-                       uint16_t command)
+                       uint16_t command,
+                       enum ofputil_protocol *usable_protocols)
 {
-    char *error = parse_ofp_str(fm, command, string);
+    char *error = parse_ofp_str(fm, command, string, usable_protocols);
     if (!error) {
         /* Normalize a copy of the match.  This ensures that non-normalized
          * flows get logged but doesn't affect what gets sent to the switch, so
@@ -1642,13 +1707,16 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
  * error.  The caller is responsible for freeing the returned string. */
 char * WARN_UNUSED_RESULT
 parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
-                        struct ofputil_flow_mod **fms, size_t *n_fms)
+                        struct ofputil_flow_mod **fms, size_t *n_fms,
+                        enum ofputil_protocol *usable_protocols)
 {
     size_t allocated_fms;
     int line_number;
     FILE *stream;
     struct ds s;
 
+    *usable_protocols = OFPUTIL_P_ANY;
+
     *fms = NULL;
     *n_fms = 0;
 
@@ -1663,11 +1731,13 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
     line_number = 0;
     while (!ds_get_preprocessed_line(&s, stream, &line_number)) {
         char *error;
+        enum ofputil_protocol usable;
 
         if (*n_fms >= allocated_fms) {
             *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
         }
-        error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command);
+        error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command,
+                                       &usable);
         if (error) {
             size_t i;
 
@@ -1685,6 +1755,7 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
 
             return xasprintf("%s:%d: %s", file_name, line_number, error);
         }
+        *usable_protocols &= usable; /* Each line can narrow the set. */
         *n_fms += 1;
     }
 
@@ -1697,16 +1768,25 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
 
 char * WARN_UNUSED_RESULT
 parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
-                                 bool aggregate, const char *string)
+                                 bool aggregate, const char *string,
+                                 enum ofputil_protocol *usable_protocols)
 {
     struct ofputil_flow_mod fm;
     char *error;
 
-    error = parse_ofp_str(&fm, -1, string);
+    error = parse_ofp_str(&fm, -1, string, usable_protocols);
     if (error) {
         return error;
     }
 
+    /* Special table ID support not required for stats requests. */
+    if (*usable_protocols & OFPUTIL_P_OF10_STD_TID) {
+        *usable_protocols |= OFPUTIL_P_OF10_STD;
+    }
+    if (*usable_protocols & OFPUTIL_P_OF10_NXM_TID) {
+        *usable_protocols |= OFPUTIL_P_OF10_NXM;
+    }
+
     fsr->aggregate = aggregate;
     fsr->cookie = fm.cookie;
     fsr->cookie_mask = fm.cookie_mask;
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
index 707f5d8..cdb6831 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -30,32 +30,40 @@ struct ofputil_flow_mod;
 struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
 struct ofputil_meter_mod;
+enum ofputil_protocol;
 
-char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_)
+char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
+                    enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
 
 char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
-                            uint16_t command)
+                             uint16_t command,
+                             enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
 char *parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
-                              struct ofputil_flow_mod **fms, size_t *n_fms)
+                              struct ofputil_flow_mod **fms, size_t *n_fms,
+                              enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
 
 char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
-                                       bool aggregate, const char *string)
+                                       bool aggregate, const char *string,
+                                       enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
 
-char *parse_ofpacts(const char *, struct ofpbuf *ofpacts)
+char *parse_ofpacts(const char *, struct ofpbuf *ofpacts,
+                    enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
 
 char *parse_ofp_exact_flow(struct flow *, const char *);
 
 char *parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
-			      int command)
+                              int command,
+                              enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
 
 char *parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
-                                const char *)
+                                 const char *,
+                                 enum ofputil_protocol *usable_protocols)
     WARN_UNUSED_RESULT;
 
 #endif /* ofp-parse.h */
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 45ff0a1..6f032b9 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -1104,158 +1104,6 @@ ofputil_packet_in_format_from_string(const char *s)
             : -1);
 }
 
-static bool
-regs_fully_wildcarded(const struct flow_wildcards *wc)
-{
-    int i;
-
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        if (wc->masks.regs[i] != 0) {
-            return false;
-        }
-    }
-    return true;
-}
-
-/* Returns a bit-mask of ofputil_protocols that can be used for sending 'match'
- * 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 match *match)
-{
-    const struct flow_wildcards *wc = &match->wc;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
-
-    /* These tunnel params can't be sent in a flow_mod */
-    if (wc->masks.tunnel.ip_ttl
-        || wc->masks.tunnel.ip_tos || wc->masks.tunnel.flags) {
-        return OFPUTIL_P_NONE;
-    }
-
-    /* skb_priority can't be sent in a flow_mod */
-    if (wc->masks.skb_priority) {
-        return OFPUTIL_P_NONE;
-    }
-
-    /* NXM and OXM support pkt_mark */
-    if (wc->masks.pkt_mark) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM, OXM, and OF1.1 support bitwise matching on ethernet addresses. */
-    if (!eth_mask_is_exact(wc->masks.dl_src)
-        && !eth_addr_is_zero(wc->masks.dl_src)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-    if (!eth_mask_is_exact(wc->masks.dl_dst)
-        && !eth_addr_is_zero(wc->masks.dl_dst)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM, OXM, and OF1.1+ support matching metadata. */
-    if (wc->masks.metadata != htonll(0)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support matching ARP hardware addresses. */
-    if (!eth_addr_is_zero(wc->masks.arp_sha) ||
-        !eth_addr_is_zero(wc->masks.arp_tha)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support matching L3 and L4 fields within IPv6.
-     *
-     * (arp_sha, arp_tha, nw_frag, and nw_ttl are covered elsewhere so they
-     * don't need to be included in this test too.) */
-    if (match->flow.dl_type == htons(ETH_TYPE_IPV6)
-        && (!ipv6_mask_is_any(&wc->masks.ipv6_src)
-            || !ipv6_mask_is_any(&wc->masks.ipv6_dst)
-            || !ipv6_mask_is_any(&wc->masks.nd_target)
-            || wc->masks.ipv6_label
-            || wc->masks.tp_src
-            || wc->masks.tp_dst
-            || wc->masks.nw_proto
-            || wc->masks.nw_tos)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support matching registers. */
-    if (!regs_fully_wildcarded(wc)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support matching tun_id, tun_src, and tun_dst. */
-    if (wc->masks.tunnel.tun_id != htonll(0)
-        || wc->masks.tunnel.ip_src != htonl(0)
-        || wc->masks.tunnel.ip_dst != htonl(0)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support matching fragments. */
-    if (wc->masks.nw_frag) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support matching IP ECN bits. */
-    if (wc->masks.nw_tos & IP_ECN_MASK) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support matching IP TTL/hop limit. */
-    if (wc->masks.nw_ttl) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support non-CIDR IPv4 address masks. */
-    if (!ip_is_cidr(wc->masks.nw_src) || !ip_is_cidr(wc->masks.nw_dst)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OXM support bitwise matching on transport port. */
-    if ((wc->masks.tp_src && wc->masks.tp_src != htons(UINT16_MAX)) ||
-        (wc->masks.tp_dst && wc->masks.tp_dst != htons(UINT16_MAX))) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OF1.1+ support matching MPLS label */
-    if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OF1.1+ support matching MPLS TC */
-    if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* NXM and OF1.3+ support matching MPLS stack flag */
-    /* Allow for OF1.2 as there doesn't seem to be a
-     * particularly good reason not to */
-    if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
-        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-
-    /* Other formats can express this rule. */
-    return OFPUTIL_P_ANY;
-}
-
 void
 ofputil_format_version(struct ds *msg, enum ofp_version version)
 {
@@ -2198,39 +2046,6 @@ 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->match);
-        if (fm->table_id != 0xff) {
-            usable_protocols &= OFPUTIL_P_TID;
-        }
-
-        /* Matching of the cookie is only supported through NXM or OF1.1+. */
-        if (fm->cookie_mask != htonll(0)) {
-            usable_protocols &= (OFPUTIL_P_OF10_NXM_ANY
-                                 | OFPUTIL_P_OF11_STD
-                                 | OFPUTIL_P_OF12_OXM
-                                 | OFPUTIL_P_OF13_OXM);
-        }
-    }
-
-    return usable_protocols;
-}
-
 static enum ofperr
 ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
                                     const struct ofp10_flow_stats_request *ofsr,
@@ -2406,24 +2221,6 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     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_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
-            | OFPUTIL_P_OF13_OXM;
-    }
-    return usable_protocols;
-}
-
 /* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract
  * ofputil_flow_stats in 'fs'.
  *
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index f3348c0..1c36a6f 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -101,6 +101,17 @@ enum ofputil_protocol {
     OFPUTIL_P_OF13_OXM      = 1 << 6,
 #define OFPUTIL_P_ANY_OXM (OFPUTIL_P_OF12_OXM | OFPUTIL_P_OF13_OXM)
 
+#define OFPUTIL_P_NXM_OF11_UP (OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF11_STD | \
+                               OFPUTIL_P_ANY_OXM)
+
+#define OFPUTIL_P_NXM_OXM_ANY (OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_ANY_OXM)
+
+#define OFPUTIL_P_OF11_UP (OFPUTIL_P_OF11_STD | OFPUTIL_P_ANY_OXM)
+
+#define OFPUTIL_P_OF12_UP (OFPUTIL_P_ANY_OXM)
+
+#define OFPUTIL_P_OF13_UP (OFPUTIL_P_OF13_OXM)
+
     /* All protocols. */
 #define OFPUTIL_P_ANY ((1 << 7) - 1)
 
@@ -129,7 +140,6 @@ enum ofputil_protocol ofputil_protocol_set_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 match *);
 
 void ofputil_format_version(struct ds *, enum ofp_version);
 void ofputil_format_version_name(struct ds *, enum ofp_version);
@@ -260,9 +270,6 @@ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
 struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
                                        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 protocol. */
 struct ofputil_flow_stats_request {
     bool aggregate;             /* Aggregate results? */
@@ -277,8 +284,6 @@ 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 ofputil_protocol);
-enum ofputil_protocol ofputil_flow_stats_request_usable_protocols(
-    const struct ofputil_flow_stats_request *);
 
 /* Flow stats reply, independent of protocol. */
 struct ofputil_flow_stats {
diff --git a/tests/learn.at b/tests/learn.at
index fc8d071..362c21c 100644
--- a/tests/learn.at
+++ b/tests/learn.at
@@ -29,11 +29,11 @@ AT_DATA([flows.txt], [[
 actions=learn(output:OXM_OF_IN_PORT[])
 actions=learn(table=1, in_port=1, load:OXM_OF_IN_PORT[]->NXM_NX_REG1[], load:0xfffffffe->OXM_OF_IN_PORT[])
 ]])
-AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
+AT_CHECK([ovs-ofctl -O OpenFlow12 parse-flows flows.txt], [0],
 [[usable protocols: any
-chosen protocol: OpenFlow10-table_id
-OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,output:OXM_OF_IN_PORT[])
-OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,in_port=1,load:OXM_OF_IN_PORT[]->NXM_NX_REG1[],load:0xfffffffe->OXM_OF_IN_PORT[])
+chosen protocol: OXM-OpenFlow12
+OFPT_FLOW_MOD (OF1.2) (xid=0x1): ADD table:255 actions=learn(table=1,output:OXM_OF_IN_PORT[])
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 actions=learn(table=1,in_port=1,load:OXM_OF_IN_PORT[]->NXM_NX_REG1[],load:0xfffffffe->OXM_OF_IN_PORT[])
 ]])
 AT_CLEANUP
 
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 996ea06..08e74bc 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -9,7 +9,7 @@ for test_case in \
     'tun_flags=0                                 none' \
     'tun_tos=0                                   none' \
     'tun_ttl=0                                   none' \
-    'metadata=0                                  NXM,OXM' \
+    'metadata=0                                  NXM,OXM,OpenFlow11' \
     'in_port=1                                   any' \
     'skb_priority=0                              none' \
     'pkt_mark=1                                  NXM,OXM' \
@@ -22,9 +22,9 @@ for test_case in \
     'reg6=6                                      NXM,OXM' \
     'reg7=7                                      NXM,OXM' \
     'dl_src=00:11:22:33:44:55                    any' \
-    'dl_src=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM' \
+    'dl_src=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM,OpenFlow11' \
     'dl_dst=00:11:22:33:44:55                    any' \
-    'dl_dst=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM' \
+    'dl_dst=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM,OpenFlow11' \
     'dl_type=0x1234                              any' \
     'dl_type=0x0800                              any' \
     'dl_type=0x0806                              any' \
@@ -35,15 +35,15 @@ for test_case in \
     'vlan_vid=11                                 any' \
     'dl_vlan_pcp=6                               any' \
     'vlan_pcp=5                                  any' \
-    'mpls,mpls_label=5                           NXM,OXM' \
-    'mpls,mpls_tc=1                              NXM,OXM' \
+    'mpls,mpls_label=5                           NXM,OXM,OpenFlow11' \
+    'mpls,mpls_tc=1                              NXM,OXM,OpenFlow11' \
     'mpls,mpls_bos=0                             NXM,OXM' \
     'ip,ip_src=1.2.3.4                           any' \
     'ip,ip_src=192.168.0.0/24                    any' \
-    'ip,ip_src=192.0.168.0/255.0.255.0           NXM,OXM' \
+    'ip,ip_src=192.0.168.0/255.0.255.0           NXM,OXM,OpenFlow11' \
     'ip,ip_dst=1.2.3.4                           any' \
     'ip,ip_dst=192.168.0.0/24                    any' \
-    'ip,ip_dst=192.0.168.0/255.0.255.0           NXM,OXM' \
+    'ip,ip_dst=192.0.168.0/255.0.255.0           NXM,OXM,OpenFlow11' \
     'ipv6,ipv6_src=::1                           NXM,OXM' \
     'ipv6,ipv6_dst=::1                           NXM,OXM' \
     'ipv6,ipv6_label=5                           NXM,OXM' \
@@ -153,7 +153,7 @@ actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_
 AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt
 ], [0], [stdout])
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
-[[usable protocols: any
+[[usable protocols: NXM,OXM
 chosen protocol: OXM-OpenFlow12
 OFPT_FLOW_MOD (OF1.2): ADD table:255 tcp,tp_src=123 actions=FLOOD
 OFPT_FLOW_MOD (OF1.2): ADD table:255 in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
@@ -2063,7 +2063,7 @@ AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy tun_id=123,actions=drop],
   [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10)
 ])
 AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy metadata=123,actions=drop],
-  [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10)
+  [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM,OpenFlow11) is among the allowed flow formats (OpenFlow10)
 ])
 AT_CLEANUP
 
diff --git a/utilities/ovs-controller.c b/utilities/ovs-controller.c
index d9ae983..f487d8c 100644
--- a/utilities/ovs-controller.c
+++ b/utilities/ovs-controller.c
@@ -83,6 +83,7 @@ static struct simap port_queues = SIMAP_INITIALIZER(&port_queues);
 /* --with-flows: Flows to send to switch. */
 static struct ofputil_flow_mod *default_flows;
 static size_t n_default_flows;
+static enum ofputil_protocol usable_protocols;
 
 /* --unixctl: Name of unixctl socket, or null to use the default. */
 static char *unixctl_path = NULL;
@@ -216,6 +217,7 @@ new_switch(struct switch_ *sw, struct vconn *vconn)
     cfg.max_idle = set_up_flows ? max_idle : -1;
     cfg.default_flows = default_flows;
     cfg.n_default_flows = n_default_flows;
+    cfg.usable_protocols = usable_protocols;
     cfg.default_queue = default_queue;
     cfg.port_queues = &port_queues;
     cfg.mute = mute;
@@ -329,7 +331,8 @@ parse_options(int argc, char *argv[])
 
         case OPT_WITH_FLOWS:
             error = parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows,
-                                            &n_default_flows);
+                                            &n_default_flows,
+                                            &usable_protocols);
             if (error) {
                 ovs_fatal(0, "%s", error);
             }
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 899ce4e..46611b0 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -871,13 +871,12 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
     char *error;
 
     error = parse_ofp_flow_stats_request_str(&fsr, aggregate,
-                                             argc > 2 ? argv[2] : "");
+                                             argc > 2 ? argv[2] : "",
+                                             &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
 
-    usable_protocols = ofputil_flow_stats_request_usable_protocols(&fsr);
-
     protocol = open_vconn(argv[1], &vconn);
     protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
     *requestp = ofputil_encode_flow_stats_request(&fsr, protocol);
@@ -1031,17 +1030,13 @@ ofctl_queue_stats(int argc, char *argv[])
 }
 
 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)
+open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp,
+                        enum ofputil_protocol usable_protocols)
 {
-    enum ofputil_protocol usable_protocols;
     enum ofputil_protocol cur_protocol;
     char *usable_s;
     int i;
 
-    /* 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);
@@ -1073,13 +1068,13 @@ open_vconn_for_flow_mod(const char *remote,
 
 static void
 ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
-                 size_t n_fms)
+                 size_t n_fms, enum ofputil_protocol usable_protocols)
 {
     enum ofputil_protocol protocol;
     struct vconn *vconn;
     size_t i;
 
-    protocol = open_vconn_for_flow_mod(remote, fms, n_fms, &vconn);
+    protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
 
     for (i = 0; i < n_fms; i++) {
         struct ofputil_flow_mod *fm = &fms[i];
@@ -1093,32 +1088,37 @@ ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
 static void
 ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
 {
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
     char *error;
 
-    error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
+    error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
+                                    &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
-    ofctl_flow_mod__(argv[1], fms, n_fms);
+    ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
     free(fms);
 }
 
 static void
 ofctl_flow_mod(int argc, char *argv[], uint16_t command)
 {
+    enum ofputil_protocol usable_protocols;
+
     if (argc > 2 && !strcmp(argv[2], "-")) {
         ofctl_flow_mod_file(argc, argv, command);
     } else {
         struct ofputil_flow_mod fm;
         char *error;
 
-        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command);
+        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
+                                       &usable_protocols);
         if (error) {
             ovs_fatal(0, "%s", error);
         }
-        ofctl_flow_mod__(argv[1], &fm, 1);
+        ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
     }
 }
 
@@ -1440,6 +1440,7 @@ ofctl_monitor(int argc, char *argv[])
 {
     struct vconn *vconn;
     int i;
+    enum ofputil_protocol usable_protocols;
 
     open_vconn(argv[1], &vconn);
     for (i = 2; i < argc; i++) {
@@ -1458,7 +1459,8 @@ ofctl_monitor(int argc, char *argv[])
             struct ofpbuf *msg;
             char *error;
 
-            error = parse_flow_monitor_request(&fmr, arg + 6);
+            error = parse_flow_monitor_request(&fmr, arg + 6,
+                                               &usable_protocols);
             if (error) {
                 ovs_fatal(0, "%s", error);
             }
@@ -1561,9 +1563,10 @@ ofctl_packet_out(int argc, char *argv[])
     struct vconn *vconn;
     char *error;
     int i;
+    enum ofputil_protocol usable_protocols; /* TODO: Use in proto selection */
 
     ofpbuf_init(&ofpacts, 64);
-    error = parse_ofpacts(argv[3], &ofpacts);
+    error = parse_ofpacts(argv[3], &ofpacts, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
@@ -2000,11 +2003,13 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
         struct fte_version *version;
         struct ofputil_flow_mod fm;
         char *error;
+        enum ofputil_protocol usable;
 
-        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s));
+        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable);
         if (error) {
             ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
         }
+        usable_protocols &= usable;
 
         version = xmalloc(sizeof *version);
         version->cookie = fm.new_cookie;
@@ -2014,8 +2019,6 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
         version->ofpacts = fm.ofpacts;
         version->ofpacts_len = fm.ofpacts_len;
 
-        usable_protocols &= ofputil_usable_protocols(&fm.match);
-
         fte_insert(cls, &fm.match, fm.priority, version, index);
     }
     ds_destroy(&s);
@@ -2279,14 +2282,13 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
 /* Undocumented commands for unit testing. */
 
 static void
-ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
+                    enum ofputil_protocol usable_protocols)
 {
-    enum ofputil_protocol usable_protocols;
     enum ofputil_protocol protocol = 0;
     char *usable_s;
     size_t i;
 
-    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);
@@ -2321,14 +2323,15 @@ ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
 static void
 ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
 {
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_mod fm;
     char *error;
 
-    error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD);
+    error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
-    ofctl_parse_flows__(&fm, 1);
+    ofctl_parse_flows__(&fm, 1, usable_protocols);
 }
 
 /* "parse-flows FILENAME": reads the named file as a sequence of flows (like
@@ -2336,15 +2339,17 @@ ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
 static void
 ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
 {
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
     char *error;
 
-    error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+    error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms,
+                                    &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
-    ofctl_parse_flows__(fms, n_fms);
+    ofctl_parse_flows__(fms, n_fms, usable_protocols);
     free(fms);
 }
 
@@ -2799,6 +2804,8 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     enum ofperr error;
     char *error_s;
 
+    enum ofputil_protocol usable_protocols; /* Unused for now. */
+
     match_init_catchall(&match);
     match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
     match.wc.masks.vlan_tci = htons(strtoul(argv[2], NULL, 16));
@@ -2807,7 +2814,7 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
     printf("%s -> ", string_s);
     fflush(stdout);
-    error_s = parse_ofp_str(&fm, -1, string_s);
+    error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols);
     if (error_s) {
         ovs_fatal(0, "%s", error_s);
     }
-- 
1.7.10.4




More information about the dev mailing list