]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/ofp-util.c
other-config: Add tc-policy switch to control tc flower flag
[mirror_ovs.git] / lib / ofp-util.c
index 0c9343ec400b99aca1c3e2e6f179b234e2fdf20f..da171cdf6fe5f675015123dda4054721deee0bff 100644 (file)
@@ -33,6 +33,7 @@
 #include "id-pool.h"
 #include "openflow/netronome-ext.h"
 #include "openvswitch/dynamic-string.h"
+#include "openvswitch/json.h"
 #include "openvswitch/meta-flow.h"
 #include "openvswitch/ofp-actions.h"
 #include "openvswitch/ofp-errors.h"
@@ -101,7 +102,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -141,10 +142,10 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 
     /* VLAN TCI mask. */
     if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) {
-        wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
+        wc->masks.vlans[0].tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
     }
     if (!(ofpfw & OFPFW10_DL_VLAN)) {
-        wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+        wc->masks.vlans[0].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
     }
 }
 
@@ -158,6 +159,7 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
     /* Initialize match->wc. */
     memset(&match->flow, 0, sizeof match->flow);
     ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc);
+    memset(&match->tun_md, 0, sizeof match->tun_md);
 
     /* Initialize most of match->flow. */
     match->flow.nw_src = ofmatch->nw_src;
@@ -182,8 +184,8 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
          * because we can't have a specific PCP without an 802.1Q header.
          * However, older versions of OVS treated this as matching packets
          * withut an 802.1Q header, so we do here too. */
-        match->flow.vlan_tci = htons(0);
-        match->wc.masks.vlan_tci = htons(0xffff);
+        match->flow.vlans[0].tci = htons(0);
+        match->wc.masks.vlans[0].tci = htons(0xffff);
     } else {
         ovs_be16 vid, pcp, tci;
         uint16_t hpcp;
@@ -192,7 +194,7 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
         hpcp = (ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK;
         pcp = htons(hpcp);
         tci = vid | pcp | htons(VLAN_CFI);
-        match->flow.vlan_tci = tci & match->wc.masks.vlan_tci;
+        match->flow.vlans[0].tci = tci & match->wc.masks.vlans[0].tci;
     }
 
     /* Clean up. */
@@ -241,22 +243,23 @@ ofputil_match_to_ofp10_match(const struct match *match,
     /* Translate VLANs. */
     ofmatch->dl_vlan = htons(0);
     ofmatch->dl_vlan_pcp = 0;
-    if (match->wc.masks.vlan_tci == htons(0)) {
+    if (match->wc.masks.vlans[0].tci == htons(0)) {
         ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
-    } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
-               && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+    } else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
+               && !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
         ofmatch->dl_vlan = htons(OFP10_VLAN_NONE);
     } else {
-        if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
+        if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
             ofpfw |= OFPFW10_DL_VLAN;
         } else {
-            ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
+            ofmatch->dl_vlan =
+                htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
         }
 
-        if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
+        if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
             ofpfw |= OFPFW10_DL_VLAN_PCP;
         } else {
-            ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
+            ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
         }
     }
 
@@ -278,6 +281,7 @@ ofputil_match_to_ofp10_match(const struct match *match,
 
 enum ofperr
 ofputil_pull_ofp11_match(struct ofpbuf *buf, const struct tun_table *tun_table,
+                         const struct vl_mff_map *vl_mff_map,
                          struct match *match, uint16_t *padded_match_len)
 {
     struct ofp11_match_header *omh = buf->data;
@@ -307,7 +311,7 @@ ofputil_pull_ofp11_match(struct ofpbuf *buf, const struct tun_table *tun_table,
         if (padded_match_len) {
             *padded_match_len = ROUND_UP(match_len, 8);
         }
-        return oxm_pull_match(buf, tun_table, match);
+        return oxm_pull_match(buf, false, tun_table, vl_mff_map, match);
 
     default:
         return OFPERR_OFPBMC_BAD_TYPE;
@@ -344,17 +348,17 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
     if (!(wc & OFPFW11_DL_VLAN)) {
         if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
             /* Match only packets without a VLAN tag. */
-            match->flow.vlan_tci = htons(0);
-            match->wc.masks.vlan_tci = OVS_BE16_MAX;
+            match->flow.vlans[0].tci = htons(0);
+            match->wc.masks.vlans[0].tci = OVS_BE16_MAX;
         } else {
             if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) {
                 /* Match any packet with a VLAN tag regardless of VID. */
-                match->flow.vlan_tci = htons(VLAN_CFI);
-                match->wc.masks.vlan_tci = htons(VLAN_CFI);
+                match->flow.vlans[0].tci = htons(VLAN_CFI);
+                match->wc.masks.vlans[0].tci = htons(VLAN_CFI);
             } else if (ntohs(ofmatch->dl_vlan) < 4096) {
                 /* Match only packets with the specified VLAN VID. */
-                match->flow.vlan_tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
-                match->wc.masks.vlan_tci = htons(VLAN_CFI | VLAN_VID_MASK);
+                match->flow.vlans[0].tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
+                match->wc.masks.vlans[0].tci = htons(VLAN_CFI | VLAN_VID_MASK);
             } else {
                 /* Invalid VID. */
                 return OFPERR_OFPBMC_BAD_VALUE;
@@ -362,9 +366,9 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
 
             if (!(wc & OFPFW11_DL_VLAN_PCP)) {
                 if (ofmatch->dl_vlan_pcp <= 7) {
-                    match->flow.vlan_tci |= htons(ofmatch->dl_vlan_pcp
+                    match->flow.vlans[0].tci |= htons(ofmatch->dl_vlan_pcp
                                                   << VLAN_PCP_SHIFT);
-                    match->wc.masks.vlan_tci |= htons(VLAN_PCP_MASK);
+                    match->wc.masks.vlans[0].tci |= htons(VLAN_PCP_MASK);
                 } else {
                     /* Invalid PCP. */
                     return OFPERR_OFPBMC_BAD_VALUE;
@@ -482,23 +486,24 @@ ofputil_match_to_ofp11_match(const struct match *match,
     ofmatch->dl_dst = match->flow.dl_dst;
     ofmatch->dl_dst_mask = eth_addr_invert(match->wc.masks.dl_dst);
 
-    if (match->wc.masks.vlan_tci == htons(0)) {
+    if (match->wc.masks.vlans[0].tci == htons(0)) {
         wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
-    } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
-               && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+    } else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
+               && !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
         ofmatch->dl_vlan = htons(OFPVID11_NONE);
         wc |= OFPFW11_DL_VLAN_PCP;
     } else {
-        if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
+        if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
             ofmatch->dl_vlan = htons(OFPVID11_ANY);
         } else {
-            ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
+            ofmatch->dl_vlan =
+                htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
         }
 
-        if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
+        if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
             wc |= OFPFW11_DL_VLAN_PCP;
         } else {
-            ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
+            ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
         }
     }
 
@@ -1585,7 +1590,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 
         ofm = ofpbuf_pull(&b, sizeof *ofm);
 
-        error = ofputil_pull_ofp11_match(&b, tun_table, &fm->match, NULL);
+        error = ofputil_pull_ofp11_match(&b, tun_table, vl_mff_map, &fm->match,
+                                         NULL);
         if (error) {
             return error;
         }
@@ -1681,7 +1687,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             nfm = ofpbuf_pull(&b, sizeof *nfm);
             error = nx_pull_match(&b, ntohs(nfm->match_len),
                                   &fm->match, &fm->cookie, &fm->cookie_mask,
-                                  tun_table);
+                                  false, tun_table, vl_mff_map);
             if (error) {
                 return error;
             }
@@ -1720,12 +1726,25 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         }
     }
 
+    /* Check for mismatched conntrack original direction tuple address fields
+     * w.r.t. the IP version of the match. */
+    if (((fm->match.wc.masks.ct_nw_src || fm->match.wc.masks.ct_nw_dst)
+         && fm->match.flow.dl_type != htons(ETH_TYPE_IP))
+        || ((ipv6_addr_is_set(&fm->match.wc.masks.ct_ipv6_src)
+             || ipv6_addr_is_set(&fm->match.wc.masks.ct_ipv6_dst))
+            && fm->match.flow.dl_type != htons(ETH_TYPE_IPV6))) {
+        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
+    }
+
     if (fm->command > OFPFC_DELETE_STRICT) {
         return OFPERR_OFPFMFC_BAD_COMMAND;
     }
 
+    fm->ofpacts_tlv_bitmap = 0;
     error = ofpacts_pull_openflow_instructions(&b, b.size, oh->version,
-                                               vl_mff_map, ofpacts);
+                                               vl_mff_map,
+                                               &fm->ofpacts_tlv_bitmap,
+                                               ofpacts);
     if (error) {
         return error;
     }
@@ -1751,7 +1770,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
     }
 
     return ofpacts_check_consistency(fm->ofpacts, fm->ofpacts_len,
-                                     &fm->match.flow, max_port,
+                                     &fm->match, max_port,
                                      fm->table_id, max_table, protocol);
 }
 
