ofp-util: Allow encoding of Open Flow 1.2 Packet In Messages
[sliver-openvswitch.git] / lib / ofp-util.c
index c9346a5..36f1c8a 100644 (file)
@@ -111,7 +111,7 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
     flow_wildcards_init_catchall(wc);
     wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
 
-    /* Wildcard fields that aren't defined by ofp10_match or tun_id. */
+    /* Wildcard fields that aren't defined by ofp10_match. */
     wc->wildcards |= FWW_NW_ECN | FWW_NW_TTL;
 
     if (ofpfw & OFPFW10_NW_TOS) {
@@ -265,6 +265,44 @@ ofputil_cls_rule_to_ofp10_match(const struct cls_rule *rule,
     memset(match->pad2, '\0', sizeof match->pad2);
 }
 
+enum ofperr
+ofputil_pull_ofp11_match(struct ofpbuf *buf, unsigned int priority,
+                         struct cls_rule *rule, uint16_t *padded_match_len)
+{
+    struct ofp11_match_header *omh = buf->data;
+    uint16_t match_len;
+
+    if (buf->size < sizeof *omh) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    match_len = ntohs(omh->length);
+
+    switch (ntohs(omh->type)) {
+    case OFPMT_STANDARD: {
+        struct ofp11_match *om;
+
+        if (match_len != sizeof *om || buf->size < sizeof *om) {
+            return OFPERR_OFPBMC_BAD_LEN;
+        }
+        om = ofpbuf_pull(buf, sizeof *om);
+        if (padded_match_len) {
+            *padded_match_len = match_len;
+        }
+        return ofputil_cls_rule_from_ofp11_match(om, priority, rule);
+    }
+
+    case OFPMT_OXM:
+        if (padded_match_len) {
+            *padded_match_len = ROUND_UP(match_len, 8);
+        }
+        return oxm_pull_match(buf, priority, rule);
+
+    default:
+        return OFPERR_OFPBMC_BAD_TYPE;
+    }
+}
+
 /* Converts the ofp11_match in 'match' into a cls_rule in 'rule', with the
  * given 'priority'.  Returns 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
@@ -444,7 +482,6 @@ ofputil_cls_rule_to_ofp11_match(const struct cls_rule *rule,
         match->in_port = ofputil_port_to_ofp11(rule->flow.in_port);
     }
 
-
     memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
     for (i = 0; i < ETH_ADDR_LEN; i++) {
         match->dl_src_mask[i] = ~rule->wc.dl_src_mask[i];
@@ -570,18 +607,22 @@ size_t ofputil_n_flow_dump_protocols = ARRAY_SIZE(ofputil_flow_dump_protocols);
  * 1.0, 0x02 for OpenFlow 1.1).  Returns 0 if 'version' is not supported or
  * outside the valid range.  */
 enum ofputil_protocol
-ofputil_protocol_from_ofp_version(int version)
+ofputil_protocol_from_ofp_version(enum ofp_version version)
 {
     switch (version) {
-    case OFP10_VERSION: return OFPUTIL_P_OF10;
-    case OFP12_VERSION: return OFPUTIL_P_OF12;
-    default: return 0;
+    case OFP10_VERSION:
+        return OFPUTIL_P_OF10;
+    case OFP12_VERSION:
+        return OFPUTIL_P_OF12;
+    case OFP11_VERSION:
+    default:
+        return 0;
     }
 }
 
 /* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION,
  * OFP11_VERSION or OFP12_VERSION) that corresponds to 'protocol'. */
-uint8_t
+enum ofp_version
 ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
 {
     switch (protocol) {
@@ -1102,133 +1143,212 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     raw = ofpraw_pull_assert(&b);
-    if (raw == OFPRAW_OFPT10_FLOW_MOD) {
+    if (raw == OFPRAW_OFPT11_FLOW_MOD) {
         /* Standard OpenFlow 1.1 flow_mod. */
-        const struct ofp_flow_mod *ofm;
-        uint16_t priority;
+        const struct ofp11_flow_mod *ofm;
         enum ofperr error;
 
-        /* Get the ofp_flow_mod. */
         ofm = ofpbuf_pull(&b, sizeof *ofm);
 
-        /* Set priority based on original wildcards.  Normally we'd allow
-         * ofputil_cls_rule_from_match() to do this for us, but
-         * ofputil_normalize_rule() can put wildcards where the original flow
-         * didn't have them. */
-        priority = ntohs(ofm->priority);
-        if (!(ofm->match.wildcards & htonl(OFPFW10_ALL))) {
-            priority = UINT16_MAX;
+        error = ofputil_pull_ofp11_match(&b, ntohs(ofm->priority), &fm->cr,
+                                         NULL);
+        if (error) {
+            return error;
         }
 
-        /* Translate the rule. */
-        ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
-        ofputil_normalize_rule(&fm->cr);
-
-        /* Now get the actions. */
-        error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+        error = ofpacts_pull_openflow11_instructions(&b, b.size, ofpacts);
         if (error) {
             return error;
         }
 
         /* Translate the message. */
-        command = ntohs(ofm->command);
-        fm->cookie = htonll(0);
-        fm->cookie_mask = htonll(0);
-        fm->new_cookie = ofm->cookie;
+        if (ofm->command == OFPFC_ADD) {
+            fm->cookie = htonll(0);
+            fm->cookie_mask = htonll(0);
+            fm->new_cookie = ofm->cookie;
+        } else {
+            /* XXX */
+            fm->cookie = ofm->cookie;
+            fm->cookie_mask = ofm->cookie_mask;
+            fm->new_cookie = htonll(UINT64_MAX);
+        }
+        fm->command = ofm->command;
+        fm->table_id = ofm->table_id;
         fm->idle_timeout = ntohs(ofm->idle_timeout);
         fm->hard_timeout = ntohs(ofm->hard_timeout);
         fm->buffer_id = ntohl(ofm->buffer_id);
-        fm->out_port = ntohs(ofm->out_port);
-        fm->flags = ntohs(ofm->flags);
-    } else if (raw == OFPRAW_NXT_FLOW_MOD) {
-        /* Nicira extended flow_mod. */
-        const struct nx_flow_mod *nfm;
-        enum ofperr error;
-
-        /* Dissect the message. */
-        nfm = ofpbuf_pull(&b, sizeof *nfm);
-        error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
-                              &fm->cr, &fm->cookie, &fm->cookie_mask);
+        error = ofputil_port_from_ofp11(ofm->out_port, &fm->out_port);
         if (error) {
             return error;
         }
-        error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
-        if (error) {
-            return error;
+        if (ofm->out_group != htonl(OFPG_ANY)) {
+            return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED;
         }
-
-        /* Translate the message. */
-        command = ntohs(nfm->command);
-        if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) {
-            /* Flow additions may only set a new cookie, not match an
-             * existing cookie. */
-            return OFPERR_NXBRC_NXM_INVALID;
-        }
-        fm->new_cookie = nfm->cookie;
-        fm->idle_timeout = ntohs(nfm->idle_timeout);
-        fm->hard_timeout = ntohs(nfm->hard_timeout);
-        fm->buffer_id = ntohl(nfm->buffer_id);
-        fm->out_port = ntohs(nfm->out_port);
-        fm->flags = ntohs(nfm->flags);
+        fm->flags = ntohs(ofm->flags);
     } else {
-        NOT_REACHED();
+        if (raw == OFPRAW_OFPT10_FLOW_MOD) {
+            /* Standard OpenFlow 1.0 flow_mod. */
+            const struct ofp10_flow_mod *ofm;
+            uint16_t priority;
+            enum ofperr error;
+
+            /* Get the ofp10_flow_mod. */
+            ofm = ofpbuf_pull(&b, sizeof *ofm);
+
+            /* Set priority based on original wildcards.  Normally we'd allow
+             * ofputil_cls_rule_from_match() to do this for us, but
+             * ofputil_normalize_rule() can put wildcards where the original
+             * flow didn't have them. */
+            priority = ntohs(ofm->priority);
+            if (!(ofm->match.wildcards & htonl(OFPFW10_ALL))) {
+                priority = UINT16_MAX;
+            }
+
+            /* Translate the rule. */
+            ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
+            ofputil_normalize_rule(&fm->cr);
+
+            /* Now get the actions. */
+            error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+            if (error) {
+                return error;
+            }
+
+            /* Translate the message. */
+            command = ntohs(ofm->command);
+            fm->cookie = htonll(0);
+            fm->cookie_mask = htonll(0);
+            fm->new_cookie = ofm->cookie;
+            fm->idle_timeout = ntohs(ofm->idle_timeout);
+            fm->hard_timeout = ntohs(ofm->hard_timeout);
+            fm->buffer_id = ntohl(ofm->buffer_id);
+            fm->out_port = ntohs(ofm->out_port);
+            fm->flags = ntohs(ofm->flags);
+        } else if (raw == OFPRAW_NXT_FLOW_MOD) {
+            /* Nicira extended flow_mod. */
+            const struct nx_flow_mod *nfm;
+            enum ofperr error;
+
+            /* Dissect the message. */
+            nfm = ofpbuf_pull(&b, sizeof *nfm);
+            error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
+                                  &fm->cr, &fm->cookie, &fm->cookie_mask);
+            if (error) {
+                return error;
+            }
+            error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+            if (error) {
+                return error;
+            }
+
+            /* Translate the message. */
+            command = ntohs(nfm->command);
+            if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) {
+                /* Flow additions may only set a new cookie, not match an
+                 * existing cookie. */
+                return OFPERR_NXBRC_NXM_INVALID;
+            }
+            fm->new_cookie = nfm->cookie;
+            fm->idle_timeout = ntohs(nfm->idle_timeout);
+            fm->hard_timeout = ntohs(nfm->hard_timeout);
+            fm->buffer_id = ntohl(nfm->buffer_id);
+            fm->out_port = ntohs(nfm->out_port);
+            fm->flags = ntohs(nfm->flags);
+        } else {
+            NOT_REACHED();
+        }
+
+        if (protocol & OFPUTIL_P_TID) {
+            fm->command = command & 0xff;
+            fm->table_id = command >> 8;
+        } else {
+            fm->command = command;
+            fm->table_id = 0xff;
+        }
     }
 
     fm->ofpacts = ofpacts->data;
     fm->ofpacts_len = ofpacts->size;
-    if (protocol & OFPUTIL_P_TID) {
-        fm->command = command & 0xff;
-        fm->table_id = command >> 8;
-    } else {
-        fm->command = command;
-        fm->table_id = 0xff;
-    }
 
     return 0;
 }
 
+static ovs_be16
+ofputil_tid_command(const struct ofputil_flow_mod *fm,
+                    enum ofputil_protocol protocol)
+{
+    return htons(protocol & OFPUTIL_P_TID
+                 ? (fm->command & 0xff) | (fm->table_id << 8)
+                 : fm->command);
+}
+
 /* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
  * 'protocol' and returns the message. */
 struct ofpbuf *
 ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
                         enum ofputil_protocol protocol)
 {
-    struct ofp_flow_mod *ofm;
-    struct nx_flow_mod *nfm;
     struct ofpbuf *msg;
-    uint16_t command;
-    int match_len;
-
-    command = (protocol & OFPUTIL_P_TID
-               ? (fm->command & 0xff) | (fm->table_id << 8)
-               : fm->command);
 
     switch (protocol) {
+    case OFPUTIL_P_OF12: {
+        struct ofp11_flow_mod *ofm;
+
+        msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, OFP12_VERSION,
+                           NXM_TYPICAL_LEN + fm->ofpacts_len);
+        ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
+        ofm->cookie = fm->new_cookie;
+        ofm->cookie_mask = fm->cookie_mask;
+        ofm->table_id = fm->table_id;
+        ofm->command = fm->command;
+        ofm->idle_timeout = htons(fm->idle_timeout);
+        ofm->hard_timeout = htons(fm->hard_timeout);
+        ofm->priority = htons(fm->cr.priority);
+        ofm->buffer_id = htonl(fm->buffer_id);
+        ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
+        ofm->out_group = htonl(OFPG11_ANY);
+        ofm->flags = htons(fm->flags);
+        oxm_put_match(msg, &fm->cr);
+        if (fm->ofpacts) {
+            ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len,
+                                                msg);
+        }
+        break;
+    }
+
     case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID:
