[ovs-dev] [PATCH 3/3] lib: Check for usable protocols for actions and matches.

Ben Pfaff blp at nicira.com
Fri Jun 28 17:31:01 UTC 2013


On Fri, Jun 28, 2013 at 07:44:05PM +0300, Jarno Rajahalme wrote:
> Keep track of usable protocols while parsing, rather than checking for them
> afterwards.  Check the usable protocols for actions as well as matches.
> This fixes silently discarded meter and goto table instructions when not
> explicitly specifying the protocol to use.
> 
> Signed-off-by: Jarno Rajahalme <jarno.rajahalme at nsn.com>

I applied this and resolved the conflicts, as appended.  It gets a
test failure in the test I added for the IPv6 fix:

    [...]

    ### test case: 'ipv6,nw_proto=1' should have usable protocols 'NXM,OXM'
    ../../tests/ovs-ofctl.at:98: ovs-ofctl parse-flow "$1,actions=drop" | sed 1q
    --- -   2013-06-28 10:28:27.096632392 -0700
    +++ /home/blp/ovs/_build/tests/testsuite.dir/at-groups/222/stdout       2013-06-28 10:28:27.000000000 -0700
    @@ -1,2 +1,2 @@
    -usable protocols: NXM,OXM
    +usable protocols: any

--8<--------------------------cut here-------------------------->8--

From: Jarno Rajahalme <jarno.rajahalme at nsn.com>
Date: Fri, 28 Jun 2013 19:44:05 +0300
Subject: [PATCH] lib: Check for usable protocols for actions and matches.

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

Signed-off-by: Jarno Rajahalme <jarno.rajahalme at nsn.com>
Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 lib/learning-switch.c      |   11 +--
 lib/learning-switch.h      |    2 +
 lib/meta-flow.c            |  120 +++++++++++++++++++++++++--
 lib/meta-flow.h            |   21 ++++-
 lib/ofp-actions.c          |   15 +++-
 lib/ofp-actions.h          |   90 +++++++++++----------
 lib/ofp-parse.c            |   61 +++++++++++---
 lib/ofp-parse.h            |   22 +++--
 lib/ofp-util.c             |  195 --------------------------------------------
 lib/ofp-util.h             |   16 ++--
 ofproto/ofproto.c          |    4 +-
 utilities/ovs-controller.c |    7 +-
 utilities/ovs-ofctl.c      |   51 ++++++------
 13 files changed, 302 insertions(+), 313 deletions(-)

diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index f2b3319..abb81b4 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 0677202..76f2269 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -54,6 +54,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),
@@ -63,6 +65,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),
@@ -72,6 +76,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),
@@ -81,6 +87,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),
@@ -90,6 +98,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),
@@ -99,6 +109,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),
@@ -108,6 +120,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),
@@ -117,6 +131,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),
@@ -126,6 +142,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),
@@ -135,6 +153,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_NONE,
+        OFPUTIL_P_NONE,
     }, {
         MFF_SKB_MARK, "skb_mark", NULL,
         MF_FIELD_SIZES(be32),
@@ -144,6 +164,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         false,
         0, NULL,
         0, NULL,
+        OFPUTIL_P_NONE,
+        OFPUTIL_P_NONE,
     },
 
 #define REGISTER(IDX)                           \
@@ -156,6 +178,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),
@@ -198,6 +222,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),
@@ -207,6 +233,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),
@@ -216,6 +244,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,
     },
 
     {
@@ -227,6 +257,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,
@@ -236,6 +268,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,
@@ -245,6 +279,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,
@@ -254,6 +290,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,
@@ -263,6 +301,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,
     },
 
     /* ## ---- ## */
@@ -277,6 +317,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,
@@ -286,6 +328,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,
@@ -295,6 +339,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,
     },
 
     /* ## -- ## */
@@ -310,6 +356,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),
@@ -319,6 +367,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,
     },
 
     {
@@ -330,6 +380,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),
@@ -339,6 +391,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,
@@ -349,6 +403,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,
     },
 
     {
@@ -360,6 +416,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),
@@ -369,6 +427,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),
@@ -378,6 +438,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,
@@ -387,6 +449,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),
@@ -396,6 +460,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,
@@ -405,6 +471,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,
     },
 
     {
@@ -416,6 +484,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),
@@ -425,6 +495,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),
@@ -434,6 +506,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),
@@ -443,6 +517,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),
@@ -452,6 +528,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,
     },
 
     /* ## -- ## */