@@ -2271,7 +2290,8 @@ ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
 static enum ofperr
 ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
                                     struct ofpbuf *b, bool aggregate,
-                                    const struct tun_table *tun_table)
+                                    const struct tun_table *tun_table,
+                                    const struct vl_mff_map *vl_mff_map)
 {
     const struct ofp11_flow_stats_request *ofsr;
     enum ofperr error;
@@ -2286,7 +2306,8 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
     fsr->out_group = ntohl(ofsr->out_group);
     fsr->cookie = ofsr->cookie;
     fsr->cookie_mask = ofsr->cookie_mask;
-    error = ofputil_pull_ofp11_match(b, tun_table, &fsr->match, NULL);
+    error = ofputil_pull_ofp11_match(b, tun_table, vl_mff_map, &fsr->match,
+                                     NULL);
     if (error) {
         return error;
     }
@@ -2297,14 +2318,16 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
 static enum ofperr
 ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
                                  struct ofpbuf *b, bool aggregate,
-                                 const struct tun_table *tun_table)
+                                 const struct tun_table *tun_table,
+                                 const struct vl_mff_map *vl_mff_map)
 {
     const struct nx_flow_stats_request *nfsr;
     enum ofperr error;
 
     nfsr = ofpbuf_pull(b, sizeof *nfsr);
     error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match,
-                          &fsr->cookie, &fsr->cookie_mask, tun_table);
+                          &fsr->cookie, &fsr->cookie_mask, false, tun_table,
+                          vl_mff_map);
     if (error) {
         return error;
     }
@@ -2589,7 +2612,7 @@ ofputil_pull_queue_get_config_reply10(struct ofpbuf *msg,
 
         hdr = ofpbuf_at_assert(msg, 0, sizeof *hdr);
         prop_len = ntohs(hdr->len);
-        if (prop_len < sizeof *hdr || prop_len > msg->size || prop_len % 8) {
+        if (prop_len < sizeof *hdr || prop_len > len || prop_len % 8) {
             return OFPERR_OFPBRC_BAD_LEN;
         }
 
@@ -2714,11 +2737,16 @@ ofputil_pull_queue_get_config_reply(struct ofpbuf *msg,
 
 /* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
  * request 'oh', into an abstract flow_stats_request in 'fsr'.  Returns 0 if
- * successful, otherwise an OpenFlow error code. */
+ * successful, otherwise an OpenFlow error code.
+ *
+ * 'vl_mff_map' is an optional parameter that is used to validate the length
+ * of variable length mf_fields in 'match'. If it is not provided, the
+ * default mf_fields with maximum length will be used. */
 enum ofperr
 ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
                                   const struct ofp_header *oh,
-                                  const struct tun_table *tun_table)
+                                  const struct tun_table *tun_table,
+                                  const struct vl_mff_map *vl_mff_map)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     enum ofpraw raw = ofpraw_pull_assert(&b);
@@ -2730,16 +2758,20 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
         return ofputil_decode_ofpst10_flow_request(fsr, b.data, true);
 
     case OFPRAW_OFPST11_FLOW_REQUEST:
-        return ofputil_decode_ofpst11_flow_request(fsr, &b, false, tun_table);
+        return ofputil_decode_ofpst11_flow_request(fsr, &b, false, tun_table,
+                                                   vl_mff_map);
 
     case OFPRAW_OFPST11_AGGREGATE_REQUEST:
-        return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table);
+        return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table,
+                                                   vl_mff_map);
 
     case OFPRAW_NXST_FLOW_REQUEST:
-        return ofputil_decode_nxst_flow_request(fsr, &b, false, tun_table);
+        return ofputil_decode_nxst_flow_request(fsr, &b, false, tun_table,
+                                                vl_mff_map);
 
     case OFPRAW_NXST_AGGREGATE_REQUEST:
-        return ofputil_decode_nxst_flow_request(fsr, &b, true, tun_table);
+        return ofputil_decode_nxst_flow_request(fsr, &b, true, tun_table,
+                                                vl_mff_map);
 
     default:
         /* Hey, the caller lied. */
@@ -2883,7 +2915,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
-        if (ofputil_pull_ofp11_match(msg, NULL, &fs->match,
+        if (ofputil_pull_ofp11_match(msg, NULL, NULL, &fs->match,
                                      &padded_match_len)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
             return EINVAL;
@@ -2966,7 +2998,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
                          "claims invalid length %"PRIuSIZE, match_len, length);
             return EINVAL;
         }
-        if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL, NULL)) {
+        if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL, false, NULL,
+                          NULL)) {
             return EINVAL;
         }
         instructions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
@@ -2997,7 +3030,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
     }
 
     if (ofpacts_pull_openflow_instructions(msg, instructions_len, oh->version,
-                                           NULL, ofpacts)) {
+                                           NULL, NULL, ofpacts)) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
         return EINVAL;
     }
@@ -3185,7 +3218,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
 
         ofr = ofpbuf_pull(&b, sizeof *ofr);
 
