[ovs-dev] [learning 5/6] meta-flow: New library for working with fields by id.

Ben Pfaff blp at nicira.com
Wed Aug 17 23:00:19 UTC 2011


OVS already has a fairly good set of functions for working with fields that
are known at compile time, but support for working with fields that are
known only at runtime is fairly limited (and fairly unneeded).  However,
with NXM identifiers becoming more and more widely used throughout Nicira
extensions, it's becoming corresponding more and more common to need to
refer to fields at runtime.  This new library represents a first attempt
at a systematic approach for doing so.
---
 lib/automake.mk  |    2 +
 lib/meta-flow.c  | 1739 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/meta-flow.h  |  207 +++++++
 lib/nx-match.c   |  362 +-----------
 lib/nx-match.def |   83 ++--
 lib/nx-match.h   |    3 +-
 lib/ofp-parse.c  |  412 +-------------
 7 files changed, 2035 insertions(+), 773 deletions(-)
 create mode 100644 lib/meta-flow.c
 create mode 100644 lib/meta-flow.h

diff --git a/lib/automake.mk b/lib/automake.mk
index 1fa2b46..bd7e095 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -75,6 +75,8 @@ lib_libopenvswitch_a_SOURCES = \
 	lib/lockfile.h \
 	lib/mac-learning.c \
 	lib/mac-learning.h \
