]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/nx-match.c
netdev: Reject empty names in netdev_open().
[mirror_ovs.git] / lib / nx-match.c
index 874e1fb0825f78a7f820e31858f79edcea562034..e9d649bf74bdbb3c023527f462fe1f43e1de7fab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include "classifier.h"
 #include "colors.h"
-#include "dynamic-string.h"
-#include "hmap.h"
-#include "meta-flow.h"
-#include "ofp-actions.h"
-#include "ofp-errors.h"
-#include "ofp-util.h"
-#include "ofpbuf.h"
+#include "openvswitch/hmap.h"
 #include "openflow/nicira-ext.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/meta-flow.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/ofp-util.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/vlog.h"
 #include "packets.h"
-#include "shash.h"
+#include "openvswitch/shash.h"
 #include "tun-metadata.h"
 #include "unaligned.h"
 #include "util.h"
-#include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(nx_match);
 
@@ -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 the header with the configured
+ * length 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_invalid(*field, vl_mff_map)) {
+            return OFPERR_NXFMFC_INVALID_TLV_FIELD;
         }
     }
 
@@ -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;
     }
@@ -474,11 +505,13 @@ nx_pull_match_entry(struct ofpbuf *b, bool allow_cookie,
 
 static enum ofperr
 nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
-            struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask)
+            struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask,
+            const struct tun_table *tun_table)
 {
     ovs_assert((cookie != NULL) == (cookie_mask != NULL));
 
     match_init_catchall(match);
+    match->flow.tunnel.metadata.tab = tun_table;
     if (cookie) {
         *cookie = *cookie_mask = htonll(0);
     }
@@ -505,7 +538,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
                 *cookie = value.be64;
                 *cookie_mask = mask.be64;
             }
-        } else if (!mf_are_prereqs_ok(field, &match->flow)) {
+        } else if (!mf_are_prereqs_ok(field, &match->flow, NULL)) {
             error = OFPERR_OFPBMC_BAD_PREREQ;
         } else if (!mf_is_all_wild(field, &match->wc)) {
             error = OFPERR_OFPBMC_DUP_FIELD;
@@ -529,13 +562,15 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
         }
     }
 
+    match->flow.tunnel.metadata.tab = NULL;
     return 0;
 }
 
 static enum ofperr
 nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
                 struct match *match,
-                ovs_be64 *cookie, ovs_be64 *cookie_mask)
+                ovs_be64 *cookie, ovs_be64 *cookie_mask,
+                const struct tun_table *tun_table)
 {
     uint8_t *p = NULL;
 
@@ -549,7 +584,8 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
         }
     }
 
-    return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask);
+    return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask,
+                       tun_table);
 }
 
 /* Parses the nx_match formatted match description in 'b' with length
@@ -562,9 +598,11 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
  * Returns 0 if successful, otherwise an OpenFlow error code. */
 enum ofperr
 nx_pull_match(struct ofpbuf *b, unsigned int match_len, struct match *match,
-              ovs_be64 *cookie, ovs_be64 *cookie_mask)
+              ovs_be64 *cookie, ovs_be64 *cookie_mask,
+              const struct tun_table *tun_table)
 {
-    return nx_pull_match__(b, match_len, true, match, cookie, cookie_mask);
+    return nx_pull_match__(b, match_len, true, match, cookie, cookie_mask,
+                           tun_table);
 }
 
 /* Behaves the same as nx_pull_match(), but skips over unknown NXM headers,
@@ -572,13 +610,16 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, struct match *match,
 enum ofperr
 nx_pull_match_loose(struct ofpbuf *b, unsigned int match_len,
                     struct match *match,
-                    ovs_be64 *cookie, ovs_be64 *cookie_mask)
+                    ovs_be64 *cookie, ovs_be64 *cookie_mask,
+                    const struct tun_table *tun_table)
 {
-    return nx_pull_match__(b, match_len, false, match, cookie, cookie_mask);
+    return nx_pull_match__(b, match_len, false, match, cookie, cookie_mask,
+                           tun_table);
 }
 
 static enum ofperr
-oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
+oxm_pull_match__(struct ofpbuf *b, bool strict,
+                 const struct tun_table *tun_table, struct match *match)
 {
     struct ofp11_match_header *omh = b->data;
     uint8_t *p;
@@ -606,7 +647,7 @@ oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
     }
 
     return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh,
-                       strict, match, NULL, NULL);
+                       strict, match, NULL, NULL, tun_table);
 }
 
 /* Parses the oxm formatted match description preceded by a struct
@@ -616,17 +657,19 @@ oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
  *
  * Returns 0 if successful, otherwise an OpenFlow error code. */
 enum ofperr