-        error = ofputil_pull_ofp11_match(&b, NULL, &fr->match, NULL);
+        error = ofputil_pull_ofp11_match(&b, NULL, NULL, &fr->match, NULL);
         if (error) {
             return error;
         }
@@ -3222,7 +3255,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
 
         nfr = ofpbuf_pull(&b, sizeof *nfr);
         error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match, NULL,
-                              NULL, NULL);
+                              NULL, false, NULL, NULL);
         if (error) {
             return error;
         }
@@ -3344,6 +3377,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
 static enum ofperr
 decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
                      const struct tun_table *tun_table,
+                     const struct vl_mff_map *vl_mff_map,
                      struct ofputil_packet_in *pin,
                      size_t *total_len, uint32_t *buffer_id,
                      struct ofpbuf *continuation)
@@ -3398,7 +3432,8 @@ decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
 
         case NXPINT_METADATA:
             error = oxm_decode_match(payload.msg, ofpbuf_msgsize(&payload),
-                                     tun_table, &pin->flow_metadata);
+                                     loose, tun_table, vl_mff_map,
+                                     &pin->flow_metadata);
             break;
 
         case NXPINT_USERDATA:
@@ -3452,10 +3487,15 @@ decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
  * it separately from the original OpenFlow message.  This is also true for
  * 'pin->userdata' (which could also end up NULL if there is no userdata).
  *
+ * 'vl_mff_map' is an optional parameter that is used to validate the length
+ * of variable length mf_fields in 'match'. If it is not provided, the
+ * default mf_fields with maximum length will be used.
+ *
  * Returns 0 if successful, otherwise an OpenFlow error code. */
 enum ofperr
 ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
                          const struct tun_table *tun_table,
+                         const struct vl_mff_map *vl_mff_map,
                          struct ofputil_packet_in *pin,
                          size_t *total_lenp, uint32_t *buffer_idp,
                          struct ofpbuf *continuation)
@@ -3476,7 +3516,7 @@ ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
         const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN
                                   ? ofpbuf_pull(&b, sizeof *cookie)
                                   : NULL);
-        enum ofperr error = oxm_pull_match_loose(&b, tun_table,
+        enum ofperr error = oxm_pull_match_loose(&b, false, tun_table,
                                                  &pin->flow_metadata);
         if (error) {
             return error;
@@ -3536,7 +3576,8 @@ ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
 
         npi = ofpbuf_pull(&b, sizeof *npi);
         error = nx_pull_match_loose(&b, ntohs(npi->match_len),
-                                    &pin->flow_metadata, NULL, NULL, NULL);
+                                    &pin->flow_metadata, NULL, NULL, false,
+                                    NULL);
         if (error) {
             return error;
         }
@@ -3555,9 +3596,9 @@ ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
         pin->packet = b.data;
         pin->packet_len = b.size;
     } else if (raw == OFPRAW_NXT_PACKET_IN2 || raw == OFPRAW_NXT_RESUME) {
-        enum ofperr error = decode_nx_packet_in2(oh, loose, tun_table, pin,
-                                                 &total_len, &buffer_id,
-                                                 continuation);
+        enum ofperr error = decode_nx_packet_in2(oh, loose, tun_table,
+                                                 vl_mff_map, pin, &total_len,
+                                                 &buffer_id, continuation);
         if (error) {
             return error;
         }
@@ -4018,7 +4059,7 @@ parse_actions_property(struct ofpbuf *property, enum ofp_version version,
     }
 
     return ofpacts_pull_openflow_actions(property, property->size,
-                                         version, NULL, ofpacts);
+                                         version, NULL, NULL, ofpacts);
 }
 
 /* This is like ofputil_decode_packet_in(), except that it decodes the
@@ -4026,11 +4067,16 @@ parse_actions_property(struct ofpbuf *property, enum ofp_version version,
  * opaque to any process other than ovs-vswitchd, so this function should not
  * be used outside ovs-vswitchd.
  *
+ * 'vl_mff_map' is an optional parameter that is used to validate the length
+ * of variable length mf_fields in 'match'. If it is not provided, the
+ * default mf_fields with maximum length will be used.
+ *
  * When successful, 'pin' contains some dynamically allocated data.  Call
  * ofputil_packet_in_private_destroy() to free this data. */
 enum ofperr
 ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose,
                                  const struct tun_table *tun_table,
+                                 const struct vl_mff_map *vl_mff_map,
                                  struct ofputil_packet_in_private *pin,
                                  size_t *total_len, uint32_t *buffer_id)
 {
@@ -4038,8 +4084,9 @@ ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose,
 
     struct ofpbuf continuation;
     enum ofperr error;
-    error = ofputil_decode_packet_in(oh, loose, tun_table, &pin->public,
-                                     total_len, buffer_id, &continuation);
+    error = ofputil_decode_packet_in(oh, loose, tun_table, vl_mff_map,
+                                     &pin->public, total_len, buffer_id,
+                                     &continuation);
     if (error) {
         return error;
     }
@@ -4154,24 +4201,49 @@ ofputil_packet_in_private_destroy(struct ofputil_packet_in_private *pin)
 enum ofperr
 ofputil_decode_packet_out(struct ofputil_packet_out *po,
                           const struct ofp_header *oh,
+                          const struct tun_table *tun_table,
                           struct ofpbuf *ofpacts)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     enum ofpraw raw = ofpraw_pull_assert(&b);
 
     ofpbuf_clear(ofpacts);
-    if (raw == OFPRAW_OFPT11_PACKET_OUT) {
+    match_init_catchall(&po->flow_metadata);
+    if (raw == OFPRAW_OFPT15_PACKET_OUT) {
         enum ofperr error;
+        const struct ofp15_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+        po->buffer_id = ntohl(opo->buffer_id);
+        error = oxm_pull_match_loose(&b, true, tun_table, &po->flow_metadata);
+        if (error) {
+            return error;
+        }
+
+        if (!po->flow_metadata.wc.masks.in_port.ofp_port) {
+            return OFPERR_OFPBRC_BAD_PORT;
+        }
+
+        error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
+                                              oh->version, NULL, NULL,
+                                              ofpacts);
+        if (error) {
+            return error;
+        }
+    } else if (raw == OFPRAW_OFPT11_PACKET_OUT) {
+        enum ofperr error;
+        ofp_port_t in_port;
         const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
 
         po->buffer_id = ntohl(opo->buffer_id);
-        error = ofputil_port_from_ofp11(opo->in_port, &po->in_port);
+        error = ofputil_port_from_ofp11(opo->in_port, &in_port);
         if (error) {
             return error;
         }
+        match_set_in_port(&po->flow_metadata, in_port);
 
         error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
-                                              oh->version, NULL, ofpacts);
+                                              oh->version, NULL, NULL,
+                                              ofpacts);
         if (error) {
             return error;
         }
@@ -4180,10 +4252,11 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
 
         po->buffer_id = ntohl(opo->buffer_id);
-        po->in_port = u16_to_ofp(ntohs(opo->in_port));
+        match_set_in_port(&po->flow_metadata, u16_to_ofp(ntohs(opo->in_port)));
 
         error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