@@ -467,6 +545,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),
@@ -476,6 +556,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,
     },
 
     {
@@ -487,6 +569,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),
@@ -496,6 +580,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,
     },
 
     {
@@ -507,6 +593,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),
@@ -516,6 +604,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,
     },
 
     {
@@ -527,6 +617,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),
@@ -536,6 +628,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,
     },
 
     /* ## ---- ## */
@@ -551,6 +645,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),
@@ -560,6 +656,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),
@@ -569,6 +667,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,
     }
 };
 
@@ -1759,17 +1859,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) {
@@ -1852,11 +1952,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);
@@ -1884,11 +1984,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:
@@ -1904,6 +2004,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 a85a193..7dfd64a 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;
@@ -277,6 +278,17 @@ struct mf_field {
     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 */
+
+    /* 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-actions.c b/lib/ofp-actions.c
index 899928a..e7b1772 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -31,6 +31,13 @@
 
 VLOG_DEFINE_THIS_MODULE(ofp_actions);
 
+/* Array of usable protocols for each OFPACT */
+static const enum ofputil_protocol ofpact_usable_protocols[N_OFPACTS] = {
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER, PROTO) PROTO,
+    OFPACTS
+#undef DEFINE_OFPACT
+};
+
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
 /* Converting OpenFlow 1.0 to ofpacts. */
@@ -1313,17 +1320,23 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
  * May temporarily modify 'flow', but restores the changes before returning. */
 enum ofperr
 ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
-              struct flow *flow, ofp_port_t max_ports, uint8_t table_id)
+              struct flow *flow, ofp_port_t max_ports, uint8_t table_id,
+              enum ofputil_protocol *protocols)
 {
     const struct ofpact *a;
     ovs_be16 dl_type = flow->dl_type;
     enum ofperr error = 0;
+    enum ofputil_protocol usable_protocols = OFPUTIL_P_ANY;
 
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
         error = ofpact_check__(a, flow, max_ports, table_id);
         if (error) {
             break;
         }
+        usable_protocols &= ofpact_usable_protocols[a->type];
+    }
+    if (protocols) {
+        *protocols &= usable_protocols;
     }
     flow->dl_type = dl_type; /* Restore. */
     return error;
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index b97afd0..5c4ccb3 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -47,65 +47,68 @@
  *
  *        - If "struct <STRUCT>" is variable-length, it must be the name of the
  *          flexible array member.
+ *
+ * 4. <PROTO>, which is a set of OFPUTIL_P_* values for protocols that
+ *    can carry this action.
  */
 #define OFPACTS                                                     \
     /* Output. */                                                   \