+    case OFPUTIL_P_OF10_TID: {
+        struct ofp10_flow_mod *ofm;
+
         msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION,
                            fm->ofpacts_len);
         ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
         ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match);
         ofm->cookie = fm->new_cookie;
-        ofm->command = htons(command);
+        ofm->command = ofputil_tid_command(fm, protocol);
         ofm->idle_timeout = htons(fm->idle_timeout);
         ofm->hard_timeout = htons(fm->hard_timeout);
         ofm->priority = htons(fm->cr.priority);
         ofm->buffer_id = htonl(fm->buffer_id);
         ofm->out_port = htons(fm->out_port);
         ofm->flags = htons(fm->flags);
+        if (fm->ofpacts) {
+            ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
+        }
         break;
+    }
 
     case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID:
+    case OFPUTIL_P_NXM_TID: {
+        struct nx_flow_mod *nfm;
+        int match_len;
+
         msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD, OFP10_VERSION,
                            NXM_TYPICAL_LEN + fm->ofpacts_len);
         nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
-        nfm->command = htons(command);
+        nfm->command = ofputil_tid_command(fm, protocol);
         nfm->cookie = fm->new_cookie;
-        match_len = nx_put_match(msg, false, &fm->cr,
-                                 fm->cookie, fm->cookie_mask);
+        match_len = nx_put_match(msg, &fm->cr, fm->cookie, fm->cookie_mask);
         nfm = msg->l3;
         nfm->idle_timeout = htons(fm->idle_timeout);
         nfm->hard_timeout = htons(fm->hard_timeout);