-                                              oh->version, NULL, ofpacts);
+                                              oh->version, NULL, NULL,
+                                              ofpacts);
         if (error) {
             return error;
         }
@@ -4191,11 +4264,13 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         OVS_NOT_REACHED();
     }
 
-    if (ofp_to_u16(po->in_port) >= ofp_to_u16(OFPP_MAX)
-        && po->in_port != OFPP_LOCAL
-        && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) {
+    ofp_port_t in_port = po->flow_metadata.flow.in_port.ofp_port;
+    if (ofp_to_u16(in_port) >= ofp_to_u16(OFPP_MAX)
+        && in_port != OFPP_LOCAL
+        && in_port != OFPP_NONE
+        && in_port != OFPP_CONTROLLER) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx32,
-                     po->in_port);
+                     po->flow_metadata.flow.in_port.ofp_port);
         return OFPERR_OFPBRC_BAD_PORT;
     }
 
@@ -4279,7 +4354,7 @@ ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
 {
     pp->port_no = u16_to_ofp(ntohs(opp->port_no));
     pp->hw_addr = opp->hw_addr;
-    ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN);
+    ovs_strlcpy_arrays(pp->name, opp->name);
 
     pp->config = ntohl(opp->config) & OFPPC10_ALL;
     pp->state = ntohl(opp->state) & OFPPS10_ALL;
@@ -4306,7 +4381,7 @@ ofputil_decode_ofp11_port(struct ofputil_phy_port *pp,
         return error;
     }
     pp->hw_addr = op->hw_addr;
-    ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
+    ovs_strlcpy_arrays(pp->name, op->name);
 
     pp->config = ntohl(op->config) & OFPPC11_ALL;
     pp->state = ntohl(op->state) & OFPPS11_ALL;
@@ -4344,31 +4419,10 @@ parse_ofp14_port_ethernet_property(const struct ofpbuf *payload,
 }
 
 static enum ofperr
-ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+ofputil_pull_ofp14_port_properties(const void *props, size_t len,
+                                   struct ofputil_phy_port *pp)
 {
-    struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
-    if (!op) {
-        return OFPERR_OFPBRC_BAD_LEN;
-    }
-
-    size_t len = ntohs(op->length);
-    if (len < sizeof *op || len - sizeof *op > msg->size) {
-        return OFPERR_OFPBRC_BAD_LEN;
-    }
-    len -= sizeof *op;
-
-    enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
-    if (error) {
-        return error;
-    }
-    pp->hw_addr = op->hw_addr;
-    ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
-
-    pp->config = ntohl(op->config) & OFPPC11_ALL;
-    pp->state = ntohl(op->state) & OFPPS11_ALL;
-
-    struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
-                                                        len);
+    struct ofpbuf properties = ofpbuf_const_initializer(props, len);
     while (properties.size > 0) {
         struct ofpbuf payload;
         enum ofperr error;
@@ -4397,6 +4451,65 @@ ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
     return 0;
 }
 
+static enum ofperr
+ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+{
+    const struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
+    if (!op) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    size_t len = ntohs(op->length);
+    if (len < sizeof *op || len - sizeof *op > msg->size) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    len -= sizeof *op;
+
+    enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+    if (error) {
+        return error;
+    }
+    pp->hw_addr = op->hw_addr;
+    ovs_strlcpy_arrays(pp->name, op->name);
+
+    pp->config = ntohl(op->config) & OFPPC11_ALL;
+    pp->state = ntohl(op->state) & OFPPS11_ALL;
+
+    return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
+}
+
+static enum ofperr
+ofputil_pull_ofp16_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+{
+    const struct ofp16_port *op = ofpbuf_try_pull(msg, sizeof *op);
+    if (!op) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    size_t len = ntohs(op->length);
+    if (len < sizeof *op || len - sizeof *op > msg->size) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    len -= sizeof *op;
+
+    enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+    if (error) {
+        return error;
+    }
+    if (op->hw_addr_type & htons(OFPPHAT16_EUI48)) {
+        pp->hw_addr = op->hw_addr;
+    }
+    if (op->hw_addr_type & htons(OFPPHAT16_EUI64)) {
+        pp->hw_addr64 = op->hw_addr64;
+    }
+    ovs_strlcpy_arrays(pp->name, op->name);
+
+    pp->config = ntohl(op->config) & OFPPC11_ALL;
+    pp->state = ntohl(op->state) & OFPPS11_ALL;
+
+    return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
+}
+
 static void
 ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
                               struct ofp10_phy_port *opp)
@@ -4405,7 +4518,7 @@ ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
 
     opp->port_no = htons(ofp_to_u16(pp->port_no));
     opp->hw_addr = pp->hw_addr;
-    ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN);
+    ovs_strlcpy_arrays(opp->name, pp->name);
 
     opp->config = htonl(pp->config & OFPPC10_ALL);
     opp->state = htonl(pp->state & OFPPS10_ALL);
@@ -4424,7 +4537,7 @@ ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
 
     op->port_no = ofputil_port_to_ofp11(pp->port_no);
     op->hw_addr = pp->hw_addr;
-    ovs_strlcpy(op->name, pp->name, OFP_MAX_PORT_NAME_LEN);
+    ovs_strlcpy_arrays(op->name, pp->name);
 
     op->config = htonl(pp->config & OFPPC11_ALL);
     op->state = htonl(pp->state & OFPPS11_ALL);
@@ -4439,8 +4552,20 @@ ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
 }
 
 static void