+	lib/meta-flow.c \
+	lib/meta-flow.h \
 	lib/multipath.c \
 	lib/multipath.h \
 	lib/netdev-dummy.c \
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
new file mode 100644
index 0000000..7591b97
--- /dev/null
+++ b/lib/meta-flow.c
@@ -0,0 +1,1739 @@
+/*
+ * Copyright (c) 2011 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "meta-flow.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+
+#include "classifier.h"
+#include "dynamic-string.h"
+#include "ofp-util.h"
+#include "packets.h"
+#include "random.h"
+#include "shash.h"
+#include "socket-util.h"
+#include "unaligned.h"
+
+static const struct mf_field mf_fields[MFF_N_IDS] = {
+    /* ## -------- ## */
+    /* ## metadata ## */
+    /* ## -------- ## */
+
+    {
+        MFF_TUN_ID, "tun_id", NULL,
+        8, 64,
+        MFM_FULLY, 0,
+        MFS_HEXADECIMAL,
+        MFP_NONE,
+        NXM_NX_TUN_ID,
+    }, {
+        MFF_IN_PORT, "in_port", NULL,
+        2, 16,
+        MFM_NONE, FWW_IN_PORT,
+        MFS_OFP_PORT,
+        MFP_NONE,
+        NXM_OF_IN_PORT,
+    },
+
+#define REGISTER(IDX)                           \
+    {                                           \
+        MFF_REG##IDX, "reg" #IDX, NULL,         \
+        4, 32,                                  \
+        MFM_FULLY, 0,                           \
+        MFS_HEXADECIMAL,                        \
+        MFP_NONE,                               \
+        NXM_NX_REG(IDX),                        \
+    }
+#if FLOW_N_REGS > 0
+    REGISTER(0),
+#endif
+#if FLOW_N_REGS > 1
+    REGISTER(1),
+#endif
+#if FLOW_N_REGS > 2
+    REGISTER(2),
+#endif
+#if FLOW_N_REGS > 3
+    REGISTER(3),
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    /* ## -- ## */
+    /* ## L2 ## */
+    /* ## -- ## */
+
+    {
+        MFF_ETH_SRC, "eth_src", "dl_src",
+        6, 48,
+        MFM_NONE, FWW_DL_SRC,
+        MFS_ETHERNET,
+        MFP_NONE,
+        NXM_OF_ETH_SRC,
+    }, {
+        MFF_ETH_DST, "eth_dst", "dl_dst",
+        6, 48,
+        MFM_MCAST, 0,
+        MFS_ETHERNET,
+        MFP_NONE,
+        NXM_OF_ETH_DST,
+    }, {
+        MFF_ETH_TYPE, "eth_type", "dl_type",
+        2, 16,
+        MFM_NONE, FWW_DL_TYPE,
+        MFS_HEXADECIMAL,
+        MFP_NONE,
+        NXM_OF_ETH_TYPE,
+    },
+
+    {
+        MFF_VLAN_TCI, "vlan_tci", NULL,
+        2, 16,
+        MFM_FULLY, 0,
+        MFS_HEXADECIMAL,
+        MFP_NONE,
+        NXM_OF_VLAN_TCI,
+    }, {
+        MFF_VLAN_VID, "dl_vlan", NULL,
+        2, 12,
+        MFM_NONE, 0,
+        MFS_DECIMAL,
+        MFP_NONE,
+        0,
+    }, {
+        MFF_VLAN_PCP, "dl_vlan_pcp", NULL,
+        1, 3,
+        MFM_NONE, 0,
+        MFS_DECIMAL,
+        MFP_NONE,
+        0,
+    },
+
+    /* ## -- ## */
+    /* ## L3 ## */
+    /* ## -- ## */
+
+    {
+        MFF_IPV4_SRC, "ip_src", "nw_src",
+        4, 32,
+        MFM_CIDR, 0,
+        MFS_IPV4,
+        MFP_IPV4,
+        NXM_OF_IP_SRC,
+    }, {
+        MFF_IPV4_DST, "ip_dst", "nw_dst",
+        4, 32,
+        MFM_CIDR, 0,
+        MFS_IPV4,
+        MFP_IPV4,
+        NXM_OF_IP_DST,
+    },
+
+    {
+        MFF_IPV6_SRC, "ipv6_src", NULL,
+        16, 128,
+        MFM_CIDR, 0,
+        MFS_IPV6,
+        MFP_IPV6,
+        NXM_NX_IPV6_SRC,
+    }, {
+        MFF_IPV6_DST, "ipv6_dst", NULL,
+        16, 128,
+        MFM_CIDR, 0,
+        MFS_IPV6,
+        MFP_IPV6,
+        NXM_NX_IPV6_DST,
+    },
+
+    {
+        MFF_IP_PROTO, "nw_proto", NULL,
+        1, 8,
+        MFM_NONE, FWW_NW_PROTO,
+        MFS_DECIMAL,
+        MFP_IP_ANY,
+        NXM_OF_IP_PROTO,
+    }, {
+        MFF_IP_TOS, "nw_tos", NULL,
+        1, 8,
+        MFM_NONE, FWW_NW_TOS,
+        MFS_DECIMAL,
+        MFP_IP_ANY,
+        NXM_OF_IP_TOS,
+    },
+
+    {
+        MFF_ARP_OP, "arp_op", NULL,
+        2, 16,
+        MFM_NONE, FWW_NW_PROTO,
+        MFS_DECIMAL,
+        MFP_ARP,
+        NXM_OF_ARP_OP,
+    }, {
+        MFF_ARP_SPA, "arp_spa", NULL,
+        4, 32,
+        MFM_CIDR, 0,
+        MFS_IPV4,
+        MFP_ARP,
+        NXM_OF_ARP_SPA,
+    }, {
+        MFF_ARP_TPA, "arp_tpa", NULL,
+        4, 32,
+        MFM_CIDR, 0,
+        MFS_IPV4,
+        MFP_ARP,
+        NXM_OF_ARP_TPA,
+    }, {
+        MFF_ARP_SHA, "arp_sha", NULL,
+        6, 48,
+        MFM_NONE, FWW_ARP_SHA,
+        MFS_ETHERNET,
+        MFP_ARP,
+        NXM_NX_ARP_SHA,
+    }, {
+        MFF_ARP_THA, "arp_tha", NULL,
+        6, 48,
+        MFM_NONE, FWW_ARP_THA,
+        MFS_ETHERNET,
+        MFP_ARP,
+        NXM_NX_ARP_THA,
+    },
+
+    /* ## -- ## */
+    /* ## L4 ## */
+    /* ## -- ## */
+
+    {
+        MFF_TCP_SRC, "tcp_src", "tp_src",
+        2, 16,
+        MFM_NONE, FWW_TP_SRC,
+        MFS_DECIMAL,
+        MFP_TCP,
+        NXM_OF_TCP_SRC,
+    }, {
+        MFF_TCP_DST, "tcp_dst", "tp_dst",
+        2, 16,
+        MFM_NONE, FWW_TP_DST,
+        MFS_DECIMAL,
+        MFP_TCP,
+        NXM_OF_TCP_DST,
+    },
+
+    {
+        MFF_UDP_SRC, "udp_src", NULL,
+        2, 16,
+        MFM_NONE, FWW_TP_SRC,
+        MFS_DECIMAL,
+        MFP_UDP,
+        NXM_OF_UDP_SRC,
+    }, {
+        MFF_UDP_DST, "udp_dst", NULL,
+        2, 16,
+        MFM_NONE, FWW_TP_DST,
+        MFS_DECIMAL,
+        MFP_UDP,
+        NXM_OF_UDP_DST,
+    },
+
+    {
+        MFF_ICMP_TYPE, "icmp_type", NULL,
+        1, 8,
+        MFM_NONE, FWW_TP_SRC,
+        MFS_DECIMAL,
+        MFP_ICMP_ANY,
+        NXM_OF_ICMP_TYPE,
+    }, {
+        MFF_ICMP_CODE, "icmp_code", NULL,
+        1, 8,
+        MFM_NONE, FWW_TP_SRC,
+        MFS_DECIMAL,
+        MFP_ICMP_ANY,
+        NXM_OF_ICMP_CODE,
+    },
+
+    /* ## ---- ## */
+    /* ## L"5" ## */
+    /* ## ---- ## */
+
+    {
+        MFF_ND_TARGET, "nd_target", NULL,
+        16, 128,
+        MFM_NONE, FWW_ND_TARGET,
+        MFS_IPV6,
+        MFP_ND,
+        NXM_NX_ND_TARGET,
+    }, {
+        MFF_ND_SLL, "nd_sll", NULL,
+        6, 48,
+        MFM_NONE, FWW_ARP_SHA,
+        MFS_ETHERNET,
+        MFP_ND_SOLICIT,
+        NXM_NX_ND_SLL,
+    }, {
+        MFF_ND_TLL, "nd_tll", NULL,
+        6, 48,
+        MFM_NONE, FWW_ARP_THA,
+        MFS_ETHERNET,
+        MFP_ND_ADVERT,
+        NXM_NX_ND_TLL,
+    }
+};
+
+static bool
+is_all_zeros(const uint8_t *field, size_t length)
+{
+    size_t i;
+
+    for (i = 0; i < length; i++) {
+        if (field[i] != 0x00) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool
+is_all_ones(const uint8_t *field, size_t length)
+{
+    size_t i;
+
+    for (i = 0; i < length; i++) {
+        if (field[i] != 0xff) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/* Returns the field with the given 'id'. */
+const struct mf_field *
+mf_from_id(enum mf_field_id id)
+{
+    assert((unsigned int) id < MFF_N_IDS);
+    return &mf_fields[id];
+}
+
+/* Returns the field with the given 'name', or a null pointer if no field has
+ * that name. */
+const struct mf_field *
+mf_from_name(const char *name)
+{
+    static struct shash mf_by_name = SHASH_INITIALIZER(&mf_by_name);
+
+    if (shash_is_empty(&mf_by_name)) {
+        const struct mf_field *mf;
+
+        for (mf = mf_fields; mf < &mf_fields[MFF_N_IDS]; mf++) {
+            shash_add_once(&mf_by_name, mf->name, mf);
+            if (mf->extra_name) {
+                shash_add_once(&mf_by_name, mf->extra_name, mf);
+            }
+        }
+    }
+
+    return shash_find_data(&mf_by_name, name);
+}
+
+/* Returns true if 'wc' wildcards all the bits in field 'mf', false if 'wc'
+ * specifies at least one bit in the field.
+ *
+ * The caller is responsible for ensuring that 'wc' corresponds to a flow that
+ * meets 'mf''s prerequisites. */
+bool
+mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
+{
+    switch (mf->id) {
+    case MFF_IN_PORT:
+    case MFF_ETH_SRC:
+    case MFF_ETH_TYPE:
+    case MFF_IP_PROTO:
+    case MFF_IP_TOS:
+    case MFF_ARP_OP:
+    case MFF_ARP_SHA:
+    case MFF_ARP_THA:
+    case MFF_TCP_SRC:
+    case MFF_TCP_DST:
+    case MFF_UDP_SRC:
+    case MFF_UDP_DST:
+    case MFF_ICMP_TYPE:
+    case MFF_ICMP_CODE:
+    case MFF_ND_TARGET:
+    case MFF_ND_SLL:
+    case MFF_ND_TLL:
+        assert(mf->fww_bit != 0);
+        return (wc->wildcards & mf->fww_bit) != 0;
+
+    case MFF_TUN_ID:
+        return !wc->tun_id_mask;
+
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+        return !wc->reg_masks[0];
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+        return !wc->reg_masks[1];
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+        return !wc->reg_masks[2];
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+        return !wc->reg_masks[3];
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    case MFF_ETH_DST:
+        return ((wc->wildcards & (FWW_ETH_MCAST | FWW_DL_DST))
+                == (FWW_ETH_MCAST | FWW_DL_DST));
+
+    case MFF_VLAN_TCI:
+        return !wc->vlan_tci_mask;
+    case MFF_VLAN_VID:
+        return !(wc->vlan_tci_mask & htons(VLAN_VID_MASK));
+    case MFF_VLAN_PCP:
+        return !(wc->vlan_tci_mask & htons(VLAN_PCP_MASK));
+
+    case MFF_IPV4_SRC:
+        return !wc->nw_src_mask;
+    case MFF_IPV4_DST:
+        return !wc->nw_dst_mask;
+
+    case MFF_IPV6_SRC:
+        return ipv6_mask_is_any(&wc->ipv6_src_mask);
+    case MFF_IPV6_DST:
+        return ipv6_mask_is_any(&wc->ipv6_dst_mask);
+
+    case MFF_ARP_SPA:
+        return !wc->nw_src_mask;
+    case MFF_ARP_TPA:
+        return !wc->nw_dst_mask;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Not yet implemented. */
+bool mf_is_all_fixed(const struct mf_field *, const struct flow_wildcards *);
+int mf_n_wild_bits(const struct mf_field *, const struct flow_wildcards *);
+int mf_n_fixed_bits(const struct mf_field *, const struct flow_wildcards *);
+
+/* Initializes the 'mf->n_bytes' in 'mask' with the wildcard bit pattern for
+ * field 'mf' within 'wc'.  Each bit in 'mask' will be set to 1 if the bit is
+ * significant for matching purposes, or to 0 if it is wildcarded.
+ *
+ * The caller is responsible for ensuring that 'wc' corresponds to a flow that
+ * meets 'mf''s prerequisites. */
+void
+mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
+            void *mask)
+{
+    uint8_t *mask8 = mask;
+
+    switch (mf->id) {
+    case MFF_IN_PORT:
+    case MFF_ETH_SRC:
+    case MFF_ETH_TYPE:
+    case MFF_IP_PROTO:
+    case MFF_IP_TOS:
+    case MFF_ARP_OP:
+    case MFF_ARP_SHA:
+    case MFF_ARP_THA:
+    case MFF_TCP_SRC:
+    case MFF_TCP_DST:
+    case MFF_UDP_SRC:
+    case MFF_UDP_DST:
+    case MFF_ICMP_TYPE:
+    case MFF_ICMP_CODE:
+    case MFF_ND_TARGET:
+    case MFF_ND_SLL:
+    case MFF_ND_TLL:
+        assert(mf->fww_bit != 0);
+        memset(mask, wc->wildcards & mf->fww_bit ? 0x00 : 0xff, mf->n_bytes);
+        break;
+
+    case MFF_TUN_ID:
+        put_unaligned_be64(mask, wc->tun_id_mask);
+        break;
+
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+        put_unaligned_be32(mask, htonl(wc->reg_masks[0]));
+        break;
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+        put_unaligned_be32(mask, htonl(wc->reg_masks[1]));
+        break;
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+        put_unaligned_be32(mask, htonl(wc->reg_masks[2]));
+        break;
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+        put_unaligned_be32(mask, htonl(wc->reg_masks[3]));
+        break;
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    case MFF_ETH_DST:
+        memcpy(mask, flow_wildcards_to_dl_dst_mask(wc->wildcards),
+               ETH_ADDR_LEN);
+        break;
+
+    case MFF_VLAN_TCI:
+        put_unaligned_be16(mask, wc->vlan_tci_mask);
+        break;
+    case MFF_VLAN_VID:
+        put_unaligned_be16(mask, wc->vlan_tci_mask & htons(VLAN_VID_MASK));
+        break;
+    case MFF_VLAN_PCP:
+        *mask8 = vlan_tci_to_pcp(wc->vlan_tci_mask);
+        break;
+
+    case MFF_IPV4_SRC:
+        put_unaligned_be32(mask, wc->nw_src_mask);
+        break;
+    case MFF_IPV4_DST:
+        put_unaligned_be32(mask, wc->nw_dst_mask);
+        break;
+
+    case MFF_IPV6_SRC:
+        memcpy(mask, &wc->ipv6_src_mask, sizeof wc->ipv6_src_mask);
+        break;
+    case MFF_IPV6_DST:
+        memcpy(mask, &wc->ipv6_dst_mask, sizeof wc->ipv6_dst_mask);
+        break;
+
+    case MFF_ARP_SPA:
+        put_unaligned_be32(mask, wc->nw_src_mask);
+        break;
+    case MFF_ARP_TPA:
+        put_unaligned_be32(mask, wc->nw_dst_mask);
+        break;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Tests whether the 'mf->n_bytes' in 'mask' are a valid wildcard bit pattern
+ * for 'mf'.  Returns true if the mask is valid, false otherwise. */
+bool
+mf_is_mask_valid(const struct mf_field *mf, const void *mask)
+{
+    switch (mf->maskable) {
+    case MFM_NONE:
+        return (is_all_zeros(mask, mf->n_bytes) ||
+                is_all_ones(mask, mf->n_bytes));
+
+    case MFM_FULLY:
+        return true;
+
+    case MFM_CIDR:
+        if (mf->n_bytes == 4) {
+            return ip_is_cidr(get_unaligned_be32(mask));
+        } else {
+            struct in6_addr ipv6_mask;
+
+            assert(mf->n_bytes == 16);
+            memcpy(&ipv6_mask, mask, sizeof ipv6_mask);
+            return ipv6_is_cidr(&ipv6_mask);
+        }
+
+    case MFM_MCAST:
+        assert(mf->n_bytes == ETH_ADDR_LEN);
+        return flow_wildcards_is_dl_dst_mask_valid(mask);
+    }
+
+    NOT_REACHED();
+}
+
+static bool
+is_ip_any(const struct flow *flow)
+{
+    return (flow->dl_type == htons(ETH_TYPE_IP) ||
+            flow->dl_type == htons(ETH_TYPE_IPV6));
+}
+
+static bool
+is_icmpv4(const struct flow *flow)
+{
+    return (flow->dl_type == htons(ETH_TYPE_IP)
+            && flow->nw_proto == IPPROTO_ICMP);
+}
+
+static bool
+is_icmpv6(const struct flow *flow)
+{
+    return (flow->dl_type == htons(ETH_TYPE_IPV6)
+            && flow->nw_proto == IPPROTO_ICMPV6);
+}
+
+/* Returns true if 'flow' meets the prerequisites for 'mf', false otherwise. */
+bool
+mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
+{
+    switch (mf->prereqs) {
+    case MFP_NONE:
+        return true;
+
+    case MFP_ARP:
+        return flow->dl_type == htons(ETH_TYPE_ARP);
+    case MFP_IPV4:
+        return flow->dl_type == htons(ETH_TYPE_IP);
+    case MFP_IPV6:
+        return flow->dl_type == htons(ETH_TYPE_IPV6);
+    case MFP_IP_ANY:
+        return is_ip_any(flow);
+
+    case MFP_TCP:
+        return is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP;
+    case MFP_UDP:
+        return is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP;
+    case MFP_ICMPV6:
+        return is_icmpv6(flow);
+    case MFP_ICMP_ANY:
+        return is_icmpv4(flow) || is_icmpv6(flow);
+
+    case MFP_ND:
+        return (is_icmpv6(flow)
+                && flow->icmp_code == htons(0)
+                && (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT) ||
+                    flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)));
+    case MFP_ND_SOLICIT:
+        return (is_icmpv6(flow)
+                && flow->icmp_code == htons(0)
+                && (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT)));
+    case MFP_ND_ADVERT:
+        return (is_icmpv6(flow)
+                && flow->icmp_code == htons(0)
+                && (flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)));
+    }
+
+    NOT_REACHED();
+}
+
+/* Returns true if 'value' may be a valid value *as part of a masked match*,
+ * false otherwise.
+ *
+ * A value is not reject just because it is not valid for the field in
+ * question, but only if it doesn't make sense to test the bits in question at
+ * all.  For example, the MFF_VLAN_TCI field will never have a nonzero value
+ * without the VLAN_CFI bit being set, but we can't reject those values because
+ * it is still legitimate to test just for those bits (see the documentation
+ * for NXM_OF_VLAN_TCI in nicira-ext.h).  On the other hand, there is never a
+ * reason to set the low bit of MFF_IP_TOS to 1, so we reject that. */
+bool
+mf_is_value_valid(const struct mf_field *mf, const void *value)
+{
+    switch (mf->id) {
+    case MFF_TUN_ID:
+    case MFF_IN_PORT:
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+    case MFF_ETH_SRC:
+    case MFF_ETH_DST:
+    case MFF_ETH_TYPE:
+    case MFF_VLAN_TCI:
+    case MFF_IPV4_SRC:
+    case MFF_IPV4_DST:
+    case MFF_IPV6_SRC:
+    case MFF_IPV6_DST:
+    case MFF_IP_PROTO:
+    case MFF_ARP_SPA:
+    case MFF_ARP_TPA:
+    case MFF_ARP_SHA:
+    case MFF_ARP_THA:
+    case MFF_TCP_SRC:
+    case MFF_TCP_DST:
+    case MFF_UDP_SRC:
+    case MFF_UDP_DST:
+    case MFF_ICMP_TYPE:
+    case MFF_ICMP_CODE:
+    case MFF_ND_TARGET:
+    case MFF_ND_SLL:
+    case MFF_ND_TLL:
+        return true;
+
+    case MFF_IP_TOS:
+        return !(*(uint8_t *) value & 0x03);
+
+    case MFF_ARP_OP:
+        return !(get_unaligned_be16(value) & htons(0xff00));
+
+    case MFF_VLAN_VID:
+        return !(get_unaligned_be16(value) & htons(VLAN_CFI | VLAN_PCP_MASK));
+
+    case MFF_VLAN_PCP:
+        return !(*(uint8_t *) value & ~7);
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Copies the value of field 'mf' from 'flow' into the 'mf->n_bytes' bytes of
+ * 'value'.  'value' will be in network byte order, regardless of whether 'mf'
+ * is stored in 'flow' in host or network byte order.
+ *
+ * The caller is responsible for ensuring that 'flow' meets 'mf''s
+ * prerequisites. */
+void
+mf_get_value(const struct mf_field *mf, const struct flow *flow, void *value)
+{
+    switch (mf->id) {
+    case MFF_TUN_ID:
+        put_unaligned_be64(value, flow->tun_id);
+        break;
+
+    case MFF_IN_PORT:
+        put_unaligned_be16(value, htons(flow->in_port));
+        break;
+
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+        put_unaligned_be32(value, htonl(flow->regs[0]));
+        break;
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+        put_unaligned_be32(value, htonl(flow->regs[1]));
+        break;
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+        put_unaligned_be32(value, htonl(flow->regs[2]));
+        break;
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+        put_unaligned_be32(value, htonl(flow->regs[3]));
+        break;
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    case MFF_ETH_SRC:
+        memcpy(value, flow->dl_src, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_DST:
+        memcpy(value, flow->dl_dst, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_TYPE:
+        put_unaligned_be16(value, flow->dl_type);
+        break;
+
+    case MFF_VLAN_TCI:
+        put_unaligned_be16(value, flow->vlan_tci);
+        break;
+
+    case MFF_VLAN_VID:
+        /* XXX? */
+        put_unaligned_be16(value, flow->vlan_tci & htons(VLAN_VID_MASK));
+        break;
+
+    case MFF_VLAN_PCP:
+        *(uint8_t *) value = vlan_tci_to_pcp(flow->vlan_tci);
+        break;
+
+    case MFF_IPV4_SRC:
+        put_unaligned_be32(value, flow->nw_src);
+        break;
+
+    case MFF_IPV4_DST:
+        put_unaligned_be32(value, flow->nw_dst);
+        break;
+
+    case MFF_IPV6_SRC:
+        memcpy(value, &flow->ipv6_src, sizeof flow->ipv6_src);
+        break;
+
+    case MFF_IPV6_DST:
+        memcpy(value, &flow->ipv6_dst, sizeof flow->ipv6_dst);
+        break;
+
+    case MFF_IP_PROTO:
+        *(uint8_t *) value = flow->nw_proto;
+        break;
+
+    case MFF_IP_TOS:
+        *(uint8_t *) value = flow->nw_tos;
+        break;
+
+    case MFF_ARP_OP:
+        put_unaligned_be16(value, htons(flow->nw_proto));
+        break;
+
+    case MFF_ARP_SPA:
+        put_unaligned_be32(value, flow->nw_src);
+        break;
+
+    case MFF_ARP_TPA:
+        put_unaligned_be32(value, flow->nw_dst);
+        break;
+
+    case MFF_ARP_SHA:
+    case MFF_ND_SLL:
+        memcpy(value, flow->arp_sha, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ARP_THA:
+    case MFF_ND_TLL:
+        memcpy(value, flow->arp_tha, ETH_ADDR_LEN);
+        break;
+
+    case MFF_TCP_SRC:
+        put_unaligned_be16(value, flow->tp_src);
+        break;
+
+    case MFF_TCP_DST:
+        put_unaligned_be16(value, flow->tp_dst);
+        break;
+
+    case MFF_UDP_SRC:
+        put_unaligned_be16(value, flow->tp_src);
+        break;
+
+    case MFF_UDP_DST:
+        put_unaligned_be16(value, flow->tp_dst);
+        break;
+
+    case MFF_ICMP_TYPE:
+        *(uint8_t *) value = ntohs(flow->tp_src);
+        break;
+
+    case MFF_ICMP_CODE:
+        *(uint8_t *) value = ntohs(flow->tp_dst);
+        break;
+
+    case MFF_ND_TARGET:
+        memcpy(value, &flow->nd_target, sizeof flow->nd_target);
+        break;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Makes 'rule' match field 'mf' exactly, with the value matched taken from the
+ * 'mf->n_bytes' bytes of 'value'.  'value' must be in network byte order,
+ * regardless of whether 'mf' is stored in 'rule' in host or network byte
+ * order.
+ *
+ * The caller is responsible for ensuring that 'rule' meets 'mf''s
+ * prerequisites. */
+void
+mf_set_value(const struct mf_field *mf,
+             const void *value, struct cls_rule *rule)
+{
+    struct in6_addr ipv6_addr;
+
+    switch (mf->id) {
+    case MFF_TUN_ID:
+        cls_rule_set_tun_id(rule, get_unaligned_be64(value));
+        break;
+
+    case MFF_IN_PORT:
+        cls_rule_set_in_port(rule, ntohs(get_unaligned_be16(value)));
+        break;
+
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+        cls_rule_set_reg(rule, 0, ntohl(get_unaligned_be32(value)));
+        break;
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+        cls_rule_set_reg(rule, 1, ntohl(get_unaligned_be32(value)));
+        break;
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+        cls_rule_set_reg(rule, 2, ntohl(get_unaligned_be32(value)));
+        break;
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+        cls_rule_set_reg(rule, 3, ntohl(get_unaligned_be32(value)));
+        break;
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    case MFF_ETH_SRC:
+        cls_rule_set_dl_src(rule, value);
+        break;
+
+    case MFF_ETH_DST:
+        cls_rule_set_dl_dst(rule, value);
+        break;
+
+    case MFF_ETH_TYPE:
+        cls_rule_set_dl_type(rule, get_unaligned_be16(value));
+        break;
+
+    case MFF_VLAN_TCI:
+        cls_rule_set_dl_tci(rule, get_unaligned_be16(value));
+        break;
+
+    case MFF_VLAN_VID:
+        /* XXX? */
+        cls_rule_set_dl_vlan(rule, get_unaligned_be16(value));
+        break;
+
+    case MFF_VLAN_PCP:
+        cls_rule_set_dl_vlan_pcp(rule, *(uint8_t *) value);
+        break;
+
+    case MFF_IPV4_SRC:
+        cls_rule_set_nw_src(rule, get_unaligned_be32(value));
+        break;
+
+    case MFF_IPV4_DST:
+        cls_rule_set_nw_dst(rule, get_unaligned_be32(value));
+        break;
+
+    case MFF_IPV6_SRC:
+        memcpy(&ipv6_addr, value, sizeof ipv6_addr);
+        cls_rule_set_ipv6_src(rule, &ipv6_addr);
+        break;
+
+    case MFF_IPV6_DST:
+        memcpy(&ipv6_addr, value, sizeof ipv6_addr);
+        cls_rule_set_ipv6_dst(rule, &ipv6_addr);
+        break;
+
+    case MFF_IP_PROTO:
+        cls_rule_set_nw_proto(rule, *(uint8_t *) value);
+        break;
+
+    case MFF_IP_TOS:
+        cls_rule_set_nw_tos(rule, *(uint8_t *) value);
+        break;
+
+    case MFF_ARP_OP:
+        cls_rule_set_nw_proto(rule, ntohs(get_unaligned_be16(value)));
+        break;
+
+    case MFF_ARP_SPA:
+        cls_rule_set_nw_src(rule, get_unaligned_be32(value));
+        break;
+
+    case MFF_ARP_TPA:
+        cls_rule_set_nw_dst(rule, get_unaligned_be32(value));
+        break;
+
+    case MFF_ARP_SHA:
+    case MFF_ND_SLL:
+        cls_rule_set_arp_sha(rule, value);
+        break;
+
+    case MFF_ARP_THA:
+    case MFF_ND_TLL:
+        cls_rule_set_arp_tha(rule, value);
+        break;
+
+    case MFF_TCP_SRC:
+        cls_rule_set_tp_src(rule, get_unaligned_be16(value));
+        break;
+
+    case MFF_TCP_DST:
+        cls_rule_set_tp_dst(rule, get_unaligned_be16(value));
+        break;
+
+    case MFF_UDP_SRC:
+        cls_rule_set_tp_src(rule, get_unaligned_be16(value));
+        break;
+
+    case MFF_UDP_DST:
+        cls_rule_set_tp_dst(rule, get_unaligned_be16(value));
+        break;
+
+    case MFF_ICMP_TYPE:
+        cls_rule_set_icmp_type(rule, *(uint8_t *) value);
+        break;
+
+    case MFF_ICMP_CODE:
+        cls_rule_set_icmp_code(rule, *(uint8_t *) value);
+        break;
+
+    case MFF_ND_TARGET:
+        memcpy(&ipv6_addr, value, sizeof ipv6_addr);
+        cls_rule_set_nd_target(rule, ipv6_addr);
+        break;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Makes 'rule' wildcard field 'mf'.
+ *
+ * The caller is responsible for ensuring that 'rule' meets 'mf''s
+ * prerequisites. */
+void
+mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
+{
+    switch (mf->id) {
+    case MFF_TUN_ID:
+        cls_rule_set_tun_id_masked(rule, htonll(0), htonll(0));
+        break;
+
+    case MFF_IN_PORT:
+        rule->wc.wildcards |= FWW_IN_PORT;
+        rule->flow.in_port = 0;
+        break;
+
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+        cls_rule_set_reg_masked(rule, 0, 0, 0);
+        break;
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+        cls_rule_set_reg_masked(rule, 1, 0, 0);
+        break;
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+        cls_rule_set_reg_masked(rule, 2, 0, 0);
+        break;
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+        cls_rule_set_reg_masked(rule, 3, 0, 0);
+        break;
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    case MFF_ETH_SRC:
+        rule->wc.wildcards |= FWW_DL_SRC;
+        memset(rule->flow.dl_src, 0, sizeof rule->flow.dl_src);
+        break;
+
+    case MFF_ETH_DST:
+        rule->wc.wildcards |= FWW_DL_DST | FWW_ETH_MCAST;
+        memset(rule->flow.dl_dst, 0, sizeof rule->flow.dl_dst);
+        break;
+
+    case MFF_ETH_TYPE:
+        rule->wc.wildcards |= FWW_DL_TYPE;
+        rule->flow.dl_type = htons(0);
+        break;
+
+    case MFF_VLAN_TCI:
+        cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
+        break;
+
+    case MFF_VLAN_VID:
+        cls_rule_set_any_vid(rule); /* XXX */
+        break;
+
+    case MFF_VLAN_PCP:
+        cls_rule_set_any_pcp(rule); /* XXX */
+        break;
+
+    case MFF_IPV4_SRC:
+    case MFF_ARP_SPA:
+        cls_rule_set_nw_src_masked(rule, htonl(0), htonl(0));
+        break;
+
+    case MFF_IPV4_DST:
+    case MFF_ARP_TPA:
+        cls_rule_set_nw_dst_masked(rule, htonl(0), htonl(0));
+        break;
+
+    case MFF_IPV6_SRC:
+        memset(&rule->wc.ipv6_src_mask, 0, sizeof rule->wc.ipv6_src_mask);
+        memset(&rule->flow.ipv6_src, 0, sizeof rule->flow.ipv6_src);
+        break;
+
+    case MFF_IPV6_DST:
+        memset(&rule->wc.ipv6_dst_mask, 0, sizeof rule->wc.ipv6_dst_mask);
+        memset(&rule->flow.ipv6_dst, 0, sizeof rule->flow.ipv6_dst);
+        break;
+
+    case MFF_IP_PROTO:
+        rule->wc.wildcards |= FWW_NW_PROTO;
+        rule->flow.nw_proto = 0;
+        break;
+
+    case MFF_IP_TOS:
+        rule->wc.wildcards |= FWW_NW_TOS;
+        rule->flow.nw_tos = 0;
+        break;
+
+    case MFF_ARP_OP:
+        rule->wc.wildcards |= FWW_NW_PROTO;
+        rule->flow.nw_proto = 0;
+        break;
+
+    case MFF_ARP_SHA:
+    case MFF_ND_SLL:
+        rule->wc.wildcards |= FWW_ARP_SHA;
+        memset(rule->flow.arp_sha, 0, sizeof rule->flow.arp_sha);
+        break;
+
+    case MFF_ARP_THA:
+    case MFF_ND_TLL:
+        rule->wc.wildcards |= FWW_ARP_THA;
+        memset(rule->flow.arp_tha, 0, sizeof rule->flow.arp_tha);
+        break;
+
+    case MFF_TCP_SRC:
+    case MFF_UDP_SRC:
+    case MFF_ICMP_TYPE:
+        rule->wc.wildcards |= FWW_TP_SRC;
+        rule->flow.tp_src = htons(0);
+        break;
+
+    case MFF_TCP_DST:
+    case MFF_UDP_DST:
+    case MFF_ICMP_CODE:
+        rule->wc.wildcards |= FWW_TP_DST;
+        rule->flow.tp_dst = htons(0);
+        break;
+
+    case MFF_ND_TARGET:
+        rule->wc.wildcards |= FWW_ND_TARGET;
+        memset(&rule->flow.nd_target, 0, sizeof rule->flow.nd_target);
+        break;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Makes 'rule' match field 'mf' with the specified 'value' and 'mask'.  The
+ * 'mf->n_bytes' bytes of 'value' specifies a value to match and 'mask'
+ * specifies a wildcard pattern, with a 1-bit indicating that the corresponding
+ * value bit must match and a 0-bit indicating a don't-care.
+ *
+ * If 'mask' is NULL or points to 'mf->n_bytes' bytes of all-1-bits, then this
+ * call is equivalent to mf_set_value(mf, value, rule).  If 'mask' points to
+ * 'mf->n_bytes' bytes of all-0-bits, then this call is equivalent to
+ * mf_set_wild(mf, rule).
+ *
+ * Both 'value' and 'mask' must be in network byte order, regardless of whether
+ * 'mf' is stored in 'rule' in host or network byte order.  'mask' must be a
+ * valid mask for 'mf' (see mf_is_mask_valid()).  The caller is responsible for
+ * ensuring that 'rule' meets 'mf''s prerequisites. */
+void
+mf_set(const struct mf_field *mf, const void *value, const void *mask,
+       struct cls_rule *rule)
+{
+    struct in6_addr ipv6_addr;
+    struct in6_addr ipv6_mask;
+
+    if (!mask || is_all_ones(mask, mf->n_bytes)) {
+        mf_set_value(mf, value, rule);
+        return;
+    } else if (is_all_zeros(mask, mf->n_bytes)) {
+        mf_set_wild(mf, rule);
+        return;
+    }
+
+    switch (mf->id) {
+    case MFF_IN_PORT:
+    case MFF_ETH_SRC:
+    case MFF_ETH_TYPE:
+    case MFF_VLAN_VID:
+    case MFF_VLAN_PCP:
+    case MFF_IP_PROTO:
+    case MFF_IP_TOS:
+    case MFF_ARP_OP:
+    case MFF_ARP_SHA:
+    case MFF_ARP_THA:
+    case MFF_TCP_SRC:
+    case MFF_TCP_DST:
+    case MFF_UDP_SRC:
+    case MFF_UDP_DST:
+    case MFF_ICMP_TYPE:
+    case MFF_ICMP_CODE:
+    case MFF_ND_TARGET:
+    case MFF_ND_SLL:
+    case MFF_ND_TLL:
+        /* not valid */
+        break;
+
+    case MFF_TUN_ID:
+        cls_rule_set_tun_id_masked(rule, get_unaligned_be64(value),
+                                   get_unaligned_be64(mask));
+        break;
+
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+        cls_rule_set_reg_masked(rule, 0, ntohl(get_unaligned_be32(value)),
+                                ntohl(get_unaligned_be32(mask)));
+        break;
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+        cls_rule_set_reg_masked(rule, 1, ntohl(get_unaligned_be32(value)),
+                                ntohl(get_unaligned_be32(mask)));
+        break;
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+        cls_rule_set_reg_masked(rule, 2, ntohl(get_unaligned_be32(value)),
+                                ntohl(get_unaligned_be32(mask)));
+        break;
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+        cls_rule_set_reg_masked(rule, 3, ntohl(get_unaligned_be32(value)),
+                                ntohl(get_unaligned_be32(mask)));
+        break;
+#endif
+
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    case MFF_ETH_DST:
+        if (flow_wildcards_is_dl_dst_mask_valid(mask)) {
+            cls_rule_set_dl_dst_masked(rule, value, mask);
+        }
+        break;
+
+    case MFF_VLAN_TCI:
+        cls_rule_set_dl_tci_masked(rule, get_unaligned_be16(value),
+                                   get_unaligned_be16(mask));
+        break;
+
+    case MFF_IPV4_SRC:
+        cls_rule_set_nw_src_masked(rule, get_unaligned_be32(value),
+                                   get_unaligned_be32(mask));
+        break;
+
+    case MFF_IPV4_DST:
+        cls_rule_set_nw_dst_masked(rule, get_unaligned_be32(value),
+                                   get_unaligned_be32(mask));
+        break;
+
+    case MFF_IPV6_SRC:
+        memcpy(&ipv6_addr, value, sizeof ipv6_addr);
+        memcpy(&ipv6_mask, mask, sizeof ipv6_mask);
+        cls_rule_set_ipv6_src_masked(rule, &ipv6_addr, &ipv6_mask);
+        break;
+
+    case MFF_IPV6_DST:
+        memcpy(&ipv6_addr, value, sizeof ipv6_addr);
+        memcpy(&ipv6_mask, mask, sizeof ipv6_mask);
+        cls_rule_set_ipv6_dst_masked(rule, &ipv6_addr, &ipv6_mask);
+        break;
+
+    case MFF_ARP_SPA:
+        cls_rule_set_nw_src_masked(rule, get_unaligned_be32(value),
+                                   get_unaligned_be32(mask));
+        break;
+
+    case MFF_ARP_TPA:
+        cls_rule_set_nw_dst_masked(rule, get_unaligned_be32(value),
+                                   get_unaligned_be32(mask));
+        break;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Makes a subfield starting at bit offset 'ofs' and continuing for 'n_bits' in
+ * 'rule''s field 'mf' exactly match the 'n_bits' least-significant bits of
+ * 'x'.
+ *
+ * Example: suppose that 'mf' is originally the following 2-byte field in
+ * 'rule':
+ *
+ *     value == 0xe00a == 2#1110000000001010
+ *      mask == 0xfc3f == 2#1111110000111111
+ *
+ * The call mf_set_subfield(mf, 0x55, 8, 7, rule) would have the following
+ * effect (note that 0x55 is 2#1010101):
+ *
+ *     value == 0xd50a == 2#1101010100001010
+ *      mask == 0xff3f == 2#1111111100111111
+ *
+ * The caller is responsible for ensuring that the result will be a valid
+ * wildcard pattern for 'mf'.  The caller is responsible for ensuring that
+ * 'rule' meets 'mf''s prerequisites. */
+void
+mf_set_subfield(const struct mf_field *mf, uint64_t x, unsigned int ofs,
+                unsigned int n_bits, struct cls_rule *rule)
+{
+    if (ofs == 0 && mf->n_bytes * 8 == n_bits) {
+        uint8_t value[MF_MAXSIZE];
+        int i;
+
+        for (i = mf->n_bytes - 1; i >= 0; i--) {
+            value[i] = x;
+            x >>= 8;
+        }
+        mf_set_value(mf, value, rule);
+    } else {
+        uint8_t value[MF_MAXSIZE], *vp;
+        uint8_t mask[MF_MAXSIZE], *mp;
+        unsigned int byte_ofs;
+
+        mf_get(mf, rule, value, mask);
+
+        byte_ofs = mf->n_bytes - ofs / 8;
+        vp = &value[byte_ofs];
+        mp = &mask[byte_ofs];
+        if (ofs % 8) {
+            unsigned int chunk = MIN(8 - ofs % 8, n_bits);
+            uint8_t chunk_mask = ((1 << chunk) - 1) << (ofs % 8);
+
+            *--vp &= ~chunk_mask;
+            *vp   |= chunk_mask & (x << (ofs % 8));
+            *--mp |= chunk_mask;
+
+            x >>= chunk;
+            n_bits -= chunk;
+            ofs += chunk;
+        }
+        while (n_bits >= 8) {
+            *--vp = x;
+            *--mp = 0xff;
+            x >>= 8;
+            n_bits -= 8;
+            ofs += 8;
+        }
+        if (n_bits) {
+            uint8_t chunk_mask = (1 << n_bits) - 1;
+
+            *--vp &= ~chunk_mask;
+            *vp   |= chunk_mask & x;
+            *--mp |= chunk_mask;
+        }
+
+        mf_set(mf, value, mask, rule);
+    }
+}
+
+
+/* Copies the value and wildcard bit pattern for 'mf' from 'rule' into the
+ * 'mf->n_bytes' bytes in each of 'value' and 'mask'. */
+void
+mf_get(const struct mf_field *mf, const struct cls_rule *rule,
+       void *value, void *mask)
+{
+    mf_get_value(mf, &rule->flow, value);
+    mf_get_mask(mf, &rule->wc, mask);
+}
+
+/* Assigns a random value for field 'mf' to the 'mf->n_bytes' bytes in
+ * 'value'. */
+void
+mf_random_value(const struct mf_field *mf, void *value)
+{
+    random_bytes(value, mf->n_bytes);
+
+    switch (mf->id) {
+    case MFF_TUN_ID:
+    case MFF_IN_PORT:
+#if FLOW_N_REGS > 0
+    case MFF_REG0:
+#endif
+#if FLOW_N_REGS > 1
+    case MFF_REG1:
+#endif
+#if FLOW_N_REGS > 2
+    case MFF_REG2:
+#endif
+#if FLOW_N_REGS > 3
+    case MFF_REG3:
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+    case MFF_ETH_SRC:
+    case MFF_ETH_DST:
+    case MFF_ETH_TYPE:
+    case MFF_VLAN_TCI:
+    case MFF_IPV4_SRC:
+    case MFF_IPV4_DST:
+    case MFF_IPV6_SRC:
+    case MFF_IPV6_DST:
+    case MFF_IP_PROTO:
+    case MFF_ARP_SPA:
+    case MFF_ARP_TPA:
+    case MFF_ARP_SHA:
+    case MFF_ARP_THA:
+    case MFF_TCP_SRC:
+    case MFF_TCP_DST:
+    case MFF_UDP_SRC:
+    case MFF_UDP_DST:
+    case MFF_ICMP_TYPE:
+    case MFF_ICMP_CODE:
+    case MFF_ND_TARGET:
+    case MFF_ND_SLL:
+    case MFF_ND_TLL:
+        break;
+
+    case MFF_IP_TOS:
+        *(uint8_t *) value &= ~0x03;
+        break;
+
+    case MFF_ARP_OP:
+        *(ovs_be16 *) value &= htons(0xff);
+        break;
+
+    case MFF_VLAN_VID:
+        *(ovs_be16 *) value &= htons(VLAN_VID_MASK); /* XXX */
+        break;
+
+    case MFF_VLAN_PCP:
+        *(uint8_t *) value &= 0x07;
+        break;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
+static char *
+mf_from_integer_string(const struct mf_field *mf, const char *s,
+                       uint8_t *valuep, uint8_t *maskp)
+{
+    unsigned long long int integer, mask;
+    char *tail;
+    int i;
+
+    errno = 0;
+    integer = strtoull(s, &tail, 0);
+    if (errno || (*tail != '\0' && *tail != '/')) {
+        goto syntax_error;
+    }
+
+    if (*tail == '/') {
+        mask = strtoull(tail + 1, &tail, 0);
+        if (errno || *tail != '\0') {
+            goto syntax_error;
+        }
+    } else {
+        mask = ULLONG_MAX;
+    }
+
+    for (i = mf->n_bytes - 1; i >= 0; i--) {
+        valuep[i] = integer;
+        maskp[i] = mask;
+        integer >>= 8;
+        mask >>= 8;
+    }
+    if (integer) {
+        return xasprintf("%s: value too large for %u-byte field %s",
+                         s, mf->n_bytes, mf->name);
+    }
+    return NULL;
+
+syntax_error:
+    return xasprintf("%s: bad syntax for %s", s, mf->name);
+}
+
+static char *
+mf_from_ethernet_string(const struct mf_field *mf, const char *s,
+                        uint8_t mac[ETH_ADDR_LEN],
+                        uint8_t mask[ETH_ADDR_LEN])
+{
+    assert(mf->n_bytes == ETH_ADDR_LEN);
+
+    switch (sscanf(s, ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT,
+                   ETH_ADDR_SCAN_ARGS(mac), ETH_ADDR_SCAN_ARGS(mask))){
+    case ETH_ADDR_SCAN_COUNT * 2:
+        return NULL;
+
+    case ETH_ADDR_SCAN_COUNT:
+        memset(mask, 0xff, ETH_ADDR_LEN);
+        return NULL;
+
+    default:
+        return xasprintf("%s: invalid Ethernet address", s);
+    }
+}
+
+static char *
+mf_from_ipv4_string(const struct mf_field *mf, const char *s,
+                    ovs_be32 *ip, ovs_be32 *mask)
+{
+    int prefix;
+
+    assert(mf->n_bytes == sizeof *ip);
+
+    if (sscanf(s, IP_SCAN_FMT"/"IP_SCAN_FMT,
+               IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask)) == IP_SCAN_COUNT * 2) {
+        /* OK. */
+    } else if (sscanf(s, IP_SCAN_FMT"/%d",
+                      IP_SCAN_ARGS(ip), &prefix) == IP_SCAN_COUNT + 1) {
+        if (prefix <= 0 || prefix > 32) {
+            return xasprintf("%s: network prefix bits not between 1 and "
+                             "32", s);
+        } else if (prefix == 32) {
+            *mask = htonl(UINT32_MAX);
+        } else {
+            *mask = htonl(((1u << prefix) - 1) << (32 - prefix));
+        }
+    } else if (sscanf(s, IP_SCAN_FMT, IP_SCAN_ARGS(ip)) == IP_SCAN_COUNT) {
+        *mask = htonl(UINT32_MAX);
+    } else {
+        return xasprintf("%s: invalid IP address", s);
+    }
+    return NULL;
+}
+
+static char *
+mf_from_ipv6_string(const struct mf_field *mf, const char *s,
+                    struct in6_addr *value, struct in6_addr *mask)
+{
+    char *str = xstrdup(s);
+    char *save_ptr = NULL;
+    const char *name, *netmask;
+    int retval;
+
+    assert(mf->n_bytes == sizeof *value);
+
+    name = strtok_r(str, "/", &save_ptr);
+    retval = name ? lookup_ipv6(name, value) : EINVAL;
+    if (retval) {
+        char *err;
+
+        err = xasprintf("%s: could not convert to IPv6 address", str);
+        free(str);
+
+        return err;
+    }
+
+    netmask = strtok_r(NULL, "/", &save_ptr);
+    if (netmask) {
+        int prefix = atoi(netmask);
+        if (prefix <= 0 || prefix > 128) {
+            free(str);
+            return xasprintf("%s: prefix bits not between 1 and 128", s);
+        } else {
+            *mask = ipv6_create_mask(prefix);
+        }
+    } else {
+        *mask = in6addr_exact;
+    }
+    free(str);
+
+    return NULL;
+}
+
+static char *
+mf_from_ofp_port_string(const struct mf_field *mf, const char *s,
+                        void *valuep, void *maskp)
+{
+    uint16_t port;
+
+    assert(mf->n_bytes == sizeof(ovs_be16));
+    if (ofputil_port_from_string(s, &port)) {
+        *(ovs_be16 *) valuep = htons(port);
+        *(ovs_be16 *) maskp = htons(UINT16_MAX);
+        return NULL;
+    } else {
+        return mf_from_integer_string(mf, s, valuep, maskp);
+    }
+}
+
+/* Parses 's', a string value for field 'mf', into the 'mf->n_bytes' bytes of
+ * 'value' and 'mask'.  Returns NULL if successful, otherwise a malloc()'d
+ * string describing the error. */
+char *
+mf_parse(const struct mf_field *mf, const char *s, void *value, void *mask)
+{
+    if (!strcasecmp(s, "any") || !strcmp(s, "*")) {
+        memset(value, 0, mf->n_bytes);
+        memset(mask, 0, mf->n_bytes);
+        return NULL;
+    }
+
+    switch (mf->string) {
+    case MFS_DECIMAL:
+    case MFS_HEXADECIMAL:
+        return mf_from_integer_string(mf, s, value, mask);
+
+    case MFS_ETHERNET:
+        return mf_from_ethernet_string(mf, s, value, mask);
+
+    case MFS_IPV4:
+        return mf_from_ipv4_string(mf, s, value, mask);
+
+    case MFS_IPV6:
+        return mf_from_ipv6_string(mf, s, value, mask);
+
+    case MFS_OFP_PORT:
+        return mf_from_ofp_port_string(mf, s, value, mask);
+    }
+    NOT_REACHED();
+}
+
+static void
+mf_format_integer_string(const struct mf_field *mf, const uint8_t *valuep,
+                         const uint8_t *maskp, struct ds *s)
+{
+    unsigned long long int integer;
+    int i;
+
+    assert(mf->n_bytes <= 8);
+
+    integer = 0;
+    for (i = 0; i < mf->n_bytes; i++) {
+        integer = (integer << 8) | valuep[i];
+    }
+    if (mf->string == MFS_DECIMAL) {
+        ds_put_format(s, "%lld", integer);
+    } else {
+        ds_put_format(s, "%#llx", integer);
+    }
+
+    if (maskp) {
+        unsigned long long int mask;
+
+        mask = 0;
+        for (i = 0; i < mf->n_bytes; i++) {
+            mask = (mask << 8) | maskp[i];
+        }
+
+        /* I guess we could write the mask in decimal for MFS_DECIMAL but I'm
+         * not sure that that a bit-mask written in decimal is ever easier to
+         * understand than the same bit-mask written in hexadecimal. */
+        ds_put_format(s, "/%#llx", mask);
+    }
+}
+
+static void
+mf_format_ethernet_string(const struct mf_field *mf, const uint8_t *mac,
+                          const uint8_t *mask, struct ds *s)
+{
+    assert(mf->n_bytes == ETH_ADDR_LEN);
+    ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
+    if (mask) {
+        ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(mask));
+    }
+}
+
+static void
+mf_format_ipv4_string(const struct mf_field *mf, const ovs_be32 *ip,
+                      const ovs_be32 *mask, struct ds *s)
+{
+    assert(mf->n_bytes == sizeof *ip);
+    ip_format_masked(*ip, mask ? *mask : htonl(UINT32_MAX), s);
+}
+
+static void
+mf_format_ipv6_string(const struct mf_field *mf, const struct in6_addr *value,
+                      const struct in6_addr *mask, struct ds *s)
+{
+    assert(mf->n_bytes == sizeof *value);
+    print_ipv6_masked(s, value, mask);
+}
+
+static void
+mf_format_ofp_port_string(const struct mf_field *mf, const void *value,
+                          const void *mask, struct ds *s)
+{
+    if (mask) {
+        mf_format_integer_string(mf, value, mask, s);
+    } else {
+        const ovs_be16 *port = value;
+
+        ofputil_format_port(ntohs(*port), s);
+    }
+}
+
+/* Appends to 's' a string representation of field 'mf' whose value is in the
+ * 'mf->n_bytes' of 'value' and 'mask'.  'mask' may be NULL to indicate an
+ * exact match. */
+void
+mf_format(const struct mf_field *mf, const void *value, const void *mask,
+          struct ds *s)
+{
+    if (mask) {
+        if (is_all_zeros(mask, mf->n_bytes)) {
+            ds_put_cstr(s, "ANY");
+            return;
+        } else if (is_all_ones(mask, mf->n_bytes)) {
+            mask = NULL;
+        }
+    }
+
+    switch (mf->string) {
+    case MFS_DECIMAL:
+    case MFS_HEXADECIMAL:
+        mf_format_integer_string(mf, value, mask, s);
+        break;
+
+    case MFS_ETHERNET:
+        mf_format_ethernet_string(mf, value, mask, s);
+        break;
+
+    case MFS_IPV4:
+        mf_format_ipv4_string(mf, value, mask, s);
+        break;
+
+    case MFS_IPV6:
+        mf_format_ipv6_string(mf, value, mask, s);
+        break;
+
+    case MFS_OFP_PORT:
+        mf_format_ofp_port_string(mf, value, mask, s);
+        break;
+    }
+
+}
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
new file mode 100644
index 0000000..16a98ba
--- /dev/null
+++ b/lib/meta-flow.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2011 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef META_FLOW_H
+#define META_FLOW_H 1
+
+#include "flow.h"
+
+struct cls_rule;
+struct ds;
+
+/* Width of the widest defined fields, in bytes. */
+#define MF_MAXSIZE 16
+
+enum mf_field_id {
+    /* Metadata. */
+    MFF_TUN_ID,
+    MFF_IN_PORT,
+
+#if FLOW_N_REGS > 0
+    MFF_REG0,
+#endif
+#if FLOW_N_REGS > 1
+    MFF_REG1,
+#endif
+#if FLOW_N_REGS > 2
+    MFF_REG2,
+#endif
+#if FLOW_N_REGS > 3
+    MFF_REG3,
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    /* L2. */
+    MFF_ETH_SRC,
+    MFF_ETH_DST,
+    MFF_ETH_TYPE,
+
+    MFF_VLAN_TCI,
+    MFF_VLAN_VID,
+    MFF_VLAN_PCP,
+
+    /* L3. */
+    MFF_IPV4_SRC,
+    MFF_IPV4_DST,
+
+    MFF_IPV6_SRC,
+    MFF_IPV6_DST,
+
+    MFF_IP_PROTO,               /* For IPv4 or IPv6. */
+    MFF_IP_TOS,                 /* For IPv4 or IPv6. */
+
+    MFF_ARP_OP,
+    MFF_ARP_SPA,
+    MFF_ARP_TPA,
+    MFF_ARP_SHA,
+    MFF_ARP_THA,
+
+    /* L4. */
+    MFF_TCP_SRC,                /* For IPv4 or IPv6. */
+    MFF_TCP_DST,                /* For IPv4 or IPv6. */
+
+    MFF_UDP_SRC,                /* For IPv4 or IPv6. */
+    MFF_UDP_DST,                /* For IPv4 or IPv6. */
+
+    MFF_ICMP_TYPE,              /* For IPv4 or IPv6. */
+    MFF_ICMP_CODE,              /* For IPv4 or IPv6. */
+
+    /* ICMPv6 Neighbor Discovery. */
+    MFF_ND_TARGET,
+    MFF_ND_SLL,
+    MFF_ND_TLL,
+
+    MFF_N_IDS
+};
+
+/* Prerequisites for matching a field.
+ *
+ * A field may only be matched if the correct lower-level protocols are also
+ * matched.  For example, the TCP port may be matched only if the Ethernet type
+ * matches ETH_TYPE_IP and the IP protocol matches IPPROTO_TCP. */
+enum mf_prereqs {
+    MFP_NONE,
+
+    /* L2 prerequisites. */
+    MFP_ARP,
+    MFP_IPV4,
+    MFP_IPV6,
+    MFP_IP_ANY,
+
+    /* L3 prerequisites. */
+    MFP_TCP,                    /* On IPv4 or IPv6. */
+    MFP_UDP,                    /* On IPv4 or IPv6. */
+    MFP_ICMPV6,
+    MFP_ICMP_ANY,
+
+    /* L4 prerequisites. */
+    MFP_ND,
+    MFP_ND_SOLICIT,
+    MFP_ND_ADVERT
+};
+
+/* Forms of partial-field masking allowed for a field.
+ *
+ * Every field may be masked as a whole. */
+enum mf_maskable {
+    MFM_NONE,                   /* No sub-field masking. */
+    MFM_FULLY,                  /* Every bit is individually maskable. */
+    MFM_CIDR,                   /* Contiguous low-order bits may be masked. */
+    MFM_MCAST                   /* Byte 0, bit 2**0 is separately maskable. */
+};
+
+/* How to format or parse a field's value. */
+enum mf_string {
+    /* Integer formats.
+     *
+     * The particular MFS_* constant sets the output format.  On input, either
+     * decimal or hexadecimal (prefixed with 0x) is accepted. */
+    MFS_DECIMAL,
+    MFS_HEXADECIMAL,
+
+    /* Other formats. */
+    MFS_ETHERNET,
+    MFS_IPV4,
+    MFS_IPV6,
+    MFS_OFP_PORT                /* An OpenFlow port number or name. */
+};
+
+struct mf_field {
+    /* Identification. */
+    enum mf_field_id id;        /* MFF_*. */
+    const char *name;           /* Name of this field, e.g. "eth_type". */
+    const char *extra_name;     /* Alternate name, e.g. "dl_type", or NULL. */
+
+    /* Size.
+     *
+     * Most fields have n_bytes * 8 == n_bits.  There are only two exceptions
+     * currently: "dl_vlan" is 2 bytes but only 12 bits, and "dl_vlan_pcp" is
+     * 1 byte but only 3 bits. */
+    unsigned int n_bytes;       /* Width of the field in bytes. */
+    unsigned int n_bits;        /* Number of significant bits in field. */
+
+    /* Properties. */
+    enum mf_maskable maskable;
+    flow_wildcards_t fww_bit;   /* Either 0 or exactly one FWW_* bit. */
+    enum mf_string string;
+    enum mf_prereqs prereqs;
+    uint32_t nxm_header;        /* An NXM_* constant (a few fields have 0). */
+};
+
+/* Finding mf_fields. */
+const struct mf_field *mf_from_id(enum mf_field_id);
+const struct mf_field *mf_from_name(const char *name);
+
+/* Inspecting wildcarded bits. */
+bool mf_is_all_wild(const struct mf_field *, const struct flow_wildcards *);
+bool mf_is_all_fixed(const struct mf_field *, const struct flow_wildcards *);
+int mf_n_wild_bits(const struct mf_field *, const struct flow_wildcards *);
+int mf_n_fixed_bits(const struct mf_field *, const struct flow_wildcards *);
+
+bool mf_is_mask_valid(const struct mf_field *, const void *mask);
+void mf_get_mask(const struct mf_field *, const struct flow_wildcards *,
+                 void *mask);
+
+/* Prerequisites. */
+bool mf_are_prereqs_ok(const struct mf_field *, const struct flow *);
+void mf_force_prereqs(const struct mf_field *, struct cls_rule *);
+
+/* Field values. */
+bool mf_is_value_valid(const struct mf_field *, const void *value);
+
+void mf_get_value(const struct mf_field *, const struct flow *, void *value);
+void mf_set_value(const struct mf_field *, const void *value,
+                  struct cls_rule *);
+
+void mf_get(const struct mf_field *, const struct cls_rule *,
+            void *value, void *mask);
+void mf_set(const struct mf_field *, const void *value, const void *mask,
+            struct cls_rule *);
+void mf_set_subfield(const struct mf_field *, uint64_t value, unsigned int ofs,
+                     unsigned int n_bits, struct cls_rule *);
+
+void mf_set_wild(const struct mf_field *, struct cls_rule *);
+
+void mf_random_value(const struct mf_field *, void *value);
+
+/* Parsing and formatting. */
+char *mf_parse(const struct mf_field *, const char *, void *value, void *mask);
+void mf_format(const struct mf_field *, const void *value, const void *mask,
+               struct ds *);
+
+#endif /* meta-flow.h */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 422ce76..460d63e 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -22,6 +22,7 @@
 
 #include "classifier.h"
 #include "dynamic-string.h"
+#include "meta-flow.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/nicira-ext.h"
@@ -48,7 +49,7 @@ enum {
 /* For each NXM_* field, define NFI_NXM_* as consecutive integers starting from
  * zero. */
 enum nxm_field_index {
-#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE) \
+#define DEFINE_FIELD(HEADER, MFF_ID, WRITABLE)  \
         NFI_NXM_##HEADER,
 #include "nx-match.def"
     N_NXM_FIELDS
@@ -58,20 +59,17 @@ struct nxm_field {
     struct hmap_node hmap_node;
     enum nxm_field_index index;       /* NFI_* value. */
     uint32_t header;                  /* NXM_* value. */
-    flow_wildcards_t wildcard;        /* FWW_* bit, if exactly one. */
-    ovs_be16 dl_type[N_NXM_DL_TYPES]; /* dl_type prerequisites. */
-    uint8_t nw_proto;                 /* nw_proto prerequisite, if nonzero. */
+    enum mf_field_id mf_id;           /* MFF_* value. */
+    const struct mf_field *mf;
     const char *name;                 /* "NXM_*" string. */
     bool writable;                    /* Writable with NXAST_REG_{MOVE,LOAD}? */
 };
 
-
 /* All the known fields. */
 static struct nxm_field nxm_fields[N_NXM_FIELDS] = {
-#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE)     \
-    { HMAP_NODE_NULL_INITIALIZER, NFI_NXM_##HEADER, NXM_##HEADER, WILDCARD, \
-        DL_CONVERT DL_TYPES, NW_PROTO, "NXM_" #HEADER, WRITABLE },
-#define DL_CONVERT(T1, T2) { CONSTANT_HTONS(T1), CONSTANT_HTONS(T2) }
+#define DEFINE_FIELD(HEADER, MFF_ID, WRITABLE)                            \
+    { HMAP_NODE_NULL_INITIALIZER, NFI_NXM_##HEADER, NXM_##HEADER, \
+      MFF_ID, NULL, "NXM_" #HEADER, WRITABLE },
 #include "nx-match.def"
 };
 
@@ -88,12 +86,13 @@ nxm_init(void)
             struct nxm_field *f = &nxm_fields[i];
             hmap_insert(&all_nxm_fields, &f->hmap_node,
                         hash_int(f->header, 0));
+            f->mf = mf_from_id(f->mf_id);
         }
 
         /* Verify that the header values are unique (duplicate "case" values
          * cause a compile error). */
         switch (0) {
-#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO, WRITABLE)  \
+#define DEFINE_FIELD(HEADER, MFF_ID, WRITABLE)  \
         case NXM_##HEADER: break;
 #include "nx-match.def"
         }
@@ -133,318 +132,15 @@ nxm_field_bits(uint32_t header)
 {
     return nxm_field_bytes(header) * 8;
 }
-
-/* nx_pull_match() and helpers. */
-
-static int
-parse_nx_reg(const struct nxm_field *f,
-             struct flow *flow, struct flow_wildcards *wc,
-             const void *value, const void *maskp)
-{
-    int idx = NXM_NX_REG_IDX(f->header);
-    if (wc->reg_masks[idx]) {
-        return NXM_DUP_TYPE;
-    } else {
-        flow_wildcards_set_reg_mask(wc, idx,
-                                    (NXM_HASMASK(f->header)
-                                     ? ntohl(get_unaligned_be32(maskp))
-                                     : UINT32_MAX));
-        flow->regs[idx] = ntohl(get_unaligned_be32(value));
-        flow->regs[idx] &= wc->reg_masks[idx];
-        return 0;
-    }
-}
 
-static int
-parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
-                const void *value, const void *mask)
+const struct mf_field *
+nxm_field_to_mf_field(uint32_t header)
 {
-    struct flow_wildcards *wc = &rule->wc;
-    struct flow *flow = &rule->flow;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
-
-    switch (f->index) {
-        /* Metadata. */
-    case NFI_NXM_OF_IN_PORT:
-        flow->in_port = ntohs(get_unaligned_be16(value));
-        return 0;
-
-        /* Ethernet header. */
-    case NFI_NXM_OF_ETH_DST:
-        if ((wc->wildcards & (FWW_DL_DST | FWW_ETH_MCAST))
-            != (FWW_DL_DST | FWW_ETH_MCAST)) {
-            return NXM_DUP_TYPE;
-        } else {
-            wc->wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
-            memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
-            return 0;
-        }
-    case NFI_NXM_OF_ETH_DST_W:
-        if ((wc->wildcards & (FWW_DL_DST | FWW_ETH_MCAST))
-            != (FWW_DL_DST | FWW_ETH_MCAST)) {
-            return NXM_DUP_TYPE;
-        } else if (flow_wildcards_is_dl_dst_mask_valid(mask)) {
-            cls_rule_set_dl_dst_masked(rule, value, mask);
-            return 0;
-        } else {
-            return NXM_BAD_MASK;
-        }
-    case NFI_NXM_OF_ETH_SRC:
-        memcpy(flow->dl_src, value, ETH_ADDR_LEN);
-        return 0;
-    case NFI_NXM_OF_ETH_TYPE:
-        flow->dl_type = ofputil_dl_type_from_openflow(get_unaligned_be16(value));
-        return 0;
-
-        /* 802.1Q header. */
-    case NFI_NXM_OF_VLAN_TCI:
-        if (wc->vlan_tci_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            cls_rule_set_dl_tci(rule, get_unaligned_be16(value));
-            return 0;
-        }
-    case NFI_NXM_OF_VLAN_TCI_W:
-        if (wc->vlan_tci_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            cls_rule_set_dl_tci_masked(rule, get_unaligned_be16(value),
-                                       get_unaligned_be16(mask));
-            return 0;
-        }
-
-        /* IP header. */
-    case NFI_NXM_OF_IP_TOS:
-        if (*(uint8_t *) value & 0x03) {
-            return NXM_BAD_VALUE;
-        } else {
-            flow->nw_tos = *(uint8_t *) value;
-            return 0;
-        }
-    case NFI_NXM_OF_IP_PROTO:
-        flow->nw_proto = *(uint8_t *) value;
-        return 0;
-
-        /* IP addresses in IP and ARP headers. */
-    case NFI_NXM_OF_IP_SRC:
-    case NFI_NXM_OF_ARP_SPA:
-        if (wc->nw_src_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            cls_rule_set_nw_src(rule, get_unaligned_be32(value));
-            return 0;
-        }
-    case NFI_NXM_OF_IP_SRC_W:
-    case NFI_NXM_OF_ARP_SPA_W:
-        if (wc->nw_src_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            ovs_be32 ip = get_unaligned_be32(value);
-            ovs_be32 netmask = get_unaligned_be32(mask);
-            if (!cls_rule_set_nw_src_masked(rule, ip, netmask)) {
-                return NXM_BAD_MASK;
-            }
-            return 0;
-        }
-    case NFI_NXM_OF_IP_DST:
-    case NFI_NXM_OF_ARP_TPA:
-        if (wc->nw_dst_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            cls_rule_set_nw_dst(rule, get_unaligned_be32(value));
-            return 0;
-        }
-    case NFI_NXM_OF_IP_DST_W:
-    case NFI_NXM_OF_ARP_TPA_W:
-        if (wc->nw_dst_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            ovs_be32 ip = get_unaligned_be32(value);
-            ovs_be32 netmask = get_unaligned_be32(mask);
-            if (!cls_rule_set_nw_dst_masked(rule, ip, netmask)) {
-                return NXM_BAD_MASK;
-            }
-            return 0;
-        }
-
-        /* IPv6 addresses. */
-    case NFI_NXM_NX_IPV6_SRC:
-        if (!ipv6_mask_is_any(&wc->ipv6_src_mask)) {
-            return NXM_DUP_TYPE;
-        } else {
-            struct in6_addr ipv6;
-            memcpy(&ipv6, value, sizeof ipv6);
-            cls_rule_set_ipv6_src(rule, &ipv6);
-            return 0;
-        }
-    case NFI_NXM_NX_IPV6_SRC_W:
-        if (!ipv6_mask_is_any(&wc->ipv6_src_mask)) {
-            return NXM_DUP_TYPE;
-        } else {
-            struct in6_addr ipv6, netmask;
-            memcpy(&ipv6, value, sizeof ipv6);
-            memcpy(&netmask, mask, sizeof netmask);
-            if (!cls_rule_set_ipv6_src_masked(rule, &ipv6, &netmask)) {
-                return NXM_BAD_MASK;
-            }
-            return 0;
-        }
-    case NFI_NXM_NX_IPV6_DST:
-        if (!ipv6_mask_is_any(&wc->ipv6_dst_mask)) {
-            return NXM_DUP_TYPE;
-        } else {
-            struct in6_addr ipv6;
-            memcpy(&ipv6, value, sizeof ipv6);
-            cls_rule_set_ipv6_dst(rule, &ipv6);
-            return 0;
-        }
-    case NFI_NXM_NX_IPV6_DST_W:
-        if (!ipv6_mask_is_any(&wc->ipv6_dst_mask)) {
-            return NXM_DUP_TYPE;
-        } else {
-            struct in6_addr ipv6, netmask;
-            memcpy(&ipv6, value, sizeof ipv6);
-            memcpy(&netmask, mask, sizeof netmask);
-            if (!cls_rule_set_ipv6_dst_masked(rule, &ipv6, &netmask)) {
-                return NXM_BAD_MASK;
-            }
-            return 0;
-        }
-
-        /* TCP header. */
-    case NFI_NXM_OF_TCP_SRC:
-        flow->tp_src = get_unaligned_be16(value);
-        return 0;
-    case NFI_NXM_OF_TCP_DST:
-        flow->tp_dst = get_unaligned_be16(value);
-        return 0;
-
-        /* UDP header. */
-    case NFI_NXM_OF_UDP_SRC:
-        flow->tp_src = get_unaligned_be16(value);
-        return 0;
-    case NFI_NXM_OF_UDP_DST:
-        flow->tp_dst = get_unaligned_be16(value);
-        return 0;
-
-        /* ICMP header. */
-    case NFI_NXM_OF_ICMP_TYPE:
-        flow->tp_src = htons(*(uint8_t *) value);
-        return 0;
-    case NFI_NXM_OF_ICMP_CODE:
-        flow->tp_dst = htons(*(uint8_t *) value);
-        return 0;
-
-        /* ICMPv6 header. */
-    case NFI_NXM_NX_ICMPV6_TYPE:
-        flow->tp_src = htons(*(uint8_t *) value);
-        return 0;
-    case NFI_NXM_NX_ICMPV6_CODE:
-        flow->tp_dst = htons(*(uint8_t *) value);
-        return 0;
-
-        /* IPv6 Neighbor Discovery. */
-    case NFI_NXM_NX_ND_TARGET:
-        /* We've already verified that it's an ICMPv6 message. */
-        if ((flow->tp_src != htons(ND_NEIGHBOR_SOLICIT))
-                    && (flow->tp_src != htons(ND_NEIGHBOR_ADVERT))) {
-            return NXM_BAD_PREREQ;
-        }
-        memcpy(&flow->nd_target, value, sizeof flow->nd_target);
-        return 0;
-    case NFI_NXM_NX_ND_SLL:
-        /* We've already verified that it's an ICMPv6 message. */
-        if (flow->tp_src != htons(ND_NEIGHBOR_SOLICIT)) {
-            return NXM_BAD_PREREQ;
-        }
-        memcpy(flow->arp_sha, value, ETH_ADDR_LEN);
-        return 0;
-    case NFI_NXM_NX_ND_TLL:
-        /* We've already verified that it's an ICMPv6 message. */
-        if (flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) {
-            return NXM_BAD_PREREQ;
-        }
-        memcpy(flow->arp_tha, value, ETH_ADDR_LEN);
-        return 0;
-
-        /* ARP header. */
-    case NFI_NXM_OF_ARP_OP:
-        if (ntohs(get_unaligned_be16(value)) > 255) {
-            return NXM_BAD_VALUE;
-        } else {
-            flow->nw_proto = ntohs(get_unaligned_be16(value));
-            return 0;
-        }
-
-    case NFI_NXM_NX_ARP_SHA:
-        memcpy(flow->arp_sha, value, ETH_ADDR_LEN);
-        return 0;
-    case NFI_NXM_NX_ARP_THA:
-        memcpy(flow->arp_tha, value, ETH_ADDR_LEN);
-        return 0;
-
-        /* Tunnel ID. */
-    case NFI_NXM_NX_TUN_ID:
-        if (wc->tun_id_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            cls_rule_set_tun_id(rule, get_unaligned_be64(value));
-            return 0;
-        }
-    case NFI_NXM_NX_TUN_ID_W:
-        if (wc->tun_id_mask) {
-            return NXM_DUP_TYPE;
-        } else {
-            ovs_be64 tun_id = get_unaligned_be64(value);
-            ovs_be64 tun_mask = get_unaligned_be64(mask);
-            cls_rule_set_tun_id_masked(rule, tun_id, tun_mask);
-            return 0;
-        }
-
-        /* Registers. */
-    case NFI_NXM_NX_REG0:
-    case NFI_NXM_NX_REG0_W:
-#if FLOW_N_REGS >= 2
-    case NFI_NXM_NX_REG1:
-    case NFI_NXM_NX_REG1_W:
-#endif
-#if FLOW_N_REGS >= 3
-    case NFI_NXM_NX_REG2:
-    case NFI_NXM_NX_REG2_W:
-#endif
-#if FLOW_N_REGS >= 4
-    case NFI_NXM_NX_REG3:
-    case NFI_NXM_NX_REG3_W:
-#endif
-#if FLOW_N_REGS > 4
-#error
-#endif
-        return parse_nx_reg(f, flow, wc, value, mask);
-
-    case N_NXM_FIELDS:
-        NOT_REACHED();
-    }
-    NOT_REACHED();
-}
-
-static bool
-nxm_prereqs_ok(const struct nxm_field *field, const struct flow *flow)
-{
-    if (field->nw_proto && field->nw_proto != flow->nw_proto) {
-        return false;
-    }
-
-    if (!field->dl_type[0]) {
-        return true;
-    } else if (field->dl_type[0] == flow->dl_type) {
-        return true;
-    } else if (field->dl_type[1] && field->dl_type[1] == flow->dl_type) {
-        return true;
-    }
-
-    return false;
+    const struct nxm_field *f = nxm_field_lookup(header);
+    return f ? f->mf : NULL;
 }
+
+/* nx_pull_match() and helpers. */
 
 static uint32_t
 nx_entry_ok(const void *p, unsigned int match_len)
@@ -501,17 +197,24 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
         f = nxm_field_lookup(header);
         if (!f) {
             error = NXM_BAD_TYPE;
-        } else if (!nxm_prereqs_ok(f, &rule->flow)) {
+        } else if (!mf_are_prereqs_ok(f->mf, &rule->flow)) {
             error = NXM_BAD_PREREQ;
-        } else if (f->wildcard && !(rule->wc.wildcards & f->wildcard)) {
+        } else if (!mf_is_all_wild(f->mf, &rule->wc)) {
             error = NXM_DUP_TYPE;
         } else {
-            /* 'hasmask' and 'length' are known to be correct at this point
-             * because they are included in 'header' and nxm_field_lookup()
-             * checked them already. */
-            rule->wc.wildcards &= ~f->wildcard;
-            error = parse_nxm_entry(rule, f, p + 4, p + 4 + length / 2);
+            const void *value = p + 4;
+            const void *mask = NXM_HASMASK(header) ? p + 4 + length / 2 : NULL;
+
+            if (mask && !mf_is_mask_valid(f->mf, mask)) {
+                error = NXM_BAD_MASK;
+            } else if (!mf_is_value_valid(f->mf, value)) {
+                error = NXM_BAD_VALUE;
+            } else {
+                error = 0;
+                mf_set(f->mf, value, mask, rule);
+            }
         }
+
         if (error) {
             VLOG_DBG_RL(&rl, "bad nxm_entry with vendor=%"PRIu32", "
                         "field=%"PRIu32", hasmask=%"PRIu32", type=%"PRIu32" "
@@ -522,7 +225,6 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
             return error;
         }
 
-
         p += 4 + length;
         match_len -= 4 + length;
     }
@@ -1177,8 +879,10 @@ nxm_format_reg_load(const struct nx_action_reg_load *load, struct ds *s)
 static bool
 field_ok(const struct nxm_field *f, const struct flow *flow, int size)
 {
-    return (f && !NXM_HASMASK(f->header)
-            && nxm_prereqs_ok(f, flow) && size <= nxm_field_bits(f->header));
+    return (f
+            && !NXM_HASMASK(f->header)
+            && mf_are_prereqs_ok(f->mf, flow)
+            && size <= nxm_field_bits(f->header));
 }
 
 int
diff --git a/lib/nx-match.def b/lib/nx-match.def
index 3fcb59c..65f4667 100644
--- a/lib/nx-match.def
+++ b/lib/nx-match.def
@@ -14,58 +14,51 @@
  * limitations under the License.
  */
 
-#define N_NXM_DL_TYPES 2
+#define DEFINE_FIELD_M(HEADER, MFF_ID, WRITABLE)  \
+    DEFINE_FIELD(HEADER,     MFF_ID, WRITABLE)        \
+    DEFINE_FIELD(HEADER##_W, MFF_ID, false)
 
-#define NXM_DL_NONE   (0, 0)
-#define NXM_DL_ARP    (ETH_TYPE_ARP, 0)
-#define NXM_DL_IP     (ETH_TYPE_IP, 0)
-#define NXM_DL_IPV6   (ETH_TYPE_IPV6, 0)
-#define NXM_DL_IP_ANY (ETH_TYPE_IP, ETH_TYPE_IPV6)
+/*             NXM_ suffix    MFF_ field ID    rw? */
+/*             ------------   ------------   ----- */
+DEFINE_FIELD_M(NX_TUN_ID,     MFF_TUN_ID,     true)
+DEFINE_FIELD  (OF_IN_PORT,    MFF_IN_PORT,   false)
+DEFINE_FIELD_M(OF_ETH_DST,    MFF_ETH_DST,    true)
+DEFINE_FIELD  (OF_ETH_SRC,    MFF_ETH_SRC,    true)
+DEFINE_FIELD  (OF_ETH_TYPE,   MFF_ETH_TYPE,  false)
+DEFINE_FIELD_M(OF_VLAN_TCI,   MFF_VLAN_TCI,   true)
+DEFINE_FIELD  (OF_IP_TOS,     MFF_IP_TOS,     true)
+DEFINE_FIELD  (OF_IP_PROTO,   MFF_IP_PROTO,  false)
+DEFINE_FIELD_M(OF_IP_SRC,     MFF_IPV4_SRC,   true)
+DEFINE_FIELD_M(OF_IP_DST,     MFF_IPV4_DST,   true)
+DEFINE_FIELD  (OF_TCP_SRC,    MFF_TCP_SRC,    true)
+DEFINE_FIELD  (OF_TCP_DST,    MFF_TCP_DST,    true)
+DEFINE_FIELD  (OF_UDP_SRC,    MFF_UDP_SRC,    true)
+DEFINE_FIELD  (OF_UDP_DST,    MFF_UDP_DST,    true)
+DEFINE_FIELD  (OF_ICMP_TYPE,  MFF_ICMP_TYPE, false)
+DEFINE_FIELD  (OF_ICMP_CODE,  MFF_ICMP_CODE, false)
+DEFINE_FIELD  (OF_ARP_OP,     MFF_ARP_OP,    false)
+DEFINE_FIELD_M(OF_ARP_SPA,    MFF_ARP_SPA,   false)
+DEFINE_FIELD_M(OF_ARP_TPA,    MFF_ARP_TPA,   false)
+DEFINE_FIELD  (NX_ARP_SHA,    MFF_ARP_SHA,   false)
+DEFINE_FIELD  (NX_ARP_THA,    MFF_ARP_THA,   false)
+DEFINE_FIELD_M(NX_IPV6_SRC,   MFF_IPV6_SRC,  false)
+DEFINE_FIELD_M(NX_IPV6_DST,   MFF_IPV6_DST,  false)
+/* XXX should we have MFF_ICMPV4_TYPE and MFF_ICMPV6_TYPE? */
+DEFINE_FIELD  (NX_ICMPV6_TYPE,MFF_ICMP_TYPE, false)
+DEFINE_FIELD  (NX_ICMPV6_CODE,MFF_ICMP_CODE, false)
+DEFINE_FIELD  (NX_ND_TARGET,  MFF_ND_TARGET, false)
+DEFINE_FIELD  (NX_ND_SLL,     MFF_ND_SLL,    false)
+DEFINE_FIELD  (NX_ND_TLL,     MFF_ND_TLL,    false)
 
-#define DEFINE_FIELD_M(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE)  \
-    DEFINE_FIELD(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE)        \
-    DEFINE_FIELD(HEADER##_W, WILDCARD, DL_TYPES, NW_PROTO, false)
-
-/*             NXM_ suffix    FWW_* bit     dl_types       nw_proto      rw? */
-/*             ------------   ------------  -----------    ------------- --- */
-DEFINE_FIELD_M(NX_TUN_ID,     0,            NXM_DL_NONE,   0,            true)
-DEFINE_FIELD  (OF_IN_PORT,    FWW_IN_PORT,  NXM_DL_NONE,   0,            false)
-DEFINE_FIELD_M(OF_ETH_DST,    0,            NXM_DL_NONE,   0,            true)
-DEFINE_FIELD  (OF_ETH_SRC,    FWW_DL_SRC,   NXM_DL_NONE,   0,            true)
-DEFINE_FIELD  (OF_ETH_TYPE,   FWW_DL_TYPE,  NXM_DL_NONE,   0,            false)
-DEFINE_FIELD_M(OF_VLAN_TCI,   0,            NXM_DL_NONE,   0,            true)
-DEFINE_FIELD  (OF_IP_TOS,     FWW_NW_TOS,   NXM_DL_IP_ANY, 0,            true)
-DEFINE_FIELD  (OF_IP_PROTO,   FWW_NW_PROTO, NXM_DL_IP_ANY, 0,            false)
-DEFINE_FIELD_M(OF_IP_SRC,     0,            NXM_DL_IP,     0,            true)
-DEFINE_FIELD_M(OF_IP_DST,     0,            NXM_DL_IP,     0,            true)
-DEFINE_FIELD  (OF_TCP_SRC,    FWW_TP_SRC,   NXM_DL_IP_ANY, IPPROTO_TCP,  true)
-DEFINE_FIELD  (OF_TCP_DST,    FWW_TP_DST,   NXM_DL_IP_ANY, IPPROTO_TCP,  true)
-DEFINE_FIELD  (OF_UDP_SRC,    FWW_TP_SRC,   NXM_DL_IP_ANY, IPPROTO_UDP,  true)
-DEFINE_FIELD  (OF_UDP_DST,    FWW_TP_DST,   NXM_DL_IP_ANY, IPPROTO_UDP,  true)
-DEFINE_FIELD  (OF_ICMP_TYPE,  FWW_TP_SRC,   NXM_DL_IP,     IPPROTO_ICMP, false)
-DEFINE_FIELD  (OF_ICMP_CODE,  FWW_TP_DST,   NXM_DL_IP,     IPPROTO_ICMP, false)
-DEFINE_FIELD  (OF_ARP_OP,     FWW_NW_PROTO, NXM_DL_ARP,    0,            false)
-DEFINE_FIELD_M(OF_ARP_SPA,    0,            NXM_DL_ARP,    0,            false)
-DEFINE_FIELD_M(OF_ARP_TPA,    0,            NXM_DL_ARP,    0,            false)
-DEFINE_FIELD  (NX_ARP_SHA,    FWW_ARP_SHA,  NXM_DL_ARP,    0,            false)
-DEFINE_FIELD  (NX_ARP_THA,    FWW_ARP_THA,  NXM_DL_ARP,    0,            false)
-DEFINE_FIELD_M(NX_IPV6_SRC,   0,            NXM_DL_IPV6,   0,            false)
-DEFINE_FIELD_M(NX_IPV6_DST,   0,            NXM_DL_IPV6,   0,            false)
-DEFINE_FIELD  (NX_ICMPV6_TYPE,FWW_TP_SRC,   NXM_DL_IPV6, IPPROTO_ICMPV6, false)
-DEFINE_FIELD  (NX_ICMPV6_CODE,FWW_TP_DST,   NXM_DL_IPV6, IPPROTO_ICMPV6, false)
-DEFINE_FIELD  (NX_ND_TARGET,  FWW_ND_TARGET,NXM_DL_IPV6, IPPROTO_ICMPV6, false)
-DEFINE_FIELD  (NX_ND_SLL,     FWW_ARP_SHA,  NXM_DL_IPV6, IPPROTO_ICMPV6, false)
-DEFINE_FIELD  (NX_ND_TLL,     FWW_ARP_THA,  NXM_DL_IPV6, IPPROTO_ICMPV6, false)
-
-DEFINE_FIELD_M(NX_REG0,        0,            NXM_DL_NONE,   0,            true)
+DEFINE_FIELD_M(NX_REG0,       MFF_REG0,       true)
 #if FLOW_N_REGS >= 2
-DEFINE_FIELD_M(NX_REG1,        0,            NXM_DL_NONE,   0,            true)
+DEFINE_FIELD_M(NX_REG1,       MFF_REG1,       true)
 #endif
 #if FLOW_N_REGS >= 3
-DEFINE_FIELD_M(NX_REG2,        0,            NXM_DL_NONE,   0,            true)
+DEFINE_FIELD_M(NX_REG2,       MFF_REG2,       true)
 #endif
 #if FLOW_N_REGS >= 4
-DEFINE_FIELD_M(NX_REG3,        0,            NXM_DL_NONE,   0,            true)
+DEFINE_FIELD_M(NX_REG3,       MFF_REG3,       true)
 #endif
 #if FLOW_N_REGS > 4
 #error
diff --git a/lib/nx-match.h b/lib/nx-match.h
index 04da239..64e22c9 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -64,6 +64,7 @@ void nxm_reg_load(ovs_be32 dst, ovs_be16 ofs_nbits, uint64_t src_data,
 
 int nxm_field_bytes(uint32_t header);
 int nxm_field_bits(uint32_t header);
+const struct mf_field *nxm_field_to_mf_field(uint32_t header);
 
 const char *nxm_parse_field_bits(const char *s,
                                  uint32_t *headerp, int *ofsp, int *n_bitsp);
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 77d3f19..115fd48 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -26,6 +26,7 @@
 #include "bundle.h"
 #include "byte-order.h"
 #include "dynamic-string.h"
+#include "meta-flow.h"
 #include "netdev.h"
 #include "multipath.h"
 #include "nx-match.h"
@@ -85,172 +86,14 @@ str_to_mac(const char *str, uint8_t mac[6])
 }
 
 static void
-str_to_eth_dst(const char *str,
-               uint8_t mac[ETH_ADDR_LEN], uint8_t mask[ETH_ADDR_LEN])
+str_to_ip(const char *str, ovs_be32 *ip)
 {
-    if (sscanf(str, ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT,
-               ETH_ADDR_SCAN_ARGS(mac), ETH_ADDR_SCAN_ARGS(mask))
-        == ETH_ADDR_SCAN_COUNT * 2) {
-        if (!flow_wildcards_is_dl_dst_mask_valid(mask)) {
-            ovs_fatal(0, "%s: invalid Ethernet destination mask (only "
-                      "00:00:00:00:00:00, 01:00:00:00:00:00, "
-                      "fe:ff:ff:ff:ff:ff, and ff:ff:ff:ff:ff:ff are allowed)",
-                      str);
-        }
-    } else if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
-               == ETH_ADDR_SCAN_COUNT) {
-        memset(mask, 0xff, ETH_ADDR_LEN);
-    } else {
-        ovs_fatal(0, "invalid mac address %s", str);
-    }
-}
-
-static void
-str_to_ip(const char *str_, ovs_be32 *ip, ovs_be32 *maskp)
-{
-    char *str = xstrdup(str_);
-    char *save_ptr = NULL;
-    const char *name, *netmask;
     struct in_addr in_addr;
-    ovs_be32 mask;
-    int retval;
 
-    name = strtok_r(str, "/", &save_ptr);
-    retval = name ? lookup_ip(name, &in_addr) : EINVAL;
-    if (retval) {
+    if (lookup_ip(str, &in_addr)) {
         ovs_fatal(0, "%s: could not convert to IP address", str);
     }
     *ip = in_addr.s_addr;
-
-    netmask = strtok_r(NULL, "/", &save_ptr);
-    if (netmask) {
-        uint8_t o[4];
-        if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
-                   &o[0], &o[1], &o[2], &o[3]) == 4) {
-            mask = htonl((o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3]);
-        } else {
-            int prefix = atoi(netmask);
-            if (prefix <= 0 || prefix > 32) {
-                ovs_fatal(0, "%s: network prefix bits not between 1 and 32",
-                          str);
-            } else if (prefix == 32) {
-                mask = htonl(UINT32_MAX);
-            } else {
-                mask = htonl(((1u << prefix) - 1) << (32 - prefix));
-            }
-        }
-    } else {
-        mask = htonl(UINT32_MAX);
-    }
-    *ip &= mask;
-
-    if (maskp) {
-        *maskp = mask;
-    } else {
-        if (mask != htonl(UINT32_MAX)) {
-            ovs_fatal(0, "%s: netmask not allowed here", str_);
-        }
-    }
-
-    free(str);
-}
-
-static void
-str_to_tun_id(const char *str, ovs_be64 *tun_idp, ovs_be64 *maskp)
-{
-    uint64_t tun_id, mask;
-    char *tail;
-
-    errno = 0;
-    tun_id = strtoull(str, &tail, 0);
-    if (errno || (*tail != '\0' && *tail != '/')) {
-        goto error;
-    }
-
-    if (*tail == '/') {
-        mask = strtoull(tail + 1, &tail, 0);
-        if (errno || *tail != '\0') {
-            goto error;
-        }
-    } else {
-        mask = UINT64_MAX;
-    }
-
-    *tun_idp = htonll(tun_id);
-    *maskp = htonll(mask);
-    return;
-
-error:
-    ovs_fatal(0, "%s: bad syntax for tunnel id", str);
-}
-
-static void
-str_to_vlan_tci(const char *str, ovs_be16 *vlan_tcip, ovs_be16 *maskp)
-{
-    uint16_t vlan_tci, mask;
-    char *tail;
-
-    errno = 0;
-    vlan_tci = strtol(str, &tail, 0);
-    if (errno || (*tail != '\0' && *tail != '/')) {
-        goto error;
-    }
-
-    if (*tail == '/') {
-        mask = strtol(tail + 1, &tail, 0);
-        if (errno || *tail != '\0') {
-            goto error;
-        }
-    } else {
-        mask = UINT16_MAX;
-    }
-
-    *vlan_tcip = htons(vlan_tci);
-    *maskp = htons(mask);
-    return;
-
-error:
-    ovs_fatal(0, "%s: bad syntax for vlan_tci", str);
-}
-
-static void
-str_to_ipv6(const char *str_, struct in6_addr *addrp, struct in6_addr *maskp)
-{
-    char *str = xstrdup(str_);
-    char *save_ptr = NULL;
-    const char *name, *netmask;
-    struct in6_addr addr, mask;
-    int retval;
-
-    name = strtok_r(str, "/", &save_ptr);
-    retval = name ? lookup_ipv6(name, &addr) : EINVAL;
-    if (retval) {
-        ovs_fatal(0, "%s: could not convert to IPv6 address", str);
-    }
-
-    netmask = strtok_r(NULL, "/", &save_ptr);
-    if (netmask) {
-        int prefix = atoi(netmask);
-        if (prefix <= 0 || prefix > 128) {
-            ovs_fatal(0, "%s: network prefix bits not between 1 and 128",
-                      str);
-        } else {
-            mask = ipv6_create_mask(prefix);
-        }
-    } else {
-        mask = in6addr_exact;
-    }
-    *addrp = ipv6_addr_bitand(&addr, &mask);
-
-    if (maskp) {
-        *maskp = mask;
-    } else {
-        if (!ipv6_mask_is_exact(&mask)) {
-            ovs_fatal(0, "%s: netmask not allowed here", str_);
-        }
-    }
-
-    free(str);
 }
 
 static struct ofp_action_output *
@@ -421,7 +264,7 @@ parse_named_action(enum ofputil_action_code code, struct ofpbuf *b, char *arg)
     case OFPUTIL_OFPAT_SET_NW_SRC:
     case OFPUTIL_OFPAT_SET_NW_DST:
         oana = ofputil_put_action(code, b);
-        str_to_ip(arg, &oana->nw_addr, NULL);
+        str_to_ip(arg, &oana->nw_addr);
         break;
 
     case OFPUTIL_OFPAT_SET_NW_TOS:
@@ -571,45 +414,6 @@ parse_protocol(const char *name, const struct protocol **p_out)
     return false;
 }
 
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
-#define FIELDS                                              \
-    FIELD(F_TUN_ID,      "tun_id",      0)                  \
-    FIELD(F_IN_PORT,     "in_port",     FWW_IN_PORT)        \
-    FIELD(F_DL_VLAN,     "dl_vlan",     0)                  \
-    FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", 0)                  \
-    FIELD(F_VLAN_TCI,    "vlan_tci",    0)                  \
-    FIELD(F_DL_SRC,      "dl_src",      FWW_DL_SRC)         \
-    FIELD(F_DL_DST,      "dl_dst",      FWW_DL_DST | FWW_ETH_MCAST) \
-    FIELD(F_DL_TYPE,     "dl_type",     FWW_DL_TYPE)        \
-    FIELD(F_NW_SRC,      "nw_src",      0)                  \
-    FIELD(F_NW_DST,      "nw_dst",      0)                  \
-    FIELD(F_NW_PROTO,    "nw_proto",    FWW_NW_PROTO)       \
-    FIELD(F_NW_TOS,      "nw_tos",      FWW_NW_TOS)         \
-    FIELD(F_TP_SRC,      "tp_src",      FWW_TP_SRC)         \
-    FIELD(F_TP_DST,      "tp_dst",      FWW_TP_DST)         \
-    FIELD(F_ICMP_TYPE,   "icmp_type",   FWW_TP_SRC)         \
-    FIELD(F_ICMP_CODE,   "icmp_code",   FWW_TP_DST)         \
-    FIELD(F_ARP_SHA,     "arp_sha",     FWW_ARP_SHA)        \
-    FIELD(F_ARP_THA,     "arp_tha",     FWW_ARP_THA)        \
-    FIELD(F_IPV6_SRC,    "ipv6_src",    0)                  \
-    FIELD(F_IPV6_DST,    "ipv6_dst",    0)                  \
-    FIELD(F_ND_TARGET,   "nd_target",   FWW_ND_TARGET)      \
-    FIELD(F_ND_SLL,      "nd_sll",      FWW_ARP_SHA)        \
-    FIELD(F_ND_TLL,      "nd_tll",      FWW_ARP_THA)
-
-enum field_index {
-#define FIELD(ENUM, NAME, WILDCARD) ENUM,
-    FIELDS
-#undef FIELD
-    N_FIELDS
-};
-
-struct field {
-    enum field_index index;
-    const char *name;
-    flow_wildcards_t wildcard;  /* FWW_* bit. */
-};
-
 static void
 ofp_fatal(const char *flow, bool verbose, const char *format, ...)
 {
@@ -623,173 +427,19 @@ ofp_fatal(const char *flow, bool verbose, const char *format, ...)
     ovs_fatal_valist(0, format, args);
 }
 
-static bool
-parse_field_name(const char *name, const struct field **f_out)
-{
-    static const struct field fields[N_FIELDS] = {
-#define FIELD(ENUM, NAME, WILDCARD) { ENUM, NAME, WILDCARD },
-        FIELDS
-#undef FIELD
-    };
-    const struct field *f;
-
-    for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
-        if (!strcmp(f->name, name)) {
-            *f_out = f;
-            return true;
-        }
-    }
-    *f_out = NULL;
-    return false;
-}
-
 static void
-parse_field_value(struct cls_rule *rule, enum field_index index,
-                  const char *value)
+parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule)
 {
-    uint8_t mac[ETH_ADDR_LEN], mac_mask[ETH_ADDR_LEN];
-    ovs_be64 tun_id, tun_mask;
-    ovs_be32 ip, mask;
-    ovs_be16 tci, tci_mask;
-    struct in6_addr ipv6, ipv6_mask;
-    uint16_t port_no;
-
-    switch (index) {
-    case F_TUN_ID:
-        str_to_tun_id(value, &tun_id, &tun_mask);
-        cls_rule_set_tun_id_masked(rule, tun_id, tun_mask);
-        break;
-
-    case F_IN_PORT:
-        if (!ofputil_port_from_string(value, &port_no)) {
-            port_no = atoi(value);
-        }
-        cls_rule_set_in_port(rule, port_no);
-        break;
-
-    case F_DL_VLAN:
-        cls_rule_set_dl_vlan(rule, htons(str_to_u32(value)));
-        break;
-
-    case F_DL_VLAN_PCP:
-        cls_rule_set_dl_vlan_pcp(rule, str_to_u32(value));
-        break;
-
-    case F_VLAN_TCI:
-        str_to_vlan_tci(value, &tci, &tci_mask);
-        cls_rule_set_dl_tci_masked(rule, tci, tci_mask);
-        break;
-
-    case F_DL_SRC:
-        str_to_mac(value, mac);
-        cls_rule_set_dl_src(rule, mac);
-        break;
-
-    case F_DL_DST:
-        str_to_eth_dst(value, mac, mac_mask);
-        cls_rule_set_dl_dst_masked(rule, mac, mac_mask);
-        break;
-
-    case F_DL_TYPE:
-        cls_rule_set_dl_type(rule, htons(str_to_u32(value)));
-        break;
-
-    case F_NW_SRC:
-        str_to_ip(value, &ip, &mask);
-        cls_rule_set_nw_src_masked(rule, ip, mask);
-        break;
-
-    case F_NW_DST:
-        str_to_ip(value, &ip, &mask);
-        cls_rule_set_nw_dst_masked(rule, ip, mask);
-        break;
-
-    case F_NW_PROTO:
-        cls_rule_set_nw_proto(rule, str_to_u32(value));
-        break;
-
-    case F_NW_TOS:
-        cls_rule_set_nw_tos(rule, str_to_u32(value));
-        break;
-
-    case F_TP_SRC:
-        cls_rule_set_tp_src(rule, htons(str_to_u32(value)));
-        break;
-
-    case F_TP_DST:
-        cls_rule_set_tp_dst(rule, htons(str_to_u32(value)));
-        break;
-
-    case F_ICMP_TYPE:
-        cls_rule_set_icmp_type(rule, str_to_u32(value));
-        break;
-
-    case F_ICMP_CODE:
-        cls_rule_set_icmp_code(rule, str_to_u32(value));
-        break;
-
-    case F_ARP_SHA:
-        str_to_mac(value, mac);
-        cls_rule_set_arp_sha(rule, mac);
-        break;
-
-    case F_ARP_THA:
-        str_to_mac(value, mac);
-        cls_rule_set_arp_tha(rule, mac);
-        break;
+    uint8_t value[MF_MAXSIZE];
+    uint8_t mask[MF_MAXSIZE];
+    char *error;
 
-    case F_IPV6_SRC:
-        str_to_ipv6(value, &ipv6, &ipv6_mask);
-        cls_rule_set_ipv6_src_masked(rule, &ipv6, &ipv6_mask);
-        break;
-
-    case F_IPV6_DST:
-        str_to_ipv6(value, &ipv6, &ipv6_mask);
-        cls_rule_set_ipv6_dst_masked(rule, &ipv6, &ipv6_mask);
-        break;
-
-    case F_ND_TARGET:
-        str_to_ipv6(value, &ipv6, NULL);
-        cls_rule_set_nd_target(rule, ipv6);
-        break;
-
-    case F_ND_SLL:
-        str_to_mac(value, mac);
-        cls_rule_set_arp_sha(rule, mac);
-        break;
-
-    case F_ND_TLL:
-        str_to_mac(value, mac);
-        cls_rule_set_arp_tha(rule, mac);
-        break;
-
-    case N_FIELDS:
-        NOT_REACHED();
+    error = mf_parse(mf, s, value, mask);
+    if (error) {
+        ovs_fatal(0, "%s", error);
     }
-}
 
-static void
-parse_reg_value(struct cls_rule *rule, int reg_idx, const char *value)
-{
-    /* This uses an oversized destination field (64 bits when 32 bits would do)
-     * because some sscanf() implementations truncate the range of %i
-     * directives, so that e.g. "%"SCNi16 interprets input of "0xfedc" as a
-     * value of 0x7fff.  The other alternatives are to allow only a single
-     * radix (e.g. decimal or hexadecimal) or to write more sophisticated
-     * parsers. */
-    unsigned long long int reg_value, reg_mask;
-
-    if (!strcmp(value, "ANY") || !strcmp(value, "*")) {
-        cls_rule_set_reg_masked(rule, reg_idx, 0, 0);
-    } else if (sscanf(value, "%lli/%lli",
-                      &reg_value, &reg_mask) == 2) {
-        cls_rule_set_reg_masked(rule, reg_idx, reg_value, reg_mask);
-    } else if (sscanf(value, "%lli", &reg_value)) {
-        cls_rule_set_reg(rule, reg_idx, reg_value);
-    } else {
-        ovs_fatal(0, "register fields must take the form <value> "
-                  "or <value>/<mask>");
-    }
+    mf_set(mf, value, mask, rule);
 }
 
 /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
@@ -888,7 +538,6 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                 cls_rule_set_nw_proto(&fm->cr, p->nw_proto);
             }
         } else {
-            const struct field *f;
             char *value;
 
             value = strtok_r(NULL, ", \t\r\n", &save_ptr);
@@ -908,41 +557,8 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                 fm->hard_timeout = atoi(value);
             } else if (fields & F_COOKIE && !strcmp(name, "cookie")) {
                 fm->cookie = htonll(str_to_u64(value));
-            } else if (parse_field_name(name, &f)) {
-                if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
-                    if (f->wildcard) {
-                        fm->cr.wc.wildcards |= f->wildcard;
-                        cls_rule_zero_wildcarded_fields(&fm->cr);
-                    } else if (f->index == F_NW_SRC) {
-                        cls_rule_set_nw_src_masked(&fm->cr, 0, 0);
-                    } else if (f->index == F_NW_DST) {
-                        cls_rule_set_nw_dst_masked(&fm->cr, 0, 0);
-                    } else if (f->index == F_IPV6_SRC) {
-                        cls_rule_set_ipv6_src_masked(&fm->cr,
-                                &in6addr_any, &in6addr_any);
-                    } else if (f->index == F_IPV6_DST) {
-                        cls_rule_set_ipv6_dst_masked(&fm->cr,
-                                &in6addr_any, &in6addr_any);
-                    } else if (f->index == F_DL_VLAN) {
-                        cls_rule_set_any_vid(&fm->cr);
-                    } else if (f->index == F_DL_VLAN_PCP) {
-                        cls_rule_set_any_pcp(&fm->cr);
-                    } else {
-                        NOT_REACHED();
-                    }
-                } else {
-                    parse_field_value(&fm->cr, f->index, value);
-                }
-            } else if (!strncmp(name, "reg", 3)
-                       && isdigit((unsigned char) name[3])) {
-                unsigned int reg_idx = atoi(name + 3);
-                if (reg_idx >= FLOW_N_REGS) {
-                    if (verbose) {
-                        fprintf(stderr, "%s:\n", str_);
-                    }
-                    ofp_fatal(str_, verbose, "only %d registers supported", FLOW_N_REGS);
-                }
-                parse_reg_value(&fm->cr, reg_idx, value);
+            } else if (mf_from_name(name)) {
+                parse_field(mf_from_name(name), value, &fm->cr);
             } else if (!strcmp(name, "duration")
                        || !strcmp(name, "n_packets")
                        || !strcmp(name, "n_bytes")) {
-- 
1.7.4.4




More information about the dev mailing list