[ovs-dev] [PATCH v2] ofp-actions: Fix variable length meta-flow field usage in flow actions.

Yi-Hung Wei yihung.wei at gmail.com
Fri Jan 20 23:12:21 UTC 2017


Previously, if a flow action that involves a tunnel metadata meta-flow
field is dumped from vswitchd, the replied field length in the OXM header
is filled with the maximum possible field length, instead of the length
configured in the tunnel TLV mapping table. To solve this issue, this patch
introduces the following changes.

In order to maintain the correct length of variable length mf_fields (i.e.
tun_metadata), this patch creates a per-switch based map (struct vl_mff_map)
that hosts the variable length mf_fields. This map is updated when a
controller adds/deletes tlv-mapping entries to/from a switch. Although the
per-swtch based vl_mff_map only hosts tun_metadata for now, it is able to
support new variable length mf_fields in the future.

With this commit, when a switch decodes a flow action with mf_field, the switch
firstly looks up the global mf_fields map to identify the mf_field type. For
the variable length mf_fields, the switch uses the vl_mff_map to get the
configured mf_field entries. By lookig up vl_mff_map, the switch can check
if the added flow action access beyond the configured size of a variable
length mf_field, and the switch reports an ofperr if the controller adds a flow
with unmapped variable length mf_field. Later on, when a controller request
flows from the switch, with the per-switch based mf_fields, the switch will
encode the OXM header with correct length for variable length mf_fields.

To use the vl_mff_map for decoding flow actions, extract-ofp-actions is
updated to pass the vl_mff_map to the required action decoding functions.
Also, a new error code is introduced to identify a flow with an invalid
variable length mf_field. Moreover, a testcase is added to prevent future
regressions.

VMWare-BZ: #1768370
Reported-by: Harold Lim <haroldl at vmware.com>
Suggested-by: Joe Stringer <joe at ovn.org>
Suggested-by: Jarno Rajahalme <jarno at ovn.org>
Signed-off-by: Yi-Hung Wei <yihung.wei at gmail.com>
---
 build-aux/extract-ofp-actions     |  46 +++++---
 build-aux/extract-ofp-fields      |   5 +-
 include/openvswitch/meta-flow.h   |  24 ++++
 include/openvswitch/ofp-actions.h |  11 +-
 include/openvswitch/ofp-errors.h  |   4 +
 include/openvswitch/ofp-util.h    |   1 +
 lib/meta-flow.c                   | 116 +++++++++++++++++++
 lib/nx-match.c                    |  89 +++++++++++----
 lib/nx-match.h                    |  17 ++-
 lib/ofp-actions.c                 | 230 +++++++++++++++++++++++++-------------
 lib/ofp-print.c                   |   2 +-
 lib/ofp-util.c                    |  21 ++--
 lib/tun-metadata.c                |   2 +-
 ofproto/ofproto-provider.h        |   5 +
 ofproto/ofproto.c                 |  20 +++-
 ovn/controller/pinctrl.c          |   9 +-
 tests/ofproto.at                  | 125 +++++++++++++++++++++
 utilities/ovs-ofctl.c             |   2 +-
 18 files changed, 579 insertions(+), 150 deletions(-)

diff --git a/build-aux/extract-ofp-actions b/build-aux/extract-ofp-actions
index 3a72349..184447b 100755
--- a/build-aux/extract-ofp-actions
+++ b/build-aux/extract-ofp-actions
@@ -123,7 +123,13 @@ def extract_ofp_actions(fn, definitions):
             fatal("unexpected syntax between actions")
 
         dsts = m.group(1)