-ofputil_put_ofp14_port(const struct ofputil_phy_port *pp,
-                       struct ofpbuf *b)
+ofputil_encode_ofp14_port_ethernet_prop(
+    const struct ofputil_phy_port *pp,
+    struct ofp14_port_desc_prop_ethernet *eth)
+{
+    eth->curr = netdev_port_features_to_ofp11(pp->curr);
+    eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
+    eth->supported = netdev_port_features_to_ofp11(pp->supported);
+    eth->peer = netdev_port_features_to_ofp11(pp->peer);
+    eth->curr_speed = htonl(pp->curr_speed);
+    eth->max_speed = htonl(pp->max_speed);
+}
+
+static void
+ofputil_put_ofp14_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
 {
     struct ofp14_port *op;
     struct ofp14_port_desc_prop_ethernet *eth;
@@ -4451,17 +4576,39 @@ ofputil_put_ofp14_port(const struct ofputil_phy_port *pp,
     op->port_no = ofputil_port_to_ofp11(pp->port_no);
     op->length = htons(sizeof *op + sizeof *eth);
     op->hw_addr = pp->hw_addr;
-    ovs_strlcpy(op->name, pp->name, sizeof op->name);
+    ovs_strlcpy_arrays(op->name, pp->name);
     op->config = htonl(pp->config & OFPPC11_ALL);
     op->state = htonl(pp->state & OFPPS11_ALL);
 
     eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
-    eth->curr = netdev_port_features_to_ofp11(pp->curr);
-    eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
-    eth->supported = netdev_port_features_to_ofp11(pp->supported);
-    eth->peer = netdev_port_features_to_ofp11(pp->peer);
-    eth->curr_speed = htonl(pp->curr_speed);
-    eth->max_speed = htonl(pp->max_speed);
+    ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
+}
+
+static void
+ofputil_put_ofp16_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
+{
+    struct ofp16_port *op;
+    struct ofp14_port_desc_prop_ethernet *eth;
+
+    ofpbuf_prealloc_tailroom(b, sizeof *op + sizeof *eth);
+
+    op = ofpbuf_put_zeros(b, sizeof *op);
+    op->port_no = ofputil_port_to_ofp11(pp->port_no);
+    op->length = htons(sizeof *op + sizeof *eth);
+    if (!eth_addr_is_zero(pp->hw_addr)) {
+        op->hw_addr_type |= htons(OFPPHAT16_EUI48);
+        op->hw_addr = pp->hw_addr;
+    }
+    if (!eth_addr64_is_zero(pp->hw_addr64)) {
+        op->hw_addr_type |= htons(OFPPHAT16_EUI64);
+        op->hw_addr64 = pp->hw_addr64;
+    }
+    ovs_strlcpy_arrays(op->name, pp->name);
+    op->config = htonl(pp->config & OFPPC11_ALL);
+    op->state = htonl(pp->state & OFPPS11_ALL);
+
+    eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
+    ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
 }
 
 static void
@@ -4485,9 +4632,11 @@ ofputil_put_phy_port(enum ofp_version ofp_version,
 
     case OFP14_VERSION:
     case OFP15_VERSION:
-    case OFP16_VERSION:
         ofputil_put_ofp14_port(pp, b);
         break;
+    case OFP16_VERSION:
+        ofputil_put_ofp16_port(pp, b);
+        break;
 
     default:
         OVS_NOT_REACHED();
@@ -4886,10 +5035,13 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
 
     case OFP14_VERSION:
     case OFP15_VERSION:
-    case OFP16_VERSION:
         raw = OFPRAW_OFPT14_PORT_STATUS;
         break;
 
+    case OFP16_VERSION:
+        raw = OFPRAW_OFPT16_PORT_STATUS;
+        break;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -4918,80 +5070,134 @@ parse_port_mod_ethernet_property(struct ofpbuf *property,
     return error;
 }
 
-/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
- * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
-enum ofperr
-ofputil_decode_port_mod(const struct ofp_header *oh,
-                        struct ofputil_port_mod *pm, bool loose)
+static enum ofperr
+ofputil_decode_ofp10_port_mod(const struct ofp10_port_mod *opm,
+                              struct ofputil_port_mod *pm)
+{
+    pm->port_no = u16_to_ofp(ntohs(opm->port_no));
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC10_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
+    pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
+    return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofp11_port_mod(const struct ofp11_port_mod *opm,
+                              struct ofputil_port_mod *pm)
 {
-    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
-    enum ofpraw raw = ofpraw_pull_assert(&b);
-    if (raw == OFPRAW_OFPT10_PORT_MOD) {
-        const struct ofp10_port_mod *opm = b.data;
+    enum ofperr error;
 
-        pm->port_no = u16_to_ofp(ntohs(opm->port_no));
-        pm->hw_addr = opm->hw_addr;
-        pm->config = ntohl(opm->config) & OFPPC10_ALL;
-        pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
-        pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
-    } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
-        const struct ofp11_port_mod *opm = b.data;
+    error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+    if (error) {
+        return error;
+    }
+
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC11_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+    pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
+
+    return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofp14_port_mod_properties(struct ofpbuf *b, bool loose,
+                                         struct ofputil_port_mod *pm)
+{
+    while (b->size > 0) {
+        struct ofpbuf property;
         enum ofperr error;
+        uint64_t type;
 
-        error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+        error = ofpprop_pull(b, &property, &type);
         if (error) {
             return error;
         }
 
-        pm->hw_addr = opm->hw_addr;
-        pm->config = ntohl(opm->config) & OFPPC11_ALL;
-        pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
-        pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
-    } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
-        const struct ofp14_port_mod *opm = ofpbuf_pull(&b, sizeof *opm);
-        enum ofperr error;
+        switch (type) {
+        case OFPPMPT14_ETHERNET:
+            error = parse_port_mod_ethernet_property(&property, pm);
+            break;
 
-        memset(pm, 0, sizeof *pm);
+        default:
+            error = OFPPROP_UNKNOWN(loose, "port_mod", type);
+            break;
+        }
 
-        error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
         if (error) {
             return error;
         }
+    }
+    return 0;
+}
 
-        pm->hw_addr = opm->hw_addr;
-        pm->config = ntohl(opm->config) & OFPPC11_ALL;
-        pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+static enum ofperr
+ofputil_decode_ofp14_port_mod(struct ofpbuf *b, bool loose,
+                              struct ofputil_port_mod *pm)
+{
+    const struct ofp14_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
+    enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+    if (error) {
+        return error;
+    }
 
-        while (b.size > 0) {
-            struct ofpbuf property;
-            enum ofperr error;
-            uint64_t type;
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC11_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
 
-            error = ofpprop_pull(&b, &property, &type);
-            if (error) {
-                return error;
-            }
+    return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
+}
 
-            switch (type) {
-            case OFPPMPT14_ETHERNET:
-                error = parse_port_mod_ethernet_property(&property, pm);
-                break;
+static enum ofperr
+ofputil_decode_ofp16_port_mod(struct ofpbuf *b, bool loose,
+                              struct ofputil_port_mod *pm)
+{
+    const struct ofp16_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
+    enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+    if (error) {
+        return error;
+    }
 
-            default:
-                error = OFPPROP_UNKNOWN(loose, "port_mod", type);
-                break;
-            }
+    if (opm->hw_addr_type & htons(OFPPHAT16_EUI48)) {
+        pm->hw_addr = opm->hw_addr;
+    }
+    if (opm->hw_addr_type & htons(OFPPHAT16_EUI64)) {
+        pm->hw_addr64 = opm->hw_addr64;
+    }
+    pm->hw_addr = opm->hw_addr;
+    pm->config = ntohl(opm->config) & OFPPC11_ALL;
+    pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
 
-            if (error) {
-                return error;
-            }
-        }
+    return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
+}
+
+/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
+ * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_port_mod(const struct ofp_header *oh,
+                        struct ofputil_port_mod *pm, bool loose)
+{
+    memset(pm, 0, sizeof *pm);
+
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
+
+    enum ofperr error;
+    if (raw == OFPRAW_OFPT10_PORT_MOD) {
+        error = ofputil_decode_ofp10_port_mod(b.data, pm);
+    } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
+        error = ofputil_decode_ofp11_port_mod(b.data, pm);
+    } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
+        error = ofputil_decode_ofp14_port_mod(&b, loose, pm);
+    } else if (raw == OFPRAW_OFPT16_PORT_MOD) {
+        error = ofputil_decode_ofp16_port_mod(&b, loose, pm);
     } else {
-        return OFPERR_OFPBRC_BAD_TYPE;
+        error = OFPERR_OFPBRC_BAD_TYPE;
     }
 
     pm->config &= pm->mask;
-    return 0;
+    return error;
 }
 
 /* Converts the abstract form of a "port mod" message in '*pm' into an OpenFlow
@@ -5033,8 +5239,7 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         break;
     }
     case OFP14_VERSION:
-    case OFP15_VERSION:
-    case OFP16_VERSION: {
+    case OFP15_VERSION: {
         struct ofp14_port_mod *opm;
 
         b = ofpraw_alloc(OFPRAW_OFPT14_PORT_MOD, ofp_version, 0);
@@ -5050,6 +5255,29 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         }
         break;
     }
+    case OFP16_VERSION: {
+        struct ofp16_port_mod *opm;
+
+        b = ofpraw_alloc(OFPRAW_OFPT16_PORT_MOD, ofp_version, 0);
+        opm = ofpbuf_put_zeros(b, sizeof *opm);
+        opm->port_no = ofputil_port_to_ofp11(pm->port_no);
+        if (!eth_addr_is_zero(pm->hw_addr)) {
+            opm->hw_addr_type |= htons(OFPPHAT16_EUI48);
+            opm->hw_addr = pm->hw_addr;
+        }
+        if (!eth_addr64_is_zero(pm->hw_addr64)) {
+            opm->hw_addr_type |= htons(OFPPHAT16_EUI64);
+            opm->hw_addr64 = pm->hw_addr64;
+        }
+        opm->config = htonl(pm->config & OFPPC11_ALL);
+        opm->mask = htonl(pm->mask & OFPPC11_ALL);
+
+        if (pm->advertise) {
+            ofpprop_put_be32(b, OFPPMPT14_ETHERNET,
+                             netdev_port_features_to_ofp11(pm->advertise));
+        }
+        break;
+    }
     default:
         OVS_NOT_REACHED();
     }
@@ -5223,7 +5451,7 @@ ofputil_decode_table_features(struct ofpbuf *msg,
         return OFPERR_OFPTFFC_BAD_TABLE;
     }
 
-    ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
+    ovs_strlcpy_arrays(tf->name, otf->name);
     tf->metadata_match = otf->metadata_match;
     tf->metadata_write = otf->metadata_write;
     tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT;
@@ -5434,7 +5662,7 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
 
     otf = ofpbuf_put_zeros(reply, sizeof *otf);
     otf->table_id = tf->table_id;
-    ovs_strlcpy(otf->name, tf->name, sizeof otf->name);
+    ovs_strlcpy_arrays(otf->name, tf->name);
     otf->metadata_match = tf->metadata_match;
     otf->metadata_write = tf->metadata_write;
     if (version >= OFP14_VERSION) {
@@ -6211,7 +6439,7 @@ ofputil_put_ofp10_table_stats(const struct ofputil_table_stats *stats,
 
     out = ofpbuf_put_zeros(buf, sizeof *out);
     out->table_id = features->table_id;
-    ovs_strlcpy(out->name, features->name, sizeof out->name);
+    ovs_strlcpy_arrays(out->name, features->name);
     out->wildcards = mf_bitmap_to_of10(&wc);
     out->max_entries = htonl(features->max_entries);
     out->active_count = htonl(stats->active_count);
@@ -6282,7 +6510,7 @@ ofputil_put_ofp11_table_stats(const struct ofputil_table_stats *stats,
 
     out = ofpbuf_put_zeros(buf, sizeof *out);
     out->table_id = features->table_id;
-    ovs_strlcpy(out->name, features->name, sizeof out->name);
+    ovs_strlcpy_arrays(out->name, features->name);
     out->wildcards = mf_bitmap_to_of11(&wc);
     out->match = mf_bitmap_to_of11(&features->match);
     out->instructions = ovsinst_bitmap_to_openflow(
@@ -6307,7 +6535,7 @@ ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats,
 
     out = ofpbuf_put_zeros(buf, sizeof *out);
     out->table_id = features->table_id;
-    ovs_strlcpy(out->name, features->name, sizeof out->name);
+    ovs_strlcpy_arrays(out->name, features->name);
     out->match = oxm_bitmap_from_mf_bitmap(&features->match, OFP12_VERSION);
     out->wildcards = oxm_bitmap_from_mf_bitmap(&features->wildcard,
                                              OFP12_VERSION);
@@ -6399,7 +6627,7 @@ ofputil_decode_ofp10_table_stats(struct ofpbuf *msg,
     }
 
     features->table_id = ots->table_id;
-    ovs_strlcpy(features->name, ots->name, sizeof features->name);
+    ovs_strlcpy_arrays(features->name, ots->name);
     features->max_entries = ntohl(ots->max_entries);
     features->match = features->wildcard = mf_bitmap_from_of10(ots->wildcards);
 
@@ -6424,7 +6652,7 @@ ofputil_decode_ofp11_table_stats(struct ofpbuf *msg,
     }
 
     features->table_id = ots->table_id;
-    ovs_strlcpy(features->name, ots->name, sizeof features->name);
+    ovs_strlcpy_arrays(features->name, ots->name);
     features->max_entries = ntohl(ots->max_entries);
     features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
         ots->instructions, OFP11_VERSION);
@@ -6460,7 +6688,7 @@ ofputil_decode_ofp12_table_stats(struct ofpbuf *msg,
     }
 
     features->table_id = ots->table_id;
-    ovs_strlcpy(features->name, ots->name, sizeof features->name);
+    ovs_strlcpy_arrays(features->name, ots->name);
     features->metadata_match = ots->metadata_match;
     features->metadata_write = ots->metadata_write;
     features->miss_config = ofputil_decode_table_miss(ots->config,
@@ -6609,7 +6837,7 @@ ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq,
     rq->table_id = nfmr->table_id;
 
     return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
-                         NULL, NULL);
+                         NULL, false, NULL, NULL);
 }
 
 void
@@ -6717,14 +6945,15 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update,
         update->cookie = nfuf->cookie;
         update->priority = ntohs(nfuf->priority);
 
-        error = nx_pull_match(msg, match_len, &update->match, NULL, NULL, NULL);
+        error = nx_pull_match(msg, match_len, &update->match, NULL, NULL,
+                              false, NULL, NULL);
         if (error) {
             return error;
         }
 
         actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
         error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version,
-                                              NULL, ofpacts);
+                                              NULL, NULL, ofpacts);
         if (error) {
             return error;
         }
@@ -6853,7 +7082,8 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
 
         opo = msg->msg;
         opo->buffer_id = htonl(po->buffer_id);
-        opo->in_port = htons(ofp_to_u16(po->in_port));
+        opo->in_port =htons(ofp_to_u16(
+                                po->flow_metadata.flow.in_port.ofp_port));
         opo->actions_len = htons(msg->size - actions_ofs);
         break;
     }
@@ -6861,9 +7091,7 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
     case OFP11_VERSION:
     case OFP12_VERSION:
     case OFP13_VERSION:
-    case OFP14_VERSION:
-    case OFP15_VERSION:
-    case OFP16_VERSION: {
+    case OFP14_VERSION: {
         struct ofp11_packet_out *opo;
         size_t len;
 
@@ -6873,7 +7101,26 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
                                            ofp_version);
         opo = msg->msg;
         opo->buffer_id = htonl(po->buffer_id);
-        opo->in_port = ofputil_port_to_ofp11(po->in_port);
+        opo->in_port =
+            ofputil_port_to_ofp11(po->flow_metadata.flow.in_port.ofp_port);
+        opo->actions_len = htons(len);
+        break;
+    }
+
+    case OFP15_VERSION:
+    case OFP16_VERSION: {
+        struct ofp15_packet_out *opo;
+        size_t len;
+
+        /* The final argument is just an estimate of the space required. */
+        msg = ofpraw_alloc(OFPRAW_OFPT15_PACKET_OUT, ofp_version,
+                           size + NXM_TYPICAL_LEN);
+        ofpbuf_put_zeros(msg, sizeof *opo);
+        oxm_put_match(msg, &po->flow_metadata, ofp_version);
+        len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg,
+                                           ofp_version);
+        opo = msg->msg;
+        opo->buffer_id = htonl(po->buffer_id);
         opo->actions_len = htons(len);
         break;
     }