-oxm_pull_match(struct ofpbuf *b, struct match *match)
+oxm_pull_match(struct ofpbuf *b, const struct tun_table *tun_table,
+               struct match *match)
 {
-    return oxm_pull_match__(b, true, match);
+    return oxm_pull_match__(b, true, tun_table, match);
 }
 
 /* Behaves the same as oxm_pull_match() with one exception.  Skips over unknown
  * OXM headers instead of failing with an error when they are encountered. */
 enum ofperr
-oxm_pull_match_loose(struct ofpbuf *b, struct match *match)
+oxm_pull_match_loose(struct ofpbuf *b, const struct tun_table *tun_table,
+                     struct match *match)
 {
-    return oxm_pull_match__(b, false, match);
+    return oxm_pull_match__(b, false, tun_table, match);
 }
 
 /* Parses the OXM match description in the 'oxm_len' bytes in 'oxm'.  Stores
@@ -636,9 +679,10 @@ oxm_pull_match_loose(struct ofpbuf *b, struct match *match)
  *
  * Returns 0 if successful, otherwise an OpenFlow error code. */
 enum ofperr
-oxm_decode_match(const void *oxm, size_t oxm_len, struct match *match)
+oxm_decode_match(const void *oxm, size_t oxm_len,
+                 const struct tun_table *tun_table, struct match *match)
 {
-    return nx_pull_raw(oxm, oxm_len, true, match, NULL, NULL);
+    return nx_pull_raw(oxm, oxm_len, true, match, NULL, NULL, tun_table);
 }
 
 /* Verify an array of OXM TLVs treating value of each TLV as a mask,
@@ -655,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;
@@ -861,7 +906,7 @@ nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
                         match->wc.masks.tp_src);
             nxm_put_16m(b, MFF_SCTP_DST, oxm, flow->tp_dst,
                         match->wc.masks.tp_dst);
-        } else if (is_icmpv4(flow)) {
+        } else if (is_icmpv4(flow, NULL)) {
             if (match->wc.masks.tp_src) {
                 nxm_put_8(b, MFF_ICMPV4_TYPE, oxm,
                           ntohs(flow->tp_src));
@@ -870,7 +915,7 @@ nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
                 nxm_put_8(b, MFF_ICMPV4_CODE, oxm,
                           ntohs(flow->tp_dst));
             }
-        } else if (is_icmpv6(flow)) {
+        } else if (is_icmpv6(flow, NULL)) {
             if (match->wc.masks.tp_src) {
                 nxm_put_8(b, MFF_ICMPV6_TYPE, oxm,
                           ntohs(flow->tp_src));
@@ -879,8 +924,7 @@ nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
                 nxm_put_8(b, MFF_ICMPV6_CODE, oxm,
                           ntohs(flow->tp_dst));
             }
-            if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
-                flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+            if (is_nd(flow, NULL)) {
                 nxm_put_ipv6(b, MFF_ND_TARGET, oxm,
                              &flow->nd_target, &match->wc.masks.nd_target);
                 if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
@@ -918,7 +962,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36);
 
     /* Metadata. */
     if (match->wc.masks.dp_hash) {
@@ -1179,12 +1223,15 @@ void
 oxm_format_field_array(struct ds *ds, const struct field_array *fa)
 {
     size_t start_len = ds->length;
-    int i;
+    size_t i, offset = 0;
 
-    for (i = 0; i < MFF_N_IDS; i++) {
-        if (bitmap_is_set(fa->used.bm, i)) {
-            nx_format_mask_tlv(ds, i, &fa->value[i]);
-        }
+    BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fa->used.bm) {
+        const struct mf_field *mf = mf_from_id(i);
+        union mf_value value;
+
+        memcpy(&value, fa->values + offset, mf->n_bytes);
+        nx_format_mask_tlv(ds, i, &value);
+        offset += mf->n_bytes;
     }
 
     if (ds->length > start_len) {
@@ -1206,7 +1253,6 @@ oxm_put_field_array(struct ofpbuf *b, const struct field_array *fa,
                     enum ofp_version version)
 {
     size_t start_len = b->size;
-    int i;
 
     /* Field arrays are only used with the group selection method
      * property and group properties are only available in OpenFlow 1.5+.
@@ -1221,13 +1267,17 @@ oxm_put_field_array(struct ofpbuf *b, const struct field_array *fa,
      */
     ovs_assert(version >= OFP15_VERSION);
 
-    for (i = 0; i < MFF_N_IDS; i++) {
-        if (bitmap_is_set(fa->used.bm, i)) {
-            int len = mf_field_len(mf_from_id(i), &fa->value[i], NULL, NULL);
-            nxm_put__(b, i, version,
-                      &fa->value[i].u8 + mf_from_id(i)->n_bytes - len, NULL,
-                      len);
-        }
+    size_t i, offset = 0;
+
+    BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fa->used.bm) {
+        const struct mf_field *mf = mf_from_id(i);
+        union mf_value value;
+
+        memcpy(&value, fa->values + offset, mf->n_bytes);
+
+        int len = mf_field_len(mf, &value, NULL, NULL);
+        nxm_put__(b, i, version, &value + mf->n_bytes - len, NULL, len);
+        offset += mf->n_bytes;
     }
 
     return b->size - start_len;
@@ -1249,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_len(b, mff->id, version, masked, mff->n_bytes);
+    } else {
+        nx_put_header(b, mff->id, version, masked);
+    }
+}
+
 static void
 nx_put_header_len(struct ofpbuf *b, enum mf_field_id field,
                   enum ofp_version version, bool masked, size_t n_bytes)
@@ -1263,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);
@@ -1301,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;
         }