-        argtype = m.group(2).strip().replace('.', '', 1)
+        argtypes = m.group(2).strip().replace('.', '', 1)
+
+        if 'VLMFF' in argtypes:
+            arg_vl_mff_map = True
+        else:
+            arg_vl_mff_map = False
+        argtype = argtypes.replace('VLMFF', '', 1).rstrip()
 
         get_line()
         m = re.match(r'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line)
@@ -210,17 +216,18 @@ def extract_ofp_actions(fn, definitions):
                     else:
                         max_length = min_length
 
-                    info = {"enum": enum,                 # 0
-                            "deprecation": deprecation,   # 1
-                            "file_name": file_name,       # 2
-                            "line_number": line_number,   # 3
-                            "min_length": min_length,     # 4
-                            "max_length": max_length,     # 5
-                            "arg_ofs": arg_ofs,           # 6
-                            "arg_len": arg_len,           # 7
-                            "base_argtype": base_argtype, # 8
-                            "version": version,           # 9
-                            "type": type_}                # 10
+                    info = {"enum": enum,                     # 0
+                            "deprecation": deprecation,       # 1
+                            "file_name": file_name,           # 2
+                            "line_number": line_number,       # 3
+                            "min_length": min_length,         # 4
+                            "max_length": max_length,         # 5
+                            "arg_ofs": arg_ofs,               # 6
+                            "arg_len": arg_len,               # 7
+                            "base_argtype": base_argtype,     # 8
+                            "arg_vl_mff_map": arg_vl_mff_map, # 9
+                            "version": version,               # 10
+                            "type": type_}                    # 11
                     domain[vendor][type_][version] = info
 
                     enums.setdefault(enum, [])
@@ -314,7 +321,8 @@ def extract_ofp_actions(fn, definitions):
         print """\
 static enum ofperr
 ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
-              enum ofp_version version, uint64_t arg, struct ofpbuf *out)
+              enum ofp_version version, uint64_t arg,
+              const struct vl_mff_map *vl_mff_map, struct ofpbuf *out)
 {
     switch (raw) {\
 """
@@ -322,6 +330,7 @@ ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
             enum = versions[0]["enum"]
             print "    case %s:" % enum
             base_argtype = versions[0]["base_argtype"]
+            arg_vl_mff_map = versions[0]["arg_vl_mff_map"]
             if base_argtype == 'void':
                 print "        return decode_%s(out);" % enum
             else:
@@ -333,7 +342,10 @@ ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
                         arg = "%s(arg)" % hton
                     else:
                         arg = "arg"
-                print "        return decode_%s(%s, version, out);" % (enum, arg)
+                if arg_vl_mff_map:
+                    print "        return decode_%s(%s, version, vl_mff_map, out);" % (enum, arg)
+                else:
+                    print "        return decode_%s(%s, version, out);" % (enum, arg)
             print
         print """\
     default:
@@ -346,11 +358,14 @@ ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
             enum = versions[0]["enum"]
             prototype = "static enum ofperr decode_%s(" % enum
             base_argtype = versions[0]["base_argtype"]
+            arg_vl_mff_map = versions[0]["arg_vl_mff_map"]
             if base_argtype != 'void':
                 if base_argtype.startswith('struct'):
                     prototype += "const %s *, enum ofp_version, " % base_argtype
                 else:
                     prototype += "%s, enum ofp_version, " % base_argtype
+                if arg_vl_mff_map:
+                    prototype += 'const struct vl_mff_map *, '
             prototype += "struct ofpbuf *);"
             print prototype
 
@@ -358,7 +373,8 @@ ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
 static enum ofperr ofpact_decode(const struct ofp_action_header *,
                                  enum ofp_raw_action_type raw,
                                  enum ofp_version version,
-                                 uint64_t arg, struct ofpbuf *out);
+                                 uint64_t arg, const struct vl_mff_map *vl_mff_map,
+                                 struct ofpbuf *out);
 """
 
 if __name__ == '__main__':
diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields
index 8d43e4b..2bc75f7 100755
--- a/build-aux/extract-ofp-fields
+++ b/build-aux/extract-ofp-fields
@@ -322,7 +322,7 @@ def make_meta_flow(fields):
             rw = 'true'
         else:
             rw = 'false'
-        output += ["    %s, %s, %s, %s,"
+        output += ["    %s, %s, %s, %s, false,"
                    % (f['mask'], f['string'], f['prereqs'], rw)]
 
         oxm = f['OXM']
@@ -374,7 +374,8 @@ def make_meta_flow(fields):
         else:
             output += ["    -1, /* not usable for prefix lookup */"]
 
-        output += ["},"]
+        output += ["    {OVSRCU_INITIALIZER(NULL)},},"]
+
     return output
 
 
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index 9f123ef..ad6f2ae 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -23,13 +23,16 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <netinet/ip6.h>
+#include "cmap.h"
 #include "openvswitch/flow.h"
 #include "openvswitch/ofp-errors.h"
 #include "openvswitch/packets.h"
+#include "openvswitch/thread.h"
 #include "openvswitch/util.h"
 
 struct ds;
 struct match;
+struct ofputil_tlv_table_mod;
 
 /* Open vSwitch fields
  * ===================
@@ -1944,6 +1947,7 @@ struct mf_field {
     enum mf_string string;
     enum mf_prereqs prereqs;
     bool writable;              /* May be written by actions? */
+    bool mapped;                /* Variable length mf_field is mapped. */
 
     /* Usable protocols.
      *
@@ -1963,6 +1967,9 @@ struct mf_field {
 
     int flow_be32ofs;  /* Field's be32 offset in "struct flow", if prefix tree
                         * lookup is supported for the field, or -1. */
+
+    /* For variable length mf_fields only. In ofproto->vl_mff_map->cmap. */
+    struct cmap_node cmap_node;
 };
 
 /* The representation of a field's value. */
@@ -2039,6 +2046,14 @@ union mf_subvalue {
 };
 BUILD_ASSERT_DECL(sizeof(union mf_value) == sizeof (union mf_subvalue));
 
+/* Variable length mf_fields mapping map. This is a single writer,
+ * multiple-reader hash table that a writer must hold the following mutex
+ * to access this map. */
+struct vl_mff_map {
+    struct cmap cmap;       /* Contains 'struct mf_field' */
+    struct ovs_mutex mutex;
+};
+
 bool mf_subvalue_intersect(const union mf_subvalue *a_value,
                            const union mf_subvalue *a_mask,
                            const union mf_subvalue *b_value,
@@ -2166,4 +2181,13 @@ void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s);
 void field_array_set(enum mf_field_id id, const union mf_value *,
                      struct field_array *);
 
+/* Variable length fields. */
+void mf_vl_mff_map_clear(struct vl_mff_map *vl_mff_map)
+    OVS_REQUIRES(vl_mff_map->mutex);
+enum ofperr mf_vl_mff_map_mod_from_tun_metadata(
+    struct vl_mff_map *vl_mff_map, const struct ofputil_tlv_table_mod *)
+    OVS_REQUIRES(vl_mff_map->mutex);
+const struct mf_field * mf_get_vl_mff(const struct mf_field *,
+                                      const struct vl_mff_map *);
+bool mf_vl_mff_not_mapped(const struct mf_field *, const struct vl_mff_map *);
 #endif /* meta-flow.h */
diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index 8ca787a..a3783c2 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -943,11 +943,14 @@ struct ofpact_unroll_xlate {
 enum ofperr ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
                                           unsigned int actions_len,
                                           enum ofp_version version,
+                                          const struct vl_mff_map *,
                                           struct ofpbuf *ofpacts);
-enum ofperr ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
-                                               unsigned int instructions_len,
-                                               enum ofp_version version,
-                                               struct ofpbuf *ofpacts);
+enum ofperr
+ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
+                                   unsigned int instructions_len,
+                                   enum ofp_version version,
+                                   const struct vl_mff_map *vl_mff_map,
+                                   struct ofpbuf *ofpacts);
 enum ofperr ofpacts_check(struct ofpact[], size_t ofpacts_len,
                           struct flow *, ofp_port_t max_ports,
                           uint8_t table_id, uint8_t n_tables,
diff --git a/include/openvswitch/ofp-errors.h b/include/openvswitch/ofp-errors.h
index a378909..ffe6c00 100644
--- a/include/openvswitch/ofp-errors.h
+++ b/include/openvswitch/ofp-errors.h
@@ -395,6 +395,10 @@ enum ofperr {
      * nxt_flow_mod_table_id extension is enabled. */
     OFPERR_NXFMFC_BAD_TABLE_ID,
 
+    /* NX1.0-1.1(1,536), NX1.2+(37).  Attempted to add a flow with an invalid
+     * variable length meta-flow field. */
+    OFPERR_NXFMFC_INVALID_VL_MFF,
+
 /* ## ---------------------- ## */
 /* ## OFPET_GROUP_MOD_FAILED ## */
 /* ## ---------------------- ## */
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index 62530e8..a255afb 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -331,6 +331,7 @@ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
                                     const struct ofp_header *,
                                     enum ofputil_protocol,
                                     const struct tun_table *,
+                                    const struct vl_mff_map *,
                                     struct ofpbuf *ofpacts,
                                     ofp_port_t max_port,
                                     uint8_t max_table);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 6fc8ff9..a20fab3 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -27,6 +27,7 @@
 #include "openvswitch/dynamic-string.h"
 #include "nx-match.h"
 #include "openvswitch/ofp-util.h"
+#include "ovs-rcu.h"
 #include "ovs-thread.h"
 #include "packets.h"
 #include "random.h"
@@ -2639,3 +2640,118 @@ field_array_set(enum mf_field_id id, const union mf_value *value,
 
     memcpy(fa->values + offset, value, value_size);
 }
+
+static inline uint32_t
+mf_field_hash(uint32_t key)
+{
+    return hash_int(key, 0);
+}
+
+void
+mf_vl_mff_map_clear(struct vl_mff_map *vl_mff_map)
+    OVS_REQUIRES(vl_mff_map->mutex)
+{
+    struct mf_field *mf;
+
+    CMAP_FOR_EACH (mf, cmap_node, &vl_mff_map->cmap) {
+        cmap_remove(&vl_mff_map->cmap, &mf->cmap_node, mf_field_hash(mf->id));
+        ovsrcu_postpone(free, mf);
+    }
+}
+
+/* Updates the tun_metadata mf_field in 'vl_mff_map' according to 'ttm'.
+ * This function is supposed to be invoked after tun_metadata_table_mod(). */
+enum ofperr
+mf_vl_mff_map_mod_from_tun_metadata(struct vl_mff_map *vl_mff_map,
+                                    const struct ofputil_tlv_table_mod *ttm)
+    OVS_REQUIRES(vl_mff_map->mutex)
+{
+    struct ofputil_tlv_map *tlv_map;
+    struct mf_field *mf;
+    unsigned int idx;
+
+    switch (ttm->command) {
+    case NXTTMC_ADD:
+        LIST_FOR_EACH (tlv_map, list_node, &ttm->mappings) {
+            idx = MFF_TUN_METADATA0 + tlv_map->index;
+            if (idx >= MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS) {
+                return OFPERR_NXTTMFC_BAD_FIELD_IDX;
+            }
+
+            mf = xmalloc(sizeof *mf);
+            *mf = mf_fields[idx];
+            mf->n_bytes = tlv_map->option_len;
+            mf->n_bits = tlv_map->option_len * 8;
+            mf->mapped = true;
+
+            cmap_insert(&vl_mff_map->cmap, &mf->cmap_node, mf_field_hash(idx));
+        }
+        break;
+
+    case NXTTMC_DELETE:
+        LIST_FOR_EACH (tlv_map, list_node, &ttm->mappings) {
+            idx = MFF_TUN_METADATA0 + tlv_map->index;
+            if (idx >= MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS) {
+                return OFPERR_NXTTMFC_BAD_FIELD_IDX;
+            }
+
+            CMAP_FOR_EACH_WITH_HASH (mf, cmap_node, mf_field_hash(idx),
+                                     &vl_mff_map->cmap) {
+                if (mf->id == idx) {
+                    cmap_remove(&vl_mff_map->cmap, &mf->cmap_node,
+                                mf_field_hash(idx));
+                    ovsrcu_postpone(free, mf);
+                    break;
+                }
+            }
+        }
+        break;
+
+    case NXTTMC_CLEAR:
+        mf_vl_mff_map_clear(vl_mff_map);
+        break;
+
+    default:
+        OVS_NOT_REACHED();
+    }
+
+    return 0;
+}
+
+/* If 'mff' is a variable length field, looks up 'vl_mff_map', returns a
+ * pointer to the variable length meta-flow field corresponding to 'mff'.
+ * Returns NULL if no mapping is existed for 'mff'. */
+const struct mf_field *
+mf_get_vl_mff(const struct mf_field *mff,
+                const struct vl_mff_map *vl_mff_map)
+{
+    const struct mf_field *field;
+
+    if (mff && mff->variable_len && vl_mff_map) {
+        const uint32_t id = mff->id;
+
+        CMAP_FOR_EACH_WITH_HASH (field, cmap_node, mf_field_hash(id),
+                                 &vl_mff_map->cmap) {
+            if (field->id == id) {
+                return field;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/* Returns true if a variable length meta-flow field 'mff' is not mapped in
+ * the 'vl_mff_map'. */
+bool
+mf_vl_mff_not_mapped(const struct mf_field *mff,
+                     const struct vl_mff_map *vl_mff_map)
+{
+    if (mff && vl_mff_map) {
+        if (mff->variable_len && !mff->mapped) {
+            return true;
+        }
+    }
+
+    return false;
+}
diff --git a/lib/nx-match.c b/lib/nx-match.c
index a7589ff..8824f22 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -231,19 +231,42 @@ mf_nxm_header(enum mf_field_id id)
     return is_experimenter_oxm(oxm) ? 0 : oxm >> 32;
 }
 
+/* Returns the 32-bit OXM or NXM header to use for field 'mff'. If 'mff' is
+ * a mapped variable length mf_field, update header with the configured
+ * lenght of 'mff'. Returns 0 if 'mff' cannot be expressed with a 32-bit NXM
+ * or OXM header.*/
+uint32_t
+nxm_header_from_mff(const struct mf_field *mff)
+{
+    uint64_t oxm = mf_oxm_header(mff->id, 0);
+
+    if (mff->mapped) {
+        oxm = nxm_no_len(oxm) | ((uint64_t) mff->n_bytes << 32);
+    }
+
+    return is_experimenter_oxm(oxm) ? 0 : oxm >> 32;
+}
+
 static const struct mf_field *
-mf_from_oxm_header(uint64_t header)
+mf_from_oxm_header(uint64_t header, const struct vl_mff_map *vl_mff_map)
 {
     const struct nxm_field *f = nxm_field_by_header(header);
-    return f ? mf_from_id(f->id) : NULL;
+
+    if (f) {
+        const struct mf_field *mff = mf_from_id(f->id);
+        const struct mf_field *vl_mff = mf_get_vl_mff(mff, vl_mff_map);
+        return vl_mff ? vl_mff : mff;
+    } else {
+        return NULL;
+    }
 }
 
 /* Returns the "struct mf_field" that corresponds to NXM or OXM header
  * 'header', or NULL if 'header' doesn't correspond to any known field.  */
 const struct mf_field *
-mf_from_nxm_header(uint32_t header)
+mf_from_nxm_header(uint32_t header, const struct vl_mff_map *vl_mff_map)
 {
-    return mf_from_oxm_header((uint64_t) header << 32);
+    return mf_from_oxm_header((uint64_t) header << 32, vl_mff_map);
 }
 
 /* Returns the width of the data for a field with the given 'header', in
@@ -286,7 +309,8 @@ is_cookie_pseudoheader(uint64_t header)
 }
 
 static enum ofperr
-nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
+nx_pull_header__(struct ofpbuf *b, bool allow_cookie,
+                 const struct vl_mff_map *vl_mff_map, uint64_t *header,
                  const struct mf_field **field)
 {
     if (b->size < 4) {
@@ -310,11 +334,13 @@ nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
     ofpbuf_pull(b, nxm_header_len(*header));
 
     if (field) {
-        *field = mf_from_oxm_header(*header);
+        *field = mf_from_oxm_header(*header, vl_mff_map);
         if (!*field && !(allow_cookie && is_cookie_pseudoheader(*header))) {
             VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" is unknown",
                         NXM_HEADER_ARGS(*header));
             return OFPERR_OFPBMC_BAD_FIELD;
+        } else if (mf_vl_mff_not_mapped(*field, vl_mff_map)) {
+            return OFPERR_NXFMFC_INVALID_VL_MFF;
         }
     }
 
@@ -350,7 +376,8 @@ copy_entry_value(const struct mf_field *field, union mf_value *value,
 }
 
 static enum ofperr
-nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
+nx_pull_entry__(struct ofpbuf *b, bool allow_cookie,
+                const struct vl_mff_map *vl_mff_map, uint64_t *header,
                 const struct mf_field **field_,
                 union mf_value *value, union mf_value *mask)
 {
@@ -360,7 +387,8 @@ nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
     const uint8_t *payload;
     int width;
 
-    header_error = nx_pull_header__(b, allow_cookie, header, &field);
+    header_error = nx_pull_header__(b, allow_cookie, vl_mff_map, header,
+                                    &field);
     if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) {
         return header_error;
     }
@@ -414,12 +442,13 @@ nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
  * errors (with OFPERR_OFPBMC_BAD_MASK).
  */
 enum ofperr
-nx_pull_entry(struct ofpbuf *b, const struct mf_field **field,
-              union mf_value *value, union mf_value *mask)
+nx_pull_entry(struct ofpbuf *b, const struct vl_mff_map *vl_mff_map,
+              const struct mf_field **field, union mf_value *value,
+              union mf_value *mask)
 {
     uint64_t header;
 
-    return nx_pull_entry__(b, false, &header, field, value, mask);
+    return nx_pull_entry__(b, false, vl_mff_map, &header, field, value, mask);
 }
 
 /* Attempts to pull an NXM or OXM header from the beginning of 'b'.  If
@@ -433,12 +462,13 @@ nx_pull_entry(struct ofpbuf *b, const struct mf_field **field,
  * errors (with OFPERR_OFPBMC_BAD_MASK).
  */
 enum ofperr
-nx_pull_header(struct ofpbuf *b, const struct mf_field **field, bool *masked)
+nx_pull_header(struct ofpbuf *b, const struct vl_mff_map *vl_mff_map,
+               const struct mf_field **field, bool *masked)
 {
     enum ofperr error;
     uint64_t header;
 
-    error = nx_pull_header__(b, false, &header, field);
+    error = nx_pull_header__(b, false, vl_mff_map,  &header, field);
     if (masked) {
         *masked = !error && nxm_hasmask(header);
     } else if (!error && nxm_hasmask(header)) {
@@ -455,7 +485,8 @@ nx_pull_match_entry(struct ofpbuf *b, bool allow_cookie,
     enum ofperr error;
     uint64_t header;
 
-    error = nx_pull_entry__(b, allow_cookie, &header, field, value, mask);
+    error = nx_pull_entry__(b, allow_cookie, NULL, &header, field, value,
+                            mask);
     if (error) {
         return error;
     }
@@ -668,7 +699,8 @@ oxm_pull_field_array(const void *fields_data, size_t fields_len,
         enum ofperr error;
         uint64_t header;
 
-        error = nx_pull_entry__(&b, false, &header, &field, &value, NULL);
+        error = nx_pull_entry__(&b, false, NULL, &header, &field, &value,
+                                NULL);
         if (error) {
             VLOG_DBG_RL(&rl, "error pulling field array field");
             return error;
@@ -1267,6 +1299,16 @@ nx_put_header(struct ofpbuf *b, enum mf_field_id field,
     nx_put_header__(b, mf_oxm_header(field, version), masked);
 }
 
+void nx_put_mff_header(struct ofpbuf *b, const struct mf_field *mff,
+                       enum ofp_version version, bool masked)
+{
+    if (!mff->mapped) {
+        nx_put_header(b, mff->id, version, masked);
+    } else {
+        nx_put_header_len(b, mff->id, version, masked, mff->n_bytes);
+    }
+}
+
 static void
 nx_put_header_len(struct ofpbuf *b, enum mf_field_id field,
                   enum ofp_version version, bool masked, size_t n_bytes)
@@ -1281,18 +1323,17 @@ nx_put_header_len(struct ofpbuf *b, enum mf_field_id field,
 }
 
 void
-nx_put_entry(struct ofpbuf *b,
-             enum mf_field_id field, enum ofp_version version,
-             const union mf_value *value, const union mf_value *mask)
+nx_put_entry(struct ofpbuf *b, const struct mf_field *mff,
+             enum ofp_version version, const union mf_value *value,
+             const union mf_value *mask)
 {
-    const struct mf_field *mf = mf_from_id(field);
     bool masked;
     int len, offset;
 
-    len = mf_field_len(mf, value, mask, &masked);
-    offset = mf->n_bytes - len;
+    len = mf_field_len(mff, value, mask, &masked);
+    offset = mff->n_bytes - len;
 
-    nx_put_header_len(b, field, version, masked, len);
+    nx_put_header_len(b, mff->id, version, masked, len);
     ofpbuf_put(b, &value->u8 + offset, len);
     if (masked) {
         ofpbuf_put(b, &mask->u8 + offset, len);
@@ -1319,7 +1360,7 @@ nx_match_to_string(const uint8_t *p, unsigned int match_len)
         uint64_t header;
         int value_len;
 
-        error = nx_pull_entry__(&b, true, &header, NULL, &value, &mask);
+        error = nx_pull_entry__(&b, true, NULL, &header, NULL, &value, &mask);
         if (error) {
             break;
         }
@@ -1505,7 +1546,7 @@ nx_match_from_string_raw(const char *s, struct ofpbuf *b)
         b->header = ofpbuf_put_uninit(b, nxm_header_len(header));
         s = ofpbuf_put_hex(b, s, &n);
         if (n != nxm_field_bytes(header)) {
-            const struct mf_field *field = mf_from_oxm_header(header);
+            const struct mf_field *field = mf_from_oxm_header(header, NULL);
 
             if (field && field->variable_len) {
                 if (n <= field->n_bytes) {
diff --git a/lib/nx-match.h b/lib/nx-match.h
index 0ba9f5e..83eff3e 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -76,17 +76,20 @@ int oxm_put_field_array(struct ofpbuf *, const struct field_array *,
 
 /* Decoding and encoding OXM/NXM headers (just a field ID) or entries (a field
  * ID followed by a value and possibly a mask). */
-enum ofperr nx_pull_entry(struct ofpbuf *, const struct mf_field **,
-                          union mf_value *value, union mf_value *mask);
-enum ofperr nx_pull_header(struct ofpbuf *, const struct mf_field **,
-                           bool *masked);
+enum ofperr nx_pull_entry(struct ofpbuf *, const struct vl_mff_map *,
+                          const struct mf_field **, union mf_value *value,
+                          union mf_value *mask);
+enum ofperr nx_pull_header(struct ofpbuf *, const struct vl_mff_map *,
+                           const struct mf_field **, bool *masked);
 void nxm_put__(struct ofpbuf *b, enum mf_field_id field,
                enum ofp_version version, const void *value,
                const void *mask, size_t n_bytes);
-void nx_put_entry(struct ofpbuf *, enum mf_field_id, enum ofp_version,
+void nx_put_entry(struct ofpbuf *, const struct mf_field *, enum ofp_version,
                   const union mf_value *value, const union mf_value *mask);
 void nx_put_header(struct ofpbuf *, enum mf_field_id, enum ofp_version,
                    bool masked);
+void nx_put_mff_header(struct ofpbuf *, const struct mf_field *,
+                       enum ofp_version, bool);
 
 /* NXM and OXM protocol headers values.
  *
@@ -95,7 +98,9 @@ void nx_put_header(struct ofpbuf *, enum mf_field_id, enum ofp_version,
  * the nx_*() functions should be preferred because they can support the 64-bit
  * "experimenter" OXM format (even though it is not yet implemented). */
 uint32_t mf_nxm_header(enum mf_field_id);
-const struct mf_field *mf_from_nxm_header(uint32_t nxm_header);
+uint32_t nxm_header_from_mff(const struct mf_field *);
+const struct mf_field *mf_from_nxm_header(uint32_t nxm_header,
+                                          const struct vl_mff_map *);
 
 char *nx_match_to_string(const uint8_t *, unsigned int match_len);
 char *oxm_match_to_string(const struct ofpbuf *, unsigned int match_len);
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index cf1ad0f..180344a 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -114,6 +114,12 @@ OFP_ASSERT(sizeof(struct ext_action_header) == 16);
  *         # "void": The action is just a (standard or vendor extension)
  *           header.
  *
+ *         # Optionally, one may add "VLMFF" in the end of the second part if
+ *           the Openflow action may use a variable length meta-flow field
+ *           (i.e. tun_metadata). Adding "VLMFF" will pass the per-switch based
+ *           variable length meta-flow field mapping map (struct vl_mff_map) to
+ *           the corresponding action decoding function.
+ *
  *   - Optional additional text enclosed in square brackets is commentary for
  *     the human reader.
  */
@@ -226,26 +232,26 @@ enum ofp_raw_action_type {
     /* NX1.0+(21): struct nx_action_cnt_ids, ... */
     NXAST_RAW_DEC_TTL_CNT_IDS,
 
-    /* OF1.2-1.4(25): struct ofp12_action_set_field, ... */
+    /* OF1.2-1.4(25): struct ofp12_action_set_field, ... VLMFF */
     OFPAT_RAW12_SET_FIELD,
-    /* OF1.5+(25): struct ofp12_action_set_field, ... */
+    /* OF1.5+(25): struct ofp12_action_set_field, ... VLMFF */
     OFPAT_RAW15_SET_FIELD,
-    /* NX1.0-1.4(7): struct nx_action_reg_load.
+    /* NX1.0-1.4(7): struct nx_action_reg_load. VLMFF
      *
      * [In OpenFlow 1.5, set_field is a superset of reg_load functionality, so
      * we drop reg_load.] */
     NXAST_RAW_REG_LOAD,
-    /* NX1.0-1.4(33): struct ext_action_header, ...
+    /* NX1.0-1.4(33): struct ext_action_header, ... VLMFF
      *
      * [In OpenFlow 1.5, set_field is a superset of reg_load2 functionality, so
      * we drop reg_load2.] */
     NXAST_RAW_REG_LOAD2,
 
-    /* OF1.5+(28): struct ofp15_action_copy_field, ... */
+    /* OF1.5+(28): struct ofp15_action_copy_field, ... VLMFF */
     OFPAT_RAW15_COPY_FIELD,
-    /* ONF1.3-1.4(3200): struct onf_action_copy_field, ... */
+    /* ONF1.3-1.4(3200): struct onf_action_copy_field, ... VLMFF */
     ONFACT_RAW13_COPY_FIELD,
-    /* NX1.0-1.4(6): struct nx_action_reg_move, ... */
+    /* NX1.0-1.4(6): struct nx_action_reg_move, ... VLMFF */
     NXAST_RAW_REG_MOVE,
 
 /* ## ------------------------- ## */
@@ -270,20 +276,20 @@ enum ofp_raw_action_type {
     /* NX1.0+(8): struct nx_action_note, ... */
     NXAST_RAW_NOTE,
 
-    /* NX1.0+(10): struct nx_action_multipath. */
+    /* NX1.0+(10): struct nx_action_multipath. VLMFF */
     NXAST_RAW_MULTIPATH,
 
     /* NX1.0+(12): struct nx_action_bundle, ... */
     NXAST_RAW_BUNDLE,
-    /* NX1.0+(13): struct nx_action_bundle, ... */
+    /* NX1.0+(13): struct nx_action_bundle, ... VLMFF */
     NXAST_RAW_BUNDLE_LOAD,
 
-    /* NX1.0+(15): struct nx_action_output_reg. */
+    /* NX1.0+(15): struct nx_action_output_reg. VLMFF */
     NXAST_RAW_OUTPUT_REG,
-    /* NX1.0+(32): struct nx_action_output_reg2. */
+    /* NX1.0+(32): struct nx_action_output_reg2. VLMFF */
     NXAST_RAW_OUTPUT_REG2,
 
-    /* NX1.0+(16): struct nx_action_learn, ... */
+    /* NX1.0+(16): struct nx_action_learn, ... VLMFF */
     NXAST_RAW_LEARN,
 
     /* NX1.0+(17): void. */
@@ -300,10 +306,10 @@ enum ofp_raw_action_type {
     /* NX1.0+(22): struct nx_action_write_metadata. */
     NXAST_RAW_WRITE_METADATA,
 
-    /* NX1.0+(27): struct nx_action_stack. */
+    /* NX1.0+(27): struct nx_action_stack. VLMFF */
     NXAST_RAW_STACK_PUSH,
 
-    /* NX1.0+(28): struct nx_action_stack. */
+    /* NX1.0+(28): struct nx_action_stack. VLMFF */
     NXAST_RAW_STACK_POP,
 
     /* NX1.0+(29): struct nx_action_sample. */
@@ -316,7 +322,7 @@ enum ofp_raw_action_type {
     /* NX1.0+(34): struct nx_action_conjunction. */
     NXAST_RAW_CONJUNCTION,
 
-    /* NX1.0+(35): struct nx_action_conntrack, ... */
+    /* NX1.0+(35): struct nx_action_conntrack, ... VLMFF */
     NXAST_RAW_CT,
 
     /* NX1.0+(36): struct nx_action_nat, ... */
@@ -325,7 +331,7 @@ enum ofp_raw_action_type {
     /* NX1.0+(39): struct nx_action_output_trunc. */
     NXAST_RAW_OUTPUT_TRUNC,
 
-    /* NX1.0+(42): struct ext_action_header, ... */
+    /* NX1.0+(42): struct ext_action_header, ... VLMFF */
     NXAST_RAW_CLONE,
 
     /* NX1.0+(43): void. */
@@ -397,7 +403,8 @@ static char *OVS_WARN_UNUSED_RESULT ofpacts_parse(
 static enum ofperr ofpacts_pull_openflow_actions__(
     struct ofpbuf *openflow, unsigned int actions_len,
     enum ofp_version version, uint32_t allowed_ovsinsts,
-    struct ofpbuf *ofpacts, enum ofpact_type outer_action);
+    struct ofpbuf *ofpacts, enum ofpact_type outer_action,
+    const struct vl_mff_map *vl_mff_map);
 static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy(
     const char *s_, struct ofpbuf *ofpacts,
     enum ofputil_protocol *usable_protocols,
@@ -1112,6 +1119,7 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg2) == 24);
 static enum ofperr
 decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor,
                             enum ofp_version ofp_version OVS_UNUSED,
+                            const struct vl_mff_map *vl_mff_map,
                             struct ofpbuf *out)
 {
     struct ofpact_output_reg *output_reg;
@@ -1122,17 +1130,22 @@ decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor,
 
     output_reg = ofpact_put_OUTPUT_REG(out);
     output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG;
-    output_reg->src.field = mf_from_nxm_header(ntohl(naor->src));
+    output_reg->src.field = mf_from_nxm_header(ntohl(naor->src), vl_mff_map);
     output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits);
     output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits);
     output_reg->max_len = ntohs(naor->max_len);
 
+    if (mf_vl_mff_not_mapped(output_reg->src.field, vl_mff_map)) {
+        return OFPERR_NXFMFC_INVALID_VL_MFF;
+    }
+
     return mf_check_src(&output_reg->src, NULL);
 }
 
 static enum ofperr
 decode_NXAST_RAW_OUTPUT_REG2(const struct nx_action_output_reg2 *naor,
                              enum ofp_version ofp_version OVS_UNUSED,
+                             const struct vl_mff_map *vl_mff_map,
                              struct ofpbuf *out)
 {
     struct ofpact_output_reg *output_reg;
@@ -1145,7 +1158,8 @@ decode_NXAST_RAW_OUTPUT_REG2(const struct nx_action_output_reg2 *naor,
     struct ofpbuf b = ofpbuf_const_initializer(naor, ntohs(naor->len));
     ofpbuf_pull(&b, OBJECT_OFFSETOF(naor, pad));
 
-    enum ofperr error = nx_pull_header(&b, &output_reg->src.field, NULL);
+    enum ofperr error = nx_pull_header(&b, vl_mff_map, &output_reg->src.field,
+                                       NULL);
     if (error) {
         return error;
     }
@@ -1174,14 +1188,14 @@ encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg,
         naor->max_len = htons(output_reg->max_len);
 
         out->size = size - sizeof naor->pad;
-        nx_put_header(out, output_reg->src.field->id, 0, false);
+        nx_put_mff_header(out, output_reg->src.field, 0, false);
         out->size = size;
     } else {
         struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out);
 
         naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
                                                output_reg->src.n_bits);
-        naor->src = htonl(mf_nxm_header(output_reg->src.field->id));
+        naor->src = htonl(nxm_header_from_mff(output_reg->src.field));
         naor->max_len = htons(output_reg->max_len);
     }
 }
@@ -1271,7 +1285,7 @@ OFP_ASSERT(sizeof(struct nx_action_bundle) == 32);
 
 static enum ofperr
 decode_bundle(bool load, const struct nx_action_bundle *nab,
-              struct ofpbuf *ofpacts)
+              const struct vl_mff_map *vl_mff_map, struct ofpbuf *ofpacts)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
     struct ofpact_bundle *bundle;
@@ -1308,9 +1322,12 @@ decode_bundle(bool load, const struct nx_action_bundle *nab,
     }
 
     if (load) {
-        bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst));
+        bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst), vl_mff_map);
         bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits);
         bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits);
+        if (mf_vl_mff_not_mapped(bundle->dst.field, vl_mff_map)) {
+            return OFPERR_NXFMFC_INVALID_VL_MFF;
+        }
 
         if (bundle->dst.n_bits < 16) {
             VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit "
@@ -1351,15 +1368,16 @@ decode_NXAST_RAW_BUNDLE(const struct nx_action_bundle *nab,
                         enum ofp_version ofp_version OVS_UNUSED,
                         struct ofpbuf *out)
 {
-    return decode_bundle(false, nab, out);
+    return decode_bundle(false, nab, NULL, out);
 }
 
 static enum ofperr
 decode_NXAST_RAW_BUNDLE_LOAD(const struct nx_action_bundle *nab,
                              enum ofp_version ofp_version OVS_UNUSED,
+                             const struct vl_mff_map *vl_mff_map,
                              struct ofpbuf *out)
 {
-    return decode_bundle(true, nab, out);
+    return decode_bundle(true, nab, vl_mff_map, out);
 }
 
 static void
@@ -1384,7 +1402,7 @@ encode_BUNDLE(const struct ofpact_bundle *bundle,
     if (bundle->dst.field) {
         nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
                                               bundle->dst.n_bits);
-        nab->dst = htonl(mf_nxm_header(bundle->dst.field->id));
+        nab->dst = htonl(nxm_header_from_mff(bundle->dst.field));
     }
 
     slaves = ofpbuf_put_zeros(out, slaves_len);
@@ -2262,6 +2280,7 @@ OFP_ASSERT(sizeof(struct nx_action_reg_move) == 16);
 static enum ofperr
 decode_copy_field__(ovs_be16 src_offset, ovs_be16 dst_offset, ovs_be16 n_bits,
                     const void *action, ovs_be16 action_len, size_t oxm_offset,
+                    const struct vl_mff_map *vl_mff_map,
                     struct ofpbuf *ofpacts)
 {
     struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
@@ -2274,11 +2293,11 @@ decode_copy_field__(ovs_be16 src_offset, ovs_be16 dst_offset, ovs_be16 n_bits,
     struct ofpbuf b = ofpbuf_const_initializer(action, ntohs(action_len));
     ofpbuf_pull(&b, oxm_offset);
 
-    enum ofperr error = nx_pull_header(&b, &move->src.field, NULL);
+    enum ofperr error = nx_pull_header(&b, vl_mff_map, &move->src.field, NULL);
     if (error) {
         return error;
     }
-    error = nx_pull_header(&b, &move->dst.field, NULL);
+    error = nx_pull_header(&b, vl_mff_map, &move->dst.field, NULL);
     if (error) {
         return error;
     }
@@ -2293,26 +2312,31 @@ decode_copy_field__(ovs_be16 src_offset, ovs_be16 dst_offset, ovs_be16 n_bits,
 static enum ofperr
 decode_OFPAT_RAW15_COPY_FIELD(const struct ofp15_action_copy_field *oacf,
                               enum ofp_version ofp_version OVS_UNUSED,
+                              const struct vl_mff_map *vl_mff_map,
                               struct ofpbuf *ofpacts)
 {
     return decode_copy_field__(oacf->src_offset, oacf->dst_offset,
                                oacf->n_bits, oacf, oacf->len,
-                               OBJECT_OFFSETOF(oacf, pad2), ofpacts);
+                               OBJECT_OFFSETOF(oacf, pad2), vl_mff_map,
+                               ofpacts);
 }
 
 static enum ofperr
 decode_ONFACT_RAW13_COPY_FIELD(const struct onf_action_copy_field *oacf,
                                enum ofp_version ofp_version OVS_UNUSED,
+                               const struct vl_mff_map *vl_mff_map,
                                struct ofpbuf *ofpacts)
 {
     return decode_copy_field__(oacf->src_offset, oacf->dst_offset,
                                oacf->n_bits, oacf, oacf->len,
-                               OBJECT_OFFSETOF(oacf, pad3), ofpacts);
+                               OBJECT_OFFSETOF(oacf, pad3), vl_mff_map,
+                               ofpacts);
 }
 
 static enum ofperr
 decode_NXAST_RAW_REG_MOVE(const struct nx_action_reg_move *narm,
                           enum ofp_version ofp_version OVS_UNUSED,
+                          const struct vl_mff_map *vl_mff_map,
                           struct ofpbuf *ofpacts)
 {
     struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
@@ -2325,14 +2349,15 @@ decode_NXAST_RAW_REG_MOVE(const struct nx_action_reg_move *narm,
     struct ofpbuf b = ofpbuf_const_initializer(narm, ntohs(narm->len));
     ofpbuf_pull(&b, sizeof *narm);
 
-    enum ofperr error = nx_pull_header(&b, &move->src.field, NULL);
+    enum ofperr error = nx_pull_header(&b, vl_mff_map, &move->src.field, NULL);
     if (error) {
         return error;
     }
-    error = nx_pull_header(&b, &move->dst.field, NULL);
+    error = nx_pull_header(&b, vl_mff_map, &move->dst.field, NULL);
     if (error) {
         return error;
     }
+
     if (!is_all_zeros(b.data, b.size)) {
         return OFPERR_NXBRC_MUST_BE_ZERO;
     }
@@ -2357,8 +2382,8 @@ encode_REG_MOVE(const struct ofpact_reg_move *move,
         copy->src_offset = htons(move->src.ofs);
         copy->dst_offset = htons(move->dst.ofs);
         out->size = out->size - sizeof copy->pad2;
-        nx_put_header(out, move->src.field->id, ofp_version, false);
-        nx_put_header(out, move->dst.field->id, ofp_version, false);
+        nx_put_mff_header(out, move->src.field, ofp_version, false);
+        nx_put_mff_header(out, move->dst.field, ofp_version, false);
     } else if (ofp_version == OFP13_VERSION
                && move->ofpact.raw == ONFACT_RAW13_COPY_FIELD) {
         struct onf_action_copy_field *copy = put_ONFACT13_COPY_FIELD(out);
@@ -2366,15 +2391,15 @@ encode_REG_MOVE(const struct ofpact_reg_move *move,
         copy->src_offset = htons(move->src.ofs);
         copy->dst_offset = htons(move->dst.ofs);
         out->size = out->size - sizeof copy->pad3;
-        nx_put_header(out, move->src.field->id, ofp_version, false);
-        nx_put_header(out, move->dst.field->id, ofp_version, false);
+        nx_put_mff_header(out, move->src.field, ofp_version, false);
+        nx_put_mff_header(out, move->dst.field, ofp_version, false);
     } else {
         struct nx_action_reg_move *narm = put_NXAST_REG_MOVE(out);
         narm->n_bits = htons(move->dst.n_bits);
         narm->src_ofs = htons(move->src.ofs);
         narm->dst_ofs = htons(move->dst.ofs);
-        nx_put_header(out, move->src.field->id, 0, false);
-        nx_put_header(out, move->dst.field->id, 0, false);
+        nx_put_mff_header(out, move->src.field, 0, false);
+        nx_put_mff_header(out, move->dst.field, 0, false);
     }
     pad_ofpat(out, start_ofs);
 }
@@ -2454,14 +2479,15 @@ OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24);
 
 static enum ofperr
 decode_ofpat_set_field(const struct ofp12_action_set_field *oasf,
-                       bool may_mask, struct ofpbuf *ofpacts)
+                       bool may_mask, const struct vl_mff_map *vl_mff_map,
+                       struct ofpbuf *ofpacts)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oasf, ntohs(oasf->len));
     ofpbuf_pull(&b, OBJECT_OFFSETOF(oasf, pad));
 
     union mf_value value, mask;
     const struct mf_field *field;
-    enum ofperr error = nx_pull_entry(&b, &field, &value,
+    enum ofperr error = nx_pull_entry(&b, vl_mff_map, &field, &value,
                                       may_mask ? &mask : NULL);
     if (error) {
         return (error == OFPERR_OFPBMC_BAD_MASK
@@ -2512,30 +2538,37 @@ decode_ofpat_set_field(const struct ofp12_action_set_field *oasf,
 static enum ofperr
 decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf,
                              enum ofp_version ofp_version OVS_UNUSED,
+                             const struct vl_mff_map *vl_mff_map,
                              struct ofpbuf *ofpacts)
 {
-    return decode_ofpat_set_field(oasf, false, ofpacts);
+    return decode_ofpat_set_field(oasf, false, vl_mff_map, ofpacts);
 }
 
 static enum ofperr
 decode_OFPAT_RAW15_SET_FIELD(const struct ofp12_action_set_field *oasf,
                              enum ofp_version ofp_version OVS_UNUSED,
+                             const struct vl_mff_map *vl_mff_map,
                              struct ofpbuf *ofpacts)
 {
-    return decode_ofpat_set_field(oasf, true, ofpacts);
+    return decode_ofpat_set_field(oasf, true, vl_mff_map, ofpacts);
 }
 
 static enum ofperr
 decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl,
                           enum ofp_version ofp_version OVS_UNUSED,
+                          const struct vl_mff_map *vl_mff_map,
                           struct ofpbuf *out)
 {
     struct mf_subfield dst;
     enum ofperr error;
 
-    dst.field = mf_from_nxm_header(ntohl(narl->dst));
+    dst.field = mf_from_nxm_header(ntohl(narl->dst), vl_mff_map);
     dst.ofs = nxm_decode_ofs(narl->ofs_nbits);
     dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits);
+    if (mf_vl_mff_not_mapped(dst.field, vl_mff_map)) {
+        return OFPERR_NXFMFC_INVALID_VL_MFF;
+    }
+
     error = mf_check_dst(&dst, NULL);
     if (error) {
         return error;
@@ -2561,6 +2594,7 @@ decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl,
 static enum ofperr
 decode_NXAST_RAW_REG_LOAD2(const struct ext_action_header *eah,
                            enum ofp_version ofp_version OVS_UNUSED,
+                           const struct vl_mff_map *vl_mff_map,
                            struct ofpbuf *out)
 {
     struct ofpbuf b = ofpbuf_const_initializer(eah, ntohs(eah->len));
@@ -2568,7 +2602,7 @@ decode_NXAST_RAW_REG_LOAD2(const struct ext_action_header *eah,
 
     union mf_value value, mask;
     const struct mf_field *field;
-    enum ofperr error = nx_pull_entry(&b, &field, &value, &mask);
+    enum ofperr error = nx_pull_entry(&b, vl_mff_map, &field, &value, &mask);
     if (error) {
         return error;
     }
@@ -2599,7 +2633,7 @@ put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version,
 
     oasf = put_OFPAT12_SET_FIELD(openflow);
     openflow->size = openflow->size - sizeof oasf->pad;
-    nx_put_entry(openflow, field, ofp_version, &value, NULL);
+    nx_put_entry(openflow, mf_from_id(field), ofp_version, &value, NULL);
     pad_ofpat(openflow, start_ofs);
 }
 
@@ -2611,7 +2645,7 @@ put_reg_load(struct ofpbuf *openflow,
 
     struct nx_action_reg_load *narl = put_NXAST_REG_LOAD(openflow);
     narl->ofs_nbits = nxm_encode_ofs_nbits(dst->ofs, dst->n_bits);
-    narl->dst = htonl(mf_nxm_header(dst->field->id));
+    narl->dst = htonl(nxm_header_from_mff(dst->field));
     narl->value = htonll(value);
 }
 
@@ -2653,7 +2687,7 @@ set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow)
 
         eah = put_NXAST_REG_LOAD2(openflow);
         openflow->size = openflow->size - sizeof eah->pad;
-        nx_put_entry(openflow, sf->field->id, 0, sf->value,
+        nx_put_entry(openflow, sf->field, 0, sf->value,
                      ofpact_set_field_mask(sf));
         pad_ofpat(openflow, start_ofs);
     } else {
@@ -2799,7 +2833,7 @@ set_field_to_set_field(const struct ofpact_set_field *sf,
 
     oasf = put_OFPAT12_SET_FIELD(out);
     out->size = out->size - sizeof oasf->pad;
-    nx_put_entry(out, sf->field->id, ofp_version, sf->value,
+    nx_put_entry(out, sf->field, ofp_version, sf->value,
                  ofpact_set_field_mask(sf));
     pad_ofpat(out, start_ofs);
 }
@@ -3074,14 +3108,15 @@ OFP_ASSERT(sizeof(struct nx_action_stack) == 24);
 
 static enum ofperr
 decode_stack_action(const struct nx_action_stack *nasp,
+                    const struct vl_mff_map *vl_mff_map,
                     struct ofpact_stack *stack_action)
 {
     stack_action->subfield.ofs = ntohs(nasp->offset);
 
     struct ofpbuf b = ofpbuf_const_initializer(nasp, sizeof *nasp);
     ofpbuf_pull(&b, OBJECT_OFFSETOF(nasp, pad));
-    enum ofperr error = nx_pull_header(&b, &stack_action->subfield.field,
-                                       NULL);
+    enum ofperr error = nx_pull_header(&b, vl_mff_map,
+                                       &stack_action->subfield.field, NULL);
     if (error) {
         return error;
     }
@@ -3097,20 +3132,22 @@ decode_stack_action(const struct nx_action_stack *nasp,
 static enum ofperr
 decode_NXAST_RAW_STACK_PUSH(const struct nx_action_stack *nasp,
                             enum ofp_version ofp_version OVS_UNUSED,
+                            const struct vl_mff_map *vl_mff_map,
                             struct ofpbuf *ofpacts)
 {
     struct ofpact_stack *push = ofpact_put_STACK_PUSH(ofpacts);
-    enum ofperr error = decode_stack_action(nasp, push);
+    enum ofperr error = decode_stack_action(nasp, vl_mff_map, push);
     return error ? error : nxm_stack_push_check(push, NULL);
 }
 
 static enum ofperr
 decode_NXAST_RAW_STACK_POP(const struct nx_action_stack *nasp,
                            enum ofp_version ofp_version OVS_UNUSED,
+                           const struct vl_mff_map *vl_mff_map,
                            struct ofpbuf *ofpacts)
 {
     struct ofpact_stack *pop = ofpact_put_STACK_POP(ofpacts);
-    enum ofperr error = decode_stack_action(nasp, pop);
+    enum ofperr error = decode_stack_action(nasp, vl_mff_map, pop);
     return error ? error : nxm_stack_pop_check(pop, NULL);
 }
 
@@ -3125,7 +3162,7 @@ encode_STACK_op(const struct ofpact_stack *stack_action,
 
     ofpbuf_use_stack(&b, nasp, ntohs(nasp->len));
     ofpbuf_put_uninit(&b, OBJECT_OFFSETOF(nasp, pad));
-    nx_put_header(&b, stack_action->subfield.field->id, 0, false);
+    nx_put_mff_header(&b, stack_action->subfield.field, 0, false);
     n_bits = htons(stack_action->subfield.n_bits);
     ofpbuf_put(&b, &n_bits, sizeof n_bits);
 }
@@ -4239,12 +4276,17 @@ get_be32(const void **pp)
     return value;
 }
 
-static void
-get_subfield(int n_bits, const void **p, struct mf_subfield *sf)
+static enum ofperr
+get_subfield(int n_bits, const void **p, struct mf_subfield *sf,
+             const struct vl_mff_map *vl_mff_map)
 {
-    sf->field = mf_from_nxm_header(ntohl(get_be32(p)));
+    sf->field = mf_from_nxm_header(ntohl(get_be32(p)), vl_mff_map);
     sf->ofs = ntohs(get_be16(p));
     sf->n_bits = n_bits;
+    if (mf_vl_mff_not_mapped(sf->field, vl_mff_map)) {
+        return OFPERR_NXFMFC_INVALID_VL_MFF;
+    }
+    return 0;
 }
 
 static unsigned int
@@ -4275,6 +4317,7 @@ learn_min_len(uint16_t header)
 static enum ofperr
 decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal,
                        enum ofp_version ofp_version OVS_UNUSED,
+                       const struct vl_mff_map *vl_mff_map,
                        struct ofpbuf *ofpacts)
 {
     struct ofpact_learn *learn;
@@ -4338,8 +4381,12 @@ decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal,
         /* Get the source. */
         const uint8_t *imm = NULL;
         unsigned int imm_bytes = 0;
+        enum ofperr error;
         if (spec->src_type == NX_LEARN_SRC_FIELD) {
-            get_subfield(spec->n_bits, &p, &spec->src);
+            error = get_subfield(spec->n_bits, &p, &spec->src, vl_mff_map);
+            if (error) {
+                return error;
+            }
         } else {
             int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
             p = (const uint8_t *) p + p_bytes;
@@ -4351,7 +4398,10 @@ decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal,
         /* Get the destination. */
         if (spec->dst_type == NX_LEARN_DST_MATCH ||
             spec->dst_type == NX_LEARN_DST_LOAD) {
-            get_subfield(spec->n_bits, &p, &spec->dst);
+            error = get_subfield(spec->n_bits, &p, &spec->dst, vl_mff_map);
+            if (error) {
+                return error;
+            }
         }
 
         if (imm) {
@@ -4418,7 +4468,7 @@ encode_LEARN(const struct ofpact_learn *learn,
         put_u16(out, spec->n_bits | spec->dst_type | spec->src_type);
 
         if (spec->src_type == NX_LEARN_SRC_FIELD) {
-            put_u32(out, mf_nxm_header(spec->src.field->id));
+            put_u32(out, nxm_header_from_mff(spec->src.field));
             put_u16(out, spec->src.ofs);
         } else {
             size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
@@ -4431,7 +4481,7 @@ encode_LEARN(const struct ofpact_learn *learn,
 
         if (spec->dst_type == NX_LEARN_DST_MATCH ||
             spec->dst_type == NX_LEARN_DST_LOAD) {
-            put_u32(out, mf_nxm_header(spec->dst.field->id));
+            put_u32(out, nxm_header_from_mff(spec->dst.field));
             put_u16(out, spec->dst.ofs);
         }
     }
@@ -4597,6 +4647,7 @@ OFP_ASSERT(sizeof(struct nx_action_multipath) == 32);
 static enum ofperr
 decode_NXAST_RAW_MULTIPATH(const struct nx_action_multipath *nam,
                            enum ofp_version ofp_version OVS_UNUSED,
+                           const struct vl_mff_map *vl_mff_map,
                            struct ofpbuf *out)
 {
     uint32_t n_links = ntohs(nam->max_link) + 1;
@@ -4609,10 +4660,14 @@ decode_NXAST_RAW_MULTIPATH(const struct nx_action_multipath *nam,
     mp->algorithm = ntohs(nam->algorithm);
     mp->max_link = ntohs(nam->max_link);
     mp->arg = ntohl(nam->arg);
-    mp->dst.field = mf_from_nxm_header(ntohl(nam->dst));
+    mp->dst.field = mf_from_nxm_header(ntohl(nam->dst), vl_mff_map);
     mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits);
     mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits);
 
+    if (mf_vl_mff_not_mapped(mp->dst.field, vl_mff_map)) {
+        return OFPERR_NXFMFC_INVALID_VL_MFF;
+    }
+
     if (!flow_hash_fields_valid(mp->fields)) {
         VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields);
         return OFPERR_OFPBAC_BAD_ARGUMENT;
@@ -4643,7 +4698,7 @@ encode_MULTIPATH(const struct ofpact_multipath *mp,
     nam->max_link = htons(mp->max_link);
     nam->arg = htonl(mp->arg);
     nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits);
-    nam->dst = htonl(mf_nxm_header(mp->dst.field->id));
+    nam->dst = htonl(nxm_header_from_mff(mp->dst.field));
 }
 
 static char * OVS_WARN_UNUSED_RESULT
@@ -4797,8 +4852,9 @@ format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a, struct ds *s)
 
 static enum ofperr
 decode_NXAST_RAW_CLONE(const struct ext_action_header *eah,
-                        enum ofp_version ofp_version,
-                        struct ofpbuf *out)
+                       enum ofp_version ofp_version,
+                       const struct vl_mff_map *vl_mff_map,
+                       struct ofpbuf *out)
 {
     int error;
     struct ofpbuf openflow;
@@ -4812,7 +4868,7 @@ decode_NXAST_RAW_CLONE(const struct ext_action_header *eah,
     error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
                                             ofp_version,
                                             1u << OVSINST_OFPIT11_APPLY_ACTIONS,
-                                            out, 0);
+                                            out, 0, vl_mff_map);
     clone = ofpbuf_push_uninit(out, sizeof *clone);
     out->header = &clone->ofpact;
     ofpact_finish_CLONE(out, &clone);
@@ -5258,14 +5314,20 @@ OFP_ASSERT(sizeof(struct nx_action_conntrack) == 24);
 
 static enum ofperr
 decode_ct_zone(const struct nx_action_conntrack *nac,
-               struct ofpact_conntrack *out)
+               struct ofpact_conntrack *out,
+               const struct vl_mff_map *vl_mff_map)
 {
     if (nac->zone_src) {
         enum ofperr error;
 
-        out->zone_src.field = mf_from_nxm_header(ntohl(nac->zone_src));
+        out->zone_src.field = mf_from_nxm_header(ntohl(nac->zone_src),
+                                                 vl_mff_map);
         out->zone_src.ofs = nxm_decode_ofs(nac->zone_ofs_nbits);
         out->zone_src.n_bits = nxm_decode_n_bits(nac->zone_ofs_nbits);
+        if (mf_vl_mff_not_mapped(out->zone_src.field, vl_mff_map)) {
+            return OFPERR_NXFMFC_INVALID_VL_MFF;
+        }
+
         error = mf_check_src(&out->zone_src, NULL);
         if (error) {
             return error;
@@ -5286,13 +5348,14 @@ decode_ct_zone(const struct nx_action_conntrack *nac,
 
 static enum ofperr
 decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
-                    enum ofp_version ofp_version, struct ofpbuf *out)
+                    enum ofp_version ofp_version,
+                    const struct vl_mff_map *vl_mff_map, struct ofpbuf *out)
 {
     const size_t ct_offset = ofpacts_pull(out);
     struct ofpact_conntrack *conntrack = ofpact_put_CT(out);
     conntrack->flags = ntohs(nac->flags);
 
-    int error = decode_ct_zone(nac, conntrack);
+    int error = decode_ct_zone(nac, conntrack, vl_mff_map);
     if (error) {
         goto out;
     }
@@ -5306,7 +5369,7 @@ decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
     error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
                                             ofp_version,
                                             1u << OVSINST_OFPIT11_APPLY_ACTIONS,
-                                            out, OFPACT_CT);
+                                            out, OFPACT_CT, vl_mff_map);
     if (error) {
         goto out;
     }
@@ -5347,7 +5410,7 @@ encode_CT(const struct ofpact_conntrack *conntrack,
     nac = put_NXAST_CT(out);
     nac->flags = htons(conntrack->flags);
     if (conntrack->zone_src.field) {
-        nac->zone_src = htonl(mf_nxm_header(conntrack->zone_src.field->id));
+        nac->zone_src = htonl(nxm_header_from_mff(conntrack->zone_src.field));
         nac->zone_ofs_nbits = nxm_encode_ofs_nbits(conntrack->zone_src.ofs,
                                                    conntrack->zone_src.n_bits);
     } else {
@@ -6191,7 +6254,8 @@ log_bad_action(const struct ofp_action_header *actions, size_t actions_len,
 
 static enum ofperr
 ofpacts_decode(const void *actions, size_t actions_len,
-               enum ofp_version ofp_version, struct ofpbuf *ofpacts)
+               enum ofp_version ofp_version,
+               const struct vl_mff_map *vl_mff_map, struct ofpbuf *ofpacts)
 {
     struct ofpbuf openflow = ofpbuf_const_initializer(actions, actions_len);
     while (openflow.size) {
@@ -6202,7 +6266,8 @@ ofpacts_decode(const void *actions, size_t actions_len,
 
         error = ofpact_pull_raw(&openflow, ofp_version, &raw, &arg);
         if (!error) {
-            error = ofpact_decode(action, raw, ofp_version, arg, ofpacts);
+            error = ofpact_decode(action, raw, ofp_version, arg, vl_mff_map,
+                                  ofpacts);
         }
 
         if (error) {
@@ -6219,7 +6284,8 @@ ofpacts_pull_openflow_actions__(struct ofpbuf *openflow,
                                 enum ofp_version version,
                                 uint32_t allowed_ovsinsts,
                                 struct ofpbuf *ofpacts,
-                                enum ofpact_type outer_action)
+                                enum ofpact_type outer_action,
+                                const struct vl_mff_map *vl_mff_map)
 {
     const struct ofp_action_header *actions;
     size_t orig_size = ofpacts->size;
@@ -6239,7 +6305,7 @@ ofpacts_pull_openflow_actions__(struct ofpbuf *openflow,
         return OFPERR_OFPBRC_BAD_LEN;
     }
 
-    error = ofpacts_decode(actions, actions_len, version, ofpacts);
+    error = ofpacts_decode(actions, actions_len, version, vl_mff_map, ofpacts);
     if (error) {
         ofpacts->size = orig_size;
         return error;
@@ -6274,11 +6340,12 @@ enum ofperr
 ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
                               unsigned int actions_len,
                               enum ofp_version version,
+                              const struct vl_mff_map *vl_mff_map,
                               struct ofpbuf *ofpacts)
 {
     return ofpacts_pull_openflow_actions__(openflow, actions_len, version,
                                            1u << OVSINST_OFPIT11_APPLY_ACTIONS,
-                                           ofpacts, 0);
+                                           ofpacts, 0, vl_mff_map);
 }
 
 /* OpenFlow 1.1 actions. */
@@ -6517,13 +6584,14 @@ ofpacts_execute_action_set(struct ofpbuf *action_list,
 static enum ofperr
 ofpacts_decode_for_action_set(const struct ofp_action_header *in,
                               size_t n_in, enum ofp_version version,
+                              const struct vl_mff_map *vl_mff_map,
                               struct ofpbuf *out)
 {
     enum ofperr error;
     struct ofpact *a;
     size_t start = out->size;
 
-    error = ofpacts_decode(in, n_in, version, out);
+    error = ofpacts_decode(in, n_in, version, vl_mff_map, out);
 
     if (error) {
         return error;
@@ -6821,6 +6889,7 @@ enum ofperr
 ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
                                    unsigned int instructions_len,
                                    enum ofp_version version,
+                                   const struct vl_mff_map *vl_mff_map,
                                    struct ofpbuf *ofpacts)
 {
     const struct ofp11_instruction *instructions;
@@ -6832,7 +6901,7 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
         return ofpacts_pull_openflow_actions__(openflow, instructions_len,
                                                version,
                                                (1u << N_OVS_INSTRUCTIONS) - 1,
-                                               ofpacts, 0);
+                                               ofpacts, 0, vl_mff_map);
     }
 
     if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) {
@@ -6875,7 +6944,8 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
 
         get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS],
                                      &actions, &actions_len);
-        error = ofpacts_decode(actions, actions_len, version, ofpacts);
+        error = ofpacts_decode(actions, actions_len, version, vl_mff_map,
+                               ofpacts);
         if (error) {
             goto exit;
         }
@@ -6895,7 +6965,7 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
         get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS],
                                      &actions, &actions_len);
         error = ofpacts_decode_for_action_set(actions, actions_len,
-                                              version, ofpacts);
+                                              version, vl_mff_map, ofpacts);
         if (error) {
             goto exit;
         }
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 7719e5c..22f7fa8 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -797,7 +797,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     protocol = ofputil_protocol_set_tid(protocol, true);
 
     ofpbuf_init(&ofpacts, 64);
-    error = ofputil_decode_flow_mod(&fm, oh, protocol, NULL, &ofpacts,
+    error = ofputil_decode_flow_mod(&fm, oh, protocol, NULL, NULL, &ofpacts,
                                     OFPP_MAX, 255);
     if (error) {
         ofpbuf_uninit(&ofpacts);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index b5e294b..fec2ef3 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -1571,6 +1571,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                         const struct ofp_header *oh,
                         enum ofputil_protocol protocol,
                         const struct tun_table *tun_table,
+                        const struct vl_mff_map *vl_mff_map,
                         struct ofpbuf *ofpacts,
                         ofp_port_t max_port, uint8_t max_table)
 {
@@ -1723,8 +1724,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         return OFPERR_OFPFMFC_BAD_COMMAND;
     }
 
-    error = ofpacts_pull_openflow_instructions(&b, b.size,
-                                               oh->version, ofpacts);
+    error = ofpacts_pull_openflow_instructions(&b, b.size, oh->version,
+                                               vl_mff_map, ofpacts);
     if (error) {
         return error;
     }
@@ -2996,7 +2997,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
     }
 
     if (ofpacts_pull_openflow_instructions(msg, instructions_len, oh->version,
-                                           ofpacts)) {
+                                           NULL, ofpacts)) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
         return EINVAL;
     }
@@ -4017,7 +4018,7 @@ parse_actions_property(struct ofpbuf *property, enum ofp_version version,
     }
 
     return ofpacts_pull_openflow_actions(property, property->size,
-                                         version, ofpacts);
+                                         version, NULL, ofpacts);
 }
 
 /* This is like ofputil_decode_packet_in(), except that it decodes the
@@ -4170,7 +4171,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         }
 
         error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
-                                              oh->version, ofpacts);
+                                              oh->version, NULL, ofpacts);
         if (error) {
             return error;
         }
@@ -4182,7 +4183,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         po->in_port = u16_to_ofp(ntohs(opo->in_port));
 
         error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
-                                              oh->version, ofpacts);
+                                              oh->version, NULL, ofpacts);
         if (error) {
             return error;
         }
@@ -5156,7 +5157,7 @@ parse_oxms(struct ofpbuf *payload, bool loose,
         enum ofperr error;
         bool hasmask;
 
-        error = nx_pull_header(payload, &field, &hasmask);
+        error = nx_pull_header(payload, NULL, &field, &hasmask);
         if (!error) {
             bitmap_set1(hasmask ? masked.bm : exact.bm, field->id);
         } else if (error != OFPERR_OFPBMC_BAD_FIELD || !loose) {
@@ -6723,7 +6724,7 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update,
 
         actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
         error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version,
-                                              ofpacts);
+                                              NULL, ofpacts);
         if (error) {
             return error;
         }
@@ -8707,7 +8708,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, &ofpacts);
+                                              version, NULL, &ofpacts);
         if (error) {
             ofpbuf_uninit(&ofpacts);
             ofputil_bucket_list_destroy(buckets);
@@ -8781,7 +8782,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,
-                                            &ofpacts);
+                                            NULL, &ofpacts);
         if (err) {
             goto err;
         }
diff --git a/lib/tun-metadata.c b/lib/tun-metadata.c
index 92643b3..eb27e25 100644
--- a/lib/tun-metadata.c
+++ b/lib/tun-metadata.c
@@ -32,7 +32,7 @@
 
 struct tun_meta_entry {
     struct hmap_node node;      /* In struct tun_table's key_hmap. */
-    uint32_t key;               /* (class << 16) | type. */
+    uint32_t key;               /* (class << 8) | type. */
     struct tun_metadata_loc loc;
     bool valid;                 /* True if allocated to a class and type. */
 };
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 3739ebc..b08a3fd 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -59,6 +59,7 @@ struct bfd_cfg;
 struct meter;
 struct ofoperation;
 struct ofproto_packet_out;
+struct vl_mff_map;
 
 extern struct ovs_mutex ofproto_mutex;
 
@@ -126,6 +127,10 @@ struct ofproto {
 
      /* Tunnel TLV mapping table. */
      OVSRCU_TYPE(struct tun_table *) metadata_tab;
+
+    /* Variable length mf_field mapping map. Stores all variable length
+     * meta-flow fields (struct mf_field) in a switch. */
+    struct vl_mff_map vl_mff_map;
 };
 
 void ofproto_init_tables(struct ofproto *, int n_tables);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 0b5e0fa..dd7b2da 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -531,6 +531,9 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     }
     ovsrcu_set(&ofproto->metadata_tab, tun_metadata_alloc(NULL));
 
+    ovs_mutex_init(&ofproto->vl_mff_map.mutex);
+    cmap_init(&ofproto->vl_mff_map.cmap);
+
     error = ofproto->ofproto_class->construct(ofproto);
     if (error) {
         VLOG_ERR("failed to open datapath %s: %s",
@@ -1571,6 +1574,13 @@ ofproto_destroy__(struct ofproto *ofproto)
     hmap_remove(&all_ofprotos, &ofproto->hmap_node);
     tun_metadata_free(ovsrcu_get_protected(struct tun_table *,
                                            &ofproto->metadata_tab));
+
+    ovs_mutex_lock(&ofproto->vl_mff_map.mutex);
+    mf_vl_mff_map_clear(&ofproto->vl_mff_map);
+    ovs_mutex_unlock(&ofproto->vl_mff_map.mutex);
+    cmap_destroy(&ofproto->vl_mff_map.cmap);
+    ovs_mutex_destroy(&ofproto->vl_mff_map.mutex);
+
     free(ofproto->name);
     free(ofproto->type);
     free(ofproto->mfr_desc);
@@ -5707,7 +5717,8 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
-                                    ofproto_get_tun_tab(ofproto), &ofpacts,
+                                    ofproto_get_tun_tab(ofproto),
+                                    &ofproto->vl_mff_map, &ofpacts,
                                     u16_to_ofp(ofproto->max_ports),
                                     ofproto->n_tables);
     if (!error) {
@@ -7707,7 +7718,7 @@ handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh)
         error = ofputil_decode_flow_mod(&fm, badd.msg,
                                         ofconn_get_protocol(ofconn),
                                         ofproto_get_tun_tab(ofproto),
-                                        &ofpacts,
+                                        &ofproto->vl_mff_map, &ofpacts,
                                         u16_to_ofp(ofproto->max_ports),
                                         ofproto->n_tables);
         if (!error) {
@@ -7767,6 +7778,11 @@ handle_tlv_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     if (!error) {
         ovsrcu_set(&ofproto->metadata_tab, new_tab);
         tun_metadata_postpone_free(old_tab);
+
+        ovs_mutex_lock(&ofproto->vl_mff_map.mutex);
+        error = mf_vl_mff_map_mod_from_tun_metadata(&ofproto->vl_mff_map,
+                                                    &ttm);
+        ovs_mutex_unlock(&ofproto->vl_mff_map.mutex);
     }
 
     ofputil_uninit_tlv_table(&ttm.mappings);
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 7320a84..198263b 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -166,7 +166,8 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
     enum ofp_version version = rconn_get_version(swconn);
 
     reload_metadata(&ofpacts, md);
-    enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size, version, &ofpacts);
+    enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
+                                                      version, NULL, &ofpacts);
     if (error) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         VLOG_WARN_RL(&rl, "failed to parse arp actions (%s)",
@@ -202,7 +203,7 @@ pinctrl_handle_put_dhcp_opts(
 
     /* Parse result field. */
     const struct mf_field *f;
-    enum ofperr ofperr = nx_pull_header(userdata, &f, NULL);
+    enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
     if (ofperr) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr));
@@ -493,7 +494,7 @@ pinctrl_handle_put_dhcpv6_opts(
 
     /* Parse result field. */
     const struct mf_field *f;
-    enum ofperr ofperr = nx_pull_header(userdata, &f, NULL);
+    enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
     if (ofperr) {
        VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr));
        goto exit;
@@ -1396,7 +1397,7 @@ pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md,
     reload_metadata(&ofpacts, md);
 
     enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
-                                                      version, &ofpacts);
+                                                      version, NULL, &ofpacts);
     if (error) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         VLOG_WARN_RL(&rl, "failed to parse actions for 'na' (%s)",
diff --git a/tests/ofproto.at b/tests/ofproto.at
index ad8df28..48f30cf 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -5685,3 +5685,128 @@ NXST_FLOW reply:
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto - flow mod with tunnel metadata])
+AT_KEYWORDS([ofp-actions])
+OVS_VSWITCHD_START
+
+AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"])
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 actions=move:tun_metadata0[[0..31]]->NXM_NX_REG0[[]]"])
+
+dnl Check the length of tun_metadata0 in the replied OXM header.
+dnl Ignore the first 0x50 bytes of hex dump from the reply msg since the NXM
+dnl header that describes the tunnel metadata starts at offset 0x50.
+AT_CHECK([ovs-ofctl dump-flows br0 -mmmm], [0], [stdout])
+AT_CHECK([sed -e 's/duration=[[0-9.]]*s/duration=?s/' -e 's/idle_age=[[0-9]]*/idle_age=?/' -e '/^000000[[0-4]]0 / d' stdout], [0], [dnl
+NXST_FLOW reply (xid=0x4):
+ cookie=0x0, duration=?s, table=0, n_packets=0, n_bytes=0, idle_age=?, in_port=1 actions=move:NXM_NX_TUN_METADATA0[[0..31]]->NXM_NX_REG0[[]]
+00000050  ff ff 00 18 00 00 23 20-00 06 00 20 00 00 00 00 |......# ... ....|
+00000060  00 01 50 04 00 01 00 04-                        |..P.....        |
+])
+
+dnl Check actions that may use tun_metadata
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=move:tun_metadata1[[0..31]]->NXM_NX_REG0[[]]"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=move:tun_metadata0[[32..63]]->NXM_NX_REG0[[]]"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=push:tun_metadata1[[0..31]]"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=pop:tun_metadata0[[32..63]]"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=3, actions=load:0x11223344->tun_metadata1"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=output:tun_metadata1[[0..31]]"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=output:tun_metadata0[[32..63]]"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=multipath(eth_src,50,modulo_n,1,0,tun_metadata1[[0..31]])"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=multipath(eth_src,50,modulo_n,1,0,tun_metadata0[[32..63]])"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=bundle_load(eth_src,50,hrw,ofport,tun_metadata1[[0..31]], slaves:4,8)"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=bundle_load(eth_src,50,hrw,ofport,tun_metadata0[[32..63]], slaves:4,8)"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=learn(tun_metadata1[[0..31]]=reg0[[0..31]])"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=learn(tun_metadata0[[32..63]]=reg0[[0..31]])"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=clone(move:tun_metadata1[[0..31]]->reg0[[0..31]])"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2 actions=clone(move:tun_metadata0[[32..63]]->reg0[[0..31]])"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=tun_metadata1[[0..15]],exec(set_field:0x01->ct_mark))"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=tun_metadata0[[32..47]],exec(set_field:0x01->ct_mark))"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=1,exec(move:tun_metadata1[[0..31]]->ct_mark))"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: NXFMFC_INVALID_VL_MFF
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "ip actions=ct(commit,zone=1,exec(move:tun_metadata0[[32..63]]->ct_mark))"], [1], [], [stderr])
+AT_CHECK([strip_xids < stderr | sed '/FLOW_MOD/,$d'], [0], [dnl
+OFPT_ERROR: OFPBAC_BAD_SET_LEN
+])
+
+AT_CHECK([ovs-ofctl dump-flows br0], [0], [stdout])
+AT_CHECK([sed -e 's/duration=[[0-9.]]*s/duration=?s/' -e 's/idle_age=[[0-9]]*/idle_age=?/' stdout], [0], [dnl
+NXST_FLOW reply (xid=0x4):
+ cookie=0x0, duration=?s, table=0, n_packets=0, n_bytes=0, idle_age=?, in_port=1 actions=move:NXM_NX_TUN_METADATA0[[0..31]]->NXM_NX_REG0[[]]
+])
+
+OVS_VSWITCHD_STOP(["/NXFMFC_INVALID_VL_MFF/d
+/tun_metadata0/d
+/OFPBAC_BAD_SET_LEN/d"])
+AT_CLEANUP
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index e4935d5..42369ce 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -3871,7 +3871,7 @@ ofctl_parse_actions__(const char *version_s, bool instructions)
         error = (instructions
                  ? ofpacts_pull_openflow_instructions
                  : ofpacts_pull_openflow_actions)(
-                     &of_in, of_in.size, version, &ofpacts);
+                     &of_in, of_in.size, version, NULL, &ofpacts);
         if (!error && instructions) {
             /* Verify actions, enforce consistency. */
             enum ofputil_protocol protocol;
-- 
2.7.4



More information about the dev mailing list