-    DEFINE_OFPACT(OUTPUT,          ofpact_output,        ofpact)    \
-    DEFINE_OFPACT(CONTROLLER,      ofpact_controller,    ofpact)    \
-    DEFINE_OFPACT(ENQUEUE,         ofpact_enqueue,       ofpact)    \
-    DEFINE_OFPACT(OUTPUT_REG,      ofpact_output_reg,    ofpact)    \
-    DEFINE_OFPACT(BUNDLE,          ofpact_bundle,        slaves)    \
+    DEFINE_OFPACT(OUTPUT,          ofpact_output,        ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(CONTROLLER,      ofpact_controller,    ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(ENQUEUE,         ofpact_enqueue,       ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(OUTPUT_REG,      ofpact_output_reg,    ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(BUNDLE,          ofpact_bundle,        slaves, OFPUTIL_P_ANY) \
                                                                     \
     /* Header changes. */                                           \
-    DEFINE_OFPACT(SET_VLAN_VID,    ofpact_vlan_vid,      ofpact)    \
-    DEFINE_OFPACT(SET_VLAN_PCP,    ofpact_vlan_pcp,      ofpact)    \
-    DEFINE_OFPACT(STRIP_VLAN,      ofpact_null,          ofpact)    \
-    DEFINE_OFPACT(PUSH_VLAN,       ofpact_null,          ofpact)    \
-    DEFINE_OFPACT(SET_ETH_SRC,     ofpact_mac,           ofpact)    \
-    DEFINE_OFPACT(SET_ETH_DST,     ofpact_mac,           ofpact)    \
-    DEFINE_OFPACT(SET_IPV4_SRC,    ofpact_ipv4,          ofpact)    \
-    DEFINE_OFPACT(SET_IPV4_DST,    ofpact_ipv4,          ofpact)    \
-    DEFINE_OFPACT(SET_IPV4_DSCP,   ofpact_dscp,          ofpact)    \
-    DEFINE_OFPACT(SET_L4_SRC_PORT, ofpact_l4_port,       ofpact)    \
-    DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port,       ofpact)    \
-    DEFINE_OFPACT(REG_MOVE,        ofpact_reg_move,      ofpact)    \
-    DEFINE_OFPACT(REG_LOAD,        ofpact_reg_load,      ofpact)    \
-    DEFINE_OFPACT(STACK_PUSH,      ofpact_stack,         ofpact)    \
-    DEFINE_OFPACT(STACK_POP,       ofpact_stack,         ofpact)    \
-    DEFINE_OFPACT(DEC_TTL,         ofpact_cnt_ids,       cnt_ids)   \
-    DEFINE_OFPACT(SET_MPLS_TTL,    ofpact_mpls_ttl,      ofpact)    \
-    DEFINE_OFPACT(DEC_MPLS_TTL,    ofpact_null,          ofpact)    \
-    DEFINE_OFPACT(PUSH_MPLS,       ofpact_push_mpls,     ofpact)    \
-    DEFINE_OFPACT(POP_MPLS,        ofpact_pop_mpls,      ofpact)    \
+    DEFINE_OFPACT(SET_VLAN_VID,    ofpact_vlan_vid,      ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_VLAN_PCP,    ofpact_vlan_pcp,      ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(STRIP_VLAN,      ofpact_null,          ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(PUSH_VLAN,       ofpact_null,          ofpact, OFPUTIL_P_OF11_UP) \
+    DEFINE_OFPACT(SET_ETH_SRC,     ofpact_mac,           ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_ETH_DST,     ofpact_mac,           ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_IPV4_SRC,    ofpact_ipv4,          ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_IPV4_DST,    ofpact_ipv4,          ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_IPV4_DSCP,   ofpact_dscp,          ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_L4_SRC_PORT, ofpact_l4_port,       ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port,       ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(REG_MOVE,        ofpact_reg_move,      ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(REG_LOAD,        ofpact_reg_load,      ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(STACK_PUSH,      ofpact_stack,         ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(STACK_POP,       ofpact_stack,         ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(DEC_TTL,         ofpact_cnt_ids,       cnt_ids, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_MPLS_TTL,    ofpact_mpls_ttl,      ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(DEC_MPLS_TTL,    ofpact_null,          ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(PUSH_MPLS,       ofpact_push_mpls,     ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(POP_MPLS,        ofpact_pop_mpls,      ofpact, OFPUTIL_P_ANY) \
                                                                     \
     /* Metadata. */                                                 \
-    DEFINE_OFPACT(SET_TUNNEL,      ofpact_tunnel,        ofpact)    \
-    DEFINE_OFPACT(SET_QUEUE,       ofpact_queue,         ofpact)    \
-    DEFINE_OFPACT(POP_QUEUE,       ofpact_null,          ofpact)    \
-    DEFINE_OFPACT(FIN_TIMEOUT,     ofpact_fin_timeout,   ofpact)    \
+    DEFINE_OFPACT(SET_TUNNEL,      ofpact_tunnel,        ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SET_QUEUE,       ofpact_queue,         ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(POP_QUEUE,       ofpact_null,          ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(FIN_TIMEOUT,     ofpact_fin_timeout,   ofpact, OFPUTIL_P_ANY) \
                                                                     \
     /* Flow table interaction. */                                   \
-    DEFINE_OFPACT(RESUBMIT,        ofpact_resubmit,      ofpact)    \
-    DEFINE_OFPACT(LEARN,           ofpact_learn,         specs)     \
+    DEFINE_OFPACT(RESUBMIT,        ofpact_resubmit,      ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(LEARN,           ofpact_learn,         specs,  OFPUTIL_P_ANY) \
                                                                     \
     /* Arithmetic. */                                               \
-    DEFINE_OFPACT(MULTIPATH,       ofpact_multipath,     ofpact)    \
+    DEFINE_OFPACT(MULTIPATH,       ofpact_multipath,     ofpact, OFPUTIL_P_ANY) \
                                                                     \
     /* Other. */                                                    \
-    DEFINE_OFPACT(NOTE,            ofpact_note,          data)      \
-    DEFINE_OFPACT(EXIT,            ofpact_null,          ofpact)    \
-    DEFINE_OFPACT(SAMPLE,          ofpact_sample,        ofpact)    \
+    DEFINE_OFPACT(NOTE,            ofpact_note,          data,   OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(EXIT,            ofpact_null,          ofpact, OFPUTIL_P_ANY) \
+    DEFINE_OFPACT(SAMPLE,          ofpact_sample,        ofpact, OFPUTIL_P_ANY) \
                                                                     \
     /* Instructions */                                              \
-    DEFINE_OFPACT(METER,           ofpact_meter,         ofpact)    \
+    DEFINE_OFPACT(METER,           ofpact_meter,         ofpact, OFPUTIL_P_OF13_UP) \
     /* XXX Write-Actions */                                         \
-    DEFINE_OFPACT(CLEAR_ACTIONS,   ofpact_null,          ofpact)    \
-    DEFINE_OFPACT(WRITE_METADATA,  ofpact_metadata,      ofpact)    \
-    DEFINE_OFPACT(GOTO_TABLE,      ofpact_goto_table,    ofpact)
+    DEFINE_OFPACT(CLEAR_ACTIONS,   ofpact_null,          ofpact, OFPUTIL_P_OF11_UP) \
+    DEFINE_OFPACT(WRITE_METADATA,  ofpact_metadata,      ofpact, OFPUTIL_P_ANY)    \
+    DEFINE_OFPACT(GOTO_TABLE,      ofpact_goto_table,    ofpact, OFPUTIL_P_OF11_UP)
 
 /* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
 enum OVS_PACKED_ENUM ofpact_type {
-#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) OFPACT_##ENUM,
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER, PROTO) OFPACT_##ENUM,
     OFPACTS
 #undef DEFINE_OFPACT
 };
@@ -113,7 +116,7 @@ enum OVS_PACKED_ENUM ofpact_type {
 /* N_OFPACTS, the number of values of "enum ofpact_type". */
 enum {
     N_OFPACTS =
-#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) + 1
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER, PROTO) + 1
     OFPACTS
 #undef DEFINE_OFPACT
 };
@@ -502,7 +505,8 @@ enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
                                                  struct ofpbuf *ofpacts);
 enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len,
                           struct flow *, ofp_port_t max_ports,
-                          uint8_t table_id);
+                          uint8_t table_id,
+                          enum ofputil_protocol *usable_protocols);
 enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len);
 
 /* Converting ofpacts to OpenFlow. */
@@ -567,7 +571,7 @@ void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len);
  *     An integer constant, the value of OFPACT_<ENUM>_RAW_SIZE rounded up to a
  *     multiple of OFPACT_ALIGNTO.
  */
-#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER)                             \
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER, PROTO)                      \
     BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0);            \
                                                                         \
     enum { OFPACT_##ENUM##_RAW_SIZE                                     \
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index b1e369c..a4a1c09 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -826,7 +826,7 @@ ofp_fatal(const char *flow, bool verbose, const char *format, ...)
     ovs_fatal_valist(0, format, args);
 }
 
-static void
+static enum ofputil_protocol
 parse_field(const struct mf_field *mf, const char *s, struct match *match)
 {
     union mf_value value, mask;
@@ -837,7 +837,7 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match)
         ovs_fatal(0, "%s", error);
     }
 
-    mf_set(mf, &value, &mask, match);
+    return mf_set(mf, &value, &mask, match);
 }
 
 /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
@@ -847,11 +847,15 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match)
  *
  * 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
- * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */
-void
+ * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'.
+ *
+ * Returns a set of protocols that can be used to communicate the 'fm' to an
+ * OpenFlow switch. */
+enum ofputil_protocol
 parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
               bool verbose)
 {
+    enum ofputil_protocol usable_protocols = OFPUTIL_P_ANY;
     enum {
         F_OUT_PORT = 1 << 0,
         F_ACTIONS = 1 << 1,
@@ -939,10 +943,13 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             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;
 
@@ -953,6 +960,9 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
 
             if (!strcmp(name, "table")) {
                 fm->table_id = str_to_u8(value, name);
+                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)) {
                     ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
@@ -976,6 +986,12 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                     *mask = '\0';
                     fm->cookie = htonll(str_to_u64(value));
                     fm->cookie_mask = htonll(str_to_u64(mask+1));
+
+                    /* 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
@@ -985,7 +1001,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                     fm->new_cookie = htonll(str_to_u64(value));
                 }
             } else if (mf_from_name(name)) {
-                parse_field(mf_from_name(name), value, &fm->match);
+                usable_protocols &= parse_field(mf_from_name(name), value, &fm->match);
             } else if (!strcmp(name, "duration")
                        || !strcmp(name, "n_packets")
                        || !strcmp(name, "n_bytes")
@@ -1016,17 +1032,18 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         fm->ofpacts = ofpbuf_steal_data(&ofpacts);
 
         err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
-                            OFPP_MAX, 0);
+                            OFPP_MAX, 0, &usable_protocols);
         if (err) {
             exit(EXIT_FAILURE);
         }
-
     } else {
         fm->ofpacts_len = 0;
         fm->ofpacts = NULL;
     }
 
     free(string);
+
+    return usable_protocols;
 }
 
 /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
@@ -1298,25 +1315,30 @@ parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
 
 /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
  * (one of OFPFC_*) into 'fm'. */
-void
+enum ofputil_protocol
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
                        uint16_t command, bool verbose)
 {
+    enum ofputil_protocol usable_protocols;
+
     struct match match_copy;
 
-    parse_ofp_str(fm, command, string, verbose);
+    usable_protocols = parse_ofp_str(fm, command, string, verbose);
 
     /* 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 that the
      * switch can do whatever it likes with the flow. */
     match_copy = fm->match;
     ofputil_normalize_match(&match_copy);
+
+    return usable_protocols;
 }
 
-void
+enum ofputil_protocol
 parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
                         struct ofputil_flow_mod **fms, size_t *n_fms)
 {
+    enum ofputil_protocol usable_protocols = OFPUTIL_P_ANY;
     size_t allocated_fms;
     FILE *stream;
     struct ds s;
@@ -1332,7 +1354,8 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
         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);
+        usable_protocols &= parse_ofp_flow_mod_str(&(*fms)[*n_fms],
+                                                   ds_cstr(&s), command, false);
         *n_fms += 1;
     }
     ds_destroy(&s);
@@ -1340,21 +1363,33 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
     if (stream != stdin) {
         fclose(stream);
     }
+    return usable_protocols;
 }
 
-void
+enum ofputil_protocol
 parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
                                  bool aggregate, const char *string)
 {
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_mod fm;
 
-    parse_ofp_str(&fm, -1, string, false);
+    usable_protocols = parse_ofp_str(&fm, -1, string, false);
+    /* 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;
     fsr->match = fm.match;
     fsr->out_port = fm.out_port;
     fsr->table_id = fm.table_id;
+
+    return usable_protocols;
 }
 
 /* Parses a specification of a flow from 's' into 'flow'.  's' must take the
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
index 6ee25a4..bb8d4ae 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -30,16 +30,20 @@ struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
 struct ofputil_meter_mod;
 
-void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
-                   bool verbose);
+enum ofputil_protocol parse_ofp_str(struct ofputil_flow_mod *, int command,
+                                    const char *str_, bool verbose);
 
-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);
+enum ofputil_protocol parse_ofp_flow_mod_str(struct ofputil_flow_mod *,
+                                             const char *string,
+                                             uint16_t command, bool verbose);
+enum ofputil_protocol 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);
+enum ofputil_protocol parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
+                                                       bool aggregate,
+                                                       const char *string);
 
 
 void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
@@ -47,7 +51,7 @@ void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
 char *parse_ofp_exact_flow(struct flow *, const char *);
 
 void parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
-			      int command, bool verbose);
+                             int command, bool verbose);
 
 void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
                                 const char *);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index aa4009d..0decabd 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -1026,152 +1026,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_mark and skb_priority can't be sent in a flow_mod */
-    if (wc->masks.skb_mark || wc->masks.skb_priority) {
-        return OFPUTIL_P_NONE;
-    }
-
-    /* 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)
 {
@@ -2096,37 +1950,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_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,
@@ -2301,24 +2124,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 39c81be..8da9b3d 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -92,6 +92,16 @@ enum ofputil_protocol {
     OFPUTIL_P_OF13_OXM      = 1 << 5,
 #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_ANY_OXM)
+
+#define OFPUTIL_P_NXM_OXM_ANY (OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_ANY_OXM)
+
+#define OFPUTIL_P_OF11_UP (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 << 6) - 1)
 
@@ -119,7 +129,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);
@@ -236,9 +245,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? */
@@ -253,8 +259,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/ofproto/ofproto.c b/ofproto/ofproto.c
index d857902..791edb8 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -2417,7 +2417,7 @@ ofproto_check_ofpacts(struct ofproto *ofproto,
     uint32_t mid;
 
     error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports,
-                          table_id);
+                          table_id, NULL);
     if (error) {
         return error;
     }
@@ -3465,7 +3465,7 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 
         /* Verify actions. */
         error = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
-                              ofproto->max_ports, rule->table_id);
+                              ofproto->max_ports, rule->table_id, NULL);
         if (error) {
             return error;
         }