@@ -1468,7 +1527,6 @@ nx_match_from_string_raw(const char *s, struct ofpbuf *b)
         const char *name;
         uint64_t header;
         ovs_be64 nw_header;
-        ovs_be64 *header_ptr;
         int name_len;
         size_t n;
 
@@ -1485,10 +1543,10 @@ nx_match_from_string_raw(const char *s, struct ofpbuf *b)
 
         s += name_len + 1;
 
-        header_ptr = ofpbuf_put_uninit(b, nxm_header_len(header));
+        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) {
@@ -1508,7 +1566,7 @@ nx_match_from_string_raw(const char *s, struct ofpbuf *b)
             }
         }
         nw_header = htonll(header);
-        memcpy(header_ptr, &nw_header, nxm_header_len(header));
+        memcpy(b->header, &nw_header, nxm_header_len(header));
 
         if (nxm_hasmask(header)) {
             s += strspn(s, " ");
@@ -1618,30 +1676,6 @@ nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
 \f
 /* nxm_execute_reg_move(). */
 
-void
-nxm_execute_reg_move(const struct ofpact_reg_move *move,
-                     struct flow *flow, struct flow_wildcards *wc)
-{
-    union mf_value src_value;
-    union mf_value dst_value;
-
-    mf_mask_field_and_prereqs(move->dst.field, wc);
-    mf_mask_field_and_prereqs(move->src.field, wc);
-
-    /* A flow may wildcard nw_frag.  Do nothing if setting a transport
-     * header field on a packet that does not have them. */
-    if (mf_are_prereqs_ok(move->dst.field, flow)
-        && mf_are_prereqs_ok(move->src.field, flow)) {
-
-        mf_get_value(move->dst.field, flow, &dst_value);
-        mf_get_value(move->src.field, flow, &src_value);
-        bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
-                     &dst_value, move->dst.field->n_bytes, move->dst.ofs,
-                     move->src.n_bits);
-        mf_set_flow_value(move->dst.field, &dst_value, flow);
-    }
-}
-
 void
 nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
              struct flow *flow, struct flow_wildcards *wc)
@@ -1711,25 +1745,52 @@ nxm_stack_pop_check(const struct ofpact_stack *pop,
     return mf_check_dst(&pop->subfield, flow);
 }
 