@@ -1237,16 +1357,16 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         nfm->out_port = htons(fm->out_port);
         nfm->flags = htons(fm->flags);
         nfm->match_len = htons(match_len);
+        if (fm->ofpacts) {
+            ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
+        }
         break;
+    }
 
-    case OFPUTIL_P_OF12:
     default:
         NOT_REACHED();
     }
 
-    if (fm->ofpacts) {
-        ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
-    }
     ofpmsg_update_length(msg);
     return msg;
 }
@@ -1284,7 +1404,7 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
 
 static enum ofperr
 ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
-                                  const struct ofp_flow_stats_request *ofsr,
+                                  const struct ofp10_flow_stats_request *ofsr,
                                   bool aggregate)
 {
     fsr->aggregate = aggregate;
@@ -1364,7 +1484,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     switch (protocol) {
     case OFPUTIL_P_OF10:
     case OFPUTIL_P_OF10_TID: {
-        struct ofp_flow_stats_request *ofsr;
+        struct ofp10_flow_stats_request *ofsr;
 
         raw = (fsr->aggregate
                ? OFPRAW_OFPST_AGGREGATE_REQUEST
@@ -1387,7 +1507,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
                : OFPRAW_NXST_FLOW_REQUEST);
         msg = ofpraw_alloc(raw, OFP10_VERSION, 0);
         ofpbuf_put_zeros(msg, sizeof *nfsr);
-        match_len = nx_put_match(msg, false, &fsr->match,
+        match_len = nx_put_match(msg, &fsr->match,
                                  fsr->cookie, fsr->cookie_mask);
 
         nfsr = msg->l3;
@@ -1461,7 +1581,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
     if (!msg->size) {
         return EOF;
     } else if (raw == OFPRAW_OFPST_FLOW_REPLY) {
-        const struct ofp_flow_stats *ofs;
+        const struct ofp10_flow_stats *ofs;
         size_t length;
 
         ofs = ofpbuf_try_pull(msg, sizeof *ofs);
@@ -1573,7 +1693,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
 
     ofpraw_decode_partial(&raw, reply->data, reply->size);
     if (raw == OFPRAW_OFPST_FLOW_REPLY) {
-        struct ofp_flow_stats *ofs;
+        struct ofp10_flow_stats *ofs;
 
         ofpbuf_put_uninit(reply, sizeof *ofs);
         ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
@@ -1599,7 +1719,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         int match_len;
 
         ofpbuf_put_uninit(reply, sizeof *nfs);
-        match_len = nx_put_match(reply, false, &fs->rule, 0, 0);
+        match_len = nx_put_match(reply, &fs->rule, 0, 0);
         ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
 
         nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
@@ -1768,7 +1888,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION,
                                htonl(0), NXM_TYPICAL_LEN);
         nfr = ofpbuf_put_zeros(msg, sizeof *nfr);
-        match_len = nx_put_match(msg, false, &fr->rule, 0, 0);
+        match_len = nx_put_match(msg, &fr->rule, 0, 0);
 
         nfr = msg->l3;
         nfr->cookie = fr->cookie;
@@ -1857,17 +1977,58 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
     return 0;
 }
 
+static void
+ofputil_packet_in_to_rule(const struct ofputil_packet_in *pin,
+                          struct cls_rule *rule)
+{
+    int i;
+
+    cls_rule_init_catchall(rule, 0);
+    cls_rule_set_tun_id_masked(rule, pin->fmd.tun_id,
+                               pin->fmd.tun_id_mask);
+    cls_rule_set_metadata_masked(rule, pin->fmd.metadata,
+                                 pin->fmd.metadata_mask);
+
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        cls_rule_set_reg_masked(rule, i, pin->fmd.regs[i],
+                                pin->fmd.reg_masks[i]);
+    }
+
+    cls_rule_set_in_port(rule, pin->fmd.in_port);
+}
+
 /* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message
  * in the format specified by 'packet_in_format'.  */
 struct ofpbuf *
 ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
+                         enum ofputil_protocol protocol,
                          enum nx_packet_in_format packet_in_format)
 {
     size_t send_len = MIN(pin->send_len, pin->packet_len);
     struct ofpbuf *packet;
 
     /* Add OFPT_PACKET_IN. */
-    if (packet_in_format == NXPIF_OPENFLOW10) {
+    if (protocol == OFPUTIL_P_OF12) {
+        struct ofp12_packet_in *opi;
+        struct cls_rule rule;
+
+        ofputil_packet_in_to_rule(pin, &rule);
+
+        /* The final argument is just an estimate of the space required. */
+        packet = ofpraw_alloc_xid(OFPRAW_OFPT12_PACKET_IN, OFP12_VERSION,
+                                  htonl(0), (sizeof(struct flow_metadata) * 2
+                                             + 2 + send_len));
+        ofpbuf_put_zeros(packet, sizeof *opi);
+        oxm_put_match(packet, &rule);
+        ofpbuf_put_zeros(packet, 2);
+        ofpbuf_put(packet, pin->packet, send_len);
+
+        opi = packet->l3;
+        opi->buffer_id = htonl(pin->buffer_id);
+        opi->total_len = htons(pin->total_len);
+        opi->reason = pin->reason;
+        opi->table_id = pin->table_id;
+   } else if (packet_in_format == NXPIF_OPENFLOW10) {
         struct ofp_packet_in *opi;
 
         packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
@@ -1883,28 +2044,15 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         struct nx_packet_in *npi;
         struct cls_rule rule;
         size_t match_len;
-        size_t i;
-
-        cls_rule_init_catchall(&rule, 0);
-        cls_rule_set_tun_id_masked(&rule, pin->fmd.tun_id,
-                                   pin->fmd.tun_id_mask);
-        cls_rule_set_metadata_masked(&rule, pin->fmd.metadata,
-                                   pin->fmd.metadata_mask);
 
-
-        for (i = 0; i < FLOW_N_REGS; i++) {
-            cls_rule_set_reg_masked(&rule, i, pin->fmd.regs[i],
-                                    pin->fmd.reg_masks[i]);
-        }
-
-        cls_rule_set_in_port(&rule, pin->fmd.in_port);
+        ofputil_packet_in_to_rule(pin, &rule);
 
         /* The final argument is just an estimate of the space required. */
         packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
                                   htonl(0), (sizeof(struct flow_metadata) * 2
                                              + 2 + send_len));
         ofpbuf_put_zeros(packet, sizeof *npi);
-        match_len = nx_put_match(packet, false, &rule, 0, 0);
+        match_len = nx_put_match(packet, &rule, 0, 0);
         ofpbuf_put_zeros(packet, 2);
         ofpbuf_put(packet, pin->packet, send_len);
 
@@ -2122,10 +2270,17 @@ ofputil_decode_ofp11_port(struct ofputil_phy_port *pp,
 }
 
 static size_t
-ofputil_get_phy_port_size(uint8_t ofp_version)
-{
-    return ofp_version == OFP10_VERSION ? sizeof(struct ofp10_phy_port)
-                                        : sizeof(struct ofp11_port);
+ofputil_get_phy_port_size(enum ofp_version ofp_version)
+{
+    switch (ofp_version) {
+    case OFP10_VERSION:
+        return sizeof(struct ofp10_phy_port);
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        return sizeof(struct ofp11_port);
+    default:
+        NOT_REACHED();
+    }
 }
 
 static void
@@ -2170,46 +2325,66 @@ ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
 }
 
 static void
-ofputil_put_phy_port(uint8_t ofp_version, const struct ofputil_phy_port *pp,
-                     struct ofpbuf *b)
+ofputil_put_phy_port(enum ofp_version ofp_version,
+                     const struct ofputil_phy_port *pp, struct ofpbuf *b)
 {
-    if (ofp_version == OFP10_VERSION) {
+    switch (ofp_version) {
+    case OFP10_VERSION: {
         struct ofp10_phy_port *opp;
         if (b->size + sizeof *opp <= UINT16_MAX) {
             opp = ofpbuf_put_uninit(b, sizeof *opp);
             ofputil_encode_ofp10_phy_port(pp, opp);
         }
-    } else {
+        break;
+    }
+
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
         struct ofp11_port *op;
         if (b->size + sizeof *op <= UINT16_MAX) {
             op = ofpbuf_put_uninit(b, sizeof *op);
             ofputil_encode_ofp11_port(pp, op);
         }
+        break;
+    }
+
+    default:
+        NOT_REACHED();
     }
 }
 
 void
-ofputil_append_port_desc_stats_reply(uint8_t ofp_version,
+ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
                                      const struct ofputil_phy_port *pp,
                                      struct list *replies)
 {
-    if (ofp_version == OFP10_VERSION) {
+    switch (ofp_version) {
+    case OFP10_VERSION: {
         struct ofp10_phy_port *opp;
 
         opp = ofpmp_append(replies, sizeof *opp);
         ofputil_encode_ofp10_phy_port(pp, opp);
-    } else {
+        break;
+    }
+
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
         struct ofp11_port *op;
 
         op = ofpmp_append(replies, sizeof *op);
         ofputil_encode_ofp11_port(pp, op);
+        break;
+    }
+
+    default:
+      NOT_REACHED();
     }
 }
 \f
 /* ofputil_switch_features */
 
 #define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
-                     OFPC_IP_REASM | OFPC_QUEUE_STATS | OFPC_ARP_MATCH_IP)
+                     OFPC_IP_REASM | OFPC_QUEUE_STATS)
 BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS);
 BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS);
 BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS);