diff --git a/utilities/ovs-controller.c b/utilities/ovs-controller.c
index 70bc2e6..f947126 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;
@@ -327,8 +329,9 @@ parse_options(int argc, char *argv[])
             break;
 
         case OPT_WITH_FLOWS:
-            parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows,
-                                    &n_default_flows);
+            usable_protocols = parse_ofp_flow_mod_file(optarg, OFPFC_ADD,
+                                                       &default_flows,
+                                                       &n_default_flows);
             break;
 
         case OPT_UNIXCTL:
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 2d6872b..2e5902e 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -867,9 +867,8 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
     struct ofputil_flow_stats_request fsr;
     struct vconn *vconn;
 
-    parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
-    usable_protocols = ofputil_flow_stats_request_usable_protocols(&fsr);
-
+    usable_protocols = parse_ofp_flow_stats_request_str(&fsr, aggregate,
+                                                        argc > 2 ? argv[2] : "");
     protocol = open_vconn(argv[1], &vconn);
     protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
     *requestp = ofputil_encode_flow_stats_request(&fsr, protocol);
@@ -1023,17 +1022,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);
@@ -1065,13 +1060,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];
@@ -1085,23 +1080,27 @@ 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;
 
-    parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
-    ofctl_flow_mod__(argv[1], fms, n_fms);
+    usable_protocols = parse_ofp_flow_mod_file(argv[2], command, &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;
-        parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
-        ofctl_flow_mod__(argv[1], &fm, 1);
+        usable_protocols = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "",
+                                                  command, false);
+        ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
     }
 }
 