-/* nxm_execute_stack_push(), nxm_execute_stack_pop(). */
-static void
-nx_stack_push(struct ofpbuf *stack, union mf_subvalue *v)
+/* nxm_execute_stack_push(), nxm_execute_stack_pop().
+ *
+ * A stack is an ofpbuf with 'data' pointing to the bottom of the stack and
+ * 'size' indexing the top of the stack.  Each value of some byte length is
+ * stored to the stack immediately followed by the length of the value as an
+ * unsigned byte.  This way a POP operation can first read the length byte, and
+ * then the appropriate number of bytes from the stack.  This also means that
+ * it is only possible to traverse the stack from top to bottom.  It is
+ * possible, however, to push values also to the bottom of the stack, which is
+ * useful when a stack has been serialized to a wire format in reverse order
+ * (topmost value first).
+ */
+
+/* Push value 'v' of length 'bytes' to the top of 'stack'. */
+void
+nx_stack_push(struct ofpbuf *stack, const void *v, uint8_t bytes)
 {
-    ofpbuf_put(stack, v, sizeof *v);
+    ofpbuf_put(stack, v, bytes);
+    ofpbuf_put(stack, &bytes, sizeof bytes);
 }
 
-static union mf_subvalue *
-nx_stack_pop(struct ofpbuf *stack)
+/* Push value 'v' of length 'bytes' to the bottom of 'stack'. */
+void
+nx_stack_push_bottom(struct ofpbuf *stack, const void *v, uint8_t bytes)
 {
-    union mf_subvalue *v = NULL;
-
-    if (stack->size) {
+    ofpbuf_push(stack, &bytes, sizeof bytes);
+    ofpbuf_push(stack, v, bytes);
+}
 
-        stack->size -= sizeof *v;
-        v = (union mf_subvalue *) ofpbuf_tail(stack);
+/* Pop the topmost value from 'stack', returning a pointer to the value in the
+ * stack and the length of the value in '*bytes'.  In case of underflow a NULL
+ * is returned and length is returned as zero via '*bytes'. */
+void *
+nx_stack_pop(struct ofpbuf *stack, uint8_t *bytes)
+{
+    if (!stack->size) {
+        *bytes = 0;
+        return NULL;
     }
 
-    return v;
+    stack->size -= sizeof *bytes;
+    memcpy(bytes, ofpbuf_tail(stack), sizeof *bytes);
+
+    ovs_assert(stack->size >= *bytes);
+    stack->size -= *bytes;
+    return ofpbuf_tail(stack);
 }
 
 void
@@ -1737,39 +1798,41 @@ nxm_execute_stack_push(const struct ofpact_stack *push,
                        const struct flow *flow, struct flow_wildcards *wc,
                        struct ofpbuf *stack)
 {
-    union mf_subvalue mask_value;
     union mf_subvalue dst_value;
 
-    memset(&mask_value, 0xff, sizeof mask_value);
-    mf_write_subfield_flow(&push->subfield, &mask_value, &wc->masks);
+    mf_write_subfield_flow(&push->subfield,
+                           (union mf_subvalue *)&exact_match_mask,
+                           &wc->masks);
 
     mf_read_subfield(&push->subfield, flow, &dst_value);
-    nx_stack_push(stack, &dst_value);
+    uint8_t bytes = DIV_ROUND_UP(push->subfield.n_bits, 8);
+    nx_stack_push(stack, &dst_value.u8[sizeof dst_value - bytes], bytes);
 }
 
-void
+bool
 nxm_execute_stack_pop(const struct ofpact_stack *pop,
                       struct flow *flow, struct flow_wildcards *wc,
                       struct ofpbuf *stack)
 {
-    union mf_subvalue *src_value;
-
-    src_value = nx_stack_pop(stack);
-
-    /* Only pop if stack is not empty. Otherwise, give warning. */
-    if (src_value) {
-        union mf_subvalue mask_value;
+    uint8_t src_bytes;
+    const void *src = nx_stack_pop(stack, &src_bytes);
+    if (src) {
+        union mf_subvalue src_value;
+        uint8_t dst_bytes = DIV_ROUND_UP(pop->subfield.n_bits, 8);
 
-        memset(&mask_value, 0xff, sizeof mask_value);
-        mf_write_subfield_flow(&pop->subfield, &mask_value, &wc->masks);
-        mf_write_subfield_flow(&pop->subfield, src_value, flow);
-    } else {
-        if (!VLOG_DROP_WARN(&rl)) {
-            char *flow_str = flow_to_string(flow);
-            VLOG_WARN_RL(&rl, "Failed to pop from an empty stack. On flow\n"
-                           " %s", flow_str);
-            free(flow_str);
+        if (src_bytes < dst_bytes) {
+            memset(&src_value.u8[sizeof src_value - dst_bytes], 0,
+                   dst_bytes - src_bytes);
         }
+        memcpy(&src_value.u8[sizeof src_value - src_bytes], src, src_bytes);
+        mf_write_subfield_flow(&pop->subfield,
+                               (union mf_subvalue *)&exact_match_mask,
+                               &wc->masks);
+        mf_write_subfield_flow(&pop->subfield, &src_value, flow);
+        return true;
+    } else {
+        /* Attempted to pop from an empty stack. */
+        return false;
     }
 }
 \f
@@ -1819,7 +1882,7 @@ mf_parse_subfield_name(const char *name, int name_len, bool *wild)
 char * OVS_WARN_UNUSED_RESULT
 mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
 {
-    const struct mf_field *field;
+    const struct mf_field *field = NULL;
     const struct nxm_field *f;
     const char *name;
     int start, end;
@@ -1829,30 +1892,31 @@ mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
 
     s = *sp;
     name = s;
-    name_len = strcspn(s, "[");
-    if (s[name_len] != '[') {
-        return xasprintf("%s: missing [ looking for field name", *sp);
-    }
+    name_len = strcspn(s, "[-");
 
     f = mf_parse_subfield_name(name, name_len, &wild);
-    if (!f) {
+    field = f ? mf_from_id(f->id) : mf_from_name_len(name, name_len);
+    if (!field) {
         return xasprintf("%s: unknown field `%.*s'", *sp, name_len, s);
     }
-    field = mf_from_id(f->id);
 
     s += name_len;
-    if (ovs_scan(s, "[%d..%d]", &start, &end)) {
-        /* Nothing to do. */
-    } else if (ovs_scan(s, "[%d]", &start)) {
-        end = start;
-    } else if (!strncmp(s, "[]", 2)) {
-        start = 0;
-        end = field->n_bits - 1;
-    } else {
-        return xasprintf("%s: syntax error expecting [] or [<bit>] or "
-                         "[<start>..<end>]", *sp);
+    /* Assume full field. */
+    start = 0;
+    end = field->n_bits - 1;
+    if (*s == '[') {
+        if (!strncmp(s, "[]", 2)) {
+            /* Nothing to do. */
+        } else if (ovs_scan(s, "[%d..%d]", &start, &end)) {
+            /* Nothing to do. */
+        } else if (ovs_scan(s, "[%d]", &start)) {
+            end = start;
+        } else {
+            return xasprintf("%s: syntax error expecting [] or [<bit>] or "
+                             "[<start>..<end>]", *sp);
+        }
+        s = strchr(s, ']') + 1;
     }
-    s = strchr(s, ']') + 1;
 
     if (start > end) {
         return xasprintf("%s: starting bit %d is after ending bit %d",
@@ -2010,7 +2074,7 @@ nxm_init(void)
         hmap_init(&nxm_header_map);
         hmap_init(&nxm_name_map);
         for (int i = 0; i < MFF_N_IDS; i++) {
-            list_init(&nxm_mf_map[i]);
+            ovs_list_init(&nxm_mf_map[i]);
         }
         for (struct nxm_field_index *nfi = all_nxm_fields;
              nfi < &all_nxm_fields[ARRAY_SIZE(all_nxm_fields)]; nfi++) {
@@ -2018,7 +2082,7 @@ nxm_init(void)
                         hash_uint64(nxm_no_len(nfi->nf.header)));
             hmap_insert(&nxm_name_map, &nfi->name_node,
                         hash_string(nfi->nf.name, 0));
-            list_push_back(&nxm_mf_map[nfi->nf.id], &nfi->mf_node);
+            ovs_list_push_back(&nxm_mf_map[nfi->nf.id], &nfi->mf_node);
         }
         ovsthread_once_done(&once);
     }