@@ -2238,35 +2413,6 @@ static const struct ofputil_action_bit_translation of10_action_bits[] = {
     { 0, 0 },
 };
 
-static const struct ofputil_action_bit_translation of11_action_bits[] = {
-    { OFPUTIL_A_OUTPUT,         OFPAT11_OUTPUT },
-    { OFPUTIL_A_SET_VLAN_VID,   OFPAT11_SET_VLAN_VID },
-    { OFPUTIL_A_SET_VLAN_PCP,   OFPAT11_SET_VLAN_PCP },
-    { OFPUTIL_A_SET_DL_SRC,     OFPAT11_SET_DL_SRC },
-    { OFPUTIL_A_SET_DL_DST,     OFPAT11_SET_DL_DST },
-    { OFPUTIL_A_SET_NW_SRC,     OFPAT11_SET_NW_SRC },
-    { OFPUTIL_A_SET_NW_DST,     OFPAT11_SET_NW_DST },
-    { OFPUTIL_A_SET_NW_TOS,     OFPAT11_SET_NW_TOS },
-    { OFPUTIL_A_SET_NW_ECN,     OFPAT11_SET_NW_ECN },
-    { OFPUTIL_A_SET_TP_SRC,     OFPAT11_SET_TP_SRC },
-    { OFPUTIL_A_SET_TP_DST,     OFPAT11_SET_TP_DST },
-    { OFPUTIL_A_COPY_TTL_OUT,   OFPAT11_COPY_TTL_OUT },
-    { OFPUTIL_A_COPY_TTL_IN,    OFPAT11_COPY_TTL_IN },
-    { OFPUTIL_A_SET_MPLS_LABEL, OFPAT11_SET_MPLS_LABEL },
-    { OFPUTIL_A_SET_MPLS_TC,    OFPAT11_SET_MPLS_TC },
-    { OFPUTIL_A_SET_MPLS_TTL,   OFPAT11_SET_MPLS_TTL },
-    { OFPUTIL_A_DEC_MPLS_TTL,   OFPAT11_DEC_MPLS_TTL },
-    { OFPUTIL_A_PUSH_VLAN,      OFPAT11_PUSH_VLAN },
-    { OFPUTIL_A_POP_VLAN,       OFPAT11_POP_VLAN },
-    { OFPUTIL_A_PUSH_MPLS,      OFPAT11_PUSH_MPLS },
-    { OFPUTIL_A_POP_MPLS,       OFPAT11_POP_MPLS },
-    { OFPUTIL_A_SET_QUEUE,      OFPAT11_SET_QUEUE },
-    { OFPUTIL_A_GROUP,          OFPAT11_GROUP },
-    { OFPUTIL_A_SET_NW_TTL,     OFPAT11_SET_NW_TTL },
-    { OFPUTIL_A_DEC_NW_TTL,     OFPAT11_DEC_NW_TTL },
-    { 0, 0 },
-};
-
 static enum ofputil_action_bitmap
 decode_action_bits(ovs_be32 of_actions,
                    const struct ofputil_action_bit_translation *x)