@@ -1919,7 +1918,7 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
         struct fte_version *version;
         struct ofputil_flow_mod fm;
 
-        parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
+        usable_protocols &= parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
 
         version = xmalloc(sizeof *version);
         version->cookie = fm.new_cookie;
@@ -1929,8 +1928,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);
@@ -2189,14 +2186,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);
@@ -2231,10 +2227,11 @@ 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;
 
-    parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
-    ofctl_parse_flows__(&fm, 1);
+    usable_protocols = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
+    ofctl_parse_flows__(&fm, 1, usable_protocols);
 }
 
 /* "parse-flows FILENAME": reads the named file as a sequence of flows (like
@@ -2242,11 +2239,13 @@ 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;
 
-    parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
-    ofctl_parse_flows__(fms, n_fms);
+    usable_protocols = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms,
+                                               &n_fms);
+    ofctl_parse_flows__(fms, n_fms, usable_protocols);
     free(fms);
 }
 
@@ -2644,7 +2643,7 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
             struct flow flow;
             memset(&flow, 0, sizeof flow);
             error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX,
-                                  table_id ? atoi(table_id) : 0);
+                                  table_id ? atoi(table_id) : 0, NULL);
         }
         if (error) {
             printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
-- 
1.7.2.5




More information about the dev mailing list