@@ -7027,7 +7274,8 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port)
 
 /* Stores the port number represented by 's' into '*portp'.  's' may be an
  * integer or, for reserved ports, the standard OpenFlow name for the port
- * (e.g. "LOCAL").
+ * (e.g. "LOCAL").  If 'port_map' is nonnull, also accepts names in it (quoted
+ * or unquoted).
  *
  * Returns true if successful, false if 's' is not a valid OpenFlow port number
  * or name.  The caller should issue an error message in this case, because
@@ -7039,7 +7287,9 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port)
  * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit
  * range as described in include/openflow/openflow-1.1.h. */
 bool
-ofputil_port_from_string(const char *s, ofp_port_t *portp)
+ofputil_port_from_string(const char *s,
+                         const struct ofputil_port_map *port_map,
+                         ofp_port_t *portp)
 {
     unsigned int port32; /* int is at least 32 bits wide. */
 
@@ -7056,15 +7306,16 @@ ofputil_port_from_string(const char *s, ofp_port_t *portp)
                       "be translated to %u when talking to an OF1.1 or "
                       "later controller", port32, port32 + OFPP11_OFFSET);
         } else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) {
-            char name[OFP_MAX_PORT_NAME_LEN];
+            char name[OFP10_MAX_PORT_NAME_LEN];
 
-            ofputil_port_to_string(u16_to_ofp(port32), name, sizeof name);
+            ofputil_port_to_string(u16_to_ofp(port32), NULL,
+                                   name, sizeof name);
             VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated "
                            "for compatibility with OpenFlow 1.1 and later",
                            name, port32);
         } else if (port32 < ofp11_to_u32(OFPP11_MAX)) {
             VLOG_WARN("port %u is outside the supported range 0 through "
-                      "%"PRIx16" or 0x%x through 0x%"PRIx32, port32,
+                      "%x or 0x%x through 0x%"PRIx32, port32,
                       UINT16_MAX, ofp11_to_u32(OFPP11_MAX), UINT32_MAX);
             return false;
         } else {
@@ -7091,20 +7342,89 @@ ofputil_port_from_string(const char *s, ofp_port_t *portp)
                 return true;
             }
         }