@@ -2282,6 +2428,22 @@ decode_action_bits(ovs_be32 of_actions,
     return ofputil_actions;
 }
 
+static uint32_t
+ofputil_capabilities_mask(enum ofp_version ofp_version)
+{
+    /* Handle capabilities whose bit is unique for all Open Flow versions */
+    switch (ofp_version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+        return OFPC_COMMON | OFPC_ARP_MATCH_IP;
+    case OFP12_VERSION:
+        return OFPC_COMMON | OFPC12_PORT_BLOCKED;
+    default:
+        /* Caller needs to check osf->header.version itself */
+        return 0;
+    }
+}
+
 /* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an
  * abstract representation in '*features'.  Initializes '*b' to iterate over
  * the OpenFlow port structures following 'osf' with later calls to
@@ -2303,7 +2465,8 @@ ofputil_decode_switch_features(const struct ofp_header *oh,
     features->n_buffers = ntohl(osf->n_buffers);
     features->n_tables = osf->n_tables;
 
-    features->capabilities = ntohl(osf->capabilities) & OFPC_COMMON;
+    features->capabilities = ntohl(osf->capabilities) &
+        ofputil_capabilities_mask(oh->version);
 
     if (b->size % ofputil_get_phy_port_size(oh->version)) {
         return OFPERR_OFPBRC_BAD_LEN;
@@ -2318,7 +2481,7 @@ ofputil_decode_switch_features(const struct ofp_header *oh,
         if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
             features->capabilities |= OFPUTIL_C_GROUP_STATS;
         }
-        features->actions = decode_action_bits(osf->actions, of11_action_bits);
+        features->actions = 0;
     } else {
         return OFPERR_OFPBRC_BAD_VERSION;
     }
@@ -2383,29 +2546,45 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features,
 {
     struct ofp_switch_features *osf;
     struct ofpbuf *b;
-    uint8_t version;
+    enum ofp_version version;
+    enum ofpraw raw;
 
     version = ofputil_protocol_to_ofp_version(protocol);
-    b = ofpraw_alloc_xid(version == OFP10_VERSION
-                         ? OFPRAW_OFPT10_FEATURES_REPLY
-                         : OFPRAW_OFPT11_FEATURES_REPLY,
-                         version, xid, 0);
+    switch (version) {
+    case OFP10_VERSION:
+        raw = OFPRAW_OFPT10_FEATURES_REPLY;
+        break;
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        raw = OFPRAW_OFPT11_FEATURES_REPLY;
+        break;
+    default:
+        NOT_REACHED();
+    }
+    b = ofpraw_alloc_xid(raw, version, xid, 0);
     osf = ofpbuf_put_zeros(b, sizeof *osf);
     osf->datapath_id = htonll(features->datapath_id);
     osf->n_buffers = htonl(features->n_buffers);
     osf->n_tables = features->n_tables;
 
     osf->capabilities = htonl(features->capabilities & OFPC_COMMON);
-    if (version == OFP10_VERSION) {
+    osf->capabilities = htonl(features->capabilities &
+                              ofputil_capabilities_mask(version));
+    switch (version) {
+    case OFP10_VERSION:
         if (features->capabilities & OFPUTIL_C_STP) {
             osf->capabilities |= htonl(OFPC10_STP);
         }
         osf->actions = encode_action_bits(features->actions, of10_action_bits);
-    } else {
+        break;
+    case OFP11_VERSION:
+    case OFP12_VERSION:
         if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
             osf->capabilities |= htonl(OFPC11_GROUP_STATS);
         }
-        osf->actions = encode_action_bits(features->actions, of11_action_bits);
+        break;
+    default:
+        NOT_REACHED();
     }
 
     return b;
@@ -2460,13 +2639,25 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
 {
     struct ofp_port_status *ops;
     struct ofpbuf *b;
-    uint8_t version;
+    enum ofp_version version;
+    enum ofpraw raw;
 
     version = ofputil_protocol_to_ofp_version(protocol);
-    b = ofpraw_alloc_xid(version == OFP10_VERSION
-                         ? OFPRAW_OFPT10_PORT_STATUS
-                         : OFPRAW_OFPT11_PORT_STATUS,
-                         version, htonl(0), 0);
+    switch (version) {
+    case OFP10_VERSION:
+        raw = OFPRAW_OFPT10_PORT_STATUS;
+        break;
+
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        raw = OFPRAW_OFPT11_PORT_STATUS;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
     ops = ofpbuf_put_zeros(b, sizeof *ops);
     ops->reason = ps->reason;
     ofputil_put_phy_port(version, &ps->desc, b);
@@ -2524,10 +2715,11 @@ struct ofpbuf *
 ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
                         enum ofputil_protocol protocol)
 {
-    uint8_t ofp_version = ofputil_protocol_to_ofp_version(protocol);
+    enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
     struct ofpbuf *b;
 
-    if (ofp_version == OFP10_VERSION) {
+    switch (ofp_version) {
+    case OFP10_VERSION: {
         struct ofp10_port_mod *opm;
 
         b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
@@ -2537,7 +2729,10 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         opm->config = htonl(pm->config & OFPPC10_ALL);
         opm->mask = htonl(pm->mask & OFPPC10_ALL);
         opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
-    } else if (ofp_version == OFP11_VERSION) {
+        break;
+    }
+
+    case OFP11_VERSION: {
         struct ofp11_port_mod *opm;
 
         b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
@@ -2547,7 +2742,11 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         opm->config = htonl(pm->config & OFPPC11_ALL);
         opm->mask = htonl(pm->mask & OFPPC11_ALL);
         opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
-    } else {
+        break;
+    }
+
+    case OFP12_VERSION:
+    default:
         NOT_REACHED();
     }
 
@@ -2625,7 +2824,7 @@ ofputil_append_flow_monitor_request(
 
     start_ofs = msg->size;
     ofpbuf_put_zeros(msg, sizeof *nfmr);
-    match_len = nx_put_match(msg, false, &rq->match, htonll(0), htonll(0));
+    match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
 
     nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
     nfmr->id = htonl(rq->id);
@@ -2793,8 +2992,7 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update,
         int match_len;
 
         ofpbuf_put_zeros(msg, sizeof *nfuf);
-        match_len = nx_put_match(msg, false, update->match,
-                                 htonll(0), htonll(0));
+        match_len = nx_put_match(msg, update->match, htonll(0), htonll(0));
         ofpacts_put_openflow10(update->ofpacts, update->ofpacts_len, msg);
 
         nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
@@ -2848,9 +3046,9 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po)
 \f
 /* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
 struct ofpbuf *
-make_echo_request(void)
+make_echo_request(enum ofp_version ofp_version)
 {
-    return ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+    return ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, ofp_version,
                             htonl(0), 0);
 }
 
@@ -2871,9 +3069,25 @@ make_echo_reply(const struct ofp_header *rq)
 }
 
 struct ofpbuf *
-ofputil_encode_barrier_request(void)
+ofputil_encode_barrier_request(enum ofp_version ofp_version)
 {
-    return ofpraw_alloc(OFPRAW_OFPT10_BARRIER_REQUEST, OFP10_VERSION, 0);
+    enum ofpraw type;
+
+    switch (ofp_version) {
+    case OFP12_VERSION:
+    case OFP11_VERSION:
+        type = OFPRAW_OFPT11_BARRIER_REQUEST;
+        break;
+
+    case OFP10_VERSION:
+        type = OFPRAW_OFPT10_BARRIER_REQUEST;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    return ofpraw_alloc(type, ofp_version, 0);
 }
 
 const char *
@@ -3037,16 +3251,22 @@ ofputil_format_port(uint16_t port, struct ds *s)
  * port and returns 0.  If no ports remain to be decoded, returns EOF.
  * On an error, returns a positive OFPERR_* value. */
 int
-ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b,
+ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
                       struct ofputil_phy_port *pp)
 {
-    if (ofp_version == OFP10_VERSION) {
+    switch (ofp_version) {
+    case OFP10_VERSION: {
         const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
         return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
-    } else {
+    }
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
         const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
         return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
     }
+    default:
+        NOT_REACHED();
+    }
 }
 
 /* Given a buffer 'b' that contains an array of OpenFlow ports of type
@@ -3141,23 +3361,8 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
     }
 #include "ofp-util.def"
 
-/* "Normalizes" the wildcards in 'rule'.  That means:
- *
- *    1. If the type of level N is known, then only the valid fields for that
- *       level may be specified.  For example, ARP does not have a TOS field,
- *       so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
- *       Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
- *       ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
- *       IPv4 flow.
- *
- *    2. If the type of level N is not known (or not understood by Open
- *       vSwitch), then no fields at all for that level may be specified.  For
- *       example, Open vSwitch does not understand SCTP, an L4 protocol, so the
- *       L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
- *       SCTP flow.
- */
-void
-ofputil_normalize_rule(struct cls_rule *rule)
+static void
+ofputil_normalize_rule__(struct cls_rule *rule, bool may_log)
 {
     enum {
         MAY_NW_ADDR     = 1 << 0, /* nw_src, nw_dst */
@@ -3231,7 +3436,7 @@ ofputil_normalize_rule(struct cls_rule *rule)
 
     /* Log any changes. */
     if (!flow_wildcards_equal(&wc, &rule->wc)) {
-        bool log = !VLOG_DROP_INFO(&bad_ofmsg_rl);
+        bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
         char *pre = log ? cls_rule_to_string(rule) : NULL;
 
         rule->wc = wc;
@@ -3248,6 +3453,39 @@ ofputil_normalize_rule(struct cls_rule *rule)
     }
 }
 
+/* "Normalizes" the wildcards in 'rule'.  That means:
+ *
+ *    1. If the type of level N is known, then only the valid fields for that
+ *       level may be specified.  For example, ARP does not have a TOS field,
+ *       so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
+ *       Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
+ *       ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
+ *       IPv4 flow.
+ *
+ *    2. If the type of level N is not known (or not understood by Open
+ *       vSwitch), then no fields at all for that level may be specified.  For
+ *       example, Open vSwitch does not understand SCTP, an L4 protocol, so the
+ *       L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
+ *       SCTP flow.
+ *
+ * If this function changes 'rule', it logs a rate-limited informational
+ * message. */
+void
+ofputil_normalize_rule(struct cls_rule *rule)
+{
+    ofputil_normalize_rule__(rule, true);
+}
+
+/* Same as ofputil_normalize_rule() without the logging.  Thus, this function
+ * is suitable for a program's internal use, whereas ofputil_normalize_rule()
+ * sense for use on flows received from elsewhere (so that a bug in the program
+ * that sent them can be reported and corrected). */
+void
+ofputil_normalize_rule_quiet(struct cls_rule *rule)
+{
+    ofputil_normalize_rule__(rule, false);
+}
+
 /* Parses a key or a key-value pair from '*stringp'.
  *
  * On success: Stores the key into '*keyp'.  Stores the value, if present, into