+
+        ofp_port_t ofp_port = OFPP_NONE;
+        if (s[0] != '"') {
+            ofp_port = ofputil_port_map_get_number(port_map, s);
+        } else {
+            size_t length = strlen(s);
+            char *name = NULL;
+            if (length > 1
+                && s[length - 1] == '"'
+                && json_string_unescape(s + 1, length - 2, &name)) {
+                ofp_port = ofputil_port_map_get_number(port_map, name);
+            }
+            free(name);
+        }
+        if (ofp_port != OFPP_NONE) {
+            *portp = ofp_port;
+            return true;
+        }
+
         return false;
     }
 }
 
+const char *
+ofputil_port_get_reserved_name(ofp_port_t port)
+{
+    switch (port) {
+#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: return #NAME;
+        OFPUTIL_NAMED_PORTS
+#undef OFPUTIL_NAMED_PORT
+
+    default:
+        return NULL;
+    }
+}
+
+/* A port name doesn't need to be quoted if it is alphanumeric and starts with
+ * a letter. */
+static bool
+port_name_needs_quotes(const char *port_name)
+{
+    if (!isalpha((unsigned char) port_name[0])) {
+        return true;
+    }
+
+    for (const char *p = port_name + 1; *p; p++) {
+        if (!isalnum((unsigned char) *p)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void
+put_port_name(const char *port_name, struct ds *s)
+{
+    if (port_name_needs_quotes(port_name)) {
+        json_string_escape(port_name, s);
+    } else {
+        ds_put_cstr(s, port_name);
+    }
+}
+
 /* Appends to 's' a string representation of the OpenFlow port number 'port'.
  * Most ports' string representation is just the port number, but for special
  * ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */
 void
-ofputil_format_port(ofp_port_t port, struct ds *s)
+ofputil_format_port(ofp_port_t port, const struct ofputil_port_map *port_map,
+                    struct ds *s)
 {
-    char name[OFP_MAX_PORT_NAME_LEN];
+    const char *reserved_name = ofputil_port_get_reserved_name(port);
+    if (reserved_name) {
+        ds_put_cstr(s, reserved_name);
+        return;
+    }
 
-    ofputil_port_to_string(port, name, sizeof name);
-    ds_put_cstr(s, name);
+    const char *port_name = ofputil_port_map_get_name(port_map, port);
+    if (port_name) {
+        put_port_name(port_name, s);
+        return;
+    }
+
+    ds_put_format(s, "%"PRIu32, port);
 }
 
 /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
@@ -7113,22 +7433,149 @@ ofputil_format_port(ofp_port_t port, struct ds *s)
  * by name, e.g. "LOCAL". */
 void
 ofputil_port_to_string(ofp_port_t port,
-                       char namebuf[OFP_MAX_PORT_NAME_LEN], size_t bufsize)
+                       const struct ofputil_port_map *port_map,
+                       char *namebuf, size_t bufsize)
 {
-    switch (port) {
-#define OFPUTIL_NAMED_PORT(NAME)                        \
-        case OFPP_##NAME:                               \
-            ovs_strlcpy(namebuf, #NAME, bufsize);       \
-            break;
-        OFPUTIL_NAMED_PORTS
-#undef OFPUTIL_NAMED_PORT
+    const char *reserved_name = ofputil_port_get_reserved_name(port);
+    if (reserved_name) {
+        ovs_strlcpy(namebuf, reserved_name, bufsize);
+        return;
+    }
 
-    default:
-        snprintf(namebuf, bufsize, "%"PRIu32, port);
-        break;
+    const char *port_name = ofputil_port_map_get_name(port_map, port);
+    if (port_name) {
+        struct ds s = DS_EMPTY_INITIALIZER;
+        put_port_name(port_name, &s);
+        ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
+        ds_destroy(&s);
+        return;
     }
+
+    snprintf(namebuf, bufsize, "%"PRIu32, port);
 }
+\f
+/* ofputil_port_map.  */
+struct ofputil_port_map_node {
+    struct hmap_node name_node;
+    struct hmap_node number_node;
+    ofp_port_t ofp_port;        /* Port number. */
+    char *name;                 /* Port name. */
+
+    /* OpenFlow doesn't require port names to be unique, although that's the
+     * only sensible way.  However, even in Open vSwitch it's possible for two
+     * ports to appear to have the same name if their names are longer than the
+     * maximum length supported by a given version of OpenFlow.  So, we guard
+     * against duplicate names to avoid giving unexpected results in this
+     * corner case.
+     *
+     * OpenFlow does require port numbers to be unique.  We check for duplicate
+     * ports numbers just in case a switch has a bug. */
+    bool duplicate;
+};
+
+void
+ofputil_port_map_init(struct ofputil_port_map *map)
+{
+    hmap_init(&map->by_name);
+    hmap_init(&map->by_number);
+}
+
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_name(const struct ofputil_port_map *map,
+                              const char *name)
+{
+    struct ofputil_port_map_node *node;
 
+    HMAP_FOR_EACH_WITH_HASH (node, name_node, hash_string(name, 0),
+                             &map->by_name) {
+        if (!strcmp(name, node->name)) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_number(const struct ofputil_port_map *map,
+                                ofp_port_t ofp_port)
+{
+    struct ofputil_port_map_node *node;
+
+    HMAP_FOR_EACH_IN_BUCKET (node, number_node, hash_ofp_port(ofp_port),
+                             &map->by_number) {
+        if (node->ofp_port == ofp_port) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+void
+ofputil_port_map_put(struct ofputil_port_map *map,
+                     ofp_port_t ofp_port, const char *name)
+{
+    struct ofputil_port_map_node *node;
+
+    /* Look for duplicate name. */
+    node = ofputil_port_map_find_by_name(map, name);
+    if (node) {
+        if (node->ofp_port != ofp_port) {
+            node->duplicate = true;
+        }
+        return;
+    }
+
+    /* Look for duplicate number. */
+    node = ofputil_port_map_find_by_number(map, ofp_port);
+    if (node) {
+        node->duplicate = true;
+        return;
+    }
+
+    /* Add new node. */
+    node = xmalloc(sizeof *node);
+    hmap_insert(&map->by_number, &node->number_node, hash_ofp_port(ofp_port));
+    hmap_insert(&map->by_name, &node->name_node, hash_string(name, 0));
+    node->ofp_port = ofp_port;
+    node->name = xstrdup(name);
+    node->duplicate = false;
+}
+
+const char *
+ofputil_port_map_get_name(const struct ofputil_port_map *map,
+                          ofp_port_t ofp_port)
+{
+    struct ofputil_port_map_node *node
+        = map ? ofputil_port_map_find_by_number(map, ofp_port) : NULL;
+    return node && !node->duplicate ? node->name : NULL;
+}
+
+ofp_port_t
+ofputil_port_map_get_number(const struct ofputil_port_map *map,
+                            const char *name)
+{
+    struct ofputil_port_map_node *node
+        = map ? ofputil_port_map_find_by_name(map, name) : NULL;
+    return node && !node->duplicate ? node->ofp_port : OFPP_NONE;
+}
+
+void
+ofputil_port_map_destroy(struct ofputil_port_map *map)
+{
+    if (map) {
+        struct ofputil_port_map_node *node, *next;
+
+        HMAP_FOR_EACH_SAFE (node, next, name_node, &map->by_name) {
+            hmap_remove(&map->by_name, &node->name_node);
+            hmap_remove(&map->by_number, &node->number_node);
+            free(node->name);
+            free(node);
+        }
+        hmap_destroy(&map->by_name);
+        hmap_destroy(&map->by_number);
+    }
+}
+\f
 /* Stores the group id represented by 's' into '*group_idp'.  's' may be an
  * integer or, for reserved group IDs, the standard OpenFlow name for the group
  * (either "ANY" or "ALL").
@@ -7212,8 +7659,9 @@ ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
     }
     case OFP14_VERSION:
     case OFP15_VERSION:
-    case OFP16_VERSION:
         return b->size ? ofputil_pull_ofp14_port(pp, b) : EOF;
+    case OFP16_VERSION:
+        return b->size ? ofputil_pull_ofp16_port(pp, b) : EOF;
     default:
         OVS_NOT_REACHED();
     }
@@ -7303,13 +7751,15 @@ ofputil_normalize_match__(struct match *match, bool may_log)
     /* Log any changes. */
     if (!flow_wildcards_equal(&wc, &match->wc)) {
         bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
-        char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL;
+        char *pre = (log
+                     ? match_to_string(match, NULL, OFP_DEFAULT_PRIORITY)
+                     : NULL);
 
         match->wc = wc;
         match_zero_wildcarded_fields(match);
 
         if (log) {
-            char *post = match_to_string(match, OFP_DEFAULT_PRIORITY);
+            char *post = match_to_string(match, NULL, OFP_DEFAULT_PRIORITY);
             VLOG_INFO("normalization changed ofp_match, details:");
             VLOG_INFO(" pre: %s", pre);
             VLOG_INFO("post: %s", post);
@@ -8708,7 +9158,7 @@ ofputil_pull_ofp11_buckets(struct ofpbuf *msg, size_t buckets_length,
 
         ofpbuf_init(&ofpacts, 0);
         error = ofpacts_pull_openflow_actions(msg, ob_len - sizeof *ob,
-                                              version, NULL, &ofpacts);
+                                              version, NULL, NULL, &ofpacts);
         if (error) {
             ofpbuf_uninit(&ofpacts);
             ofputil_bucket_list_destroy(buckets);
@@ -8782,7 +9232,7 @@ ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
         buckets_length -= ob_len;
 
         err = ofpacts_pull_openflow_actions(msg, actions_len, version,
-                                            NULL, &ofpacts);
+                                            NULL, NULL, &ofpacts);
         if (err) {
             goto err;
         }
@@ -10508,8 +10958,6 @@ struct ofpbuf *
 ofputil_encode_get_async_reply(const struct ofp_header *oh,
                                const struct ofputil_async_cfg *ac)
 {
-    struct ofpbuf *buf;
-
     enum ofpraw raw = (oh->version < OFP14_VERSION
                        ? OFPRAW_OFPT13_GET_ASYNC_REPLY
                        : OFPRAW_OFPT14_GET_ASYNC_REPLY);
@@ -10518,8 +10966,6 @@ ofputil_encode_get_async_reply(const struct ofp_header *oh,
                                raw == OFPRAW_OFPT14_GET_ASYNC_REPLY,
                                oh->version, UINT32_MAX);
     return reply;
-
-    return buf;
 }
 
 /* Encodes and returns a message, in a format appropriate for OpenFlow version