Merge branch 'master' of git://openvswitch.org/openvswitch
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 13 Sep 2012 09:16:45 +0000 (11:16 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 13 Sep 2012 09:16:45 +0000 (11:16 +0200)
78 files changed:
AUTHORS
INSTALL
NEWS
build-aux/extract-ofp-errors
datapath/actions.c
datapath/flow.h
datapath/vport.h
lib/automake.mk
lib/classifier.c
lib/classifier.h
lib/dpif-linux.c
lib/flow.c
lib/flow.h
lib/hash.c
lib/hash.h
lib/jsonrpc.c
lib/jsonrpc.h
lib/learn.c
lib/learning-switch.c
lib/lockfile.c
lib/match.c [new file with mode: 0644]
lib/match.h [new file with mode: 0644]
lib/meta-flow.c
lib/meta-flow.h
lib/nx-match.c
lib/nx-match.h
lib/ofp-actions.c
lib/ofp-actions.h
lib/ofp-errors.h
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.def
lib/ofp-util.h
lib/ovsdb-idl-provider.h
lib/reconnect.c
lib/reconnect.h
lib/util.c
lib/util.h
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/fail-open.c
ofproto/in-band.c
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ovsdb/execution.c
ovsdb/mutation.c
ovsdb/ovsdb-idlc.in
python/ovs/db/idl.py
python/ovs/jsonrpc.py
python/ovs/poller.py
python/ovs/reconnect.py
python/ovs/socket_util.py
tests/classifier.at
tests/library.at
tests/lockfile.at
tests/ofp-actions.at
tests/ofp-print.at
tests/ovs-vsctl.at
tests/ovsdb-execution.at
tests/ovsdb-idl.at
tests/ovsdb-mutation.at
tests/reconnect.at
tests/test-bundle.c
tests/test-classifier.c
tests/test-flows.c
tests/test-hash.c
tests/test-lockfile.c
tests/test-multipath.c
tests/test-ovsdb.py
tests/test-reconnect.c
tests/test-reconnect.py
tests/test-util.c
third-party/README
third-party/ofp-tcpdump.patch
utilities/ovs-ofctl.c
utilities/ovs-vsctl.c

diff --git a/AUTHORS b/AUTHORS
index 28dc742..70257e5 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -168,6 +168,7 @@ Stephen Hemminger       shemminger@vyatta.com
 Takayuki HAMA           t-hama@cb.jp.nec.com
 Teemu Koponen           koponen@nicira.com
 Timothy Chen            tchen@nicira.com
+Valentin Bud            valentin@hackaserver.com
 Vishal Swarankar        vishal.swarnkar@gmail.com
 Vjekoslav Brajkovic     balkan@cs.washington.edu
 Voravit T.              voravit@kth.se
@@ -175,6 +176,7 @@ YAMAMOTO Takashi        yamamoto@valinux.co.jp
 Yongqiang Liu           liuyq7809@gmail.com
 kk yap                  yapkke@stanford.edu
 likunyun                kunyunli@hotmail.com
+rahim entezari          rahim.entezari@gmail.com
 冯全树(Crab)            fqs888@126.com
 胡靖飞                  hujingfei914@msn.com
 
diff --git a/INSTALL b/INSTALL
index 671c4a4..4d94c52 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -261,6 +261,11 @@ Prerequisites section, follow the procedure below to build.
    you do not understand what this means or do not know if your driver
    will work, do not set this.
 
+   Once you verify that the kernel modules load properly, you should
+   install them:
+
+      % make modules_install
+
 7. Initialize the configuration database using ovsdb-tool, e.g.:
 
       % mkdir -p /usr/local/etc/openvswitch
diff --git a/NEWS b/NEWS
index 872f8d0..cbc5c58 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,8 @@ post-v1.8.0
       are true, but because we do not know of any users for this
       feature it seems better on balance to remove it.  (The ovs-pki-cgi
       program was not included in distribution packaging.)
+    - ovsdb-server now enforces the immutability of immutable columns.  This
+      was not enforced in earlier versions due to an oversight.
     - Stable bond mode is deprecated and will be removed no earlier than
       February 2013.  Please email dev@openvswitch.org with concerns.
     - The autopath action is deprecated and will be removed no earlier than
index e513b7f..db28af8 100755 (executable)
@@ -207,7 +207,7 @@ def extract_ofp_errors(filenames):
 
             enum = m.group(1)
 
-            comments.append(comment)
+            comments.append(re.sub('\[[^]]*\]', '', comment))
             names.append(enum)
 
             for dst in dsts.split(', '):
@@ -238,6 +238,11 @@ def extract_ofp_errors(filenames):
                               "NX1.2":  ("OF1.2",)}
                 if targets not in target_map:
                     fatal("%s: unknown error domain" % targets)
+                if targets.startswith('NX') and code < 0x100:
+                    fatal("%s: NX domain code cannot be less than 0x100" % dst)
+                if targets.startswith('OF') and code >= 0x100:
+                    fatal("%s: OF domain code cannot be greater than 0x100"
+                          % dst)
                 for target in target_map[targets]:
                     domain[target].setdefault(type_, {})
                     if code in domain[target][type_]:
index 208f260..ec9b595 100644 (file)
@@ -47,7 +47,7 @@ static int make_writable(struct sk_buff *skb, int write_len)
        return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
 }
 
-/* remove VLAN header from packet and update csum accrodingly. */
+/* remove VLAN header from packet and update csum accordingly. */
 static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
 {
        struct vlan_hdr *vhdr;
index 5be481e..02c563a 100644 (file)
@@ -152,15 +152,17 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
  *  OVS_KEY_ATTR_TUN_ID        8    --     4     12
  *  OVS_KEY_ATTR_IN_PORT       4    --     4      8
  *  OVS_KEY_ATTR_ETHERNET     12    --     4     16
+ *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (outer VLAN ethertype)
  *  OVS_KEY_ATTR_8021Q         4    --     4      8
- *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8
+ *  OVS_KEY_ATTR_ENCAP         0    --     4      4  (VLAN encapsulation)
+ *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (inner VLAN ethertype)
  *  OVS_KEY_ATTR_IPV6         40    --     4     44
  *  OVS_KEY_ATTR_ICMPV6        2     2     4      8
  *  OVS_KEY_ATTR_ND           28    --     4     32
  *  -------------------------------------------------
- *  total                                       144
+ *  total                                       156
  */
-#define FLOW_BUFSIZE 144
+#define FLOW_BUFSIZE 156
 
 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
 int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
index b0cdeae..5cd3c18 100644 (file)
@@ -80,7 +80,6 @@ struct vport_err_stats {
  * @linkname: The name of the link from /sys/class/net/<datapath>/brif to this
  * &struct vport.  (We keep this around so that we can delete it if the
  * device gets renamed.)  Set to the null string when no link exists.
- * @node: Element in @dp's @port_list.
  * @upcall_pid: The Netlink port to use for packets received on this port that
  * miss the flow table.
  * @hash_node: Element in @dev_table hash table in vport.c.
@@ -98,7 +97,6 @@ struct vport {
        struct datapath *dp;
        struct kobject kobj;
        char linkname[IFNAMSIZ];
-       struct list_head node;
        u32 upcall_pid;
 
        struct hlist_node hash_node;
index 8f081eb..a7f469c 100644 (file)
@@ -79,6 +79,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/lockfile.h \
        lib/mac-learning.c \
        lib/mac-learning.h \
+       lib/match.c \
+       lib/match.h \
        lib/memory.c \
        lib/memory.h \
        lib/meta-flow.c \
index 38f1a4f..e5d226e 100644 (file)
 #include "packets.h"
 
 static struct cls_table *find_table(const struct classifier *,
-                                    const struct flow_wildcards *);
+                                    const struct minimask *);
 static struct cls_table *insert_table(struct classifier *,
-                                      const struct flow_wildcards *);
+                                      const struct minimask *);
 
 static void destroy_table(struct classifier *, struct cls_table *);
 
 static struct cls_rule *find_match(const struct cls_table *,
                                    const struct flow *);
-static struct cls_rule *find_equal(struct cls_table *, const struct flow *,
-                                   uint32_t hash);
+static struct cls_rule *find_equal(struct cls_table *,
+                                   const struct miniflow *, uint32_t hash);
 static struct cls_rule *insert_rule(struct cls_table *, struct cls_rule *);
 
-static bool flow_equal_except(const struct flow *, const struct flow *,
-                                const struct flow_wildcards *);
-
 /* Iterates RULE over HEAD and all of the cls_rules on HEAD->list. */
 #define FOR_EACH_RULE_IN_LIST(RULE, HEAD)                               \
     for ((RULE) = (HEAD); (RULE) != NULL; (RULE) = next_rule_in_list(RULE))
@@ -53,764 +50,82 @@ static bool flow_equal_except(const struct flow *, const struct flow *,
 
 static struct cls_rule *next_rule_in_list__(struct cls_rule *);
 static struct cls_rule *next_rule_in_list(struct cls_rule *);
+\f
+/* cls_rule. */
 
-/* Converts the flow in 'flow' into a cls_rule in 'rule', with the given
- * 'wildcards' and 'priority'. */
-void
-cls_rule_init(const struct flow *flow, const struct flow_wildcards *wildcards,
-              unsigned int priority, struct cls_rule *rule)
-{
-    rule->flow = *flow;
-    rule->wc = *wildcards;
-    rule->priority = priority;
-    cls_rule_zero_wildcarded_fields(rule);
-}
-
-/* Converts the flow in 'flow' into an exact-match cls_rule in 'rule', with the
- * given 'priority'.  (For OpenFlow 1.0, exact-match rule are always highest
- * priority, so 'priority' should be at least 65535.) */
+/* Initializes 'rule' to match packets specified by 'match' at the given
+ * 'priority'.  'match' must satisfy the invariant described in the comment at
+ * the definition of struct match.
+ *
+ * The caller must eventually destroy 'rule' with cls_rule_destroy().
+ *
+ * (OpenFlow uses priorities between 0 and UINT16_MAX, inclusive, but
+ * internally Open vSwitch supports a wider range.) */
 void
-cls_rule_init_exact(const struct flow *flow,
-                    unsigned int priority, struct cls_rule *rule)
+cls_rule_init(struct cls_rule *rule,
+              const struct match *match, unsigned int priority)
 {
-    rule->flow = *flow;
-    rule->flow.skb_priority = 0;
-    flow_wildcards_init_exact(&rule->wc);
+    minimatch_init(&rule->match, match);
     rule->priority = priority;
 }
 
-/* Initializes 'rule' as a "catch-all" rule that matches every packet, with
- * priority 'priority'. */
+/* Same as cls_rule_init() for initialization from a "struct minimatch". */
 void
-cls_rule_init_catchall(struct cls_rule *rule, unsigned int priority)
+cls_rule_init_from_minimatch(struct cls_rule *rule,
+                             const struct minimatch *match,
+                             unsigned int priority)
 {
-    memset(&rule->flow, 0, sizeof rule->flow);
-    flow_wildcards_init_catchall(&rule->wc);
+    minimatch_clone(&rule->match, match);
     rule->priority = priority;
 }
 
-/* For each bit or field wildcarded in 'rule', sets the corresponding bit or
- * field in 'flow' to all-0-bits.  It is important to maintain this invariant
- * in a clr_rule that might be inserted into a classifier.
+/* Initializes 'dst' as a copy of 'src'.
  *
- * It is never necessary to call this function directly for a cls_rule that is
- * initialized or modified only by cls_rule_*() functions.  It is useful to
- * restore the invariant in a cls_rule whose 'wc' member is modified by hand.
- */
-void
-cls_rule_zero_wildcarded_fields(struct cls_rule *rule)
-{
-    flow_zero_wildcards(&rule->flow, &rule->wc);
-}
-
-void
-cls_rule_set_reg(struct cls_rule *rule, unsigned int reg_idx, uint32_t value)
-{
-    cls_rule_set_reg_masked(rule, reg_idx, value, UINT32_MAX);
-}
-
-void
-cls_rule_set_reg_masked(struct cls_rule *rule, unsigned int reg_idx,
-                        uint32_t value, uint32_t mask)
-{
-    assert(reg_idx < FLOW_N_REGS);
-    flow_wildcards_set_reg_mask(&rule->wc, reg_idx, mask);
-    rule->flow.regs[reg_idx] = value & mask;
-}
-
-void
-cls_rule_set_metadata(struct cls_rule *rule, ovs_be64 metadata)
-{
-    cls_rule_set_metadata_masked(rule, metadata, htonll(UINT64_MAX));
-}
-
-void
-cls_rule_set_metadata_masked(struct cls_rule *rule, ovs_be64 metadata,
-                             ovs_be64 mask)
-{
-    rule->wc.metadata_mask = mask;
-    rule->flow.metadata = metadata & mask;
-}
-
-void
-cls_rule_set_tun_id(struct cls_rule *rule, ovs_be64 tun_id)
-{
-    cls_rule_set_tun_id_masked(rule, tun_id, htonll(UINT64_MAX));
-}
-
-void
-cls_rule_set_tun_id_masked(struct cls_rule *rule,
-                           ovs_be64 tun_id, ovs_be64 mask)
-{
-    rule->wc.tun_id_mask = mask;
-    rule->flow.tun_id = tun_id & mask;
-}
-
-void
-cls_rule_set_in_port(struct cls_rule *rule, uint16_t ofp_port)
-{
-    rule->wc.wildcards &= ~FWW_IN_PORT;
-    rule->flow.in_port = ofp_port;
-}
-
-void
-cls_rule_set_dl_type(struct cls_rule *rule, ovs_be16 dl_type)
-{
-    rule->wc.wildcards &= ~FWW_DL_TYPE;
-    rule->flow.dl_type = dl_type;
-}
-
-/* Modifies 'value_src' so that the Ethernet address must match
- * 'value_dst' exactly. 'mask_dst' is set to all 1s */
-static void
-cls_rule_set_eth(const uint8_t value_src[ETH_ADDR_LEN],
-                 uint8_t value_dst[ETH_ADDR_LEN],
-                 uint8_t mask_dst[ETH_ADDR_LEN])
-{
-    memcpy(value_dst, value_src, ETH_ADDR_LEN);
-    memset(mask_dst, 0xff, ETH_ADDR_LEN);
-}
-
-/* Modifies 'value_src' so that the Ethernet address must match
- * 'value_src' after each byte is ANDed with the appropriate byte in
- * 'mask_src'. 'mask_dst' is set to 'mask_src' */
-static void
-cls_rule_set_eth_masked(const uint8_t value_src[ETH_ADDR_LEN],
-                        const uint8_t mask_src[ETH_ADDR_LEN],
-                        uint8_t value_dst[ETH_ADDR_LEN],
-                        uint8_t mask_dst[ETH_ADDR_LEN])
-{
-    size_t i;
-
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        value_dst[i] = value_src[i] & mask_src[i];
-        mask_dst[i] = mask_src[i];
-    }
-}
-
-/* Modifies 'rule' so that the source Ethernet address
- * must match 'dl_src' exactly. */
-void
-cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth(dl_src, rule->flow.dl_src, rule->wc.dl_src_mask);
-}
-
-/* Modifies 'rule' so that the source Ethernet address
- * must match 'dl_src' after each byte is ANDed with
- * the appropriate byte in 'mask'. */
-void
-cls_rule_set_dl_src_masked(struct cls_rule *rule,
-                           const uint8_t dl_src[ETH_ADDR_LEN],
-                           const uint8_t mask[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth_masked(dl_src, mask,
-                            rule->flow.dl_src, rule->wc.dl_src_mask);
-}
-
-/* Modifies 'rule' so that the destination Ethernet address
- * must match 'dl_dst' exactly. */
-void
-cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth(dl_dst, rule->flow.dl_dst, rule->wc.dl_dst_mask);
-}
-
-/* Modifies 'rule' so that the destination Ethernet address
- * must match 'dl_src' after each byte is ANDed with
- * the appropriate byte in 'mask'. */
-void
-cls_rule_set_dl_dst_masked(struct cls_rule *rule,
-                           const uint8_t dl_dst[ETH_ADDR_LEN],
-                           const uint8_t mask[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth_masked(dl_dst, mask,
-                            rule->flow.dl_dst, rule->wc.dl_dst_mask);
-}
-
-void
-cls_rule_set_dl_tci(struct cls_rule *rule, ovs_be16 tci)
-{
-    cls_rule_set_dl_tci_masked(rule, tci, htons(0xffff));
-}
-
-void
-cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
-{
-    rule->flow.vlan_tci = tci & mask;
-    rule->wc.vlan_tci_mask = mask;
-}
-
-/* Modifies 'rule' so that the VLAN VID is wildcarded.  If the PCP is already
- * wildcarded, then 'rule' will match a packet regardless of whether it has an
- * 802.1Q header or not. */
+ * The caller must eventually destroy 'rule' with cls_rule_destroy(). */
 void
-cls_rule_set_any_vid(struct cls_rule *rule)
+cls_rule_clone(struct cls_rule *dst, const struct cls_rule *src)
 {
-    if (rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK)) {
-        rule->wc.vlan_tci_mask &= ~htons(VLAN_VID_MASK);
-        rule->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
-    } else {
-        cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
-    }
+    minimatch_clone(&dst->match, &src->match);
+    dst->priority = src->priority;
 }
 
-/* Modifies 'rule' depending on 'dl_vlan':
+/* Frees memory referenced by 'rule'.  Doesn't free 'rule' itself (it's
+ * normally embedded into a larger structure).
  *
- *   - If 'dl_vlan' is htons(OFP_VLAN_NONE), makes 'rule' match only packets
- *     without an 802.1Q header.
- *
- *   - Otherwise, makes 'rule' match only packets with an 802.1Q header whose
- *     VID equals the low 12 bits of 'dl_vlan'.
- */
-void
-cls_rule_set_dl_vlan(struct cls_rule *rule, ovs_be16 dl_vlan)
-{
-    flow_set_dl_vlan(&rule->flow, dl_vlan);
-    if (dl_vlan == htons(OFP10_VLAN_NONE)) {
-        rule->wc.vlan_tci_mask = htons(UINT16_MAX);
-    } else {
-        rule->wc.vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
-    }
-}
-
-/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
- * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
- * plus CFI). */
-void
-cls_rule_set_vlan_vid(struct cls_rule *rule, ovs_be16 vid)
-{
-    cls_rule_set_vlan_vid_masked(rule, vid, htons(VLAN_VID_MASK | VLAN_CFI));
-}
-
-
-/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
- * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
- * plus CFI), with the corresponding 'mask'. */
-void
-cls_rule_set_vlan_vid_masked(struct cls_rule *rule,
-                             ovs_be16 vid, ovs_be16 mask)
-{
-    ovs_be16 pcp_mask = htons(VLAN_PCP_MASK);
-    ovs_be16 vid_mask = htons(VLAN_VID_MASK | VLAN_CFI);
-
-    mask &= vid_mask;
-    flow_set_vlan_vid(&rule->flow, vid & mask);
-    rule->wc.vlan_tci_mask = mask | (rule->wc.vlan_tci_mask & pcp_mask);
-}
-
-/* Modifies 'rule' so that the VLAN PCP is wildcarded.  If the VID is already
- * wildcarded, then 'rule' will match a packet regardless of whether it has an
- * 802.1Q header or not. */
-void
-cls_rule_set_any_pcp(struct cls_rule *rule)
-{
-    if (rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK)) {
-        rule->wc.vlan_tci_mask &= ~htons(VLAN_PCP_MASK);
-        rule->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
-    } else {
-        cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
-    }
-}
-
-/* Modifies 'rule' so that it matches only packets with an 802.1Q header whose
- * PCP equals the low 3 bits of 'dl_vlan_pcp'. */
-void
-cls_rule_set_dl_vlan_pcp(struct cls_rule *rule, uint8_t dl_vlan_pcp)
-{
-    flow_set_vlan_pcp(&rule->flow, dl_vlan_pcp);
-    rule->wc.vlan_tci_mask |= htons(VLAN_CFI | VLAN_PCP_MASK);
-}
-
-void
-cls_rule_set_tp_src(struct cls_rule *rule, ovs_be16 tp_src)
-{
-    cls_rule_set_tp_src_masked(rule, tp_src, htons(UINT16_MAX));
-}
-
-void
-cls_rule_set_tp_src_masked(struct cls_rule *rule, ovs_be16 port, ovs_be16 mask)
-{
-    rule->flow.tp_src = port & mask;
-    rule->wc.tp_src_mask = mask;
-}
-
-void
-cls_rule_set_tp_dst(struct cls_rule *rule, ovs_be16 tp_dst)
-{
-    cls_rule_set_tp_dst_masked(rule, tp_dst, htons(UINT16_MAX));
-}
-
-void
-cls_rule_set_tp_dst_masked(struct cls_rule *rule, ovs_be16 port, ovs_be16 mask)
-{
-    rule->flow.tp_dst = port & mask;
-    rule->wc.tp_dst_mask = mask;
-}
-
-void
-cls_rule_set_nw_proto(struct cls_rule *rule, uint8_t nw_proto)
-{
-    rule->wc.wildcards &= ~FWW_NW_PROTO;
-    rule->flow.nw_proto = nw_proto;
-}
-
-void
-cls_rule_set_nw_src(struct cls_rule *rule, ovs_be32 nw_src)
-{
-    rule->flow.nw_src = nw_src;
-    rule->wc.nw_src_mask = htonl(UINT32_MAX);
-}
-
-void
-cls_rule_set_nw_src_masked(struct cls_rule *rule,
-                           ovs_be32 nw_src, ovs_be32 mask)
-{
-    rule->flow.nw_src = nw_src & mask;
-    rule->wc.nw_src_mask = mask;
-}
-
-void
-cls_rule_set_nw_dst(struct cls_rule *rule, ovs_be32 nw_dst)
-{
-    rule->flow.nw_dst = nw_dst;
-    rule->wc.nw_dst_mask = htonl(UINT32_MAX);
-}
-
-void
-cls_rule_set_nw_dst_masked(struct cls_rule *rule, ovs_be32 ip, ovs_be32 mask)
-{
-    rule->flow.nw_dst = ip & mask;
-    rule->wc.nw_dst_mask = mask;
-}
-
-void
-cls_rule_set_nw_dscp(struct cls_rule *rule, uint8_t nw_dscp)
-{
-    rule->wc.wildcards &= ~FWW_NW_DSCP;
-    rule->flow.nw_tos &= ~IP_DSCP_MASK;
-    rule->flow.nw_tos |= nw_dscp & IP_DSCP_MASK;
-}
-
-void
-cls_rule_set_nw_ecn(struct cls_rule *rule, uint8_t nw_ecn)
-{
-    rule->wc.wildcards &= ~FWW_NW_ECN;
-    rule->flow.nw_tos &= ~IP_ECN_MASK;
-    rule->flow.nw_tos |= nw_ecn & IP_ECN_MASK;
-}
-
-void
-cls_rule_set_nw_ttl(struct cls_rule *rule, uint8_t nw_ttl)
-{
-    rule->wc.wildcards &= ~FWW_NW_TTL;
-    rule->flow.nw_ttl = nw_ttl;
-}
-
+ * ('rule' must not currently be in a classifier.) */
 void
-cls_rule_set_nw_frag(struct cls_rule *rule, uint8_t nw_frag)
+cls_rule_destroy(struct cls_rule *rule)
 {
-    rule->wc.nw_frag_mask |= FLOW_NW_FRAG_MASK;
-    rule->flow.nw_frag = nw_frag;
+    minimatch_destroy(&rule->match);
 }
 
-void
-cls_rule_set_nw_frag_masked(struct cls_rule *rule,
-                            uint8_t nw_frag, uint8_t mask)
-{
-    rule->flow.nw_frag = nw_frag & mask;
-    rule->wc.nw_frag_mask = mask;
-}
-
-void
-cls_rule_set_icmp_type(struct cls_rule *rule, uint8_t icmp_type)
-{
-    cls_rule_set_tp_src(rule, htons(icmp_type));
-}
-
-void
-cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t icmp_code)
-{
-    cls_rule_set_tp_dst(rule, htons(icmp_code));
-}
-
-void
-cls_rule_set_arp_sha(struct cls_rule *rule, const uint8_t sha[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth(sha, rule->flow.arp_sha, rule->wc.arp_sha_mask);
-}
-
-void
-cls_rule_set_arp_sha_masked(struct cls_rule *rule,
-                           const uint8_t arp_sha[ETH_ADDR_LEN],
-                           const uint8_t mask[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth_masked(arp_sha, mask,
-                            rule->flow.arp_sha, rule->wc.arp_sha_mask);
-}
-
-void
-cls_rule_set_arp_tha(struct cls_rule *rule, const uint8_t tha[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth(tha, rule->flow.arp_tha, rule->wc.arp_tha_mask);
-}
-
-void
-cls_rule_set_arp_tha_masked(struct cls_rule *rule,
-                           const uint8_t arp_tha[ETH_ADDR_LEN],
-                           const uint8_t mask[ETH_ADDR_LEN])
-{
-    cls_rule_set_eth_masked(arp_tha, mask,
-                            rule->flow.arp_tha, rule->wc.arp_tha_mask);
-}
-
-void
-cls_rule_set_ipv6_src(struct cls_rule *rule, const struct in6_addr *src)
-{
-    rule->flow.ipv6_src = *src;
-    rule->wc.ipv6_src_mask = in6addr_exact;
-}
-
-void
-cls_rule_set_ipv6_src_masked(struct cls_rule *rule, const struct in6_addr *src,
-                             const struct in6_addr *mask)
-{
-    rule->flow.ipv6_src = ipv6_addr_bitand(src, mask);
-    rule->wc.ipv6_src_mask = *mask;
-}
-
-void
-cls_rule_set_ipv6_dst(struct cls_rule *rule, const struct in6_addr *dst)
-{
-    rule->flow.ipv6_dst = *dst;
-    rule->wc.ipv6_dst_mask = in6addr_exact;
-}
-
-void
-cls_rule_set_ipv6_dst_masked(struct cls_rule *rule, const struct in6_addr *dst,
-                             const struct in6_addr *mask)
-{
-    rule->flow.ipv6_dst = ipv6_addr_bitand(dst, mask);
-    rule->wc.ipv6_dst_mask = *mask;
-}
-
-void
-cls_rule_set_ipv6_label(struct cls_rule *rule, ovs_be32 ipv6_label)
-{
-    cls_rule_set_ipv6_label_masked(rule, ipv6_label, htonl(UINT32_MAX));
-}
-
-void
-cls_rule_set_ipv6_label_masked(struct cls_rule *rule, ovs_be32 ipv6_label,
-                               ovs_be32 mask)
-{
-    rule->flow.ipv6_label = ipv6_label & mask;
-    rule->wc.ipv6_label_mask = mask;
-}
-
-void
-cls_rule_set_nd_target(struct cls_rule *rule, const struct in6_addr *target)
-{
-    rule->flow.nd_target = *target;
-    rule->wc.nd_target_mask = in6addr_exact;
-}
-
-void
-cls_rule_set_nd_target_masked(struct cls_rule *rule,
-                              const struct in6_addr *target,
-                              const struct in6_addr *mask)
-{
-    rule->flow.nd_target = ipv6_addr_bitand(target, mask);
-    rule->wc.nd_target_mask = *mask;
-}
-
-/* Returns true if 'a' and 'b' have the same priority, wildcard the same
- * fields, and have the same values for fixed fields, otherwise false. */
+/* Returns true if 'a' and 'b' match the same packets at the same priority,
+ * false if they differ in some way. */
 bool
 cls_rule_equal(const struct cls_rule *a, const struct cls_rule *b)
 {
-    return (a->priority == b->priority
-            && flow_wildcards_equal(&a->wc, &b->wc)
-            && flow_equal(&a->flow, &b->flow));
+    return a->priority == b->priority && minimatch_equal(&a->match, &b->match);
 }
 
-/* Returns a hash value for the flow, wildcards, and priority in 'rule',
- * starting from 'basis'. */
+/* Returns a hash value for 'rule', folding in 'basis'. */
 uint32_t
 cls_rule_hash(const struct cls_rule *rule, uint32_t basis)
 {
-    uint32_t h0 = flow_hash(&rule->flow, basis);
-    uint32_t h1 = flow_wildcards_hash(&rule->wc, h0);
-    return hash_int(rule->priority, h1);
-}
-
-static void
-format_eth_masked(struct ds *s, const char *name, const uint8_t eth[6],
-                  const uint8_t mask[6])
-{
-    if (!eth_addr_is_zero(mask)) {
-        ds_put_format(s, "%s=", name);
-        eth_format_masked(eth, mask, s);
-        ds_put_char(s, ',');
-    }
-}
-
-static void
-format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
-                  ovs_be32 netmask)
-{
-    if (netmask) {
-        ds_put_format(s, "%s=", name);
-        ip_format_masked(ip, netmask, s);
-        ds_put_char(s, ',');
-    }
-}
-
-static void
-format_ipv6_netmask(struct ds *s, const char *name,
-                    const struct in6_addr *addr,
-                    const struct in6_addr *netmask)
-{
-    if (!ipv6_mask_is_any(netmask)) {
-        ds_put_format(s, "%s=", name);
-        print_ipv6_masked(s, addr, netmask);
-        ds_put_char(s, ',');
-    }
-}
-
-
-static void
-format_be16_masked(struct ds *s, const char *name,
-                   ovs_be16 value, ovs_be16 mask)
-{
-    if (mask != htons(0)) {
-        ds_put_format(s, "%s=", name);
-        if (mask == htons(UINT16_MAX)) {
-            ds_put_format(s, "%"PRIu16, ntohs(value));
-        } else {
-            ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16,
-                          ntohs(value), ntohs(mask));
-        }
-        ds_put_char(s, ',');
-    }
+    return minimatch_hash(&rule->match, hash_int(rule->priority, basis));
 }
 
+/* Appends a string describing 'rule' to 's'. */
 void
 cls_rule_format(const struct cls_rule *rule, struct ds *s)
 {
-    const struct flow_wildcards *wc = &rule->wc;
-    size_t start_len = s->length;
-    flow_wildcards_t w = wc->wildcards;
-    const struct flow *f = &rule->flow;
-    bool skip_type = false;
-    bool skip_proto = false;
-
-    int i;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    if (rule->priority != OFP_DEFAULT_PRIORITY) {
-        ds_put_format(s, "priority=%d,", rule->priority);
-    }
-
-    if (!(w & FWW_DL_TYPE)) {
-        skip_type = true;
-        if (f->dl_type == htons(ETH_TYPE_IP)) {
-            if (!(w & FWW_NW_PROTO)) {
-                skip_proto = true;
-                if (f->nw_proto == IPPROTO_ICMP) {
-                    ds_put_cstr(s, "icmp,");
-                } else if (f->nw_proto == IPPROTO_TCP) {
-                    ds_put_cstr(s, "tcp,");
-                } else if (f->nw_proto == IPPROTO_UDP) {
-                    ds_put_cstr(s, "udp,");
-                } else {
-                    ds_put_cstr(s, "ip,");
-                    skip_proto = false;
-                }
-            } else {
-                ds_put_cstr(s, "ip,");
-            }
-        } else if (f->dl_type == htons(ETH_TYPE_IPV6)) {
-            if (!(w & FWW_NW_PROTO)) {
-                skip_proto = true;
-                if (f->nw_proto == IPPROTO_ICMPV6) {
-                    ds_put_cstr(s, "icmp6,");
-                } else if (f->nw_proto == IPPROTO_TCP) {
-                    ds_put_cstr(s, "tcp6,");
-                } else if (f->nw_proto == IPPROTO_UDP) {
-                    ds_put_cstr(s, "udp6,");
-                } else {
-                    ds_put_cstr(s, "ipv6,");
-                    skip_proto = false;
-                }
-            } else {
-                ds_put_cstr(s, "ipv6,");
-            }
-        } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
-            ds_put_cstr(s, "arp,");
-        } else {
-            skip_type = false;
-        }
-    }
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        switch (wc->reg_masks[i]) {
-        case 0:
-            break;
-        case UINT32_MAX:
-            ds_put_format(s, "reg%d=0x%"PRIx32",", i, f->regs[i]);
-            break;
-        default:
-            ds_put_format(s, "reg%d=0x%"PRIx32"/0x%"PRIx32",",
-                          i, f->regs[i], wc->reg_masks[i]);
-            break;
-        }
-    }
-    switch (wc->tun_id_mask) {
-    case 0:
-        break;
-    case CONSTANT_HTONLL(UINT64_MAX):
-        ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(f->tun_id));
-        break;
-    default:
-        ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
-                      ntohll(f->tun_id), ntohll(wc->tun_id_mask));
-        break;
-    }
-    switch (wc->metadata_mask) {
-    case 0:
-        break;
-    case CONSTANT_HTONLL(UINT64_MAX):
-        ds_put_format(s, "metadata=%#"PRIx64",", ntohll(f->metadata));
-        break;
-    default:
-        ds_put_format(s, "metadata=%#"PRIx64"/%#"PRIx64",",
-                      ntohll(f->metadata), ntohll(wc->metadata_mask));
-        break;
-    }
-    if (!(w & FWW_IN_PORT)) {
-        ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
-    }
-    if (wc->vlan_tci_mask) {
-        ovs_be16 vid_mask = wc->vlan_tci_mask & htons(VLAN_VID_MASK);
-        ovs_be16 pcp_mask = wc->vlan_tci_mask & htons(VLAN_PCP_MASK);
-        ovs_be16 cfi = wc->vlan_tci_mask & htons(VLAN_CFI);
-
-        if (cfi && f->vlan_tci & htons(VLAN_CFI)
-            && (!vid_mask || vid_mask == htons(VLAN_VID_MASK))
-            && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
-            && (vid_mask || pcp_mask)) {
-            if (vid_mask) {
-                ds_put_format(s, "dl_vlan=%"PRIu16",",
-                              vlan_tci_to_vid(f->vlan_tci));
-            }
-            if (pcp_mask) {
-                ds_put_format(s, "dl_vlan_pcp=%d,",
-                              vlan_tci_to_pcp(f->vlan_tci));
-            }
-        } else if (wc->vlan_tci_mask == htons(0xffff)) {
-            ds_put_format(s, "vlan_tci=0x%04"PRIx16",", ntohs(f->vlan_tci));
-        } else {
-            ds_put_format(s, "vlan_tci=0x%04"PRIx16"/0x%04"PRIx16",",
-                          ntohs(f->vlan_tci), ntohs(wc->vlan_tci_mask));
-        }
-    }
-    format_eth_masked(s, "dl_src", f->dl_src, wc->dl_src_mask);
-    format_eth_masked(s, "dl_dst", f->dl_dst, wc->dl_dst_mask);
-    if (!skip_type && !(w & FWW_DL_TYPE)) {
-        ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
-    }
-    if (f->dl_type == htons(ETH_TYPE_IPV6)) {
-        format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->ipv6_src_mask);
-        format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->ipv6_dst_mask);
-        if (wc->ipv6_label_mask) {
-            if (wc->ipv6_label_mask == htonl(UINT32_MAX)) {
-                ds_put_format(s, "ipv6_label=0x%05"PRIx32",",
-                              ntohl(f->ipv6_label));
-            } else {
-                ds_put_format(s, "ipv6_label=0x%05"PRIx32"/0x%05"PRIx32",",
-                              ntohl(f->ipv6_label),
-                              ntohl(wc->ipv6_label_mask));
-            }
-        }
-    } else {
-        format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
-        format_ip_netmask(s, "nw_dst", f->nw_dst, wc->nw_dst_mask);
-    }
-    if (!skip_proto && !(w & FWW_NW_PROTO)) {
-        if (f->dl_type == htons(ETH_TYPE_ARP)) {
-            ds_put_format(s, "arp_op=%"PRIu8",", f->nw_proto);
-        } else {
-            ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto);
-        }
-    }
-    if (f->dl_type == htons(ETH_TYPE_ARP)) {
-        format_eth_masked(s, "arp_sha", f->arp_sha, wc->arp_sha_mask);
-        format_eth_masked(s, "arp_tha", f->arp_tha, wc->arp_tha_mask);
-    }
-    if (!(w & FWW_NW_DSCP)) {
-        ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos & IP_DSCP_MASK);
-    }
-    if (!(w & FWW_NW_ECN)) {
-        ds_put_format(s, "nw_ecn=%"PRIu8",", f->nw_tos & IP_ECN_MASK);
-    }
-    if (!(w & FWW_NW_TTL)) {
-        ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
-    }
-    switch (wc->nw_frag_mask) {
-    case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER:
-        ds_put_format(s, "nw_frag=%s,",
-                      f->nw_frag & FLOW_NW_FRAG_ANY
-                      ? (f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "first")
-                      : (f->nw_frag & FLOW_NW_FRAG_LATER ? "<error>" : "no"));
-        break;
-
-    case FLOW_NW_FRAG_ANY:
-        ds_put_format(s, "nw_frag=%s,",
-                      f->nw_frag & FLOW_NW_FRAG_ANY ? "yes" : "no");
-        break;
-
-    case FLOW_NW_FRAG_LATER:
-        ds_put_format(s, "nw_frag=%s,",
-                      f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "not_later");
-        break;
-    }
-    if (f->nw_proto == IPPROTO_ICMP) {
-        format_be16_masked(s, "icmp_type", f->tp_src, wc->tp_src_mask);
-        format_be16_masked(s, "icmp_code", f->tp_dst, wc->tp_dst_mask);
-    } else if (f->nw_proto == IPPROTO_ICMPV6) {
-        format_be16_masked(s, "icmp_type", f->tp_src, wc->tp_src_mask);
-        format_be16_masked(s, "icmp_code", f->tp_dst, wc->tp_dst_mask);
-        format_ipv6_netmask(s, "nd_target", &f->nd_target,
-                            &wc->nd_target_mask);
-        format_eth_masked(s, "nd_sll", f->arp_sha, wc->arp_sha_mask);
-        format_eth_masked(s, "nd_tll", f->arp_tha, wc->arp_tha_mask);
-   } else {
-        format_be16_masked(s, "tp_src", f->tp_src, wc->tp_src_mask);
-        format_be16_masked(s, "tp_dst", f->tp_dst, wc->tp_dst_mask);
-    }
-
-    if (s->length > start_len && ds_last(s) == ',') {
-        s->length--;
-    }
-}
-
-/* Converts 'rule' to a string and returns the string.  The caller must free
- * the string (with free()). */
-char *
-cls_rule_to_string(const struct cls_rule *rule)
-{
-    struct ds s = DS_EMPTY_INITIALIZER;
-    cls_rule_format(rule, &s);
-    return ds_steal_cstr(&s);
+    minimatch_format(&rule->match, s, rule->priority);
 }
 
-void
-cls_rule_print(const struct cls_rule *rule)
+/* Returns true if 'rule' matches every packet, false otherwise. */
+bool
+cls_rule_is_catchall(const struct cls_rule *rule)
 {
-    char *s = cls_rule_to_string(rule);
-    puts(s);
-    free(s);
+    return minimask_is_catchall(&rule->match.mask);
 }
 \f
 /* Initializes 'cls' as a classifier that initially contains no classification
@@ -846,7 +161,7 @@ classifier_is_empty(const struct classifier *cls)
     return cls->n_rules == 0;
 }
 
-/* Returns the number of rules in 'classifier'. */
+/* Returns the number of rules in 'cls'. */
 int
 classifier_count(const struct classifier *cls)
 {
@@ -859,7 +174,8 @@ classifier_count(const struct classifier *cls)
  * If 'cls' already contains an identical rule (including wildcards, values of
  * fixed fields, and priority), replaces the old rule by 'rule' and returns the
  * rule that was replaced.  The caller takes ownership of the returned rule and
- * is thus responsible for freeing it, etc., as necessary.
+ * is thus responsible for destroying it with cls_rule_destroy(), freeing the
+ * memory block in which it resides, etc., as necessary.
  *
  * Returns NULL if 'cls' does not contain a rule with an identical key, after
  * inserting the new rule.  In this case, no rules are displaced by the new
@@ -871,9 +187,9 @@ classifier_replace(struct classifier *cls, struct cls_rule *rule)
     struct cls_rule *old_rule;
     struct cls_table *table;
 
-    table = find_table(cls, &rule->wc);
+    table = find_table(cls, &rule->match.mask);
     if (!table) {
-        table = insert_table(cls, &rule->wc);
+        table = insert_table(cls, &rule->match.mask);
     }
 
     old_rule = insert_rule(table, rule);
@@ -897,16 +213,17 @@ classifier_insert(struct classifier *cls, struct cls_rule *rule)
     assert(!displaced_rule);
 }
 
-/* Removes 'rule' from 'cls'.  It is the caller's responsibility to free
- * 'rule', if this is desirable. */
+/* Removes 'rule' from 'cls'.  It is the caller's responsibility to destroy
+ * 'rule' with cls_rule_destroy(), freeing the memory block in which 'rule'
+ * resides, etc., as necessary. */
 void
 classifier_remove(struct classifier *cls, struct cls_rule *rule)
 {
     struct cls_rule *head;
     struct cls_table *table;
 
-    table = find_table(cls, &rule->wc);
-    head = find_equal(table, &rule->flow, rule->hmap_node.hash);
+    table = find_table(cls, &rule->match.mask);
+    head = find_equal(table, &rule->match.flow, rule->hmap_node.hash);
     if (head != rule) {
         list_remove(&rule->list);
     } else if (list_is_empty(&rule->list)) {
@@ -955,12 +272,14 @@ classifier_find_rule_exactly(const struct classifier *cls,
     struct cls_rule *head, *rule;
     struct cls_table *table;
 
-    table = find_table(cls, &target->wc);
+    table = find_table(cls, &target->match.mask);
     if (!table) {
         return NULL;
     }
 
-    head = find_equal(table, &target->flow, flow_hash(&target->flow, 0));
+    head = find_equal(table, &target->match.flow,
+                      miniflow_hash_in_minimask(&target->match.flow,
+                                                &target->match.mask, 0));
     FOR_EACH_RULE_IN_LIST (rule, head) {
         if (target->priority >= rule->priority) {
             return target->priority == rule->priority ? rule : NULL;
@@ -969,6 +288,24 @@ classifier_find_rule_exactly(const struct classifier *cls,
     return NULL;
 }
 
+/* Finds and returns a rule in 'cls' with priority 'priority' and exactly the
+ * same matching criteria as 'target'.  Returns a null pointer if 'cls' doesn't
+ * contain an exact match. */
+struct cls_rule *
+classifier_find_match_exactly(const struct classifier *cls,
+                              const struct match *target,
+                              unsigned int priority)
+{
+    struct cls_rule *retval;
+    struct cls_rule cr;
+
+    cls_rule_init(&cr, target, priority);
+    retval = classifier_find_rule_exactly(cls, &cr);
+    cls_rule_destroy(&cr);
+
+    return retval;
+}
+
 /* Checks if 'target' would overlap any other rule in 'cls'.  Two rules are
  * considered to overlap if both rules have the same priority and a packet
  * could match both. */
@@ -979,16 +316,18 @@ classifier_rule_overlaps(const struct classifier *cls,
     struct cls_table *table;
 
     HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
-        struct flow_wildcards wc;
+        uint32_t storage[FLOW_U32S];
+        struct minimask mask;
         struct cls_rule *head;
 
-        flow_wildcards_combine(&wc, &target->wc, &table->wc);
+        minimask_combine(&mask, &target->match.mask, &table->mask, storage);
         HMAP_FOR_EACH (head, hmap_node, &table->rules) {
             struct cls_rule *rule;
 
             FOR_EACH_RULE_IN_LIST (rule, head) {
                 if (rule->priority == target->priority
-                    && flow_equal_except(&target->flow, &rule->flow, &wc)) {
+                    && miniflow_equal_in_minimask(&target->match.flow,
+                                                  &rule->match.flow, &mask)) {
                     return true;
                 }
             }
@@ -1030,13 +369,14 @@ classifier_rule_overlaps(const struct classifier *cls,
  * This is the matching rule used by OpenFlow 1.0 non-strict OFPT_FLOW_MOD
  * commands and by OpenFlow 1.0 aggregate and flow stats.
  *
- * Ignores rule->priority and criteria->priority. */
+ * Ignores rule->priority. */
 bool
 cls_rule_is_loose_match(const struct cls_rule *rule,
-                        const struct cls_rule *criteria)
+                        const struct minimatch *criteria)
 {
-    return (!flow_wildcards_has_extra(&rule->wc, &criteria->wc)
-            && flow_equal_except(&rule->flow, &criteria->flow, &criteria->wc));
+    return (!minimask_has_extra(&rule->match.mask, &criteria->mask)
+            && miniflow_equal_in_minimask(&rule->match.flow, &criteria->flow,
+                                          &criteria->mask));
 }
 \f
 /* Iteration. */
@@ -1045,13 +385,15 @@ static bool
 rule_matches(const struct cls_rule *rule, const struct cls_rule *target)
 {
     return (!target
-            || flow_equal_except(&rule->flow, &target->flow, &target->wc));
+            || miniflow_equal_in_minimask(&rule->match.flow,
+                                          &target->match.flow,
+                                          &target->match.mask));
 }
 
 static struct cls_rule *
 search_table(const struct cls_table *table, const struct cls_rule *target)
 {
-    if (!target || !flow_wildcards_has_extra(&table->wc, &target->wc)) {
+    if (!target || !minimask_has_extra(&table->mask, &target->match.mask)) {
         struct cls_rule *rule;
 
         HMAP_FOR_EACH (rule, hmap_node, &table->rules) {
@@ -1076,7 +418,7 @@ cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
                 const struct cls_rule *target)
 {
     cursor->cls = cls;
-    cursor->target = target;
+    cursor->target = target && !cls_rule_is_catchall(target) ? target : NULL;
 }
 
 /* Returns the first matching cls_rule in 'cursor''s iteration, or a null
@@ -1133,13 +475,13 @@ cls_cursor_next(struct cls_cursor *cursor, struct cls_rule *rule)
 }
 \f
 static struct cls_table *
-find_table(const struct classifier *cls, const struct flow_wildcards *wc)
+find_table(const struct classifier *cls, const struct minimask *mask)
 {
     struct cls_table *table;
 
-    HMAP_FOR_EACH_IN_BUCKET (table, hmap_node, flow_wildcards_hash(wc, 0),
+    HMAP_FOR_EACH_IN_BUCKET (table, hmap_node, minimask_hash(mask, 0),
                              &cls->tables) {
-        if (flow_wildcards_equal(wc, &table->wc)) {
+        if (minimask_equal(mask, &table->mask)) {
             return table;
         }
     }
@@ -1147,15 +489,14 @@ find_table(const struct classifier *cls, const struct flow_wildcards *wc)
 }
 
 static struct cls_table *
-insert_table(struct classifier *cls, const struct flow_wildcards *wc)
+insert_table(struct classifier *cls, const struct minimask *mask)
 {
     struct cls_table *table;
 
     table = xzalloc(sizeof *table);
     hmap_init(&table->rules);
-    table->wc = *wc;
-    table->is_catchall = flow_wildcards_is_catchall(&table->wc);
-    hmap_insert(&cls->tables, &table->hmap_node, flow_wildcards_hash(wc, 0));
+    minimask_clone(&table->mask, mask);
+    hmap_insert(&cls->tables, &table->hmap_node, minimask_hash(mask, 0));
 
     return table;
 }
@@ -1163,6 +504,7 @@ insert_table(struct classifier *cls, const struct flow_wildcards *wc)
 static void
 destroy_table(struct classifier *cls, struct cls_table *table)
 {
+    minimask_destroy(&table->mask);
     hmap_remove(&cls->tables, &table->hmap_node);
     hmap_destroy(&table->rules);
     free(table);
@@ -1171,35 +513,26 @@ destroy_table(struct classifier *cls, struct cls_table *table)
 static struct cls_rule *
 find_match(const struct cls_table *table, const struct flow *flow)
 {
+    uint32_t hash = flow_hash_in_minimask(flow, &table->mask, 0);
     struct cls_rule *rule;
 
-    if (table->is_catchall) {
-        HMAP_FOR_EACH (rule, hmap_node, &table->rules) {
+    HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &table->rules) {
+        if (miniflow_equal_flow_in_minimask(&rule->match.flow, flow,
+                                            &table->mask)) {
             return rule;
         }
-    } else {
-        struct flow f;
-
-        f = *flow;
-        flow_zero_wildcards(&f, &table->wc);
-        HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, flow_hash(&f, 0),
-                                 &table->rules) {
-            if (flow_equal(&f, &rule->flow)) {
-                return rule;
-            }
-        }
     }
 
     return NULL;
 }
 
 static struct cls_rule *
-find_equal(struct cls_table *table, const struct flow *flow, uint32_t hash)
+find_equal(struct cls_table *table, const struct miniflow *flow, uint32_t hash)
 {
     struct cls_rule *head;
 
     HMAP_FOR_EACH_WITH_HASH (head, hmap_node, hash, &table->rules) {
-        if (flow_equal(&head->flow, flow)) {
+        if (miniflow_equal(&head->match.flow, flow)) {
             return head;
         }
     }
@@ -1211,9 +544,10 @@ insert_rule(struct cls_table *table, struct cls_rule *new)
 {
     struct cls_rule *head;
 
-    new->hmap_node.hash = flow_hash(&new->flow, 0);
+    new->hmap_node.hash = miniflow_hash_in_minimask(&new->match.flow,
+                                                    &new->match.mask, 0);
 
-    head = find_equal(table, &new->flow, new->hmap_node.hash);
+    head = find_equal(table, &new->match.flow, new->hmap_node.hash);
     if (!head) {
         hmap_insert(&table->rules, &new->hmap_node, new->hmap_node.hash);
         list_init(&new->list);
@@ -1259,73 +593,3 @@ next_rule_in_list(struct cls_rule *rule)
     struct cls_rule *next = next_rule_in_list__(rule);
     return next->priority < rule->priority ? next : NULL;
 }
-
-static bool
-ipv6_equal_except(const struct in6_addr *a, const struct in6_addr *b,
-                  const struct in6_addr *mask)
-{
-    int i;
-
-#ifdef s6_addr32
-    for (i=0; i<4; i++) {
-        if ((a->s6_addr32[i] ^ b->s6_addr32[i]) & mask->s6_addr32[i]) {
-            return false;
-        }
-    }
-#else
-    for (i=0; i<16; i++) {
-        if ((a->s6_addr[i] ^ b->s6_addr[i]) & mask->s6_addr[i]) {
-            return false;
-        }
-    }
-#endif
-
-    return true;
-}
-
-
-static bool
-flow_equal_except(const struct flow *a, const struct flow *b,
-                  const struct flow_wildcards *wildcards)
-{
-    const flow_wildcards_t wc = wildcards->wildcards;
-    int i;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
-            return false;
-        }
-    }
-
-    return (!((a->tun_id ^ b->tun_id) & wildcards->tun_id_mask)
-            && !((a->metadata ^ b->metadata) & wildcards->metadata_mask)
-            && !((a->nw_src ^ b->nw_src) & wildcards->nw_src_mask)
-            && !((a->nw_dst ^ b->nw_dst) & wildcards->nw_dst_mask)
-            && (wc & FWW_IN_PORT || a->in_port == b->in_port)
-            && !((a->vlan_tci ^ b->vlan_tci) & wildcards->vlan_tci_mask)
-            && (wc & FWW_DL_TYPE || a->dl_type == b->dl_type)
-            && !((a->tp_src ^ b->tp_src) & wildcards->tp_src_mask)
-            && !((a->tp_dst ^ b->tp_dst) & wildcards->tp_dst_mask)
-            && eth_addr_equal_except(a->dl_src, b->dl_src,
-                                     wildcards->dl_src_mask)
-            && eth_addr_equal_except(a->dl_dst, b->dl_dst,
-                                     wildcards->dl_dst_mask)
-            && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
-            && (wc & FWW_NW_TTL || a->nw_ttl == b->nw_ttl)
-            && (wc & FWW_NW_DSCP || !((a->nw_tos ^ b->nw_tos) & IP_DSCP_MASK))
-            && (wc & FWW_NW_ECN || !((a->nw_tos ^ b->nw_tos) & IP_ECN_MASK))
-            && !((a->nw_frag ^ b->nw_frag) & wildcards->nw_frag_mask)
-            && eth_addr_equal_except(a->arp_sha, b->arp_sha,
-                                     wildcards->arp_sha_mask)
-            && eth_addr_equal_except(a->arp_tha, b->arp_tha,
-                                     wildcards->arp_tha_mask)
-            && !((a->ipv6_label ^ b->ipv6_label) & wildcards->ipv6_label_mask)
-            && ipv6_equal_except(&a->ipv6_src, &b->ipv6_src,
-                    &wildcards->ipv6_src_mask)
-            && ipv6_equal_except(&a->ipv6_dst, &b->ipv6_dst,
-                    &wildcards->ipv6_dst_mask)
-            && ipv6_equal_except(&a->nd_target, &b->nd_target,
-                                 &wildcards->nd_target_mask));
-}
index 9322345..bc9db33 100644 (file)
@@ -29,6 +29,7 @@
 #include "flow.h"
 #include "hmap.h"
 #include "list.h"
+#include "match.h"
 #include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
 
@@ -46,9 +47,8 @@ struct classifier {
 struct cls_table {
     struct hmap_node hmap_node; /* Within struct classifier 'tables' hmap. */
     struct hmap rules;          /* Contains "struct cls_rule"s. */
-    struct flow_wildcards wc;   /* Wildcards for fields. */
+    struct minimask mask;       /* Wildcards for fields. */
     int n_table_rules;          /* Number of rules, including duplicates. */
-    bool is_catchall;           /* True if this table wildcards every field. */
 };
 
 /* Returns true if 'table' is a "catch-all" table that will match every
@@ -56,110 +56,33 @@ struct cls_table {
 static inline bool
 cls_table_is_catchall(const struct cls_table *table)
 {
-    return table->is_catchall;
+    return minimask_is_catchall(&table->mask);
 }
 
-/* A flow classification rule.
- *
- * Use one of the cls_rule_*() functions to initialize a cls_rule.
- *
- * The cls_rule_*() functions below maintain the following important
- * invariant that the classifier depends on:
- *
- *   - If a bit or a field is wildcarded in 'wc', then the corresponding bit or
- *     field in 'flow' is set to all-0-bits.  (The
- *     cls_rule_zero_wildcarded_fields() function can be used to restore this
- *     invariant after adding wildcards.)
- */
+/* A rule in a "struct classifier". */
 struct cls_rule {
     struct hmap_node hmap_node; /* Within struct cls_table 'rules'. */
     struct list list;           /* List of identical, lower-priority rules. */
-    struct flow flow;           /* All field values. */
-    struct flow_wildcards wc;   /* Wildcards for fields. */
+    struct minimatch match;     /* Matching rule. */
     unsigned int priority;      /* Larger numbers are higher priorities. */
 };
 
-void cls_rule_init(const struct flow *, const struct flow_wildcards *,
-                   unsigned int priority, struct cls_rule *);
-void cls_rule_init_exact(const struct flow *, unsigned int priority,
-                         struct cls_rule *);
-void cls_rule_init_catchall(struct cls_rule *, unsigned int priority);
-
-void cls_rule_zero_wildcarded_fields(struct cls_rule *);
-
-bool cls_rule_is_loose_match(const struct cls_rule *rule,
-                             const struct cls_rule *criteria);
-
-void cls_rule_set_reg(struct cls_rule *, unsigned int reg_idx, uint32_t value);
-void cls_rule_set_reg_masked(struct cls_rule *, unsigned int reg_idx,
-                             uint32_t value, uint32_t mask);
-void cls_rule_set_metadata(struct cls_rule *, ovs_be64 metadata);
-void cls_rule_set_metadata_masked(struct cls_rule *, ovs_be64 metadata,
-                                  ovs_be64 mask);
-void cls_rule_set_tun_id(struct cls_rule *, ovs_be64 tun_id);
-void cls_rule_set_tun_id_masked(struct cls_rule *,
-                                ovs_be64 tun_id, ovs_be64 mask);
-void cls_rule_set_in_port(struct cls_rule *, uint16_t ofp_port);
-void cls_rule_set_dl_type(struct cls_rule *, ovs_be16);
-void cls_rule_set_dl_src(struct cls_rule *, const uint8_t[6]);
-void cls_rule_set_dl_src_masked(struct cls_rule *, const uint8_t dl_src[6],
-                                const uint8_t mask[6]);
-void cls_rule_set_dl_dst(struct cls_rule *, const uint8_t[6]);
-void cls_rule_set_dl_dst_masked(struct cls_rule *, const uint8_t dl_dst[6],
-                                const uint8_t mask[6]);
-void cls_rule_set_dl_tci(struct cls_rule *, ovs_be16 tci);
-void cls_rule_set_dl_tci_masked(struct cls_rule *,
-                                ovs_be16 tci, ovs_be16 mask);
-void cls_rule_set_any_vid(struct cls_rule *);
-void cls_rule_set_dl_vlan(struct cls_rule *, ovs_be16);
-void cls_rule_set_vlan_vid(struct cls_rule *, ovs_be16);
-void cls_rule_set_vlan_vid_masked(struct cls_rule *,
-                                  ovs_be16 vid, ovs_be16 mask);
-void cls_rule_set_any_pcp(struct cls_rule *);
-void cls_rule_set_dl_vlan_pcp(struct cls_rule *, uint8_t);
-void cls_rule_set_tp_src(struct cls_rule *, ovs_be16);
-void cls_rule_set_tp_src_masked(struct cls_rule *,
-                                ovs_be16 port, ovs_be16 mask);
-void cls_rule_set_tp_dst(struct cls_rule *, ovs_be16);
-void cls_rule_set_tp_dst_masked(struct cls_rule *,
-                                ovs_be16 port, ovs_be16 mask);
-void cls_rule_set_nw_proto(struct cls_rule *, uint8_t);
-void cls_rule_set_nw_src(struct cls_rule *, ovs_be32);
-void cls_rule_set_nw_src_masked(struct cls_rule *, ovs_be32 ip, ovs_be32 mask);
-void cls_rule_set_nw_dst(struct cls_rule *, ovs_be32);
-void cls_rule_set_nw_dst_masked(struct cls_rule *, ovs_be32 ip, ovs_be32 mask);
-void cls_rule_set_nw_dscp(struct cls_rule *, uint8_t);
-void cls_rule_set_nw_ecn(struct cls_rule *, uint8_t);
-void cls_rule_set_nw_ttl(struct cls_rule *, uint8_t);
-void cls_rule_set_nw_frag(struct cls_rule *, uint8_t nw_frag);
-void cls_rule_set_nw_frag_masked(struct cls_rule *,
-                                 uint8_t nw_frag, uint8_t mask);
-void cls_rule_set_icmp_type(struct cls_rule *, uint8_t);
-void cls_rule_set_icmp_code(struct cls_rule *, uint8_t);
-void cls_rule_set_arp_sha(struct cls_rule *, const uint8_t[6]);
-void cls_rule_set_arp_sha_masked(struct cls_rule *, const uint8_t[6],
-                                 const uint8_t [6]);
-void cls_rule_set_arp_tha(struct cls_rule *, const uint8_t[6]);
-void cls_rule_set_arp_tha_masked(struct cls_rule *, const uint8_t[6],
-                                 const uint8_t [6]);
-void cls_rule_set_ipv6_src(struct cls_rule *, const struct in6_addr *);
-void cls_rule_set_ipv6_src_masked(struct cls_rule *, const struct in6_addr *,
-                                  const struct in6_addr *);
-void cls_rule_set_ipv6_dst(struct cls_rule *, const struct in6_addr *);
-void cls_rule_set_ipv6_dst_masked(struct cls_rule *, const struct in6_addr *,
-                                  const struct in6_addr *);
-void cls_rule_set_ipv6_label(struct cls_rule *, ovs_be32);
-void cls_rule_set_ipv6_label_masked(struct cls_rule *, ovs_be32, ovs_be32);
-void cls_rule_set_nd_target(struct cls_rule *, const struct in6_addr *);
-void cls_rule_set_nd_target_masked(struct cls_rule *, const struct in6_addr *,
-                                   const struct in6_addr *);
+void cls_rule_init(struct cls_rule *, const struct match *,
+                   unsigned int priority);
+void cls_rule_init_from_minimatch(struct cls_rule *, const struct minimatch *,
+                                  unsigned int priority);
+void cls_rule_clone(struct cls_rule *, const struct cls_rule *);
+void cls_rule_destroy(struct cls_rule *);
 
 bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *);
 uint32_t cls_rule_hash(const struct cls_rule *, uint32_t basis);
 
 void cls_rule_format(const struct cls_rule *, struct ds *);
-char *cls_rule_to_string(const struct cls_rule *);
-void cls_rule_print(const struct cls_rule *);
+
+bool cls_rule_is_catchall(const struct cls_rule *);
+
+bool cls_rule_is_loose_match(const struct cls_rule *rule,
+                             const struct minimatch *criteria);
 
 void classifier_init(struct classifier *);
 void classifier_destroy(struct classifier *);
@@ -177,6 +100,9 @@ typedef void cls_cb_func(struct cls_rule *, void *aux);
 
 struct cls_rule *classifier_find_rule_exactly(const struct classifier *,
                                               const struct cls_rule *);
+struct cls_rule *classifier_find_match_exactly(const struct classifier *,
+                                               const struct match *,
+                                               unsigned int priority);
 \f
 /* Iteration. */
 
index d9c50cc..a2eb490 100644 (file)
@@ -2002,7 +2002,7 @@ report_loss(struct dpif *dpif_, struct dpif_channel *ch)
     }
     ds_chomp(&s, ',');
 
-    VLOG_ERR("%s: lost packet on channel %td%s",
-             dpif_name(dpif_), ch - dpif->channels, ds_cstr(&s));
+    VLOG_WARN("%s: lost packet on channel %td%s",
+              dpif_name(dpif_), ch - dpif->channels, ds_cstr(&s));
     ds_destroy(&s);
 }
index f4446c9..a2172a9 100644 (file)
 #include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <netinet/in.h>
 #include <netinet/icmp6.h>
 #include <netinet/ip6.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include "byte-order.h"
@@ -38,6 +40,7 @@
 VLOG_DEFINE_THIS_MODULE(flow);
 
 COVERAGE_DEFINE(flow_extract);
+COVERAGE_DEFINE(miniflow_malloc);
 
 static struct arp_eth_header *
 pull_arp(struct ofpbuf *packet)
@@ -442,59 +445,20 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id,
 void
 flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
 {
-    const flow_wildcards_t wc = wildcards->wildcards;
-    int i;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
+    uint32_t *flow_u32 = (uint32_t *) flow;
+    const uint32_t *wc_u32 = (const uint32_t *) &wildcards->masks;
+    size_t i;
 
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        flow->regs[i] &= wildcards->reg_masks[i];
-    }
-    flow->tun_id &= wildcards->tun_id_mask;
-    flow->metadata &= wildcards->metadata_mask;
-    flow->nw_src &= wildcards->nw_src_mask;
-    flow->nw_dst &= wildcards->nw_dst_mask;
-    if (wc & FWW_IN_PORT) {
-        flow->in_port = 0;
-    }
-    flow->vlan_tci &= wildcards->vlan_tci_mask;
-    if (wc & FWW_DL_TYPE) {
-        flow->dl_type = htons(0);
-    }
-    flow->tp_src &= wildcards->tp_src_mask;
-    flow->tp_dst &= wildcards->tp_dst_mask;
-    eth_addr_bitand(flow->dl_src, wildcards->dl_src_mask, flow->dl_src);
-    eth_addr_bitand(flow->dl_dst, wildcards->dl_dst_mask, flow->dl_dst);
-    if (wc & FWW_NW_PROTO) {
-        flow->nw_proto = 0;
-    }
-    flow->ipv6_label &= wildcards->ipv6_label_mask;
-    if (wc & FWW_NW_DSCP) {
-        flow->nw_tos &= ~IP_DSCP_MASK;
-    }
-    if (wc & FWW_NW_ECN) {
-        flow->nw_tos &= ~IP_ECN_MASK;
-    }
-    if (wc & FWW_NW_TTL) {
-        flow->nw_ttl = 0;
+    for (i = 0; i < FLOW_U32S; i++) {
+        flow_u32[i] &= wc_u32[i];
     }
-    flow->nw_frag &= wildcards->nw_frag_mask;
-    eth_addr_bitand(flow->arp_sha, wildcards->arp_sha_mask, flow->arp_sha);
-    eth_addr_bitand(flow->arp_tha, wildcards->arp_tha_mask, flow->arp_tha);
-    flow->ipv6_src = ipv6_addr_bitand(&flow->ipv6_src,
-            &wildcards->ipv6_src_mask);
-    flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst,
-            &wildcards->ipv6_dst_mask);
-    flow->nd_target = ipv6_addr_bitand(&flow->nd_target,
-            &wildcards->nd_target_mask);
-    flow->skb_priority = 0;
 }
 
 /* Initializes 'fmd' with the metadata found in 'flow'. */
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
 
     fmd->tun_id = flow->tun_id;
     fmd->metadata = flow->metadata;
@@ -582,27 +546,7 @@ flow_print(FILE *stream, const struct flow *flow)
 void
 flow_wildcards_init_catchall(struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    wc->wildcards = FWW_ALL;
-    wc->tun_id_mask = htonll(0);
-    wc->nw_src_mask = htonl(0);
-    wc->nw_dst_mask = htonl(0);
-    wc->ipv6_src_mask = in6addr_any;
-    wc->ipv6_dst_mask = in6addr_any;
-    wc->ipv6_label_mask = htonl(0);
-    wc->nd_target_mask = in6addr_any;
-    memset(wc->reg_masks, 0, sizeof wc->reg_masks);
-    wc->metadata_mask = htonll(0);
-    wc->vlan_tci_mask = htons(0);
-    wc->nw_frag_mask = 0;
-    wc->tp_src_mask = htons(0);
-    wc->tp_dst_mask = htons(0);
-    memset(wc->dl_src_mask, 0, ETH_ADDR_LEN);
-    memset(wc->dl_dst_mask, 0, ETH_ADDR_LEN);
-    memset(wc->arp_sha_mask, 0, ETH_ADDR_LEN);
-    memset(wc->arp_tha_mask, 0, ETH_ADDR_LEN);
-    memset(wc->zeros, 0, sizeof wc->zeros);
+    memset(&wc->masks, 0, sizeof wc->masks);
 }
 
 /* Initializes 'wc' as an exact-match set of wildcards; that is, 'wc' does not
@@ -610,65 +554,8 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
 void
 flow_wildcards_init_exact(struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    wc->wildcards = 0;
-    wc->tun_id_mask = htonll(UINT64_MAX);
-    wc->nw_src_mask = htonl(UINT32_MAX);
-    wc->nw_dst_mask = htonl(UINT32_MAX);
-    wc->ipv6_src_mask = in6addr_exact;
-    wc->ipv6_dst_mask = in6addr_exact;
-    wc->ipv6_label_mask = htonl(UINT32_MAX);
-    wc->nd_target_mask = in6addr_exact;
-    memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
-    wc->metadata_mask = htonll(UINT64_MAX);
-    wc->vlan_tci_mask = htons(UINT16_MAX);
-    wc->nw_frag_mask = UINT8_MAX;
-    wc->tp_src_mask = htons(UINT16_MAX);
-    wc->tp_dst_mask = htons(UINT16_MAX);
-    memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN);
-    memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN);
-    memset(wc->arp_sha_mask, 0xff, ETH_ADDR_LEN);
-    memset(wc->arp_tha_mask, 0xff, ETH_ADDR_LEN);
-    memset(wc->zeros, 0, sizeof wc->zeros);
-}
-
-/* Returns true if 'wc' is exact-match, false if 'wc' wildcards any bits or
- * fields. */
-bool
-flow_wildcards_is_exact(const struct flow_wildcards *wc)
-{
-    int i;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    if (wc->wildcards
-        || wc->tun_id_mask != htonll(UINT64_MAX)
-        || wc->nw_src_mask != htonl(UINT32_MAX)
-        || wc->nw_dst_mask != htonl(UINT32_MAX)
-        || wc->tp_src_mask != htons(UINT16_MAX)
-        || wc->tp_dst_mask != htons(UINT16_MAX)
-        || wc->vlan_tci_mask != htons(UINT16_MAX)
-        || wc->metadata_mask != htonll(UINT64_MAX)
-        || !eth_mask_is_exact(wc->dl_src_mask)
-        || !eth_mask_is_exact(wc->dl_dst_mask)
-        || !eth_mask_is_exact(wc->arp_sha_mask)
-        || !eth_mask_is_exact(wc->arp_tha_mask)
-        || !ipv6_mask_is_exact(&wc->ipv6_src_mask)
-        || !ipv6_mask_is_exact(&wc->ipv6_dst_mask)
-        || wc->ipv6_label_mask != htonl(UINT32_MAX)
-        || !ipv6_mask_is_exact(&wc->nd_target_mask)
-        || wc->nw_frag_mask != UINT8_MAX) {
-        return false;
-    }
-
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        if (wc->reg_masks[i] != UINT32_MAX) {
-            return false;
-        }
-    }
-
-    return true;
+    memset(&wc->masks, 0xff, sizeof wc->masks);
+    memset(wc->masks.zeros, 0, sizeof wc->masks.zeros);
 }
 
 /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or
@@ -676,36 +563,14 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
 bool
 flow_wildcards_is_catchall(const struct flow_wildcards *wc)
 {
-    int i;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    if (wc->wildcards != FWW_ALL
-        || wc->tun_id_mask != htonll(0)
-        || wc->nw_src_mask != htonl(0)
-        || wc->nw_dst_mask != htonl(0)
-        || wc->tp_src_mask != htons(0)
-        || wc->tp_dst_mask != htons(0)
-        || wc->vlan_tci_mask != htons(0)
-        || wc->metadata_mask != htonll(0)
-        || !eth_addr_is_zero(wc->dl_src_mask)
-        || !eth_addr_is_zero(wc->dl_dst_mask)
-        || !eth_addr_is_zero(wc->arp_sha_mask)
-        || !eth_addr_is_zero(wc->arp_tha_mask)
-        || !ipv6_mask_is_any(&wc->ipv6_src_mask)
-        || !ipv6_mask_is_any(&wc->ipv6_dst_mask)
-        || wc->ipv6_label_mask != htonl(0)
-        || !ipv6_mask_is_any(&wc->nd_target_mask)
-        || wc->nw_frag_mask != 0) {
-        return false;
-    }
+    const uint32_t *wc_u32 = (const uint32_t *) &wc->masks;
+    size_t i;
 
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        if (wc->reg_masks[i] != 0) {
+    for (i = 0; i < FLOW_U32S; i++) {
+        if (wc_u32[i]) {
             return false;
         }
     }
-
     return true;
 }
 
@@ -717,44 +582,21 @@ flow_wildcards_combine(struct flow_wildcards *dst,
                        const struct flow_wildcards *src1,
                        const struct flow_wildcards *src2)
 {
-    int i;
+    uint32_t *dst_u32 = (uint32_t *) &dst->masks;
+    const uint32_t *src1_u32 = (const uint32_t *) &src1->masks;
+    const uint32_t *src2_u32 = (const uint32_t *) &src2->masks;
+    size_t i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    dst->wildcards = src1->wildcards | src2->wildcards;
-    dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
-    dst->nw_src_mask = src1->nw_src_mask & src2->nw_src_mask;
-    dst->nw_dst_mask = src1->nw_dst_mask & src2->nw_dst_mask;
-    dst->ipv6_src_mask = ipv6_addr_bitand(&src1->ipv6_src_mask,
-                                        &src2->ipv6_src_mask);
-    dst->ipv6_dst_mask = ipv6_addr_bitand(&src1->ipv6_dst_mask,
-                                        &src2->ipv6_dst_mask);
-    dst->ipv6_label_mask = src1->ipv6_label_mask & src2->ipv6_label_mask;
-    dst->nd_target_mask = ipv6_addr_bitand(&src1->nd_target_mask,
-                                        &src2->nd_target_mask);
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
+    for (i = 0; i < FLOW_U32S; i++) {
+        dst_u32[i] = src1_u32[i] & src2_u32[i];
     }
-    dst->metadata_mask = src1->metadata_mask & src2->metadata_mask;
-    dst->vlan_tci_mask = src1->vlan_tci_mask & src2->vlan_tci_mask;
-    dst->tp_src_mask = src1->tp_src_mask & src2->tp_src_mask;
-    dst->tp_dst_mask = src1->tp_dst_mask & src2->tp_dst_mask;
-    dst->nw_frag_mask = src1->nw_frag_mask & src2->nw_frag_mask;
-    eth_addr_bitand(src1->dl_src_mask, src2->dl_src_mask, dst->dl_src_mask);
-    eth_addr_bitand(src1->dl_dst_mask, src2->dl_dst_mask, dst->dl_dst_mask);
-    eth_addr_bitand(src1->arp_sha_mask, src2->arp_sha_mask, dst->arp_sha_mask);
-    eth_addr_bitand(src1->arp_tha_mask, src2->arp_tha_mask, dst->arp_tha_mask);
 }
 
 /* Returns a hash of the wildcards in 'wc'. */
 uint32_t
 flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
 {
-    /* If you change struct flow_wildcards and thereby trigger this
-     * assertion, please check that the new struct flow_wildcards has no holes
-     * in it before you update the assertion. */
-    BUILD_ASSERT_DECL(sizeof *wc == 112 + FLOW_N_REGS * 4);
-    return hash_bytes(wc, sizeof *wc, basis);
+    return flow_hash(&wc->masks, basis);;
 }
 
 /* Returns true if 'a' and 'b' represent the same wildcards, false if they are
@@ -763,37 +605,7 @@ bool
 flow_wildcards_equal(const struct flow_wildcards *a,
                      const struct flow_wildcards *b)
 {
-    int i;
-
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    if (a->wildcards != b->wildcards
-        || a->tun_id_mask != b->tun_id_mask
-        || a->nw_src_mask != b->nw_src_mask
-        || a->nw_dst_mask != b->nw_dst_mask
-        || a->vlan_tci_mask != b->vlan_tci_mask
-        || a->metadata_mask != b->metadata_mask
-        || !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
-        || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)
-        || a->ipv6_label_mask != b->ipv6_label_mask
-        || !ipv6_addr_equals(&a->nd_target_mask, &b->nd_target_mask)
-        || a->tp_src_mask != b->tp_src_mask
-        || a->tp_dst_mask != b->tp_dst_mask
-        || a->nw_frag_mask != b->nw_frag_mask
-        || !eth_addr_equals(a->dl_src_mask, b->dl_src_mask)
-        || !eth_addr_equals(a->dl_dst_mask, b->dl_dst_mask)
-        || !eth_addr_equals(a->arp_sha_mask, b->arp_sha_mask)
-        || !eth_addr_equals(a->arp_tha_mask, b->arp_tha_mask)) {
-        return false;
-    }
-
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        if (a->reg_masks[i] != b->reg_masks[i]) {
-            return false;
-        }
-    }
-
-    return true;
+    return flow_equal(&a->masks, &b->masks);
 }
 
 /* Returns true if at least one bit or field is wildcarded in 'a' but not in
@@ -802,63 +614,35 @@ bool
 flow_wildcards_has_extra(const struct flow_wildcards *a,
                          const struct flow_wildcards *b)
 {
-    int i;
-    uint8_t eth_masked[ETH_ADDR_LEN];
-    struct in6_addr ipv6_masked;
+    const uint32_t *a_u32 = (const uint32_t *) &a->masks;
+    const uint32_t *b_u32 = (const uint32_t *) &b->masks;
+    size_t i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
-
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
+    for (i = 0; i < FLOW_U32S; i++) {
+        if ((a_u32[i] & b_u32[i]) != b_u32[i]) {
             return true;
         }
     }
+    return false;
+}
 
-    eth_addr_bitand(a->dl_src_mask, b->dl_src_mask, eth_masked);
-    if (!eth_addr_equals(eth_masked, b->dl_src_mask)) {
-        return true;
-    }
-
-    eth_addr_bitand(a->dl_dst_mask, b->dl_dst_mask, eth_masked);
-    if (!eth_addr_equals(eth_masked, b->dl_dst_mask)) {
-        return true;
-    }
-
-    eth_addr_bitand(a->arp_sha_mask, b->arp_sha_mask, eth_masked);
-    if (!eth_addr_equals(eth_masked, b->arp_sha_mask)) {
-        return true;
-    }
-
-    eth_addr_bitand(a->arp_tha_mask, b->arp_tha_mask, eth_masked);
-    if (!eth_addr_equals(eth_masked, b->arp_tha_mask)) {
-        return true;
-    }
-
-    ipv6_masked = ipv6_addr_bitand(&a->ipv6_src_mask, &b->ipv6_src_mask);
-    if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_src_mask)) {
-        return true;
-    }
-
-    ipv6_masked = ipv6_addr_bitand(&a->ipv6_dst_mask, &b->ipv6_dst_mask);
-    if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_dst_mask)) {
-        return true;
-    }
+/* Returns true if 'a' and 'b' are equal, except that 0-bits (wildcarded bits)
+ * in 'wc' do not need to be equal in 'a' and 'b'. */
+bool
+flow_equal_except(const struct flow *a, const struct flow *b,
+                  const struct flow_wildcards *wc)
+{
+    const uint32_t *a_u32 = (const uint32_t *) a;
+    const uint32_t *b_u32 = (const uint32_t *) b;
+    const uint32_t *wc_u32 = (const uint32_t *) &wc->masks;
+    size_t i;
 
-    ipv6_masked = ipv6_addr_bitand(&a->nd_target_mask, &b->nd_target_mask);
-    if (!ipv6_addr_equals(&ipv6_masked, &b->nd_target_mask)) {
-        return true;
+    for (i = 0; i < FLOW_U32S; i++) {
+        if ((a_u32[i] ^ b_u32[i]) & wc_u32[i]) {
+            return false;
+        }
     }
-
-    return (a->wildcards & ~b->wildcards
-            || (a->tun_id_mask & b->tun_id_mask) != b->tun_id_mask
-            || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
-            || (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask
-            || (a->ipv6_label_mask & b->ipv6_label_mask) != b->ipv6_label_mask
-            || (a->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_mask
-            || (a->metadata_mask & b->metadata_mask) != b->metadata_mask
-            || (a->tp_src_mask & b->tp_src_mask) != b->tp_src_mask
-            || (a->tp_dst_mask & b->tp_dst_mask) != b->tp_dst_mask
-            || (a->nw_frag_mask & b->nw_frag_mask) != b->nw_frag_mask);
+    return true;
 }
 
 /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
@@ -866,7 +650,7 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
 void
 flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
 {
-    wc->reg_masks[idx] = mask;
+    wc->masks.regs[idx] = mask;
 }
 
 /* Hashes 'flow' based on its L2 through L4 protocol information. */
@@ -1089,3 +873,416 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
         }
     }
 }
+\f
+/* Compressed flow. */
+
+static int
+miniflow_n_values(const struct miniflow *flow)
+{
+    int n, i;
+
+    n = 0;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        n += popcount(flow->map[i]);
+    }
+    return n;
+}
+
+static uint32_t *
+miniflow_alloc_values(struct miniflow *flow, int n)
+{
+    if (n <= MINI_N_INLINE) {
+        return flow->inline_values;
+    } else {
+        COVERAGE_INC(miniflow_malloc);
+        return xmalloc(n * sizeof *flow->values);
+    }
+}
+
+/* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
+ * with miniflow_destroy(). */
+void
+miniflow_init(struct miniflow *dst, const struct flow *src)
+{
+    const uint32_t *src_u32 = (const uint32_t *) src;
+    unsigned int ofs;
+    unsigned int i;
+    int n;
+
+    /* Initialize dst->map, counting the number of nonzero elements. */
+    n = 0;
+    memset(dst->map, 0, sizeof dst->map);
+    for (i = 0; i < FLOW_U32S; i++) {
+        if (src_u32[i]) {
+            dst->map[i / 32] |= 1u << (i % 32);
+            n++;
+        }
+    }
+
+    /* Initialize dst->values. */
+    dst->values = miniflow_alloc_values(dst, n);
+    ofs = 0;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = dst->map[i]; map; map = zero_rightmost_1bit(map)) {
+            dst->values[ofs++] = src_u32[raw_ctz(map) + i * 32];
+        }
+    }
+}
+
+/* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
+ * with miniflow_destroy(). */
+void
+miniflow_clone(struct miniflow *dst, const struct miniflow *src)
+{
+    int n = miniflow_n_values(src);
+    memcpy(dst->map, src->map, sizeof dst->map);
+    dst->values = miniflow_alloc_values(dst, n);
+    memcpy(dst->values, src->values, n * sizeof *dst->values);
+}
+
+/* Frees any memory owned by 'flow'.  Does not free the storage in which 'flow'
+ * itself resides; the caller is responsible for that. */
+void
+miniflow_destroy(struct miniflow *flow)
+{
+    if (flow->values != flow->inline_values) {
+        free(flow->values);
+    }
+}
+
+/* Initializes 'dst' as a copy of 'src'. */
+void
+miniflow_expand(const struct miniflow *src, struct flow *dst)
+{
+    uint32_t *dst_u32 = (uint32_t *) dst;
+    int ofs;
+    int i;
+
+    memset(dst_u32, 0, sizeof *dst);
+
+    ofs = 0;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = src->map[i]; map; map = zero_rightmost_1bit(map)) {
+            dst_u32[raw_ctz(map) + i * 32] = src->values[ofs++];
+        }
+    }
+}
+
+static const uint32_t *
+miniflow_get__(const struct miniflow *flow, unsigned int u32_ofs)
+{
+    if (!(flow->map[u32_ofs / 32] & (1u << (u32_ofs % 32)))) {
+        static const uint32_t zero = 0;
+        return &zero;
+    } else {
+        const uint32_t *p = flow->values;
+
+        BUILD_ASSERT(MINI_N_MAPS == 2);
+        if (u32_ofs < 32) {
+            p += popcount(flow->map[0] & ((1u << u32_ofs) - 1));
+        } else {
+            p += popcount(flow->map[0]);
+            p += popcount(flow->map[1] & ((1u << (u32_ofs - 32)) - 1));
+        }
+        return p;
+    }
+}
+
+/* Returns the uint32_t that would be at byte offset '4 * u32_ofs' if 'flow'
+ * were expanded into a "struct flow". */
+uint32_t
+miniflow_get(const struct miniflow *flow, unsigned int u32_ofs)
+{
+    return *miniflow_get__(flow, u32_ofs);
+}
+
+/* Returns the ovs_be16 that would be at byte offset 'u8_ofs' if 'flow' were
+ * expanded into a "struct flow". */
+static ovs_be16
+miniflow_get_be16(const struct miniflow *flow, unsigned int u8_ofs)
+{
+    const uint32_t *u32p = miniflow_get__(flow, u8_ofs / 4);
+    const ovs_be16 *be16p = (const ovs_be16 *) u32p;
+    return be16p[u8_ofs % 4 != 0];
+}
+
+/* Returns the VID within the vlan_tci member of the "struct flow" represented
+ * by 'flow'. */
+uint16_t
+miniflow_get_vid(const struct miniflow *flow)
+{
+    ovs_be16 tci = miniflow_get_be16(flow, offsetof(struct flow, vlan_tci));
+    return vlan_tci_to_vid(tci);
+}
+
+/* Returns true if 'a' and 'b' are the same flow, false otherwise.  */
+bool
+miniflow_equal(const struct miniflow *a, const struct miniflow *b)
+{
+    int i;
+
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        if (a->map[i] != b->map[i]) {
+            return false;
+        }
+    }
+
+    return !memcmp(a->values, b->values,
+                   miniflow_n_values(a) * sizeof *a->values);
+}
+
+/* Returns true if 'a' and 'b' are equal at the places where there are 1-bits
+ * in 'mask', false if they differ. */
+bool
+miniflow_equal_in_minimask(const struct miniflow *a, const struct miniflow *b,
+                           const struct minimask *mask)
+{
+    const uint32_t *p;
+    int i;
+
+    p = mask->masks.values;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
+            int ofs = raw_ctz(map) + i * 32;
+
+            if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p) {
+                return false;
+            }
+            p++;
+        }
+    }
+
+    return true;
+}
+
+/* Returns true if 'a' and 'b' are equal at the places where there are 1-bits
+ * in 'mask', false if they differ. */
+bool
+miniflow_equal_flow_in_minimask(const struct miniflow *a, const struct flow *b,
+                                const struct minimask *mask)
+{
+    const uint32_t *b_u32 = (const uint32_t *) b;
+    const uint32_t *p;
+    int i;
+
+    p = mask->masks.values;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
+            int ofs = raw_ctz(map) + i * 32;
+
+            if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p) {
+                return false;
+            }
+            p++;
+        }
+    }
+
+    return true;
+}
+
+/* Returns a hash value for 'flow', given 'basis'. */
+uint32_t
+miniflow_hash(const struct miniflow *flow, uint32_t basis)
+{
+    BUILD_ASSERT_DECL(MINI_N_MAPS == 2);
+    return hash_3words(flow->map[0], flow->map[1],
+                       hash_words(flow->values, miniflow_n_values(flow),
+                                  basis));
+}
+
+/* Returns a hash value for the bits of 'flow' where there are 1-bits in
+ * 'mask', given 'basis'.
+ *
+ * The hash values returned by this function are the same as those returned by
+ * flow_hash_in_minimask(), only the form of the arguments differ. */
+uint32_t
+miniflow_hash_in_minimask(const struct miniflow *flow,
+                          const struct minimask *mask, uint32_t basis)
+{
+    const uint32_t *p = mask->masks.values;
+    uint32_t hash;
+    int i;
+
+    hash = basis;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
+            int ofs = raw_ctz(map) + i * 32;
+
+            hash = mhash_add(hash, miniflow_get(flow, ofs) & *p);
+            p++;
+        }
+    }
+
+    return mhash_finish(hash, p - mask->masks.values);
+}
+
+/* Returns a hash value for the bits of 'flow' where there are 1-bits in
+ * 'mask', given 'basis'.
+ *
+ * The hash values returned by this function are the same as those returned by
+ * miniflow_hash_in_minimask(), only the form of the arguments differ. */
+uint32_t
+flow_hash_in_minimask(const struct flow *flow, const struct minimask *mask,
+                      uint32_t basis)
+{
+    const uint32_t *flow_u32 = (const uint32_t *) flow;
+    const uint32_t *p = mask->masks.values;
+    uint32_t hash;
+    int i;
+
+    hash = basis;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
+            int ofs = raw_ctz(map) + i * 32;
+
+            hash = mhash_add(hash, flow_u32[ofs] & *p);
+            p++;
+        }
+    }
+
+    return mhash_finish(hash, p - mask->masks.values);
+}
+\f
+/* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
+ * with minimask_destroy(). */
+void
+minimask_init(struct minimask *mask, const struct flow_wildcards *wc)
+{
+    miniflow_init(&mask->masks, &wc->masks);
+}
+
+/* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
+ * with minimask_destroy(). */
+void
+minimask_clone(struct minimask *dst, const struct minimask *src)
+{
+    miniflow_clone(&dst->masks, &src->masks);
+}
+
+/* Initializes 'dst_' as the bit-wise "and" of 'a_' and 'b_'.
+ *
+ * The caller must provide room for FLOW_U32S "uint32_t"s in 'storage', for use
+ * by 'dst_'.  The caller must *not* free 'dst_' with minimask_destroy(). */
+void
+minimask_combine(struct minimask *dst_,
+                 const struct minimask *a_, const struct minimask *b_,
+                 uint32_t storage[FLOW_U32S])
+{
+    struct miniflow *dst = &dst_->masks;
+    const struct miniflow *a = &a_->masks;
+    const struct miniflow *b = &b_->masks;
+    int i, n;
+
+    n = 0;
+    dst->values = storage;
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        dst->map[i] = 0;
+        for (map = a->map[i] & b->map[i]; map;
+             map = zero_rightmost_1bit(map)) {
+            int ofs = raw_ctz(map) + i * 32;
+            uint32_t mask = miniflow_get(a, ofs) & miniflow_get(b, ofs);
+
+            if (mask) {
+                dst->map[i] |= rightmost_1bit(map);
+                dst->values[n++] = mask;
+            }
+        }
+    }
+}
+
+/* Frees any memory owned by 'mask'.  Does not free the storage in which 'mask'
+ * itself resides; the caller is responsible for that. */
+void
+minimask_destroy(struct minimask *mask)
+{
+    miniflow_destroy(&mask->masks);
+}
+
+/* Initializes 'dst' as a copy of 'src'. */
+void
+minimask_expand(const struct minimask *mask, struct flow_wildcards *wc)
+{
+    miniflow_expand(&mask->masks, &wc->masks);
+}
+
+/* Returns the uint32_t that would be at byte offset '4 * u32_ofs' if 'mask'
+ * were expanded into a "struct flow_wildcards". */
+uint32_t
+minimask_get(const struct minimask *mask, unsigned int u32_ofs)
+{
+    return miniflow_get(&mask->masks, u32_ofs);
+}
+
+/* Returns the VID mask within the vlan_tci member of the "struct
+ * flow_wildcards" represented by 'mask'. */
+uint16_t
+minimask_get_vid_mask(const struct minimask *mask)
+{
+    return miniflow_get_vid(&mask->masks);
+}
+
+/* Returns true if 'a' and 'b' are the same flow mask, false otherwise.  */
+bool
+minimask_equal(const struct minimask *a, const struct minimask *b)
+{
+    return miniflow_equal(&a->masks, &b->masks);
+}
+
+/* Returns a hash value for 'mask', given 'basis'. */
+uint32_t
+minimask_hash(const struct minimask *mask, uint32_t basis)
+{
+    return miniflow_hash(&mask->masks, basis);
+}
+
+/* Returns true if at least one bit is wildcarded in 'a_' but not in 'b_',
+ * false otherwise. */
+bool
+minimask_has_extra(const struct minimask *a_, const struct minimask *b_)
+{
+    const struct miniflow *a = &a_->masks;
+    const struct miniflow *b = &b_->masks;
+    int i;
+
+    for (i = 0; i < MINI_N_MAPS; i++) {
+        uint32_t map;
+
+        for (map = a->map[i] | b->map[i]; map;
+             map = zero_rightmost_1bit(map)) {
+            int ofs = raw_ctz(map) + i * 32;
+            uint32_t a_u32 = miniflow_get(a, ofs);
+            uint32_t b_u32 = miniflow_get(b, ofs);
+
+            if ((a_u32 & b_u32) != b_u32) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+/* Returns true if 'mask' matches every packet, false if 'mask' fixes any bits
+ * or fields. */
+bool
+minimask_is_catchall(const struct minimask *mask_)
+{
+    const struct miniflow *mask = &mask_->masks;
+
+    BUILD_ASSERT(MINI_N_MAPS == 2);
+    return !(mask->map[0] | mask->map[1]);
+}
index 568e291..fc62222 100644 (file)
 struct dpif_flow_stats;
 struct ds;
 struct flow_wildcards;
+struct miniflow;
+struct minimask;
 struct ofpbuf;
 
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 14
+#define FLOW_WC_SEQ 17
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -75,8 +77,14 @@ struct flow {
     uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
     uint8_t nw_ttl;             /* IP TTL/Hop Limit. */
     uint8_t nw_frag;            /* FLOW_FRAG_* flags. */
-    uint8_t reserved[2];        /* Reserved for 64-bit packing. */
+    uint8_t zeros[2];           /* Must be zero. */
 };
+BUILD_ASSERT_DECL(sizeof(struct flow) % 8 == 0);
+
+#define FLOW_U32S (sizeof(struct flow) / 4)
+
+/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
+BUILD_ASSERT_DECL(sizeof(struct flow) == 152 && FLOW_WC_SEQ == 17);
 
 /* Represents the metadata fields of struct flow. */
 struct flow_metadata {
@@ -86,17 +94,6 @@ struct flow_metadata {
     uint16_t in_port;                /* OpenFlow port or zero. */
 };
 
-/* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
- * flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (118 + FLOW_N_REGS * 4)
-#define FLOW_PAD_SIZE 2
-BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) == FLOW_SIG_SIZE - 1);
-BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_frag) == 1);
-BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
-
-/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 150 && FLOW_WC_SEQ == 14);
-
 void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
                   uint16_t in_port, struct flow *);
 void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
@@ -118,7 +115,7 @@ void flow_compose(struct ofpbuf *, const struct flow *);
 static inline int
 flow_compare_3way(const struct flow *a, const struct flow *b)
 {
-    return memcmp(a, b, FLOW_SIG_SIZE);
+    return memcmp(a, b, sizeof *a);
 }
 
 static inline bool
@@ -130,64 +127,24 @@ flow_equal(const struct flow *a, const struct flow *b)
 static inline size_t
 flow_hash(const struct flow *flow, uint32_t basis)
 {
-    return hash_bytes(flow, FLOW_SIG_SIZE, basis);
+    return hash_words((const uint32_t *) flow, sizeof *flow / 4, basis);
 }
 
-/* Open vSwitch flow wildcard bits.
- *
- * These are used only internally to Open vSwitch, in the 'wildcards' member of
- * struct flow_wildcards.  They never appear in the wire protocol in this
- * form. */
-
-typedef unsigned int OVS_BITWISE flow_wildcards_t;
-
-/* Same values and meanings as corresponding OFPFW10_* bits. */
-#define FWW_IN_PORT     ((OVS_FORCE flow_wildcards_t) (1 << 0))
-#define FWW_DL_TYPE     ((OVS_FORCE flow_wildcards_t) (1 << 4))
-#define FWW_NW_PROTO    ((OVS_FORCE flow_wildcards_t) (1 << 5))
-/* No corresponding OFPFW10_* bits. */
-#define FWW_NW_DSCP     ((OVS_FORCE flow_wildcards_t) (1 << 1))
-#define FWW_NW_ECN      ((OVS_FORCE flow_wildcards_t) (1 << 2))
-#define FWW_NW_TTL      ((OVS_FORCE flow_wildcards_t) (1 << 3))
-#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 6)) - 1))
-
-/* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */
-BUILD_ASSERT_DECL(FWW_ALL == ((1 << 6) - 1) && FLOW_WC_SEQ == 14);
-
-/* Information on wildcards for a flow, as a supplement to "struct flow".
+uint32_t flow_hash_in_minimask(const struct flow *, const struct minimask *,
+                               uint32_t basis);
+\f
+/* Wildcards for a flow.
  *
- * Note that the meaning of 1-bits in 'wildcards' is opposite that of 1-bits in
- * the rest of the members. */
+ * A 1-bit in each bit in 'masks' indicates that the corresponding bit of
+ * the flow is significant (must match).  A 0-bit indicates that the
+ * corresponding bit of the flow is wildcarded (need not match). */
 struct flow_wildcards {
-    ovs_be64 tun_id_mask;       /* 1-bit in each significant tun_id bit. */
-    ovs_be64 metadata_mask;     /* 1-bit in each significant metadata bit. */
-    flow_wildcards_t wildcards; /* 1-bit in each FWW_* wildcarded field. */
-    uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
-    ovs_be32 nw_src_mask;       /* 1-bit in each significant nw_src bit. */
-    ovs_be32 nw_dst_mask;       /* 1-bit in each significant nw_dst bit. */
-    struct in6_addr ipv6_src_mask; /* 1-bit in each signficant ipv6_src bit. */
-    struct in6_addr ipv6_dst_mask; /* 1-bit in each signficant ipv6_dst bit. */
-    struct in6_addr nd_target_mask; /* 1-bit in each significant
-                                       nd_target bit. */
-    ovs_be32 ipv6_label_mask;   /* 1 bit in each significant ipv6_label bit. */
-    ovs_be16 vlan_tci_mask;     /* 1-bit in each significant vlan_tci bit. */
-    ovs_be16 tp_src_mask;       /* 1-bit in each significant tp_src bit. */
-    ovs_be16 tp_dst_mask;       /* 1-bit in each significant tp_dst bit. */
-    uint8_t nw_frag_mask;       /* 1-bit in each significant nw_frag bit. */
-    uint8_t dl_src_mask[6];     /* 1-bit in each significant dl_src bit. */
-    uint8_t dl_dst_mask[6];     /* 1-bit in each significant dl_dst bit. */
-    uint8_t arp_sha_mask[6];    /* 1-bit in each significant dl_dst bit. */
-    uint8_t arp_tha_mask[6];    /* 1-bit in each significant dl_dst bit. */
-    uint8_t zeros[1];           /* Padding field set to zero. */
+    struct flow masks;
 };
 
-/* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */
-BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 144 && FLOW_WC_SEQ == 14);
-
 void flow_wildcards_init_catchall(struct flow_wildcards *);
 void flow_wildcards_init_exact(struct flow_wildcards *);
 
-bool flow_wildcards_is_exact(const struct flow_wildcards *);
 bool flow_wildcards_is_catchall(const struct flow_wildcards *);
 
 void flow_wildcards_set_reg_mask(struct flow_wildcards *,
@@ -204,13 +161,101 @@ bool flow_wildcards_equal(const struct flow_wildcards *,
                           const struct flow_wildcards *);
 uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis);
 
-const uint8_t *flow_wildcards_to_dl_dst_mask(flow_wildcards_t);
-bool flow_wildcards_is_dl_dst_mask_valid(const uint8_t[6]);
-flow_wildcards_t flow_wildcards_set_dl_dst_mask(flow_wildcards_t,
-                                                const uint8_t mask[6]);
 uint32_t flow_hash_fields(const struct flow *, enum nx_hash_fields,
                           uint16_t basis);
 const char *flow_hash_fields_to_str(enum nx_hash_fields);
 bool flow_hash_fields_valid(enum nx_hash_fields);
 
+bool flow_equal_except(const struct flow *a, const struct flow *b,
+                       const struct flow_wildcards *);
+\f
+/* Compressed flow. */
+
+#define MINI_N_INLINE (sizeof(void *) == 4 ? 7 : 8)
+#define MINI_N_MAPS DIV_ROUND_UP(FLOW_U32S, 32)
+
+/* A sparse representation of a "struct flow".
+ *
+ * A "struct flow" is fairly large and tends to be mostly zeros.  Sparse
+ * representation has two advantages.  First, it saves memory.  Second, it
+ * saves time when the goal is to iterate over only the nonzero parts of the
+ * struct.
+ *
+ * The 'map' member holds one bit for each uint32_t in a "struct flow".  Each
+ * 0-bit indicates that the corresponding uint32_t is zero, each 1-bit that it
+ * is nonzero.
+ *
+ * 'values' points to the start of an array that has one element for each 1-bit
+ * in 'map'.  The least-numbered 1-bit is in values[0], the next 1-bit is in
+ * values[1], and so on.
+ *
+ * 'values' may point to a few different locations:
+ *
+ *     - If 'map' has MINI_N_INLINE or fewer 1-bits, it may point to
+ *       'inline_values'.  One hopes that this is the common case.
+ *
+ *     - If 'map' has more than MINI_N_INLINE 1-bits, it may point to memory
+ *       allocated with malloc().
+ *
+ *     - The caller could provide storage on the stack for situations where
+ *       that makes sense.  So far that's only proved useful for
+ *       minimask_combine(), but the principle works elsewhere.
+ *
+ * The implementation maintains and depends on the invariant that every element
+ * in 'values' is nonzero; that is, wherever a 1-bit appears in 'map', the
+ * corresponding element of 'values' must be nonzero.
+ */
+struct miniflow {
+    uint32_t *values;
+    uint32_t inline_values[MINI_N_INLINE];
+    uint32_t map[MINI_N_MAPS];
+};
+
+void miniflow_init(struct miniflow *, const struct flow *);
+void miniflow_clone(struct miniflow *, const struct miniflow *);
+void miniflow_destroy(struct miniflow *);
+
+void miniflow_expand(const struct miniflow *, struct flow *);
+
+uint32_t miniflow_get(const struct miniflow *, unsigned int u32_ofs);
+uint16_t miniflow_get_vid(const struct miniflow *);
+
+bool miniflow_equal(const struct miniflow *a, const struct miniflow *b);
+bool miniflow_equal_in_minimask(const struct miniflow *a,
+                                const struct miniflow *b,
+                                const struct minimask *);
+bool miniflow_equal_flow_in_minimask(const struct miniflow *a,
+                                     const struct flow *b,
+                                     const struct minimask *);
+uint32_t miniflow_hash(const struct miniflow *, uint32_t basis);
+uint32_t miniflow_hash_in_minimask(const struct miniflow *,
+                                   const struct minimask *, uint32_t basis);
+\f
+/* Compressed flow wildcards. */
+
+/* A sparse representation of a "struct flow_wildcards".
+ *
+ * See the large comment on struct miniflow for details. */
+struct minimask {
+    struct miniflow masks;
+};
+
+void minimask_init(struct minimask *, const struct flow_wildcards *);
+void minimask_clone(struct minimask *, const struct minimask *);
+void minimask_combine(struct minimask *dst,
+                      const struct minimask *a, const struct minimask *b,
+                      uint32_t storage[FLOW_U32S]);
+void minimask_destroy(struct minimask *);
+
+void minimask_expand(const struct minimask *, struct flow_wildcards *);
+
+uint32_t minimask_get(const struct minimask *, unsigned int u32_ofs);
+uint16_t minimask_get_vid_mask(const struct minimask *);
+
+bool minimask_equal(const struct minimask *a, const struct minimask *b);
+uint32_t minimask_hash(const struct minimask *, uint32_t basis);
+
+bool minimask_has_extra(const struct minimask *, const struct minimask *);
+bool minimask_is_catchall(const struct minimask *);
+
 #endif /* flow.h */
index f7aa916..41ad1bf 100644 (file)
@@ -102,3 +102,18 @@ hash_bytes(const void *p_, size_t n, uint32_t basis)
 
     return c;
 }
+
+/* Returns the hash of the 'n' 32-bit words at 'p', starting from 'basis'.
+ * 'p' must be properly aligned. */
+uint32_t
+mhash_words(const uint32_t p[], size_t n_words, uint32_t basis)
+{
+    uint32_t hash;
+    size_t i;
+
+    hash = basis;
+    for (i = 0; i < n_words; i++) {
+        hash = mhash_add(hash, p[i]);
+    }
+    return mhash_finish(hash, n_words);
+}
index ac6a63c..701e686 100644 (file)
@@ -118,6 +118,48 @@ static inline uint32_t hash_pointer(const void *p, uint32_t basis)
     return hash_int((uint32_t) (uintptr_t) p, basis);
 }
 
+/* Murmurhash by Austin Appleby,
+ * from http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp.
+ *
+ * The upstream license there says:
+ *
+ * // MurmurHash3 was written by Austin Appleby, and is placed in the public
+ * // domain. The author hereby disclaims copyright to this source code.
+ *
+ * Murmurhash is faster and higher-quality than the Jenkins lookup3 hash.  When
+ * we have a little more familiarity with it, it's probably a good idea to
+ * switch all of OVS to it.
+ *
+ * For now, we have this implementation here for use by code that needs a hash
+ * that is convenient for use one word at a time, since the Jenkins lookup3
+ * hash works three words at a time.
+ *
+ * See mhash_words() for sample usage. */
+
+uint32_t mhash_words(const uint32_t data[], size_t n_words, uint32_t basis);
+
+static inline uint32_t mhash_add(uint32_t hash, uint32_t data)
+{
+    data *= 0xcc9e2d51;
+    data = hash_rot(data, 15);
+    data *= 0x1b873593;
+
+    hash ^= data;
+    hash = hash_rot(hash, 13);
+    return hash * 5 + 0xe6546b64;
+}
+
+static inline uint32_t mhash_finish(uint32_t hash, size_t n)
+{
+    hash ^= n * 4;
+    hash ^= hash_rot(hash, 16);
+    hash *= 0x85ebca6b;
+    hash ^= hash_rot(hash, 13);
+    hash *= 0xc2b2ae35;
+    hash ^= hash_rot(hash, 16);
+    return hash;
+}
+
 #ifdef __cplusplus
 }
 #endif
index 5761369..0e1788c 100644 (file)
@@ -178,6 +178,14 @@ jsonrpc_get_backlog(const struct jsonrpc *rpc)
     return rpc->status ? 0 : rpc->backlog;
 }
 
+/* Returns the number of bytes that have been received on 'rpc''s underlying
+ * stream.  (The value wraps around if it exceeds UINT_MAX.) */
+unsigned int
+jsonrpc_get_received_bytes(const struct jsonrpc *rpc)
+{
+    return rpc->input.head;
+}
+
 /* Returns 'rpc''s name, that is, the name returned by stream_get_name() for
  * the stream underlying 'rpc' when 'rpc' was created. */
 const char *
@@ -880,9 +888,23 @@ jsonrpc_session_run(struct jsonrpc_session *s)
     }
 
     if (s->rpc) {
+        size_t backlog;
         int error;
 
+        backlog = jsonrpc_get_backlog(s->rpc);
         jsonrpc_run(s->rpc);
+        if (jsonrpc_get_backlog(s->rpc) < backlog) {
+            /* Data previously caught in a queue was successfully sent (or
+             * there's an error, which we'll catch below.)
+             *
+             * We don't count data that is successfully sent immediately as
+             * activity, because there's a lot of queuing downstream from us,
+             * which means that we can push a lot of data into a connection
+             * that has stalled and won't ever recover.
+             */
+            reconnect_activity(s->reconnect, time_msec());
+        }
+
         error = jsonrpc_get_status(s->rpc);
         if (error) {
             reconnect_disconnected(s->reconnect, time_msec(), error);
@@ -974,10 +996,21 @@ struct jsonrpc_msg *
 jsonrpc_session_recv(struct jsonrpc_session *s)
 {
     if (s->rpc) {
+        unsigned int received_bytes;
         struct jsonrpc_msg *msg;
+
+        received_bytes = jsonrpc_get_received_bytes(s->rpc);
         jsonrpc_recv(s->rpc, &msg);
+        if (received_bytes != jsonrpc_get_received_bytes(s->rpc)) {
+            /* Data was successfully received.
+             *
+             * Previously we only counted receiving a full message as activity,
+             * but with large messages or a slow connection that policy could
+             * time out the session mid-message. */
+            reconnect_activity(s->reconnect, time_msec());
+        }
+
         if (msg) {
-            reconnect_received(s->reconnect, time_msec());
             if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
                 /* Echo request.  Send reply. */
                 struct jsonrpc_msg *reply;
index cd78170..44ae06f 100644 (file)
@@ -50,6 +50,7 @@ void jsonrpc_wait(struct jsonrpc *);
 
 int jsonrpc_get_status(const struct jsonrpc *);
 size_t jsonrpc_get_backlog(const struct jsonrpc *);
+unsigned int jsonrpc_get_received_bytes(const struct jsonrpc *);
 const char *jsonrpc_get_name(const struct jsonrpc *);
 
 int jsonrpc_send(struct jsonrpc *, struct jsonrpc_msg *);
index 28b8012..b9bbc97 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "byte-order.h"
 #include "dynamic-string.h"
+#include "match.h"
 #include "meta-flow.h"
 #include "nx-match.h"
 #include "ofp-actions.h"
@@ -170,9 +171,9 @@ enum ofperr
 learn_check(const struct ofpact_learn *learn, const struct flow *flow)
 {
     const struct ofpact_learn_spec *spec;
-    struct cls_rule rule;
+    struct match match;
 
-    cls_rule_init_catchall(&rule, 0);
+    match_init_catchall(&match);
     for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
         enum ofperr error;
 
@@ -187,16 +188,16 @@ learn_check(const struct ofpact_learn *learn, const struct flow *flow)
         /* Check the destination. */
         switch (spec->dst_type) {
         case NX_LEARN_DST_MATCH:
-            error = mf_check_src(&spec->dst, &rule.flow);
+            error = mf_check_src(&spec->dst, &match.flow);
             if (error) {
                 return error;
             }
 
-            mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
+            mf_write_subfield(&spec->dst, &spec->src_imm, &match);
             break;
 
         case NX_LEARN_DST_LOAD:
-            error = mf_check_dst(&spec->dst, &rule.flow);
+            error = mf_check_dst(&spec->dst, &match.flow);
             if (error) {
                 return error;
             }
@@ -297,7 +298,8 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
 {
     const struct ofpact_learn_spec *spec;
 
-    cls_rule_init_catchall(&fm->cr, learn->priority);
+    match_init_catchall(&fm->match);
+    fm->priority = learn->priority;
     fm->cookie = htonll(0);
     fm->cookie_mask = htonll(0);
     fm->new_cookie = htonll(learn->cookie);
@@ -331,13 +333,12 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
 
         switch (spec->dst_type) {
         case NX_LEARN_DST_MATCH:
-            mf_write_subfield(&spec->dst, &value, &fm->cr);
+            mf_write_subfield(&spec->dst, &value, &fm->match);
             break;
 
         case NX_LEARN_DST_LOAD:
             for (ofs = 0; ofs < spec->n_bits; ofs += chunk) {
                 struct ofpact_reg_load *load;
-                ovs_be64 value_be;
 
                 chunk = MIN(spec->n_bits - ofs, 64);
 
@@ -345,12 +346,9 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
                 load->dst.field = spec->dst.field;
                 load->dst.ofs = spec->dst.ofs + ofs;
                 load->dst.n_bits = chunk;
-
-                memset(&value_be, 0, sizeof value_be);
                 bitwise_copy(&value, sizeof value, ofs,
-                             &value_be, sizeof value_be, 0,
+                             &load->subvalue, sizeof load->subvalue, 0,
                              chunk);
-                load->value = ntohll(value_be);
             }
             break;
 
@@ -508,7 +506,7 @@ learn_parse_spec(const char *orig, char *name, char *value,
  *
  * Prints an error on stderr and aborts the program if 'arg' syntax is invalid.
  *
- * If 'flow' is nonnull, then it should be the flow from a cls_rule that is
+ * If 'flow' is nonnull, then it should be the flow from a struct match that is
  * the matching rule for the learning action.  This helps to better validate
  * the action's arguments.
  *
@@ -520,7 +518,7 @@ learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
     char *name, *value;
 
     struct ofpact_learn *learn;
-    struct cls_rule rule;
+    struct match match;
     enum ofperr error;
 
     learn = ofpact_put_LEARN(ofpacts);
@@ -529,7 +527,7 @@ learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
     learn->priority = OFP_DEFAULT_PRIORITY;
     learn->table_id = 1;
 
-    cls_rule_init_catchall(&rule, 0);
+    match_init_catchall(&match);
     while (ofputil_parse_key_value(&arg, &name, &value)) {
         if (!strcmp(name, "table")) {
             learn->table_id = atoi(value);
@@ -567,17 +565,17 @@ learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
             }
             if ((spec->dst_type == NX_LEARN_DST_MATCH
                  || spec->dst_type == NX_LEARN_DST_LOAD)
-                && !mf_are_prereqs_ok(spec->dst.field, &rule.flow)) {
+                && !mf_are_prereqs_ok(spec->dst.field, &match.flow)) {
                 ovs_fatal(0, "%s: cannot specify destination field %s because "
                           "prerequisites are not satisfied",
                           orig, spec->dst.field->name);
             }
 
-            /* Update 'rule' to allow for satisfying destination
+            /* Update 'match' to allow for satisfying destination
              * prerequisites. */
             if (spec->src_type == NX_LEARN_SRC_IMMEDIATE
                 && spec->dst_type == NX_LEARN_DST_MATCH) {
-                mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
+                mf_write_subfield(&spec->dst, &spec->src_imm, &match);
             }
         }
     }
@@ -593,32 +591,15 @@ learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
     free(orig);
 }
 
-static void
-format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
-{
-    int i;
-
-    for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) {
-        if (subvalue->u8[i]) {
-            ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]);
-            for (i++; i < ARRAY_SIZE(subvalue->u8); i++) {
-                ds_put_format(s, "%02"PRIx8, subvalue->u8[i]);
-            }
-            return;
-        }
-    }
-    ds_put_char(s, '0');
-}
-
 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
  * describes. */
 void
 learn_format(const struct ofpact_learn *learn, struct ds *s)
 {
     const struct ofpact_learn_spec *spec;
-    struct cls_rule rule;
+    struct match match;
 
-    cls_rule_init_catchall(&rule, 0);
+    match_init_catchall(&match);
 
     ds_put_format(s, "learn(table=%"PRIu8, learn->table_id);
     if (learn->idle_timeout != OFP_FLOW_PERMANENT) {
@@ -661,7 +642,7 @@ learn_format(const struct ofpact_learn *learn, struct ds *s)
             } else {
                 mf_format_subfield(&spec->dst, s);
                 ds_put_char(s, '=');
-                format_subvalue(&spec->src_imm, s);
+                mf_format_subvalue(&spec->src_imm, s);
             }
             break;
 
@@ -676,7 +657,7 @@ learn_format(const struct ofpact_learn *learn, struct ds *s)
 
         case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD:
             ds_put_format(s, "load:");
-            format_subvalue(&spec->src_imm, s);
+            mf_format_subvalue(&spec->src_imm, s);
             ds_put_cstr(s, "->");
             mf_format_subfield(&spec->dst, s);
             break;
index 235e9d4..dc19aa5 100644 (file)
@@ -575,8 +575,9 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh)
         /* The output port is known, or we always flood everything, so add a
          * new flow. */
         memset(&fm, 0, sizeof fm);
-        cls_rule_init(&flow, &sw->wc, 0, &fm.cr);
-        ofputil_normalize_rule_quiet(&fm.cr);
+        match_init(&fm.match, &flow, &sw->wc);
+        ofputil_normalize_match_quiet(&fm.match);
+        fm.priority = 0;
         fm.table_id = 0xff;
         fm.command = OFPFC_ADD;
         fm.idle_timeout = sw->max_idle;
index db84aeb..3708aec 100644 (file)
@@ -55,7 +55,8 @@ struct lockfile {
 static struct hmap lock_table = HMAP_INITIALIZER(&lock_table);
 
 static void lockfile_unhash(struct lockfile *);
-static int lockfile_try_lock(const char *name, struct lockfile **lockfilep);
+static int lockfile_try_lock(const char *name, pid_t *pidp,
+                             struct lockfile **lockfilep);
 
 /* Returns the name of the lockfile that would be created for locking a file
  * named 'filename_'.  The caller is responsible for freeing the returned name,
@@ -98,21 +99,27 @@ lockfile_lock(const char *file, struct lockfile **lockfilep)
      * stylized ways such that any number of readers may access a file while it
      * is being written. */
     char *lock_name;
+    pid_t pid;
     int error;
 
     COVERAGE_INC(lockfile_lock);
 
     lock_name = lockfile_name(file);
 
-    error = lockfile_try_lock(lock_name, lockfilep);
+    error = lockfile_try_lock(lock_name, &pid, lockfilep);
 
     if (error) {
         COVERAGE_INC(lockfile_error);
         if (error == EACCES) {
             error = EAGAIN;
         }
-        VLOG_WARN("%s: failed to lock file: %s",
-                  lock_name, strerror(error));
+        if (pid) {
+            VLOG_WARN("%s: cannot lock file because it is already locked by "
+                      "pid %ld", lock_name, (long int) pid);
+        } else {
+            VLOG_WARN("%s: failed to lock file: %s",
+                      lock_name, strerror(error));
+        }
     }
 
     free(lock_name);
@@ -201,7 +208,7 @@ lockfile_register(const char *name, dev_t device, ino_t inode, int fd)
 }
 
 static int
-lockfile_try_lock(const char *name, struct lockfile **lockfilep)
+lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep)
 {
     struct flock l;
     struct stat s;
@@ -209,6 +216,7 @@ lockfile_try_lock(const char *name, struct lockfile **lockfilep)
     int fd;
 
     *lockfilep = NULL;
+    *pidp = 0;
 
     /* Check whether we've already got a lock on that file. */
     if (!stat(name, &s)) {
@@ -250,6 +258,9 @@ lockfile_try_lock(const char *name, struct lockfile **lockfilep)
     if (!error) {
         *lockfilep = lockfile_register(name, s.st_dev, s.st_ino, fd);
     } else {
+        if (!fcntl(fd, F_GETLK, &l) && l.l_type != F_UNLCK) {
+            *pidp = l.l_pid;
+        }
         close(fd);
     }
     return error;
diff --git a/lib/match.c b/lib/match.c
new file mode 100644 (file)
index 0000000..69129d4
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ *
+ * 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 "match.h"
+#include <assert.h>
+#include <stdlib.h>
+#include "byte-order.h"
+#include "dynamic-string.h"
+#include "packets.h"
+
+/* Converts the flow in 'flow' into a match in 'match', with the given
+ * 'wildcards'. */
+void
+match_init(struct match *match,
+           const struct flow *flow, const struct flow_wildcards *wc)
+{
+    match->flow = *flow;
+    match->wc = *wc;
+    match_zero_wildcarded_fields(match);
+}
+
+/* Converts the flow in 'flow' into an exact-match match in 'match'. */
+void
+match_init_exact(struct match *match, const struct flow *flow)
+{
+    match->flow = *flow;
+    match->flow.skb_priority = 0;
+    flow_wildcards_init_exact(&match->wc);
+}
+
+/* Initializes 'match' as a "catch-all" match that matches every packet. */
+void
+match_init_catchall(struct match *match)
+{
+    memset(&match->flow, 0, sizeof match->flow);
+    flow_wildcards_init_catchall(&match->wc);
+}
+
+/* For each bit or field wildcarded in 'match', sets the corresponding bit or
+ * field in 'flow' to all-0-bits.  It is important to maintain this invariant
+ * in a match that might be inserted into a classifier.
+ *
+ * It is never necessary to call this function directly for a match that is
+ * initialized or modified only by match_*() functions.  It is useful to
+ * restore the invariant in a match whose 'wc' member is modified by hand.
+ */
+void
+match_zero_wildcarded_fields(struct match *match)
+{
+    flow_zero_wildcards(&match->flow, &match->wc);
+}
+
+void
+match_set_reg(struct match *match, unsigned int reg_idx, uint32_t value)
+{
+    match_set_reg_masked(match, reg_idx, value, UINT32_MAX);
+}
+
+void
+match_set_reg_masked(struct match *match, unsigned int reg_idx,
+                     uint32_t value, uint32_t mask)
+{
+    assert(reg_idx < FLOW_N_REGS);
+    flow_wildcards_set_reg_mask(&match->wc, reg_idx, mask);
+    match->flow.regs[reg_idx] = value & mask;
+}
+
+void
+match_set_metadata(struct match *match, ovs_be64 metadata)
+{
+    match_set_metadata_masked(match, metadata, htonll(UINT64_MAX));
+}
+
+void
+match_set_metadata_masked(struct match *match,
+                          ovs_be64 metadata, ovs_be64 mask)
+{
+    match->wc.masks.metadata = mask;
+    match->flow.metadata = metadata & mask;
+}
+
+void
+match_set_tun_id(struct match *match, ovs_be64 tun_id)
+{
+    match_set_tun_id_masked(match, tun_id, htonll(UINT64_MAX));
+}
+
+void
+match_set_tun_id_masked(struct match *match, ovs_be64 tun_id, ovs_be64 mask)
+{
+    match->wc.masks.tun_id = mask;
+    match->flow.tun_id = tun_id & mask;
+}
+
+void
+match_set_in_port(struct match *match, uint16_t ofp_port)
+{
+    match->wc.masks.in_port = UINT16_MAX;
+    match->flow.in_port = ofp_port;
+}
+
+void
+match_set_dl_type(struct match *match, ovs_be16 dl_type)
+{
+    match->wc.masks.dl_type = htons(UINT16_MAX);
+    match->flow.dl_type = dl_type;
+}
+
+/* Modifies 'value_src' so that the Ethernet address must match 'value_dst'
+ * exactly.  'mask_dst' is set to all 1s. */
+static void
+set_eth(const uint8_t value_src[ETH_ADDR_LEN],
+        uint8_t value_dst[ETH_ADDR_LEN],
+        uint8_t mask_dst[ETH_ADDR_LEN])
+{
+    memcpy(value_dst, value_src, ETH_ADDR_LEN);
+    memset(mask_dst, 0xff, ETH_ADDR_LEN);
+}
+
+/* Modifies 'value_src' so that the Ethernet address must match 'value_src'
+ * after each byte is ANDed with the appropriate byte in 'mask_src'.
+ * 'mask_dst' is set to 'mask_src' */
+static void
+set_eth_masked(const uint8_t value_src[ETH_ADDR_LEN],
+               const uint8_t mask_src[ETH_ADDR_LEN],
+               uint8_t value_dst[ETH_ADDR_LEN],
+               uint8_t mask_dst[ETH_ADDR_LEN])
+{
+    size_t i;
+
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        value_dst[i] = value_src[i] & mask_src[i];
+        mask_dst[i] = mask_src[i];
+    }
+}
+
+/* Modifies 'rule' so that the source Ethernet address must match 'dl_src'
+ * exactly. */
+void
+match_set_dl_src(struct match *match, const uint8_t dl_src[ETH_ADDR_LEN])
+{
+    set_eth(dl_src, match->flow.dl_src, match->wc.masks.dl_src);
+}
+
+/* Modifies 'rule' so that the source Ethernet address must match 'dl_src'
+ * after each byte is ANDed with the appropriate byte in 'mask'. */
+void
+match_set_dl_src_masked(struct match *match,
+                        const uint8_t dl_src[ETH_ADDR_LEN],
+                        const uint8_t mask[ETH_ADDR_LEN])
+{
+    set_eth_masked(dl_src, mask, match->flow.dl_src, match->wc.masks.dl_src);
+}
+
+/* Modifies 'match' so that the Ethernet address must match 'dl_dst'
+ * exactly. */
+void
+match_set_dl_dst(struct match *match, const uint8_t dl_dst[ETH_ADDR_LEN])
+{
+    set_eth(dl_dst, match->flow.dl_dst, match->wc.masks.dl_dst);
+}
+
+/* Modifies 'match' so that the Ethernet address must match 'dl_dst' after each
+ * byte is ANDed with the appropriate byte in 'mask'.
+ *
+ * This function will assert-fail if 'mask' is invalid.  Only 'mask' values
+ * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
+void
+match_set_dl_dst_masked(struct match *match,
+                        const uint8_t dl_dst[ETH_ADDR_LEN],
+                        const uint8_t mask[ETH_ADDR_LEN])
+{
+    set_eth_masked(dl_dst, mask, match->flow.dl_dst, match->wc.masks.dl_dst);
+}
+
+void
+match_set_dl_tci(struct match *match, ovs_be16 tci)
+{
+    match_set_dl_tci_masked(match, tci, htons(0xffff));
+}
+
+void
+match_set_dl_tci_masked(struct match *match, ovs_be16 tci, ovs_be16 mask)
+{
+    match->flow.vlan_tci = tci & mask;
+    match->wc.masks.vlan_tci = mask;
+}
+
+/* Modifies 'match' so that the VLAN VID is wildcarded.  If the PCP is already
+ * wildcarded, then 'match' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+match_set_any_vid(struct match *match)
+{
+    if (match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK)) {
+        match->wc.masks.vlan_tci &= ~htons(VLAN_VID_MASK);
+        match->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+    } else {
+        match_set_dl_tci_masked(match, htons(0), htons(0));
+    }
+}
+
+/* Modifies 'match' depending on 'dl_vlan':
+ *
+ *   - If 'dl_vlan' is htons(OFP_VLAN_NONE), makes 'match' match only packets
+ *     without an 802.1Q header.
+ *
+ *   - Otherwise, makes 'match' match only packets with an 802.1Q header whose
+ *     VID equals the low 12 bits of 'dl_vlan'.
+ */
+void
+match_set_dl_vlan(struct match *match, ovs_be16 dl_vlan)
+{
+    flow_set_dl_vlan(&match->flow, dl_vlan);
+    if (dl_vlan == htons(OFP10_VLAN_NONE)) {
+        match->wc.masks.vlan_tci = htons(UINT16_MAX);
+    } else {
+        match->wc.masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+    }
+}
+
+/* Sets the VLAN VID that 'match' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI). */
+void
+match_set_vlan_vid(struct match *match, ovs_be16 vid)
+{
+    match_set_vlan_vid_masked(match, vid, htons(VLAN_VID_MASK | VLAN_CFI));
+}
+
+
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI), with the corresponding 'mask'. */
+void
+match_set_vlan_vid_masked(struct match *match, ovs_be16 vid, ovs_be16 mask)
+{
+    ovs_be16 pcp_mask = htons(VLAN_PCP_MASK);
+    ovs_be16 vid_mask = htons(VLAN_VID_MASK | VLAN_CFI);
+
+    mask &= vid_mask;
+    flow_set_vlan_vid(&match->flow, vid & mask);
+    match->wc.masks.vlan_tci = mask | (match->wc.masks.vlan_tci & pcp_mask);
+}
+
+/* Modifies 'match' so that the VLAN PCP is wildcarded.  If the VID is already
+ * wildcarded, then 'match' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+match_set_any_pcp(struct match *match)
+{
+    if (match->wc.masks.vlan_tci & htons(VLAN_VID_MASK)) {
+        match->wc.masks.vlan_tci &= ~htons(VLAN_PCP_MASK);
+        match->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+    } else {
+        match_set_dl_tci_masked(match, htons(0), htons(0));
+    }
+}
+
+/* Modifies 'match' so that it matches only packets with an 802.1Q header whose
+ * PCP equals the low 3 bits of 'dl_vlan_pcp'. */
+void
+match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp)
+{
+    flow_set_vlan_pcp(&match->flow, dl_vlan_pcp);
+    match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
+}
+
+void
+match_set_tp_src(struct match *match, ovs_be16 tp_src)
+{
+    match_set_tp_src_masked(match, tp_src, htons(UINT16_MAX));
+}
+
+void
+match_set_tp_src_masked(struct match *match, ovs_be16 port, ovs_be16 mask)
+{
+    match->flow.tp_src = port & mask;
+    match->wc.masks.tp_src = mask;
+}
+
+void
+match_set_tp_dst(struct match *match, ovs_be16 tp_dst)
+{
+    match_set_tp_dst_masked(match, tp_dst, htons(UINT16_MAX));
+}
+
+void
+match_set_tp_dst_masked(struct match *match, ovs_be16 port, ovs_be16 mask)
+{
+    match->flow.tp_dst = port & mask;
+    match->wc.masks.tp_dst = mask;
+}
+
+void
+match_set_nw_proto(struct match *match, uint8_t nw_proto)
+{
+    match->flow.nw_proto = nw_proto;
+    match->wc.masks.nw_proto = UINT8_MAX;
+}
+
+void
+match_set_nw_src(struct match *match, ovs_be32 nw_src)
+{
+    match->flow.nw_src = nw_src;
+    match->wc.masks.nw_src = htonl(UINT32_MAX);
+}
+
+void
+match_set_nw_src_masked(struct match *match,
+                        ovs_be32 nw_src, ovs_be32 mask)
+{
+    match->flow.nw_src = nw_src & mask;
+    match->wc.masks.nw_src = mask;
+}
+
+void
+match_set_nw_dst(struct match *match, ovs_be32 nw_dst)
+{
+    match->flow.nw_dst = nw_dst;
+    match->wc.masks.nw_dst = htonl(UINT32_MAX);
+}
+
+void
+match_set_nw_dst_masked(struct match *match, ovs_be32 ip, ovs_be32 mask)
+{
+    match->flow.nw_dst = ip & mask;
+    match->wc.masks.nw_dst = mask;
+}
+
+void
+match_set_nw_dscp(struct match *match, uint8_t nw_dscp)
+{
+    match->wc.masks.nw_tos |= IP_DSCP_MASK;
+    match->flow.nw_tos &= ~IP_DSCP_MASK;
+    match->flow.nw_tos |= nw_dscp & IP_DSCP_MASK;
+}
+
+void
+match_set_nw_ecn(struct match *match, uint8_t nw_ecn)
+{
+    match->wc.masks.nw_tos |= IP_ECN_MASK;
+    match->flow.nw_tos &= ~IP_ECN_MASK;
+    match->flow.nw_tos |= nw_ecn & IP_ECN_MASK;
+}
+
+void
+match_set_nw_ttl(struct match *match, uint8_t nw_ttl)
+{
+    match->wc.masks.nw_ttl = UINT8_MAX;
+    match->flow.nw_ttl = nw_ttl;
+}
+
+void
+match_set_nw_frag(struct match *match, uint8_t nw_frag)
+{
+    match->wc.masks.nw_frag |= FLOW_NW_FRAG_MASK;
+    match->flow.nw_frag = nw_frag;
+}
+
+void
+match_set_nw_frag_masked(struct match *match,
+                         uint8_t nw_frag, uint8_t mask)
+{
+    match->flow.nw_frag = nw_frag & mask;
+    match->wc.masks.nw_frag = mask;
+}
+
+void
+match_set_icmp_type(struct match *match, uint8_t icmp_type)
+{
+    match_set_tp_src(match, htons(icmp_type));
+}
+
+void
+match_set_icmp_code(struct match *match, uint8_t icmp_code)
+{
+    match_set_tp_dst(match, htons(icmp_code));
+}
+
+void
+match_set_arp_sha(struct match *match, const uint8_t sha[ETH_ADDR_LEN])
+{
+    memcpy(match->flow.arp_sha, sha, ETH_ADDR_LEN);
+    memset(match->wc.masks.arp_sha, UINT8_MAX, ETH_ADDR_LEN);
+}
+
+void
+match_set_arp_sha_masked(struct match *match,
+                         const uint8_t arp_sha[ETH_ADDR_LEN],
+                         const uint8_t mask[ETH_ADDR_LEN])
+{
+    set_eth_masked(arp_sha, mask,
+                   match->flow.arp_sha, match->wc.masks.arp_sha);
+}
+
+void
+match_set_arp_tha(struct match *match, const uint8_t tha[ETH_ADDR_LEN])
+{
+    memcpy(match->flow.arp_tha, tha, ETH_ADDR_LEN);
+    memset(match->wc.masks.arp_tha, UINT8_MAX, ETH_ADDR_LEN);
+}
+
+void
+match_set_arp_tha_masked(struct match *match,
+                         const uint8_t arp_tha[ETH_ADDR_LEN],
+                         const uint8_t mask[ETH_ADDR_LEN])
+{
+    set_eth_masked(arp_tha, mask,
+                   match->flow.arp_tha, match->wc.masks.arp_tha);
+}
+
+void
+match_set_ipv6_src(struct match *match, const struct in6_addr *src)
+{
+    match->flow.ipv6_src = *src;
+    match->wc.masks.ipv6_src = in6addr_exact;
+}
+
+void
+match_set_ipv6_src_masked(struct match *match, const struct in6_addr *src,
+                          const struct in6_addr *mask)
+{
+    match->flow.ipv6_src = ipv6_addr_bitand(src, mask);
+    match->wc.masks.ipv6_src = *mask;
+}
+
+void
+match_set_ipv6_dst(struct match *match, const struct in6_addr *dst)
+{
+    match->flow.ipv6_dst = *dst;
+    match->wc.masks.ipv6_dst = in6addr_exact;
+}
+
+void
+match_set_ipv6_dst_masked(struct match *match, const struct in6_addr *dst,
+                          const struct in6_addr *mask)
+{
+    match->flow.ipv6_dst = ipv6_addr_bitand(dst, mask);
+    match->wc.masks.ipv6_dst = *mask;
+}
+
+void
+match_set_ipv6_label(struct match *match, ovs_be32 ipv6_label)
+{
+    match->wc.masks.ipv6_label = htonl(UINT32_MAX);
+    match->flow.ipv6_label = ipv6_label;
+}
+
+
+void
+match_set_ipv6_label_masked(struct match *match, ovs_be32 ipv6_label,
+                            ovs_be32 mask)
+{
+    match->flow.ipv6_label = ipv6_label & mask;
+    match->wc.masks.ipv6_label = mask;
+}
+
+void
+match_set_nd_target(struct match *match, const struct in6_addr *target)
+{
+    match->flow.nd_target = *target;
+    match->wc.masks.nd_target = in6addr_exact;
+}
+
+void
+match_set_nd_target_masked(struct match *match,
+                           const struct in6_addr *target,
+                           const struct in6_addr *mask)
+{
+    match->flow.nd_target = ipv6_addr_bitand(target, mask);
+    match->wc.masks.nd_target = *mask;
+}
+
+/* Returns true if 'a' and 'b' wildcard the same fields and have the same
+ * values for fixed fields, otherwise false. */
+bool
+match_equal(const struct match *a, const struct match *b)
+{
+    return (flow_wildcards_equal(&a->wc, &b->wc)
+            && flow_equal(&a->flow, &b->flow));
+}
+
+/* Returns a hash value for the flow and wildcards in 'match', starting from
+ * 'basis'. */
+uint32_t
+match_hash(const struct match *match, uint32_t basis)
+{
+    return flow_wildcards_hash(&match->wc, flow_hash(&match->flow, basis));
+}
+
+static void
+format_eth_masked(struct ds *s, const char *name, const uint8_t eth[6],
+                  const uint8_t mask[6])
+{
+    if (!eth_addr_is_zero(mask)) {
+        ds_put_format(s, "%s=", name);
+        eth_format_masked(eth, mask, s);
+        ds_put_char(s, ',');
+    }
+}
+
+static void
+format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
+                  ovs_be32 netmask)
+{
+    if (netmask) {
+        ds_put_format(s, "%s=", name);
+        ip_format_masked(ip, netmask, s);
+        ds_put_char(s, ',');
+    }
+}
+
+static void
+format_ipv6_netmask(struct ds *s, const char *name,
+                    const struct in6_addr *addr,
+                    const struct in6_addr *netmask)
+{
+    if (!ipv6_mask_is_any(netmask)) {
+        ds_put_format(s, "%s=", name);
+        print_ipv6_masked(s, addr, netmask);
+        ds_put_char(s, ',');
+    }
+}
+
+
+static void
+format_be16_masked(struct ds *s, const char *name,
+                   ovs_be16 value, ovs_be16 mask)
+{
+    if (mask != htons(0)) {
+        ds_put_format(s, "%s=", name);
+        if (mask == htons(UINT16_MAX)) {
+            ds_put_format(s, "%"PRIu16, ntohs(value));
+        } else {
+            ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16,
+                          ntohs(value), ntohs(mask));
+        }
+        ds_put_char(s, ',');
+    }
+}
+
+/* Appends a string representation of 'match' to 's'.  If 'priority' is
+ * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
+void
+match_format(const struct match *match, struct ds *s, unsigned int priority)
+{
+    const struct flow_wildcards *wc = &match->wc;
+    size_t start_len = s->length;
+    const struct flow *f = &match->flow;
+    bool skip_type = false;
+    bool skip_proto = false;
+
+    int i;
+
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+
+    if (priority != OFP_DEFAULT_PRIORITY) {
+        ds_put_format(s, "priority=%u,", priority);
+    }
+
+    if (wc->masks.dl_type) {
+        skip_type = true;
+        if (f->dl_type == htons(ETH_TYPE_IP)) {
+            if (wc->masks.nw_proto) {
+                skip_proto = true;
+                if (f->nw_proto == IPPROTO_ICMP) {
+                    ds_put_cstr(s, "icmp,");
+                } else if (f->nw_proto == IPPROTO_TCP) {
+                    ds_put_cstr(s, "tcp,");
+                } else if (f->nw_proto == IPPROTO_UDP) {
+                    ds_put_cstr(s, "udp,");
+                } else {
+                    ds_put_cstr(s, "ip,");
+                    skip_proto = false;
+                }
+            } else {
+                ds_put_cstr(s, "ip,");
+            }
+        } else if (f->dl_type == htons(ETH_TYPE_IPV6)) {
+            if (wc->masks.nw_proto) {
+                skip_proto = true;
+                if (f->nw_proto == IPPROTO_ICMPV6) {
+                    ds_put_cstr(s, "icmp6,");
+                } else if (f->nw_proto == IPPROTO_TCP) {
+                    ds_put_cstr(s, "tcp6,");
+                } else if (f->nw_proto == IPPROTO_UDP) {
+                    ds_put_cstr(s, "udp6,");
+                } else {
+                    ds_put_cstr(s, "ipv6,");
+                    skip_proto = false;
+                }
+            } else {
+                ds_put_cstr(s, "ipv6,");
+            }
+        } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
+            ds_put_cstr(s, "arp,");
+        } else {
+            skip_type = false;
+        }
+    }
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        switch (wc->masks.regs[i]) {
+        case 0:
+            break;
+        case UINT32_MAX:
+            ds_put_format(s, "reg%d=0x%"PRIx32",", i, f->regs[i]);
+            break;
+        default:
+            ds_put_format(s, "reg%d=0x%"PRIx32"/0x%"PRIx32",",
+                          i, f->regs[i], wc->masks.regs[i]);
+            break;
+        }
+    }
+    switch (wc->masks.tun_id) {
+    case 0:
+        break;
+    case CONSTANT_HTONLL(UINT64_MAX):
+        ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(f->tun_id));
+        break;
+    default:
+        ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
+                      ntohll(f->tun_id), ntohll(wc->masks.tun_id));
+        break;
+    }
+    switch (wc->masks.metadata) {
+    case 0:
+        break;
+    case CONSTANT_HTONLL(UINT64_MAX):
+        ds_put_format(s, "metadata=%#"PRIx64",", ntohll(f->metadata));
+        break;
+    default:
+        ds_put_format(s, "metadata=%#"PRIx64"/%#"PRIx64",",
+                      ntohll(f->metadata), ntohll(wc->masks.metadata));
+        break;
+    }
+    if (wc->masks.in_port) {
+        ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
+    }
+    if (wc->masks.vlan_tci) {
+        ovs_be16 vid_mask = wc->masks.vlan_tci & htons(VLAN_VID_MASK);
+        ovs_be16 pcp_mask = wc->masks.vlan_tci & htons(VLAN_PCP_MASK);
+        ovs_be16 cfi = wc->masks.vlan_tci & htons(VLAN_CFI);
+
+        if (cfi && f->vlan_tci & htons(VLAN_CFI)
+            && (!vid_mask || vid_mask == htons(VLAN_VID_MASK))
+            && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
+            && (vid_mask || pcp_mask)) {
+            if (vid_mask) {
+                ds_put_format(s, "dl_vlan=%"PRIu16",",
+                              vlan_tci_to_vid(f->vlan_tci));
+            }
+            if (pcp_mask) {
+                ds_put_format(s, "dl_vlan_pcp=%d,",
+                              vlan_tci_to_pcp(f->vlan_tci));
+            }
+        } else if (wc->masks.vlan_tci == htons(0xffff)) {
+            ds_put_format(s, "vlan_tci=0x%04"PRIx16",", ntohs(f->vlan_tci));
+        } else {
+            ds_put_format(s, "vlan_tci=0x%04"PRIx16"/0x%04"PRIx16",",
+                          ntohs(f->vlan_tci), ntohs(wc->masks.vlan_tci));
+        }
+    }
+    format_eth_masked(s, "dl_src", f->dl_src, wc->masks.dl_src);
+    format_eth_masked(s, "dl_dst", f->dl_dst, wc->masks.dl_dst);
+    if (!skip_type && wc->masks.dl_type) {
+        ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
+    }
+    if (f->dl_type == htons(ETH_TYPE_IPV6)) {
+        format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->masks.ipv6_src);
+        format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->masks.ipv6_dst);
+        if (wc->masks.ipv6_label) {
+            if (wc->masks.ipv6_label == htonl(UINT32_MAX)) {
+                ds_put_format(s, "ipv6_label=0x%05"PRIx32",",
+                              ntohl(f->ipv6_label));
+            } else {
+                ds_put_format(s, "ipv6_label=0x%05"PRIx32"/0x%05"PRIx32",",
+                              ntohl(f->ipv6_label),
+                              ntohl(wc->masks.ipv6_label));
+            }
+        }
+    } else {
+        format_ip_netmask(s, "nw_src", f->nw_src, wc->masks.nw_src);
+        format_ip_netmask(s, "nw_dst", f->nw_dst, wc->masks.nw_dst);
+    }
+    if (!skip_proto && wc->masks.nw_proto) {
+        if (f->dl_type == htons(ETH_TYPE_ARP)) {
+            ds_put_format(s, "arp_op=%"PRIu8",", f->nw_proto);
+        } else {
+            ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto);
+        }
+    }
+    if (f->dl_type == htons(ETH_TYPE_ARP)) {
+        format_eth_masked(s, "arp_sha", f->arp_sha, wc->masks.arp_sha);
+        format_eth_masked(s, "arp_tha", f->arp_tha, wc->masks.arp_tha);
+    }
+    if (wc->masks.nw_tos & IP_DSCP_MASK) {
+        ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos & IP_DSCP_MASK);
+    }
+    if (wc->masks.nw_tos & IP_ECN_MASK) {
+        ds_put_format(s, "nw_ecn=%"PRIu8",", f->nw_tos & IP_ECN_MASK);
+    }
+    if (wc->masks.nw_ttl) {
+        ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
+    }
+    switch (wc->masks.nw_frag) {
+    case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER:
+        ds_put_format(s, "nw_frag=%s,",
+                      f->nw_frag & FLOW_NW_FRAG_ANY
+                      ? (f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "first")
+                      : (f->nw_frag & FLOW_NW_FRAG_LATER ? "<error>" : "no"));
+        break;
+
+    case FLOW_NW_FRAG_ANY:
+        ds_put_format(s, "nw_frag=%s,",
+                      f->nw_frag & FLOW_NW_FRAG_ANY ? "yes" : "no");
+        break;
+
+    case FLOW_NW_FRAG_LATER:
+        ds_put_format(s, "nw_frag=%s,",
+                      f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "not_later");
+        break;
+    }
+    if (f->nw_proto == IPPROTO_ICMP) {
+        format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src);
+        format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst);
+    } else if (f->nw_proto == IPPROTO_ICMPV6) {
+        format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src);
+        format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst);
+        format_ipv6_netmask(s, "nd_target", &f->nd_target,
+                            &wc->masks.nd_target);
+        format_eth_masked(s, "nd_sll", f->arp_sha, wc->masks.arp_sha);
+        format_eth_masked(s, "nd_tll", f->arp_tha, wc->masks.arp_tha);
+    } else {
+        format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src);
+        format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
+    }
+
+    if (s->length > start_len && ds_last(s) == ',') {
+        s->length--;
+    }
+}
+
+/* Converts 'match' to a string and returns the string.  If 'priority' is
+ * different from OFP_DEFAULT_PRIORITY, includes it in the string.  The caller
+ * must free the string (with free()). */
+char *
+match_to_string(const struct match *match, unsigned int priority)
+{
+    struct ds s = DS_EMPTY_INITIALIZER;
+    match_format(match, &s, priority);
+    return ds_steal_cstr(&s);
+}
+
+void
+match_print(const struct match *match)
+{
+    char *s = match_to_string(match, OFP_DEFAULT_PRIORITY);
+    puts(s);
+    free(s);
+}
+\f
+/* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
+ * with minimatch_destroy(). */
+void
+minimatch_init(struct minimatch *dst, const struct match *src)
+{
+    miniflow_init(&dst->flow, &src->flow);
+    minimask_init(&dst->mask, &src->wc);
+}
+
+/* Initializes 'dst' as a copy of 'src'.  The caller must eventually free 'dst'
+ * with minimatch_destroy(). */
+void
+minimatch_clone(struct minimatch *dst, const struct minimatch *src)
+{
+    miniflow_clone(&dst->flow, &src->flow);
+    minimask_clone(&dst->mask, &src->mask);
+}
+
+/* Frees any memory owned by 'match'.  Does not free the storage in which
+ * 'match' itself resides; the caller is responsible for that. */
+void
+minimatch_destroy(struct minimatch *match)
+{
+    miniflow_destroy(&match->flow);
+    minimask_destroy(&match->mask);
+}
+
+/* Initializes 'dst' as a copy of 'src'. */
+void
+minimatch_expand(const struct minimatch *src, struct match *dst)
+{
+    miniflow_expand(&src->flow, &dst->flow);
+    minimask_expand(&src->mask, &dst->wc);
+}
+
+/* Returns true if 'a' and 'b' match the same packets, false otherwise.  */
+bool
+minimatch_equal(const struct minimatch *a, const struct minimatch *b)
+{
+    return (miniflow_equal(&a->flow, &b->flow)
+            && minimask_equal(&a->mask, &b->mask));
+}
+
+/* Returns a hash value for 'match', given 'basis'. */
+uint32_t
+minimatch_hash(const struct minimatch *match, uint32_t basis)
+{
+    return miniflow_hash(&match->flow, minimask_hash(&match->mask, basis));
+}
+
+/* Appends a string representation of 'match' to 's'.  If 'priority' is
+ * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
+void
+minimatch_format(const struct minimatch *match, struct ds *s,
+                 unsigned int priority)
+{
+    struct match megamatch;
+
+    minimatch_expand(match, &megamatch);
+    match_format(&megamatch, s, priority);
+}
+
+/* Converts 'match' to a string and returns the string.  If 'priority' is
+ * different from OFP_DEFAULT_PRIORITY, includes it in the string.  The caller
+ * must free the string (with free()). */
+char *
+minimatch_to_string(const struct minimatch *match, unsigned int priority)
+{
+    struct match megamatch;
+
+    minimatch_expand(match, &megamatch);
+    return match_to_string(&megamatch, priority);
+}
diff --git a/lib/match.h b/lib/match.h
new file mode 100644 (file)
index 0000000..2d05819
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ *
+ * 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 MATCH_H
+#define MATCH_H 1
+
+#include "flow.h"
+
+struct ds;
+
+/* A flow classification match.
+ *
+ * Use one of the match_*() functions to initialize a "struct match".
+ *
+ * The match_*() functions below maintain the following important invariant.
+ * If a bit or a field is wildcarded in 'wc', then the corresponding bit or
+ * field in 'flow' is set to all-0-bits.  (The match_zero_wildcarded_fields()
+ * function can be used to restore this invariant after adding wildcards.) */
+struct match {
+    struct flow flow;
+    struct flow_wildcards wc;
+};
+
+void match_init(struct match *,
+                const struct flow *, const struct flow_wildcards *);
+void match_init_catchall(struct match *);
+void match_init_exact(struct match *, const struct flow *);
+
+void match_zero_wildcarded_fields(struct match *);
+
+void match_set_reg(struct match *, unsigned int reg_idx, uint32_t value);
+void match_set_reg_masked(struct match *, unsigned int reg_idx,
+                          uint32_t value, uint32_t mask);
+void match_set_metadata(struct match *, ovs_be64 metadata);
+void match_set_metadata_masked(struct match *,
+                               ovs_be64 metadata, ovs_be64 mask);
+void match_set_tun_id(struct match *, ovs_be64 tun_id);
+void match_set_tun_id_masked(struct match *, ovs_be64 tun_id, ovs_be64 mask);
+void match_set_in_port(struct match *, uint16_t ofp_port);
+void match_set_dl_type(struct match *, ovs_be16);
+void match_set_dl_src(struct match *, const uint8_t[6]);
+void match_set_dl_src_masked(struct match *, const uint8_t dl_src[6],
+                             const uint8_t mask[6]);
+void match_set_dl_dst(struct match *, const uint8_t[6]);
+void match_set_dl_dst_masked(struct match *, const uint8_t dl_dst[6],
+                             const uint8_t mask[6]);
+void match_set_dl_tci(struct match *, ovs_be16 tci);
+void match_set_dl_tci_masked(struct match *, ovs_be16 tci, ovs_be16 mask);
+void match_set_any_vid(struct match *);
+void match_set_dl_vlan(struct match *, ovs_be16);
+void match_set_vlan_vid(struct match *, ovs_be16);
+void match_set_vlan_vid_masked(struct match *, ovs_be16 vid, ovs_be16 mask);
+void match_set_any_pcp(struct match *);
+void match_set_dl_vlan_pcp(struct match *, uint8_t);
+void match_set_tp_src(struct match *, ovs_be16);
+void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
+void match_set_tp_dst(struct match *, ovs_be16);
+void match_set_tp_dst_masked(struct match *, ovs_be16 port, ovs_be16 mask);
+void match_set_nw_proto(struct match *, uint8_t);
+void match_set_nw_src(struct match *, ovs_be32);
+void match_set_nw_src_masked(struct match *, ovs_be32 ip, ovs_be32 mask);
+void match_set_nw_dst(struct match *, ovs_be32);
+void match_set_nw_dst_masked(struct match *, ovs_be32 ip, ovs_be32 mask);
+void match_set_nw_dscp(struct match *, uint8_t);
+void match_set_nw_ecn(struct match *, uint8_t);
+void match_set_nw_ttl(struct match *, uint8_t);
+void match_set_nw_frag(struct match *, uint8_t nw_frag);
+void match_set_nw_frag_masked(struct match *, uint8_t nw_frag, uint8_t mask);
+void match_set_icmp_type(struct match *, uint8_t);
+void match_set_icmp_code(struct match *, uint8_t);
+void match_set_arp_sha(struct match *, const uint8_t[6]);
+void match_set_arp_sha_masked(struct match *,
+                              const uint8_t arp_sha[6],
+                              const uint8_t mask[6]);
+void match_set_arp_tha(struct match *, const uint8_t[6]);
+void match_set_arp_tha_masked(struct match *,
+                              const uint8_t arp_tha[6],
+                              const uint8_t mask[6]);
+void match_set_ipv6_src(struct match *, const struct in6_addr *);
+void match_set_ipv6_src_masked(struct match *, const struct in6_addr *,
+                               const struct in6_addr *);
+void match_set_ipv6_dst(struct match *, const struct in6_addr *);
+void match_set_ipv6_dst_masked(struct match *, const struct in6_addr *,
+                               const struct in6_addr *);
+void match_set_ipv6_label(struct match *, ovs_be32);
+void match_set_ipv6_label_masked(struct match *, ovs_be32, ovs_be32);
+void match_set_nd_target(struct match *, const struct in6_addr *);
+void match_set_nd_target_masked(struct match *, const struct in6_addr *,
+                                const struct in6_addr *);
+
+bool match_equal(const struct match *, const struct match *);
+uint32_t match_hash(const struct match *, uint32_t basis);
+
+void match_format(const struct match *, struct ds *, unsigned int priority);
+char *match_to_string(const struct match *, unsigned int priority);
+void match_print(const struct match *);
+\f
+/* Compressed match. */
+
+/* A sparse representation of a "struct match".
+ *
+ * This has the same invariant as "struct match", that is, a 1-bit in the
+ * 'flow' must correspond to a 1-bit in 'mask'.
+ *
+ * The invariants for the underlying miniflow and minimask are also maintained,
+ * which means that 'flow' and 'mask' can have different 'map's.  In
+ * particular, if the match checks that a given 32-bit field has value 0, then
+ * 'map' will have a 1-bit in 'mask' but a 0-bit in 'flow' for that field. */
+struct minimatch {
+    struct miniflow flow;
+    struct minimask mask;
+};
+
+void minimatch_init(struct minimatch *, const struct match *);
+void minimatch_clone(struct minimatch *, const struct minimatch *);
+void minimatch_destroy(struct minimatch *);
+
+void minimatch_expand(const struct minimatch *, struct match *);
+
+bool minimatch_equal(const struct minimatch *a, const struct minimatch *b);
+uint32_t minimatch_hash(const struct minimatch *, uint32_t basis);
+
+void minimatch_format(const struct minimatch *, struct ds *,
+                      unsigned int priority);
+char *minimatch_to_string(const struct minimatch *, unsigned int priority);
+
+#endif /* match.h */
index 3713ca4..aa44ce8 100644 (file)
@@ -49,7 +49,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_TUN_ID, "tun_id", NULL,
         MF_FIELD_SIZES(be64),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_HEXADECIMAL,
         MFP_NONE,
         true,
@@ -58,7 +58,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_METADATA, "metadata", NULL,
         MF_FIELD_SIZES(be64),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_HEXADECIMAL,
         MFP_NONE,
         true,
@@ -67,7 +67,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IN_PORT, "in_port", NULL,
         MF_FIELD_SIZES(be16),
-        MFM_NONE, FWW_IN_PORT,
+        MFM_NONE,
         MFS_OFP_PORT,
         MFP_NONE,
         false,
@@ -79,7 +79,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {                                           \
         MFF_REG##IDX, "reg" #IDX, NULL,         \
         MF_FIELD_SIZES(be32),                   \
-        MFM_FULLY, 0,                           \
+        MFM_FULLY,                              \
         MFS_HEXADECIMAL,                        \
         MFP_NONE,                               \
         true,                                   \
@@ -121,7 +121,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_ETH_SRC, "eth_src", "dl_src",
         MF_FIELD_SIZES(mac),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_ETHERNET,
         MFP_NONE,
         true,
@@ -130,7 +130,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ETH_DST, "eth_dst", "dl_dst",
         MF_FIELD_SIZES(mac),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_ETHERNET,
         MFP_NONE,
         true,
@@ -139,7 +139,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ETH_TYPE, "eth_type", "dl_type",
         MF_FIELD_SIZES(be16),
-        MFM_NONE, FWW_DL_TYPE,
+        MFM_NONE,
         MFS_HEXADECIMAL,
         MFP_NONE,
         false,
@@ -150,7 +150,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_VLAN_TCI, "vlan_tci", NULL,
         MF_FIELD_SIZES(be16),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_HEXADECIMAL,
         MFP_NONE,
         true,
@@ -159,7 +159,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_DL_VLAN, "dl_vlan", NULL,
         sizeof(ovs_be16), 12,
-        MFM_NONE, 0,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_NONE,
         true,
@@ -168,7 +168,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_VLAN_VID, "vlan_vid", NULL,
         sizeof(ovs_be16), 12,
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_DECIMAL,
         MFP_NONE,
         true,
@@ -177,7 +177,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_DL_VLAN_PCP, "dl_vlan_pcp", NULL,
         1, 3,
-        MFM_NONE, 0,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_NONE,
         true,
@@ -186,7 +186,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_VLAN_PCP, "vlan_pcp", NULL,
         1, 3,
-        MFM_NONE, 0,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_VLAN_VID,
         true,
@@ -201,7 +201,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_IPV4_SRC, "ip_src", "nw_src",
         MF_FIELD_SIZES(be32),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_IPV4,
         MFP_IPV4,
         true,
@@ -210,7 +210,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IPV4_DST, "ip_dst", "nw_dst",
         MF_FIELD_SIZES(be32),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_IPV4,
         MFP_IPV4,
         true,
@@ -221,7 +221,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_IPV6_SRC, "ipv6_src", NULL,
         MF_FIELD_SIZES(ipv6),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_IPV6,
         MFP_IPV6,
         true,
@@ -230,7 +230,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IPV6_DST, "ipv6_dst", NULL,
         MF_FIELD_SIZES(ipv6),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_IPV6,
         MFP_IPV6,
         true,
@@ -240,7 +240,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_IPV6_LABEL, "ipv6_label", NULL,
         4, 20,
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_HEXADECIMAL,
         MFP_IPV6,
         false,
@@ -251,7 +251,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_IP_PROTO, "nw_proto", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, FWW_NW_PROTO,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_IP_ANY,
         false,
@@ -260,7 +260,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IP_DSCP, "nw_tos", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, FWW_NW_DSCP,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_IP_ANY,
         true,
@@ -269,7 +269,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IP_ECN, "nw_ecn", NULL,
         1, 2,
-        MFM_NONE, FWW_NW_ECN,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_IP_ANY,
         true,
@@ -278,7 +278,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IP_TTL, "nw_ttl", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, FWW_NW_TTL,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_IP_ANY,
         true,
@@ -287,7 +287,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IP_FRAG, "ip_frag", NULL,
         1, 2,
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_FRAG,
         MFP_IP_ANY,
         false,
@@ -298,7 +298,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_ARP_OP, "arp_op", NULL,
         MF_FIELD_SIZES(be16),
-        MFM_NONE, FWW_NW_PROTO,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_ARP,
         false,
@@ -307,7 +307,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_SPA, "arp_spa", NULL,
         MF_FIELD_SIZES(be32),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_IPV4,
         MFP_ARP,
         false,
@@ -316,7 +316,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_TPA, "arp_tpa", NULL,
         MF_FIELD_SIZES(be32),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_IPV4,
         MFP_ARP,
         false,
@@ -325,7 +325,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_SHA, "arp_sha", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_ETHERNET,
         MFP_ARP,
         false,
@@ -334,7 +334,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_THA, "arp_tha", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_ETHERNET,
         MFP_ARP,
         false,
@@ -349,7 +349,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_TCP_SRC, "tcp_src", "tp_src",
         MF_FIELD_SIZES(be16),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_DECIMAL,
         MFP_TCP,
         true,
@@ -358,7 +358,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_TCP_DST, "tcp_dst", "tp_dst",
         MF_FIELD_SIZES(be16),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_DECIMAL,
         MFP_TCP,
         true,
@@ -369,7 +369,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_UDP_SRC, "udp_src", NULL,
         MF_FIELD_SIZES(be16),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_DECIMAL,
         MFP_UDP,
         true,
@@ -378,7 +378,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_UDP_DST, "udp_dst", NULL,
         MF_FIELD_SIZES(be16),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_DECIMAL,
         MFP_UDP,
         true,
@@ -389,7 +389,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_ICMPV4_TYPE, "icmp_type", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, 0,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_ICMPV4,
         false,
@@ -398,7 +398,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ICMPV4_CODE, "icmp_code", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, 0,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_ICMPV4,
         false,
@@ -409,7 +409,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_ICMPV6_TYPE, "icmpv6_type", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, 0,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_ICMPV6,
         false,
@@ -418,7 +418,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ICMPV6_CODE, "icmpv6_code", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, 0,
+        MFM_NONE,
         MFS_DECIMAL,
         MFP_ICMPV6,
         false,
@@ -433,7 +433,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_ND_TARGET, "nd_target", NULL,
         MF_FIELD_SIZES(ipv6),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_IPV6,
         MFP_ND,
         false,
@@ -442,7 +442,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ND_SLL, "nd_sll", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_ETHERNET,
         MFP_ND_SOLICIT,
         false,
@@ -451,7 +451,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ND_TLL, "nd_tll", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_FULLY, 0,
+        MFM_FULLY,
         MFS_ETHERNET,
         MFP_ND_ADVERT,
         false,
@@ -573,81 +573,85 @@ 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_TYPE:
-    case MFF_IP_PROTO:
-    case MFF_IP_DSCP:
-    case MFF_IP_ECN:
-    case MFF_IP_TTL:
-    case MFF_ARP_OP:
-        assert(mf->fww_bit != 0);
-        return (wc->wildcards & mf->fww_bit) != 0;
-
     case MFF_TUN_ID:
-        return !wc->tun_id_mask;
+        return !wc->masks.tun_id;
     case MFF_METADATA:
-        return !wc->metadata_mask;
-
+        return !wc->masks.metadata;
+    case MFF_IN_PORT:
+        return !wc->masks.in_port;
     CASE_MFF_REGS:
-        return !wc->reg_masks[mf->id - MFF_REG0];
+        return !wc->masks.regs[mf->id - MFF_REG0];
 
     case MFF_ETH_SRC:
-        return eth_addr_is_zero(wc->dl_src_mask);
+        return eth_addr_is_zero(wc->masks.dl_src);
     case MFF_ETH_DST:
-        return eth_addr_is_zero(wc->dl_dst_mask);
+        return eth_addr_is_zero(wc->masks.dl_dst);
+    case MFF_ETH_TYPE:
+        return !wc->masks.dl_type;
 
     case MFF_ARP_SHA:
     case MFF_ND_SLL:
-        return eth_addr_is_zero(wc->arp_sha_mask);
+        return eth_addr_is_zero(wc->masks.arp_sha);
 
     case MFF_ARP_THA:
     case MFF_ND_TLL:
-        return eth_addr_is_zero(wc->arp_tha_mask);
+        return eth_addr_is_zero(wc->masks.arp_tha);
 
     case MFF_VLAN_TCI:
-        return !wc->vlan_tci_mask;
+        return !wc->masks.vlan_tci;
     case MFF_DL_VLAN:
-        return !(wc->vlan_tci_mask & htons(VLAN_VID_MASK));
+        return !(wc->masks.vlan_tci & htons(VLAN_VID_MASK));
     case MFF_VLAN_VID:
-        return !(wc->vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI));
+        return !(wc->masks.vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI));
     case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
-        return !(wc->vlan_tci_mask & htons(VLAN_PCP_MASK));
+        return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK));
 
     case MFF_IPV4_SRC:
-        return !wc->nw_src_mask;
+        return !wc->masks.nw_src;
     case MFF_IPV4_DST:
-        return !wc->nw_dst_mask;
+        return !wc->masks.nw_dst;
 
     case MFF_IPV6_SRC:
-        return ipv6_mask_is_any(&wc->ipv6_src_mask);
+        return ipv6_mask_is_any(&wc->masks.ipv6_src);
     case MFF_IPV6_DST:
-        return ipv6_mask_is_any(&wc->ipv6_dst_mask);
+        return ipv6_mask_is_any(&wc->masks.ipv6_dst);
 
     case MFF_IPV6_LABEL:
-        return !wc->ipv6_label_mask;
+        return !wc->masks.ipv6_label;
+
+    case MFF_IP_PROTO:
+        return !wc->masks.nw_proto;
+    case MFF_IP_DSCP:
+        return !(wc->masks.nw_tos & IP_DSCP_MASK);
+    case MFF_IP_ECN:
+        return !(wc->masks.nw_tos & IP_ECN_MASK);
+    case MFF_IP_TTL:
+        return !wc->masks.nw_ttl;
 
     case MFF_ND_TARGET:
-        return ipv6_mask_is_any(&wc->nd_target_mask);
+        return ipv6_mask_is_any(&wc->masks.nd_target);
 
     case MFF_IP_FRAG:
-        return !(wc->nw_frag_mask & FLOW_NW_FRAG_MASK);
+        return !(wc->masks.nw_frag & FLOW_NW_FRAG_MASK);
 
+    case MFF_ARP_OP:
+        return !wc->masks.nw_proto;
     case MFF_ARP_SPA:
-        return !wc->nw_src_mask;
+        return !wc->masks.nw_src;
     case MFF_ARP_TPA:
-        return !wc->nw_dst_mask;
+        return !wc->masks.nw_dst;
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
-        return !wc->tp_src_mask;
+        return !wc->masks.tp_src;
     case MFF_TCP_DST:
     case MFF_UDP_DST:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
-        return !wc->tp_dst_mask;
+        return !wc->masks.tp_dst;
 
     case MFF_N_IDS:
     default:
@@ -666,106 +670,115 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
             union mf_value *mask)
 {
     switch (mf->id) {
-    case MFF_IN_PORT:
-    case MFF_ETH_TYPE:
-    case MFF_IP_PROTO:
-    case MFF_IP_DSCP:
-    case MFF_IP_ECN:
-    case MFF_IP_TTL:
-    case MFF_ARP_OP:
-        assert(mf->fww_bit != 0);
-        memset(mask, wc->wildcards & mf->fww_bit ? 0x00 : 0xff, mf->n_bytes);
-        break;
-
     case MFF_TUN_ID:
-        mask->be64 = wc->tun_id_mask;
+        mask->be64 = wc->masks.tun_id;
         break;
     case MFF_METADATA:
-        mask->be64 = wc->metadata_mask;
+        mask->be64 = wc->masks.metadata;
+        break;
+    case MFF_IN_PORT:
+        mask->be16 = htons(wc->masks.in_port);
         break;
-
     CASE_MFF_REGS:
-        mask->be32 = htonl(wc->reg_masks[mf->id - MFF_REG0]);
+        mask->be32 = htonl(wc->masks.regs[mf->id - MFF_REG0]);
         break;
 
     case MFF_ETH_DST:
-        memcpy(mask->mac, wc->dl_dst_mask, ETH_ADDR_LEN);
+        memcpy(mask->mac, wc->masks.dl_dst, ETH_ADDR_LEN);
         break;
-
     case MFF_ETH_SRC:
-        memcpy(mask->mac, wc->dl_src_mask, ETH_ADDR_LEN);
+        memcpy(mask->mac, wc->masks.dl_src, ETH_ADDR_LEN);
+        break;
+    case MFF_ETH_TYPE:
+        mask->be16 = wc->masks.dl_type;
         break;
 
     case MFF_VLAN_TCI:
-        mask->be16 = wc->vlan_tci_mask;
+        mask->be16 = wc->masks.vlan_tci;
         break;
     case MFF_DL_VLAN:
-        mask->be16 = wc->vlan_tci_mask & htons(VLAN_VID_MASK);
+        mask->be16 = wc->masks.vlan_tci & htons(VLAN_VID_MASK);
         break;
     case MFF_VLAN_VID:
-        mask->be16 = wc->vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI);
+        mask->be16 = wc->masks.vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
         break;
     case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
-        mask->u8 = vlan_tci_to_pcp(wc->vlan_tci_mask);
+        mask->u8 = vlan_tci_to_pcp(wc->masks.vlan_tci);
         break;
 
     case MFF_IPV4_SRC:
-        mask->be32 = wc->nw_src_mask;
+        mask->be32 = wc->masks.nw_src;
         break;
     case MFF_IPV4_DST:
-        mask->be32 = wc->nw_dst_mask;
+        mask->be32 = wc->masks.nw_dst;
         break;
 
     case MFF_IPV6_SRC:
-        mask->ipv6 = wc->ipv6_src_mask;
+        mask->ipv6 = wc->masks.ipv6_src;
         break;
     case MFF_IPV6_DST:
-        mask->ipv6 = wc->ipv6_dst_mask;
+        mask->ipv6 = wc->masks.ipv6_dst;
         break;
     case MFF_IPV6_LABEL:
-        mask->be32 = wc->ipv6_label_mask;
+        mask->be32 = wc->masks.ipv6_label;
+        break;
+
+    case MFF_IP_PROTO:
+        mask->u8 = wc->masks.nw_proto;
+        break;
+    case MFF_IP_DSCP:
+        mask->u8 = wc->masks.nw_tos & IP_DSCP_MASK;
+        break;
+    case MFF_IP_ECN:
+        mask->u8 = wc->masks.nw_tos & IP_ECN_MASK;
         break;
 
     case MFF_ND_TARGET:
-        mask->ipv6 = wc->nd_target_mask;
+        mask->ipv6 = wc->masks.nd_target;
         break;
 
+    case MFF_IP_TTL:
+        mask->u8 = wc->masks.nw_ttl;
+        break;
     case MFF_IP_FRAG:
-        mask->u8 = wc->nw_frag_mask & FLOW_NW_FRAG_MASK;
+        mask->u8 = wc->masks.nw_frag & FLOW_NW_FRAG_MASK;
         break;
 
+    case MFF_ARP_OP:
+        mask->u8 = wc->masks.nw_proto;
+        break;
     case MFF_ARP_SPA:
-        mask->be32 = wc->nw_src_mask;
+        mask->be32 = wc->masks.nw_src;
         break;
     case MFF_ARP_TPA:
-        mask->be32 = wc->nw_dst_mask;
+        mask->be32 = wc->masks.nw_dst;
         break;
     case MFF_ARP_SHA:
     case MFF_ND_SLL:
-        memcpy(mask->mac, wc->arp_sha_mask, ETH_ADDR_LEN);
+        memcpy(mask->mac, wc->masks.arp_sha, ETH_ADDR_LEN);
         break;
     case MFF_ARP_THA:
     case MFF_ND_TLL:
-        memcpy(mask->mac, wc->arp_tha_mask, ETH_ADDR_LEN);
+        memcpy(mask->mac, wc->masks.arp_tha, ETH_ADDR_LEN);
         break;
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
-        mask->be16 = wc->tp_src_mask;
+        mask->be16 = wc->masks.tp_src;
         break;
     case MFF_TCP_DST:
     case MFF_UDP_DST:
-        mask->be16 = wc->tp_dst_mask;
+        mask->be16 = wc->masks.tp_dst;
         break;
 
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
-        mask->u8 = ntohs(wc->tp_src_mask);
+        mask->u8 = ntohs(wc->masks.tp_src);
         break;
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
-        mask->u8 = ntohs(wc->tp_dst_mask);
+        mask->u8 = ntohs(wc->masks.tp_dst);
         break;
 
     case MFF_N_IDS:
@@ -1073,141 +1086,141 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
     }
 }
 
-/* Makes 'rule' match field 'mf' exactly, with the value matched taken from
- * 'value'.  The caller is responsible for ensuring that 'rule' meets 'mf''s
+/* Makes 'match' match field 'mf' exactly, with the value matched taken from
+ * 'value'.  The caller is responsible for ensuring that 'match' meets 'mf''s
  * prerequisites. */
 void
 mf_set_value(const struct mf_field *mf,
-             const union mf_value *value, struct cls_rule *rule)
+             const union mf_value *value, struct match *match)
 {
     switch (mf->id) {
     case MFF_TUN_ID:
-        cls_rule_set_tun_id(rule, value->be64);
+        match_set_tun_id(match, value->be64);
         break;
     case MFF_METADATA:
-        cls_rule_set_metadata(rule, value->be64);
+        match_set_metadata(match, value->be64);
         break;
 
     case MFF_IN_PORT:
-        cls_rule_set_in_port(rule, ntohs(value->be16));
+        match_set_in_port(match, ntohs(value->be16));
         break;
 
     CASE_MFF_REGS:
-        cls_rule_set_reg(rule, mf->id - MFF_REG0, ntohl(value->be32));
+        match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32));
         break;
 
     case MFF_ETH_SRC:
-        cls_rule_set_dl_src(rule, value->mac);
+        match_set_dl_src(match, value->mac);
         break;
 
     case MFF_ETH_DST:
-        cls_rule_set_dl_dst(rule, value->mac);
+        match_set_dl_dst(match, value->mac);
         break;
 
     case MFF_ETH_TYPE:
-        cls_rule_set_dl_type(rule, value->be16);
+        match_set_dl_type(match, value->be16);
         break;
 
     case MFF_VLAN_TCI:
-        cls_rule_set_dl_tci(rule, value->be16);
+        match_set_dl_tci(match, value->be16);
         break;
 
     case MFF_DL_VLAN:
-        cls_rule_set_dl_vlan(rule, value->be16);
+        match_set_dl_vlan(match, value->be16);
         break;
     case MFF_VLAN_VID:
-        cls_rule_set_vlan_vid(rule, value->be16);
+        match_set_vlan_vid(match, value->be16);
         break;
 
     case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
-        cls_rule_set_dl_vlan_pcp(rule, value->u8);
+        match_set_dl_vlan_pcp(match, value->u8);
         break;
 
     case MFF_IPV4_SRC:
-        cls_rule_set_nw_src(rule, value->be32);
+        match_set_nw_src(match, value->be32);
         break;
 
     case MFF_IPV4_DST:
-        cls_rule_set_nw_dst(rule, value->be32);
+        match_set_nw_dst(match, value->be32);
         break;
 
     case MFF_IPV6_SRC:
-        cls_rule_set_ipv6_src(rule, &value->ipv6);
+        match_set_ipv6_src(match, &value->ipv6);
         break;
 
     case MFF_IPV6_DST:
-        cls_rule_set_ipv6_dst(rule, &value->ipv6);
+        match_set_ipv6_dst(match, &value->ipv6);
         break;
 
     case MFF_IPV6_LABEL:
-        cls_rule_set_ipv6_label(rule, value->be32);
+        match_set_ipv6_label(match, value->be32);
         break;
 
     case MFF_IP_PROTO:
-        cls_rule_set_nw_proto(rule, value->u8);
+        match_set_nw_proto(match, value->u8);
         break;
 
     case MFF_IP_DSCP:
-        cls_rule_set_nw_dscp(rule, value->u8);
+        match_set_nw_dscp(match, value->u8);
         break;
 
     case MFF_IP_ECN:
-        cls_rule_set_nw_ecn(rule, value->u8);
+        match_set_nw_ecn(match, value->u8);
         break;
 
     case MFF_IP_TTL:
-        cls_rule_set_nw_ttl(rule, value->u8);
+        match_set_nw_ttl(match, value->u8);
         break;
 
     case MFF_IP_FRAG:
-        cls_rule_set_nw_frag(rule, value->u8);
+        match_set_nw_frag(match, value->u8);
         break;
 
     case MFF_ARP_OP:
-        cls_rule_set_nw_proto(rule, ntohs(value->be16));
+        match_set_nw_proto(match, ntohs(value->be16));
         break;
 
     case MFF_ARP_SPA:
-        cls_rule_set_nw_src(rule, value->be32);
+        match_set_nw_src(match, value->be32);
         break;
 
     case MFF_ARP_TPA:
-        cls_rule_set_nw_dst(rule, value->be32);
+        match_set_nw_dst(match, value->be32);
         break;
 
     case MFF_ARP_SHA:
     case MFF_ND_SLL:
-        cls_rule_set_arp_sha(rule, value->mac);
+        match_set_arp_sha(match, value->mac);
         break;
 
     case MFF_ARP_THA:
     case MFF_ND_TLL:
-        cls_rule_set_arp_tha(rule, value->mac);
+        match_set_arp_tha(match, value->mac);
         break;
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
-        cls_rule_set_tp_src(rule, value->be16);
+        match_set_tp_src(match, value->be16);
         break;
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
-        cls_rule_set_tp_dst(rule, value->be16);
+        match_set_tp_dst(match, value->be16);
         break;
 
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
-        cls_rule_set_icmp_type(rule, value->u8);
+        match_set_icmp_type(match, value->u8);
         break;
 
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
-        cls_rule_set_icmp_code(rule, value->u8);
+        match_set_icmp_code(match, value->u8);
         break;
 
     case MFF_ND_TARGET:
-        cls_rule_set_nd_target(rule, &value->ipv6);
+        match_set_nd_target(match, &value->ipv6);
         break;
 
     case MFF_N_IDS:
@@ -1216,8 +1229,8 @@ mf_set_value(const struct mf_field *mf,
     }
 }
 
-/* Makes 'rule' match field 'mf' exactly, with the value matched taken from
- * 'value'.  The caller is responsible for ensuring that 'rule' meets 'mf''s
+/* Makes 'match' match field 'mf' exactly, with the value matched taken from
+ * 'value'.  The caller is responsible for ensuring that 'match' meets 'mf''s
  * prerequisites. */
 void
 mf_set_flow_value(const struct mf_field *mf,
@@ -1374,144 +1387,145 @@ mf_is_zero(const struct mf_field *mf, const struct flow *flow)
     return is_all_zeros((const uint8_t *) &value, mf->n_bytes);
 }
 
-/* Makes 'rule' wildcard field 'mf'.
+/* Makes 'match' wildcard field 'mf'.
  *
- * The caller is responsible for ensuring that 'rule' meets 'mf''s
+ * The caller is responsible for ensuring that 'match' meets 'mf''s
  * prerequisites. */
 void
-mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
+mf_set_wild(const struct mf_field *mf, struct match *match)
 {
     switch (mf->id) {
     case MFF_TUN_ID:
-        cls_rule_set_tun_id_masked(rule, htonll(0), htonll(0));
+        match_set_tun_id_masked(match, htonll(0), htonll(0));
         break;
     case MFF_METADATA:
-        cls_rule_set_metadata_masked(rule, htonll(0), htonll(0));
+        match_set_metadata_masked(match, htonll(0), htonll(0));
 
     case MFF_IN_PORT:
-        rule->wc.wildcards |= FWW_IN_PORT;
-        rule->flow.in_port = 0;
+        match->flow.in_port = 0;
+        match->wc.masks.in_port = 0;
         break;
 
     CASE_MFF_REGS:
-        cls_rule_set_reg_masked(rule, mf->id - MFF_REG0, 0, 0);
+        match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0);
         break;
 
     case MFF_ETH_SRC:
-        memset(rule->flow.dl_src, 0, ETH_ADDR_LEN);
-        memset(rule->wc.dl_src_mask, 0, ETH_ADDR_LEN);
+        memset(match->flow.dl_src, 0, ETH_ADDR_LEN);
+        memset(match->wc.masks.dl_src, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_ETH_DST:
-        memset(rule->flow.dl_dst, 0, ETH_ADDR_LEN);
-        memset(rule->wc.dl_dst_mask, 0, ETH_ADDR_LEN);
+        memset(match->flow.dl_dst, 0, ETH_ADDR_LEN);
+        memset(match->wc.masks.dl_dst, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_ETH_TYPE:
-        rule->wc.wildcards |= FWW_DL_TYPE;
-        rule->flow.dl_type = htons(0);
+        match->flow.dl_type = htons(0);
+        match->wc.masks.dl_type = htons(0);
         break;
 
     case MFF_VLAN_TCI:
-        cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
+        match_set_dl_tci_masked(match, htons(0), htons(0));
         break;
 
     case MFF_DL_VLAN:
     case MFF_VLAN_VID:
-        cls_rule_set_any_vid(rule);
+        match_set_any_vid(match);
         break;
 
     case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
-        cls_rule_set_any_pcp(rule);
+        match_set_any_pcp(match);
         break;
 
     case MFF_IPV4_SRC:
     case MFF_ARP_SPA:
-        cls_rule_set_nw_src_masked(rule, htonl(0), htonl(0));
+        match_set_nw_src_masked(match, htonl(0), htonl(0));
         break;
 
     case MFF_IPV4_DST:
     case MFF_ARP_TPA:
-        cls_rule_set_nw_dst_masked(rule, htonl(0), htonl(0));
+        match_set_nw_dst_masked(match, 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);
+        memset(&match->wc.masks.ipv6_src, 0, sizeof match->wc.masks.ipv6_src);
+        memset(&match->flow.ipv6_src, 0, sizeof match->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);
+        memset(&match->wc.masks.ipv6_dst, 0, sizeof match->wc.masks.ipv6_dst);
+        memset(&match->flow.ipv6_dst, 0, sizeof match->flow.ipv6_dst);
         break;
 
     case MFF_IPV6_LABEL:
-        rule->wc.ipv6_label_mask = 0;
-        rule->flow.ipv6_label = 0;
+        match->wc.masks.ipv6_label = htonl(0);
+        match->flow.ipv6_label = htonl(0);
         break;
 
     case MFF_IP_PROTO:
-        rule->wc.wildcards |= FWW_NW_PROTO;
-        rule->flow.nw_proto = 0;
+        match->wc.masks.nw_proto = 0;
+        match->flow.nw_proto = 0;
         break;
 
     case MFF_IP_DSCP:
-        rule->wc.wildcards |= FWW_NW_DSCP;
-        rule->flow.nw_tos &= ~IP_DSCP_MASK;
+        match->wc.masks.nw_tos &= ~IP_DSCP_MASK;
+        match->flow.nw_tos &= ~IP_DSCP_MASK;
         break;
 
     case MFF_IP_ECN:
-        rule->wc.wildcards |= FWW_NW_ECN;
-        rule->flow.nw_tos &= ~IP_ECN_MASK;
+        match->wc.masks.nw_tos &= ~IP_ECN_MASK;
+        match->flow.nw_tos &= ~IP_ECN_MASK;
         break;
 
     case MFF_IP_TTL:
-        rule->wc.wildcards |= FWW_NW_TTL;
-        rule->flow.nw_ttl = 0;
+        match->wc.masks.nw_ttl = 0;
+        match->flow.nw_ttl = 0;
         break;
 
     case MFF_IP_FRAG:
-        rule->wc.nw_frag_mask |= FLOW_NW_FRAG_MASK;
-        rule->flow.nw_frag &= ~FLOW_NW_FRAG_MASK;
+        match->wc.masks.nw_frag |= FLOW_NW_FRAG_MASK;
+        match->flow.nw_frag &= ~FLOW_NW_FRAG_MASK;
         break;
 
     case MFF_ARP_OP:
-        rule->wc.wildcards |= FWW_NW_PROTO;
-        rule->flow.nw_proto = 0;
+        match->wc.masks.nw_proto = 0;
+        match->flow.nw_proto = 0;
         break;
 
     case MFF_ARP_SHA:
     case MFF_ND_SLL:
-        memset(rule->flow.arp_sha, 0, ETH_ADDR_LEN);
-        memset(rule->wc.arp_sha_mask, 0, ETH_ADDR_LEN);
+        memset(match->flow.arp_sha, 0, ETH_ADDR_LEN);
+        memset(match->wc.masks.arp_sha, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_ARP_THA:
     case MFF_ND_TLL:
-        memset(rule->flow.arp_tha, 0, ETH_ADDR_LEN);
-        memset(rule->wc.arp_tha_mask, 0, ETH_ADDR_LEN);
+        memset(match->flow.arp_tha, 0, ETH_ADDR_LEN);
+        memset(match->wc.masks.arp_tha, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
-        rule->wc.tp_src_mask = htons(0);
-        rule->flow.tp_src = htons(0);
+        match->wc.masks.tp_src = htons(0);
+        match->flow.tp_src = htons(0);
         break;
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
-        rule->wc.tp_dst_mask = htons(0);
-        rule->flow.tp_dst = htons(0);
+        match->wc.masks.tp_dst = htons(0);
+        match->flow.tp_dst = htons(0);
         break;
 
     case MFF_ND_TARGET:
-        memset(&rule->wc.nd_target_mask, 0, sizeof rule->wc.nd_target_mask);
-        memset(&rule->flow.nd_target, 0, sizeof rule->flow.nd_target);
+        memset(&match->wc.masks.nd_target, 0,
+               sizeof match->wc.masks.nd_target);
+        memset(&match->flow.nd_target, 0, sizeof match->flow.nd_target);
         break;
 
     case MFF_N_IDS:
@@ -1520,27 +1534,27 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
     }
 }
 
-/* Makes 'rule' match field 'mf' with the specified 'value' and 'mask'.
+/* Makes 'match' match field 'mf' with the specified 'value' and 'mask'.
  * '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 all-1-bits, then this call is equivalent to
- * mf_set_value(mf, value, rule).  If 'mask' points to all-0-bits, then this
- * call is equivalent to mf_set_wild(mf, rule).
+ * mf_set_value(mf, value, match).  If 'mask' points to all-0-bits, then this
+ * call is equivalent to mf_set_wild(mf, match).
  *
  * '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. */
+ * is responsible for ensuring that 'match' meets 'mf''s prerequisites. */
 void
 mf_set(const struct mf_field *mf,
        const union mf_value *value, const union mf_value *mask,
-       struct cls_rule *rule)
+       struct match *match)
 {
     if (!mask || is_all_ones((const uint8_t *) mask, mf->n_bytes)) {
-        mf_set_value(mf, value, rule);
+        mf_set_value(mf, value, match);
         return;
     } else if (is_all_zeros((const uint8_t *) mask, mf->n_bytes)) {
-        mf_set_wild(mf, rule);
+        mf_set_wild(mf, match);
         return;
     }
 
@@ -1562,91 +1576,91 @@ mf_set(const struct mf_field *mf,
         NOT_REACHED();
 
     case MFF_TUN_ID:
-        cls_rule_set_tun_id_masked(rule, value->be64, mask->be64);
+        match_set_tun_id_masked(match, value->be64, mask->be64);
         break;
     case MFF_METADATA:
-        cls_rule_set_metadata_masked(rule, value->be64, mask->be64);
+        match_set_metadata_masked(match, value->be64, mask->be64);
         break;
 
     CASE_MFF_REGS:
-        cls_rule_set_reg_masked(rule, mf->id - MFF_REG0,
-                                ntohl(value->be32), ntohl(mask->be32));
+        match_set_reg_masked(match, mf->id - MFF_REG0,
+                             ntohl(value->be32), ntohl(mask->be32));
         break;
 
     case MFF_ETH_DST:
-        cls_rule_set_dl_dst_masked(rule, value->mac, mask->mac);
+        match_set_dl_dst_masked(match, value->mac, mask->mac);
         break;
 
     case MFF_ETH_SRC:
-        cls_rule_set_dl_src_masked(rule, value->mac, mask->mac);
+        match_set_dl_src_masked(match, value->mac, mask->mac);
         break;
 
     case MFF_ARP_SHA:
     case MFF_ND_SLL:
-        cls_rule_set_arp_sha_masked(rule, value->mac, mask->mac);
+        match_set_arp_sha_masked(match, value->mac, mask->mac);
         break;
 
     case MFF_ARP_THA:
     case MFF_ND_TLL:
-        cls_rule_set_arp_tha_masked(rule, value->mac, mask->mac);
+        match_set_arp_tha_masked(match, value->mac, mask->mac);
         break;
 
     case MFF_VLAN_TCI:
-        cls_rule_set_dl_tci_masked(rule, value->be16, mask->be16);
+        match_set_dl_tci_masked(match, value->be16, mask->be16);
         break;
 
     case MFF_VLAN_VID:
-        cls_rule_set_vlan_vid_masked(rule, value->be16, mask->be16);
+        match_set_vlan_vid_masked(match, value->be16, mask->be16);
         break;
 
     case MFF_IPV4_SRC:
-        cls_rule_set_nw_src_masked(rule, value->be32, mask->be32);
+        match_set_nw_src_masked(match, value->be32, mask->be32);
         break;
 
     case MFF_IPV4_DST:
-        cls_rule_set_nw_dst_masked(rule, value->be32, mask->be32);
+        match_set_nw_dst_masked(match, value->be32, mask->be32);
         break;
 
     case MFF_IPV6_SRC:
-        cls_rule_set_ipv6_src_masked(rule, &value->ipv6, &mask->ipv6);
+        match_set_ipv6_src_masked(match, &value->ipv6, &mask->ipv6);
         break;
 
     case MFF_IPV6_DST:
-        cls_rule_set_ipv6_dst_masked(rule, &value->ipv6, &mask->ipv6);
+        match_set_ipv6_dst_masked(match, &value->ipv6, &mask->ipv6);
         break;
 
     case MFF_IPV6_LABEL:
         if ((mask->be32 & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK)) {
-            mf_set_value(mf, value, rule);
+            mf_set_value(mf, value, match);
         } else {
-            cls_rule_set_ipv6_label_masked(rule, value->be32, mask->be32);
+            match_set_ipv6_label_masked(match, value->be32, mask->be32);
         }
         break;
 
     case MFF_ND_TARGET:
-        cls_rule_set_nd_target_masked(rule, &value->ipv6, &mask->ipv6);
+        match_set_nd_target_masked(match, &value->ipv6, &mask->ipv6);
         break;
 
     case MFF_IP_FRAG:
-        cls_rule_set_nw_frag_masked(rule, value->u8, mask->u8);
+        match_set_nw_frag_masked(match, value->u8, mask->u8);
         break;
 
     case MFF_ARP_SPA:
-        cls_rule_set_nw_src_masked(rule, value->be32, mask->be32);
+        match_set_nw_src_masked(match, value->be32, mask->be32);
         break;
 
     case MFF_ARP_TPA:
-        cls_rule_set_nw_dst_masked(rule, value->be32, mask->be32);
+        match_set_nw_dst_masked(match, value->be32, mask->be32);
         break;
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
-        cls_rule_set_tp_src_masked(rule, value->be16, mask->be16);
+        match_set_tp_src_masked(match, value->be16, mask->be16);
         break;
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
-        cls_rule_set_tp_dst_masked(rule, value->be16, mask->be16);
+        match_set_tp_dst_masked(match, value->be16, mask->be16);
         break;
 
     case MFF_N_IDS:
@@ -1704,14 +1718,14 @@ mf_check_dst(const struct mf_subfield *sf, const struct flow *flow)
     return error;
 }
 
-/* Copies the value and wildcard bit pattern for 'mf' from 'rule' into the
+/* Copies the value and wildcard bit pattern for 'mf' from 'match' into the
  * 'value' and 'mask', respectively. */
 void
-mf_get(const struct mf_field *mf, const struct cls_rule *rule,
+mf_get(const struct mf_field *mf, const struct match *match,
        union mf_value *value, union mf_value *mask)
 {
-    mf_get_value(mf, &rule->flow, value);
-    mf_get_mask(mf, &rule->wc, mask);
+    mf_get_value(mf, &match->flow, value);
+    mf_get_mask(mf, &match->wc, mask);
 }
 
 /* Assigns a random value for field 'mf' to 'value'. */
@@ -2139,20 +2153,36 @@ mf_format(const struct mf_field *mf,
     }
 }
 \f
-/* Makes subfield 'sf' within 'rule' exactly match the 'sf->n_bits'
+/* Makes subfield 'sf' within 'flow' exactly match the 'sf->n_bits'
+ * least-significant bits in 'x'.
+ */
+void
+mf_write_subfield_flow(const struct mf_subfield *sf,
+                       const union mf_subvalue *x, struct flow *flow)
+{
+    const struct mf_field *field = sf->field;
+    union mf_value value;
+
+    mf_get_value(field, flow, &value);
+    bitwise_copy(x, sizeof *x, 0, &value, field->n_bytes,
+                 sf->ofs, sf->n_bits);
+    mf_set_flow_value(field, &value, flow);
+}
+
+/* Makes subfield 'sf' within 'match' exactly match the 'sf->n_bits'
  * least-significant bits in 'x'.
  */
 void
 mf_write_subfield(const struct mf_subfield *sf, const union mf_subvalue *x,
-                  struct cls_rule *rule)
+                  struct match *match)
 {
     const struct mf_field *field = sf->field;
     union mf_value value, mask;
 
-    mf_get(field, rule, &value, &mask);
+    mf_get(field, match, &value, &mask);
     bitwise_copy(x, sizeof *x, 0, &value, field->n_bytes, sf->ofs, sf->n_bits);
     bitwise_one (                 &mask,  field->n_bytes, sf->ofs, sf->n_bits);
-    mf_set(field, &value, &mask, rule);
+    mf_set(field, &value, &mask, match);
 }
 
 /* Initializes 'x' to the value of 'sf' within 'flow'.  'sf' must be valid for
@@ -2321,3 +2351,20 @@ mf_parse_subfield(struct mf_subfield *sf, const char *s)
     }
     return s;
 }
+
+void
+mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) {
+        if (subvalue->u8[i]) {
+            ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]);
+            for (i++; i < ARRAY_SIZE(subvalue->u8); i++) {
+                ds_put_format(s, "%02"PRIx8, subvalue->u8[i]);
+            }
+            return;
+        }
+    }
+    ds_put_char(s, '0');
+}
index 12a4baa..60bfeca 100644 (file)
@@ -24,8 +24,8 @@
 #include "ofp-errors.h"
 #include "packets.h"
 
-struct cls_rule;
 struct ds;
+struct match;
 
 /* The comment on each of these indicates the member in "union mf_value" used
  * to represent its value. */
@@ -218,7 +218,6 @@ struct mf_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;
     bool writable;              /* May be written by actions? */
@@ -295,7 +294,7 @@ void mf_get_mask(const struct mf_field *, const struct flow_wildcards *,
 
 /* Prerequisites. */
 bool mf_are_prereqs_ok(const struct mf_field *, const struct flow *);
-void mf_force_prereqs(const struct mf_field *, struct cls_rule *);
+void mf_force_prereqs(const struct mf_field *, struct match *);
 
 /* Field values. */
 bool mf_is_value_valid(const struct mf_field *, const union mf_value *value);
@@ -303,24 +302,26 @@ bool mf_is_value_valid(const struct mf_field *, const union mf_value *value);
 void mf_get_value(const struct mf_field *, const struct flow *,
                   union mf_value *value);
 void mf_set_value(const struct mf_field *, const union mf_value *value,
-                  struct cls_rule *);
+                  struct match *);
 void mf_set_flow_value(const struct mf_field *, const union mf_value *value,
                        struct flow *);
 bool mf_is_zero(const struct mf_field *, const struct flow *);
 
-void mf_get(const struct mf_field *, const struct cls_rule *,
+void mf_get(const struct mf_field *, const struct match *,
             union mf_value *value, union mf_value *mask);
 void mf_set(const struct mf_field *,
             const union mf_value *value, const union mf_value *mask,
-            struct cls_rule *);
+            struct match *);
 
-void mf_set_wild(const struct mf_field *, struct cls_rule *);
+void mf_set_wild(const struct mf_field *, struct match *);
 
 void mf_random_value(const struct mf_field *, union mf_value *value);
 
 /* Subfields. */
+void mf_write_subfield_flow(const struct mf_subfield *,
+                            const union mf_subvalue *, struct flow *);
 void mf_write_subfield(const struct mf_subfield *, const union mf_subvalue *,
-                       struct cls_rule *);
+                       struct match *);
 
 void mf_read_subfield(const struct mf_subfield *, const struct flow *,
                       union mf_subvalue *);
@@ -341,5 +342,6 @@ char *mf_parse_value(const struct mf_field *, const char *, union mf_value *);
 void mf_format(const struct mf_field *,
                const union mf_value *value, const union mf_value *mask,
                struct ds *);
+void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s);
 
 #endif /* meta-flow.h */
index 16c1674..17ef160 100644 (file)
@@ -92,14 +92,13 @@ nx_entry_ok(const void *p, unsigned int match_len)
 
 static enum ofperr
 nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
-            uint16_t priority, struct cls_rule *rule,
-            ovs_be64 *cookie, ovs_be64 *cookie_mask)
+            struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask)
 {
     uint32_t header;
 
     assert((cookie != NULL) == (cookie_mask != NULL));
 
-    cls_rule_init_catchall(rule, priority);
+    match_init_catchall(match);
     if (cookie) {
         *cookie = *cookie_mask = htonll(0);
     }
@@ -120,9 +119,9 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
             } else {
                 continue;
             }
-        } else if (!mf_are_prereqs_ok(mf, &rule->flow)) {
+        } else if (!mf_are_prereqs_ok(mf, &match->flow)) {
             error = OFPERR_OFPBMC_BAD_PREREQ;
-        } else if (!mf_is_all_wild(mf, &rule->wc)) {
+        } else if (!mf_is_all_wild(mf, &match->wc)) {
             error = OFPERR_OFPBMC_DUP_FIELD;
         } else if (header != OXM_OF_IN_PORT) {
             unsigned int width = mf->n_bytes;
@@ -133,7 +132,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
                 error = OFPERR_OFPBMC_BAD_VALUE;
             } else if (!NXM_HASMASK(header)) {
                 error = 0;
-                mf_set_value(mf, &value, rule);
+                mf_set_value(mf, &value, match);
             } else {
                 union mf_value mask;
 
@@ -142,7 +141,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
                     error = OFPERR_OFPBMC_BAD_MASK;
                 } else {
                     error = 0;
-                    mf_set(mf, &value, &mask, rule);
+                    mf_set(mf, &value, &mask, match);
                 }
             }
         } else {
@@ -154,7 +153,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
             memcpy(&port_of11, p + 4, sizeof port_of11);
             error = ofputil_port_from_ofp11(port_of11, &port);
             if (!error) {
-                cls_rule_set_in_port(rule, port);
+                match_set_in_port(match, port);
             }
         }
 
@@ -191,7 +190,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
 
 static enum ofperr
 nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
-                uint16_t priority, struct cls_rule *rule,
+                struct match *match,
                 ovs_be64 *cookie, ovs_be64 *cookie_mask)
 {
     uint8_t *p = NULL;
@@ -206,42 +205,36 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
         }
     }
 
-    return nx_pull_raw(p, match_len, strict, priority, rule,
-                       cookie, cookie_mask);
+    return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask);
 }
 
 /* Parses the nx_match formatted match description in 'b' with length
- * 'match_len'.  The results are stored in 'rule', which is initialized with
- * 'priority'.  If 'cookie' and 'cookie_mask' contain valid pointers, then the
- * cookie and mask will be stored in them if a "NXM_NX_COOKIE*" match is
- * defined.  Otherwise, 0 is stored in both.
+ * 'match_len'.  Stores the results in 'match'.  If 'cookie' and 'cookie_mask'
+ * are valid pointers, then stores the cookie and mask in them if 'b' contains
+ * a "NXM_NX_COOKIE*" match.  Otherwise, stores 0 in both.
  *
- * Fails with an error when encountering unknown NXM headers.
+ * Fails with an error upon encountering an unknown NXM header.
  *
  * Returns 0 if successful, otherwise an OpenFlow error code. */
 enum ofperr
-nx_pull_match(struct ofpbuf *b, unsigned int match_len,
-              uint16_t priority, struct cls_rule *rule,
+nx_pull_match(struct ofpbuf *b, unsigned int match_len, struct match *match,
               ovs_be64 *cookie, ovs_be64 *cookie_mask)
 {
-    return nx_pull_match__(b, match_len, true, priority, rule, cookie,
-                           cookie_mask);
+    return nx_pull_match__(b, match_len, true, match, cookie, cookie_mask);
 }
 
-/* Behaves the same as nx_pull_match() with one exception.  Skips over unknown
- * NXM headers instead of failing with an error when they are encountered. */
+/* Behaves the same as nx_pull_match(), but skips over unknown NXM headers,
+ * instead of failing with an error. */
 enum ofperr
 nx_pull_match_loose(struct ofpbuf *b, unsigned int match_len,
-                    uint16_t priority, struct cls_rule *rule,
+                    struct match *match,
                     ovs_be64 *cookie, ovs_be64 *cookie_mask)
 {
-    return nx_pull_match__(b, match_len, false, priority, rule, cookie,
-                           cookie_mask);
+    return nx_pull_match__(b, match_len, false, match, cookie, cookie_mask);
 }
 
 static enum ofperr
-oxm_pull_match__(struct ofpbuf *b, bool strict,
-                 uint16_t priority, struct cls_rule *rule)
+oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
 {
     struct ofp11_match_header *omh = b->data;
     uint8_t *p;
@@ -269,29 +262,27 @@ oxm_pull_match__(struct ofpbuf *b, bool strict,
     }
 
     return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh,
-                       strict, priority, rule, NULL, NULL);
+                       strict, match, NULL, NULL);
 }
 
-/* Parses the oxm formatted match description preceeded by a struct
- * ofp11_match in 'b' with length 'match_len'.  The results are stored in
- * 'rule', which is initialized with 'priority'.
+/* Parses the oxm formatted match description preceeded by a struct ofp11_match
+ * in 'b' with length 'match_len'.  Stores the result in 'match'.
  *
  * Fails with an error when encountering unknown OXM headers.
  *
  * Returns 0 if successful, otherwise an OpenFlow error code. */
 enum ofperr
-oxm_pull_match(struct ofpbuf *b, uint16_t priority, struct cls_rule *rule)
+oxm_pull_match(struct ofpbuf *b, struct match *match)
 {
-    return oxm_pull_match__(b, true, priority, rule);
+    return oxm_pull_match__(b, true, match);
 }
 
 /* Behaves the same as oxm_pull_match() with one exception.  Skips over unknown
  * PXM headers instead of failing with an error when they are encountered. */
 enum ofperr
-oxm_pull_match_loose(struct ofpbuf *b, uint16_t priority,
-                     struct cls_rule *rule)
+oxm_pull_match_loose(struct ofpbuf *b, struct match *match)
 {
-    return oxm_pull_match__(b, false, priority, rule);
+    return oxm_pull_match__(b, false, match);
 }
 \f
 /* nx_put_match() and helpers.
@@ -470,10 +461,10 @@ nxm_put_ipv6(struct ofpbuf *b, uint32_t header,
 }
 
 static void
-nxm_put_frag(struct ofpbuf *b, const struct cls_rule *cr)
+nxm_put_frag(struct ofpbuf *b, const struct match *match)
 {
-    uint8_t nw_frag = cr->flow.nw_frag;
-    uint8_t nw_frag_mask = cr->wc.nw_frag_mask;
+    uint8_t nw_frag = match->flow.nw_frag;
+    uint8_t nw_frag_mask = match->wc.masks.nw_frag;
 
     switch (nw_frag_mask) {
     case 0:
@@ -491,55 +482,53 @@ nxm_put_frag(struct ofpbuf *b, const struct cls_rule *cr)
 }
 
 static void
-nxm_put_ip(struct ofpbuf *b, const struct cls_rule *cr,
+nxm_put_ip(struct ofpbuf *b, const struct match *match,
            uint8_t icmp_proto, uint32_t icmp_type, uint32_t icmp_code,
            bool oxm)
 {
-    const flow_wildcards_t wc = cr->wc.wildcards;
-    const struct flow *flow = &cr->flow;
+    const struct flow *flow = &match->flow;
 
-    nxm_put_frag(b, cr);
+    nxm_put_frag(b, match);
 
-    if (!(wc & FWW_NW_DSCP)) {
+    if (match->wc.masks.nw_tos & IP_DSCP_MASK) {
         nxm_put_8(b, oxm ? OXM_OF_IP_DSCP : NXM_OF_IP_TOS,
                   flow->nw_tos & IP_DSCP_MASK);
     }
 
-    if (!(wc & FWW_NW_ECN)) {
+    if (match->wc.masks.nw_tos & IP_ECN_MASK) {
         nxm_put_8(b, oxm ? OXM_OF_IP_ECN : NXM_NX_IP_ECN,
                   flow->nw_tos & IP_ECN_MASK);
     }
 
-    if (!oxm && !(wc & FWW_NW_TTL)) {
+    if (!oxm && match->wc.masks.nw_ttl) {
         nxm_put_8(b, NXM_NX_IP_TTL, flow->nw_ttl);
     }
 
-    if (!(wc & FWW_NW_PROTO)) {
+    if (match->wc.masks.nw_proto) {
         nxm_put_8(b, oxm ? OXM_OF_IP_PROTO : NXM_OF_IP_PROTO, flow->nw_proto);
 
         if (flow->nw_proto == IPPROTO_TCP) {
             nxm_put_16m(b, oxm ? OXM_OF_TCP_SRC : NXM_OF_TCP_SRC,
-                        flow->tp_src, cr->wc.tp_src_mask);
+                        flow->tp_src, match->wc.masks.tp_src);
             nxm_put_16m(b, oxm ? OXM_OF_TCP_DST : NXM_OF_TCP_DST,
-                        flow->tp_dst, cr->wc.tp_dst_mask);
+                        flow->tp_dst, match->wc.masks.tp_dst);
         } else if (flow->nw_proto == IPPROTO_UDP) {
             nxm_put_16m(b, oxm ? OXM_OF_UDP_SRC : NXM_OF_UDP_SRC,
-                        flow->tp_src, cr->wc.tp_src_mask);
+                        flow->tp_src, match->wc.masks.tp_src);
             nxm_put_16m(b, oxm ? OXM_OF_UDP_DST : NXM_OF_UDP_DST,
-                        flow->tp_dst, cr->wc.tp_dst_mask);
+                        flow->tp_dst, match->wc.masks.tp_dst);
         } else if (flow->nw_proto == icmp_proto) {
-            if (cr->wc.tp_src_mask) {
+            if (match->wc.masks.tp_src) {
                 nxm_put_8(b, icmp_type, ntohs(flow->tp_src));
             }
-            if (cr->wc.tp_dst_mask) {
+            if (match->wc.masks.tp_dst) {
                 nxm_put_8(b, icmp_code, ntohs(flow->tp_dst));
             }
         }
     }
 }
 
-/* Appends to 'b' the nx_match format that expresses 'cr' (except for
- * 'cr->priority', because priority is not part of nx_match).  For Flow Mod and
+/* Appends to 'b' the nx_match format that expresses 'match'.  For Flow Mod and
  * Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
  * Otherwise, 'cookie_mask' should be zero.
  *
@@ -547,22 +536,21 @@ nxm_put_ip(struct ofpbuf *b, const struct cls_rule *cr,
  *
  * Returns the number of bytes appended to 'b', excluding padding.
  *
- * If 'cr' is a catch-all rule that matches every packet, then this function
+ * If 'match' is a catch-all rule that matches every packet, then this function
  * appends nothing to 'b' and returns 0. */
 static int
-nx_put_raw(struct ofpbuf *b, bool oxm, const struct cls_rule *cr,
+nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
            ovs_be64 cookie, ovs_be64 cookie_mask)
 {
-    const flow_wildcards_t wc = cr->wc.wildcards;
-    const struct flow *flow = &cr->flow;
+    const struct flow *flow = &match->flow;
     const size_t start_len = b->size;
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
 
     /* Metadata. */
-    if (!(wc & FWW_IN_PORT)) {
+    if (match->wc.masks.in_port) {
         uint16_t in_port = flow->in_port;
         if (oxm) {
             nxm_put_32(b, OXM_OF_IN_PORT, ofputil_port_to_ofp11(in_port));
@@ -573,18 +561,18 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct cls_rule *cr,
 
     /* Ethernet. */
     nxm_put_eth_masked(b, oxm ? OXM_OF_ETH_SRC : NXM_OF_ETH_SRC,
-                       flow->dl_src, cr->wc.dl_src_mask);
+                       flow->dl_src, match->wc.masks.dl_src);
     nxm_put_eth_masked(b, oxm ? OXM_OF_ETH_DST : NXM_OF_ETH_DST,
-                       flow->dl_dst, cr->wc.dl_dst_mask);
-    if (!(wc & FWW_DL_TYPE)) {
-        nxm_put_16(b, oxm ? OXM_OF_ETH_TYPE : NXM_OF_ETH_TYPE,
-                   ofputil_dl_type_to_openflow(flow->dl_type));
-    }
+                       flow->dl_dst, match->wc.masks.dl_dst);
+    nxm_put_16m(b, oxm ? OXM_OF_ETH_TYPE : NXM_OF_ETH_TYPE,
+                ofputil_dl_type_to_openflow(flow->dl_type),
+                match->wc.masks.dl_type);
 
     /* 802.1Q. */
     if (oxm) {
-        ovs_be16 vid = flow->vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
-        ovs_be16 mask = cr->wc.vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI);
+        ovs_be16 VID_CFI_MASK = htons(VLAN_VID_MASK | VLAN_CFI);
+        ovs_be16 vid = flow->vlan_tci & VID_CFI_MASK;
+        ovs_be16 mask = match->wc.masks.vlan_tci & VID_CFI_MASK;
 
         if (mask == htons(VLAN_VID_MASK | VLAN_CFI)) {
             nxm_put_16(b, OXM_OF_VLAN_VID, vid);
@@ -592,78 +580,79 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct cls_rule *cr,
             nxm_put_16m(b, OXM_OF_VLAN_VID, vid, mask);
         }
 
-        if (vid && vlan_tci_to_pcp(cr->wc.vlan_tci_mask)) {
+        if (vid && vlan_tci_to_pcp(match->wc.masks.vlan_tci)) {
             nxm_put_8(b, OXM_OF_VLAN_PCP, vlan_tci_to_pcp(flow->vlan_tci));
         }
 
     } else {
-        nxm_put_16m(b, NXM_OF_VLAN_TCI, flow->vlan_tci, cr->wc.vlan_tci_mask);
+        nxm_put_16m(b, NXM_OF_VLAN_TCI, flow->vlan_tci,
+                    match->wc.masks.vlan_tci);
     }
 
     /* L3. */
-    if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IP)) {
+    if (flow->dl_type == htons(ETH_TYPE_IP)) {
         /* IP. */
         nxm_put_32m(b, oxm ? OXM_OF_IPV4_SRC : NXM_OF_IP_SRC,
-                    flow->nw_src, cr->wc.nw_src_mask);
+                    flow->nw_src, match->wc.masks.nw_src);
         nxm_put_32m(b, oxm ? OXM_OF_IPV4_DST : NXM_OF_IP_DST,
-                    flow->nw_dst, cr->wc.nw_dst_mask);
-        nxm_put_ip(b, cr, IPPROTO_ICMP,
+                    flow->nw_dst, match->wc.masks.nw_dst);
+        nxm_put_ip(b, match, IPPROTO_ICMP,
                    oxm ? OXM_OF_ICMPV4_TYPE : NXM_OF_ICMP_TYPE,
                    oxm ? OXM_OF_ICMPV4_CODE : NXM_OF_ICMP_CODE, oxm);
-    } else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IPV6)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         /* IPv6. */
         nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_SRC : NXM_NX_IPV6_SRC,
-                     &flow->ipv6_src, &cr->wc.ipv6_src_mask);
+                     &flow->ipv6_src, &match->wc.masks.ipv6_src);
         nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_DST : NXM_NX_IPV6_DST,
-                     &flow->ipv6_dst, &cr->wc.ipv6_dst_mask);
-        nxm_put_ip(b, cr, IPPROTO_ICMPV6,
+                     &flow->ipv6_dst, &match->wc.masks.ipv6_dst);
+        nxm_put_ip(b, match, IPPROTO_ICMPV6,
                    oxm ? OXM_OF_ICMPV6_TYPE : NXM_NX_ICMPV6_TYPE,
                    oxm ? OXM_OF_ICMPV6_CODE : NXM_NX_ICMPV6_CODE, oxm);
 
         nxm_put_32m(b, oxm ? OXM_OF_IPV6_FLABEL : NXM_NX_IPV6_LABEL,
-                    flow->ipv6_label, cr->wc.ipv6_label_mask);
+                    flow->ipv6_label, match->wc.masks.ipv6_label);
 
         if (flow->nw_proto == IPPROTO_ICMPV6
             && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
                 flow->tp_src == htons(ND_NEIGHBOR_ADVERT))) {
             nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_ND_TARGET : NXM_NX_ND_TARGET,
-                         &flow->nd_target, &cr->wc.nd_target_mask);
+                         &flow->nd_target, &match->wc.masks.nd_target);
             if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
                 nxm_put_eth_masked(b, oxm ? OXM_OF_IPV6_ND_SLL : NXM_NX_ND_SLL,
-                                   flow->arp_sha, cr->wc.arp_sha_mask);
+                                   flow->arp_sha, match->wc.masks.arp_sha);
             }
             if (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
                 nxm_put_eth_masked(b, oxm ? OXM_OF_IPV6_ND_TLL : NXM_NX_ND_TLL,
-                                   flow->arp_tha, cr->wc.arp_tha_mask);
+                                   flow->arp_tha, match->wc.masks.arp_tha);
             }
         }
-    } else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
         /* ARP. */
-        if (!(wc & FWW_NW_PROTO)) {
+        if (match->wc.masks.nw_proto) {
             nxm_put_16(b, oxm ? OXM_OF_ARP_OP : NXM_OF_ARP_OP,
                        htons(flow->nw_proto));
         }
         nxm_put_32m(b, oxm ? OXM_OF_ARP_SPA : NXM_OF_ARP_SPA,
-                    flow->nw_src, cr->wc.nw_src_mask);
+                    flow->nw_src, match->wc.masks.nw_src);
         nxm_put_32m(b, oxm ? OXM_OF_ARP_TPA : NXM_OF_ARP_TPA,
-                    flow->nw_dst, cr->wc.nw_dst_mask);
+                    flow->nw_dst, match->wc.masks.nw_dst);
         nxm_put_eth_masked(b, oxm ? OXM_OF_ARP_SHA : NXM_NX_ARP_SHA,
-                           flow->arp_sha, cr->wc.arp_sha_mask);
+                           flow->arp_sha, match->wc.masks.arp_sha);
         nxm_put_eth_masked(b, oxm ? OXM_OF_ARP_THA : NXM_NX_ARP_THA,
-                           flow->arp_tha, cr->wc.arp_tha_mask);
+                           flow->arp_tha, match->wc.masks.arp_tha);
     }
 
     /* Tunnel ID. */
-    nxm_put_64m(b, NXM_NX_TUN_ID, flow->tun_id, cr->wc.tun_id_mask);
+    nxm_put_64m(b, NXM_NX_TUN_ID, flow->tun_id, match->wc.masks.tun_id);
 
     /* Registers. */
     for (i = 0; i < FLOW_N_REGS; i++) {
         nxm_put_32m(b, NXM_NX_REG(i),
-                    htonl(flow->regs[i]), htonl(cr->wc.reg_masks[i]));
+                    htonl(flow->regs[i]), htonl(match->wc.masks.regs[i]));
     }
 
     /* OpenFlow 1.1+ Metadata. */
-    nxm_put_64m(b, OXM_OF_METADATA, flow->metadata, cr->wc.metadata_mask);
+    nxm_put_64m(b, OXM_OF_METADATA, flow->metadata, match->wc.masks.metadata);
 
     /* Cookie. */
     nxm_put_64m(b, NXM_NX_COOKIE, cookie, cookie_mask);
@@ -672,8 +661,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct cls_rule *cr,
     return match_len;
 }
 
-/* Appends to 'b' the nx_match format that expresses 'cr' (except for
- * 'cr->priority', because priority is not part of nx_match), plus enough zero
+/* Appends to 'b' the nx_match format that expresses 'match', plus enough zero
  * bytes to pad the nx_match out to a multiple of 8.  For Flow Mod and Flow
  * Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
  * Otherwise, 'cookie_mask' should be zero.
@@ -684,10 +672,10 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct cls_rule *cr,
  * value can be zero if it appended nothing at all to 'b' (which happens if
  * 'cr' is a catch-all rule that matches every packet). */
 int
-nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
+nx_put_match(struct ofpbuf *b, const struct match *match,
              ovs_be64 cookie, ovs_be64 cookie_mask)
 {
-    int match_len = nx_put_raw(b, false, cr, cookie, cookie_mask);
+    int match_len = nx_put_raw(b, false, match, cookie, cookie_mask);
 
     ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
     return match_len;
@@ -695,16 +683,15 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
 
 
 /* Appends to 'b' an struct ofp11_match_header followed by the oxm format that
- * expresses 'cr' (except for 'cr->priority', because priority is not part of
- * nx_match), plus enough zero bytes to pad the data appended out to a multiple
- * of 8.
+ * expresses 'cr', plus enough zero bytes to pad the data appended out to a
+ * multiple of 8.
  *
  * This function can cause 'b''s data to be reallocated.
  *
  * Returns the number of bytes appended to 'b', excluding the padding.  Never
  * returns zero. */
 int
-oxm_put_match(struct ofpbuf *b, const struct cls_rule *cr)
+oxm_put_match(struct ofpbuf *b, const struct match *match)
 {
     int match_len;
     struct ofp11_match_header *omh;
@@ -712,7 +699,7 @@ oxm_put_match(struct ofpbuf *b, const struct cls_rule *cr)
     ovs_be64 cookie = htonll(0), cookie_mask = htonll(0);
 
     ofpbuf_put_uninit(b, sizeof *omh);
-    match_len = nx_put_raw(b, true, cr, cookie, cookie_mask) + sizeof *omh;
+    match_len = nx_put_raw(b, true, match, cookie, cookie_mask) + sizeof *omh;
     ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
 
     omh = (struct ofp11_match_header *)((char *)b->data + start_len);
@@ -1001,8 +988,8 @@ void
 nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s)
 {
     const char *full_s = s;
+    uint64_t value = strtoull(s, (char **) &s, 0);
 
-    load->value = strtoull(s, (char **) &s, 0);
     if (strncmp(s, "->", 2)) {
         ovs_fatal(0, "%s: missing `->' following value", full_s);
     }
@@ -1012,10 +999,13 @@ nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s)
         ovs_fatal(0, "%s: trailing garbage following destination", full_s);
     }
 
-    if (load->dst.n_bits < 64 && (load->value >> load->dst.n_bits) != 0) {
+    if (load->dst.n_bits < 64 && (value >> load->dst.n_bits) != 0) {
         ovs_fatal(0, "%s: value %"PRIu64" does not fit into %d bits",
-                  full_s, load->value, load->dst.n_bits);
+                  full_s, value, load->dst.n_bits);
     }
+
+    load->subvalue.be64[0] = htonll(0);
+    load->subvalue.be64[1] = htonll(value);
 }
 \f
 /* nxm_format_reg_move(), nxm_format_reg_load(). */
@@ -1032,7 +1022,9 @@ nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s)
 void
 nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
 {
-    ds_put_format(s, "load:%#"PRIx64"->", load->value);
+    ds_put_cstr(s, "load:");
+    mf_format_subvalue(&load->subvalue, s);
+    ds_put_cstr(s, "->");
     mf_format_subfield(&load->dst, s);
 }
 \f
@@ -1063,11 +1055,12 @@ nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl,
     load->dst.field = mf_from_nxm_header(ntohl(narl->dst));
     load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits);
     load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits);
-    load->value = ntohll(narl->value);
+    load->subvalue.be64[1] = narl->value;
 
     /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in
      * narl->value. */
-    if (load->dst.n_bits < 64 && load->value >> load->dst.n_bits) {
+    if (load->dst.n_bits < 64 &&
+        ntohll(narl->value) >> load->dst.n_bits) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
@@ -1116,7 +1109,7 @@ nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
     narl = ofputil_put_NXAST_REG_LOAD(openflow);
     narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits);
     narl->dst = htonl(load->dst.field->nxm_header);
-    narl->value = htonll(load->value);
+    narl->value = load->subvalue.be64[1];
 }
 \f
 /* nxm_execute_reg_move(), nxm_execute_reg_load(). */
@@ -1139,20 +1132,18 @@ nxm_execute_reg_move(const struct ofpact_reg_move *move,
 void
 nxm_execute_reg_load(const struct ofpact_reg_load *load, struct flow *flow)
 {
-    nxm_reg_load(&load->dst, load->value, flow);
+    mf_write_subfield_flow(&load->dst, &load->subvalue, flow);
 }
 
 void
 nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
              struct flow *flow)
 {
-    union mf_value dst_value;
-    union mf_value src_value;
+    union mf_subvalue src_subvalue;
+    ovs_be64 src_data_be = htonll(src_data);
 
-    mf_get_value(dst->field, flow, &dst_value);
-    src_value.be64 = htonll(src_data);
-    bitwise_copy(&src_value, sizeof src_value.be64, 0,
-                 &dst_value, dst->field->n_bytes, dst->ofs,
-                 dst->n_bits);
-    mf_set_flow_value(dst->field, &dst_value, flow);
+    bitwise_copy(&src_data_be, sizeof src_data_be, 0,
+                 &src_subvalue, sizeof src_subvalue, 0,
+                 sizeof src_data_be * 8);
+    mf_write_subfield_flow(dst, &src_subvalue, flow);
 }
index 3bfeeb7..f504ad0 100644 (file)
 #include "flow.h"
 #include "ofp-errors.h"
 #include "openvswitch/types.h"
-#include "ofp-errors.h"
 
-struct cls_rule;
 struct ds;
-struct flow;
+struct match;
 struct mf_subfield;
 struct ofpact_reg_move;
 struct ofpact_reg_load;
@@ -41,19 +39,16 @@ struct nx_action_reg_move;
  */
 
 enum ofperr nx_pull_match(struct ofpbuf *, unsigned int match_len,
-                          uint16_t priority, struct cls_rule *,
+                          struct match *,
                           ovs_be64 *cookie, ovs_be64 *cookie_mask);
 enum ofperr nx_pull_match_loose(struct ofpbuf *, unsigned int match_len,
-                                uint16_t priority,
-                                struct cls_rule *, ovs_be64 *cookie,
+                                struct match *, ovs_be64 *cookie,
                                 ovs_be64 *cookie_mask);
-enum ofperr oxm_pull_match(struct ofpbuf *, uint16_t priority,
-                           struct cls_rule *);
-enum ofperr oxm_pull_match_loose(struct ofpbuf *, uint16_t priority,
-                                 struct cls_rule *);
-int nx_put_match(struct ofpbuf *, const struct cls_rule *,
+enum ofperr oxm_pull_match(struct ofpbuf *, struct match *);
+enum ofperr oxm_pull_match_loose(struct ofpbuf *, struct match *);
+int nx_put_match(struct ofpbuf *, const struct match *,
                  ovs_be64 cookie, ovs_be64 cookie_mask);
-int oxm_put_match(struct ofpbuf *, const struct cls_rule *);
+int oxm_put_match(struct ofpbuf *, const struct match *);
 
 char *nx_match_to_string(const uint8_t *, unsigned int match_len);
 char *oxm_match_to_string(const uint8_t *, unsigned int match_len);
index 898455e..210f4ce 100644 (file)
@@ -283,6 +283,7 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
     case OFPUTIL_ACTION_INVALID:
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
 #define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
 #include "ofp-util.def"
         NOT_REACHED();
 
@@ -397,6 +398,7 @@ ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out)
     switch (code) {
     case OFPUTIL_ACTION_INVALID:
 #define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
 #include "ofp-util.def"
         NOT_REACHED();
 
@@ -659,6 +661,7 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
     switch (code) {
     case OFPUTIL_ACTION_INVALID:
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
 #include "ofp-util.def"
         NOT_REACHED();
 
@@ -857,7 +860,7 @@ decode_openflow11_instructions(const struct ofp11_instruction insts[],
         }
 
         if (out[type]) {
-            return OFPERR_NXBIC_DUP_TYPE;
+            return OFPERR_OFPIT_BAD_INSTRUCTION;
         }
         out[type] = inst;
     }
index 4fc9094..75ccc26 100644 (file)
@@ -271,11 +271,11 @@ struct ofpact_reg_move {
 
 /* OFPACT_REG_LOAD.
  *
- * Used for NXAST_REG_LOAD. */
+ * Used for NXAST_REG_LOAD, OFPAT12_SET_FIELD. */
 struct ofpact_reg_load {
     struct ofpact ofpact;
     struct mf_subfield dst;
-    uint64_t value;
+    union mf_subvalue subvalue; /* Least-significant bits are used. */
 };
 
 /* OFPACT_SET_TUNNEL.
index 2f28a42..a883e20 100644 (file)
@@ -45,6 +45,19 @@ struct ofpbuf;
 
 #define OFPERR_OFS (1 << 30)
 
+/* OpenFlow error codes.
+ *
+ * The comments below are parsed by the extract-ofp-errors program at build
+ * time and used to determine the mapping between "enum ofperr" constants and
+ * error type/code values used in the OpenFlow protocol:
+ *
+ *   - The first part of each comment specifies OpenFlow type/code for each
+ *     protocol that supports the error.
+ *
+ *   - Additional text is a human-readable description of the meaning of each
+ *     error, used to explain the error to the user.  Any text enclosed in
+ *     square brackets is omitted; this can be used to explain rationale for
+ *     choice of error codes in the case where this is desirable. */
 enum ofperr {
 /* Expected duplications. */
 
@@ -99,13 +112,19 @@ enum ofperr {
     /* OF1.0+(1,8).  Specified buffer does not exist. */
     OFPERR_OFPBRC_BUFFER_UNKNOWN,
 
-    /* OF1.1+(1,9).  Specified table-id invalid or does not exist. */
+    /* NX1.0(1,512), OF1.1+(1,9).  Specified table-id invalid or does not exist.
+     * [ A non-standard error (1,512), formerly OFPERR_NXBRC_BAD_TABLE_ID,
+     *   is used for OpenFlow 1.0 as there seems to be no appropriste error
+     *   code defined the specification. ] */
     OFPERR_OFPBRC_BAD_TABLE_ID,
 
     /* OF1.2+(1,10).  Denied because controller is slave. */
     OFPERR_OFPBRC_IS_SLAVE,
 
-    /* OF1.2+(1,11).  Invalid port. */
+    /* NX1.0(1,514), NX1.1(1,514), OF1.2+(1,11).  Invalid port.
+     * [ A non-standard error (1,514), formerly
+     *   OFPERR_NXBRC_BAD_IN_PORT is used for OpenFlow 1.0 and 1.1 as there
+     *   seems to be no appropriste error code defined the specifications. ] */
     OFPERR_OFPBRC_BAD_PORT,
 
     /* OF1.2+(1,12).  Invalid packet in packet-out. */
@@ -118,12 +137,6 @@ enum ofperr {
      * nxm_hasmask or nxm_length or both, is invalid or not implemented. */
     OFPERR_NXBRC_NXM_BAD_TYPE,
 
-    /* NX1.0+(1,512).  A request specified a nonexistent table ID. */
-    OFPERR_NXBRC_BAD_TABLE_ID,
-
-    /* NX1.0+(1,514).  The in_port in an ofp_packet_out request is invalid. */
-    OFPERR_NXBRC_BAD_IN_PORT,
-
     /* NX1.0+(1,515).  Must-be-zero field had nonzero value. */
     OFPERR_NXBRC_MUST_BE_ZERO,
 
@@ -238,9 +251,6 @@ enum ofperr {
     /* OF1.2+(3,8).  Permissions error. */
     OFPERR_OFPBIC_EPERM,
 
-    /* NX1.1+(3,256).  Duplicate instruction type in set of instructions. */
-    OFPERR_NXBIC_DUP_TYPE,
-
 /* ## --------------- ## */
 /* ## OFPET_BAD_MATCH ## */
 /* ## --------------- ## */
@@ -271,7 +281,7 @@ enum ofperr {
     /* OF1.1+(4,6).  Unsupported field in the match. */
     OFPERR_OFPBMC_BAD_FIELD,
 
-    /* NX1.0(1,258), NX1.1(1,258), OF1.2+(4,7).  Unsupported value in a match
+    /* NX1.0(1,258), OF1.1+(4,7).  Unsupported value in a match
      * field. */
     OFPERR_OFPBMC_BAD_VALUE,
 
@@ -340,9 +350,6 @@ enum ofperr {
      * extension is enabled. */
     OFPERR_NXFMFC_BAD_TABLE_ID,
 
-    /* NX1.0+(3,258).  'out_group' specified but groups not yet supported. */
-    OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED,
-
 /* ## ---------------------- ## */
 /* ## OFPET_GROUP_MOD_FAILED ## */
 /* ## ---------------------- ## */
index 0d904b1..165a36e 100644 (file)
@@ -350,6 +350,12 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
         break;
 
+    case OFPUTIL_OFPAT12_SET_FIELD:
+        NOT_REACHED();  /* This will be implemented by later patch and
+                         * enabled using a non-NULL name in
+                         * OFPAT12_ACTION(OFPAT12_SET_FIELD, ...) */
+        break;
+
     case OFPUTIL_OFPAT10_STRIP_VLAN:
         ofpact_put_STRIP_VLAN(ofpacts);
         break;
@@ -554,7 +560,7 @@ ofp_fatal(const char *flow, bool verbose, const char *format, ...)
 }
 
 static void
-parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule)
+parse_field(const struct mf_field *mf, const char *s, struct match *match)
 {
     union mf_value value, mask;
     char *error;
@@ -564,7 +570,7 @@ parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule)
         ovs_fatal(0, "%s", error);
     }
 
-    mf_set(mf, &value, &mask, rule);
+    mf_set(mf, &value, &mask, match);
 }
 
 /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
@@ -620,7 +626,8 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         NOT_REACHED();
     }
 
-    cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
+    match_init_catchall(&fm->match);
+    fm->priority = OFP_DEFAULT_PRIORITY;
     fm->cookie = htonll(0);
     fm->cookie_mask = htonll(0);
     if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
@@ -655,9 +662,9 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         const struct protocol *p;
 
         if (parse_protocol(name, &p)) {
-            cls_rule_set_dl_type(&fm->cr, htons(p->dl_type));
+            match_set_dl_type(&fm->match, htons(p->dl_type));
             if (p->nw_proto) {
-                cls_rule_set_nw_proto(&fm->cr, p->nw_proto);
+                match_set_nw_proto(&fm->match, p->nw_proto);
             }
         } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) {
             fm->flags |= OFPFF_SEND_FLOW_REM;
@@ -676,7 +683,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             } else if (!strcmp(name, "out_port")) {
                 fm->out_port = atoi(value);
             } else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
-                fm->cr.priority = str_to_u16(value, name);
+                fm->priority = str_to_u16(value, name);
             } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
                 fm->idle_timeout = str_to_u16(value, name);
             } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
@@ -702,7 +709,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                     fm->new_cookie = htonll(str_to_u64(value));
                 }
             } else if (mf_from_name(name)) {
-                parse_field(mf_from_name(name), value, &fm->cr);
+                parse_field(mf_from_name(name), value, &fm->match);
             } else if (!strcmp(name, "duration")
                        || !strcmp(name, "n_packets")
                        || !strcmp(name, "n_bytes")) {
@@ -725,7 +732,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         struct ofpbuf ofpacts;
 
         ofpbuf_init(&ofpacts, 32);
-        str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts);
+        str_to_ofpacts(&fm->match.flow, act_str, &ofpacts);
         fm->ofpacts_len = ofpacts.size;
         fm->ofpacts = ofpbuf_steal_data(&ofpacts);
     } else {
@@ -753,7 +760,7 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
                   | NXFMF_OWN | NXFMF_ACTIONS);
     fmr->out_port = OFPP_NONE;
     fmr->table_id = 0xff;
-    cls_rule_init_catchall(&fmr->match, 0);
+    match_init_catchall(&fmr->match);
 
     for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
          name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
@@ -772,9 +779,9 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
         } else if (!strcmp(name, "!own")) {
             fmr->flags &= ~NXFMF_OWN;
         } else if (parse_protocol(name, &p)) {
-            cls_rule_set_dl_type(&fmr->match, htons(p->dl_type));
+            match_set_dl_type(&fmr->match, htons(p->dl_type));
             if (p->nw_proto) {
-                cls_rule_set_nw_proto(&fmr->match, p->nw_proto);
+                match_set_nw_proto(&fmr->match, p->nw_proto);
             }
         } else {
             char *value;
@@ -817,15 +824,15 @@ void
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
                        uint16_t command, bool verbose)
 {
-    struct cls_rule rule_copy;
+    struct match match_copy;
 
     parse_ofp_str(fm, command, string, verbose);
 
-    /* Normalize a copy of the rule.  This ensures that non-normalized flows
+    /* Normalize a copy of the match.  This ensures that non-normalized flows
      * get logged but doesn't affect what gets sent to the switch, so that the
      * switch can do whatever it likes with the flow. */
-    rule_copy = fm->cr;
-    ofputil_normalize_rule(&rule_copy);
+    match_copy = fm->match;
+    ofputil_normalize_match(&match_copy);
 }
 
 void
@@ -867,7 +874,7 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
     fsr->aggregate = aggregate;
     fsr->cookie = fm.cookie;
     fsr->cookie_mask = fm.cookie_mask;
-    fsr->match = fm.cr;
+    fsr->match = fm.match;
     fsr->out_port = fm.out_port;
     fsr->table_id = fm.table_id;
 }
index 99e6456..6789625 100644 (file)
@@ -457,6 +457,7 @@ ofputil_action_bitmap_to_name(uint32_t bit)
     case OFPUTIL_A_SET_NW_TOS:     return "SET_NW_TOS";
     case OFPUTIL_A_SET_TP_SRC:     return "SET_TP_SRC";
     case OFPUTIL_A_SET_TP_DST:     return "SET_TP_DST";
+    case OFPUTIL_A_SET_FIELD:      return "SET_FIELD";
     case OFPUTIL_A_ENQUEUE:        return "ENQUEUE";
     case OFPUTIL_A_COPY_TTL_OUT:   return "COPY_TTL_OUT";
     case OFPUTIL_A_COPY_TTL_IN:    return "COPY_TTL_IN";
@@ -733,9 +734,9 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
         /* nx_match_to_string() doesn't print priority. */
         need_priority = true;
     } else {
-        cls_rule_format(&fm.cr, s);
+        match_format(&fm.match, s, fm.priority);
 
-        /* cls_rule_format() does print priority. */
+        /* match_format() does print priority. */
         need_priority = false;
     }
 
@@ -755,8 +756,8 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     if (fm.hard_timeout != OFP_FLOW_PERMANENT) {
         ds_put_format(s, "hard:%"PRIu16" ", fm.hard_timeout);
     }
-    if (fm.cr.priority != OFP_DEFAULT_PRIORITY && need_priority) {
-        ds_put_format(s, "pri:%"PRIu16" ", fm.cr.priority);
+    if (fm.priority != OFP_DEFAULT_PRIORITY && need_priority) {
+        ds_put_format(s, "pri:%"PRIu16" ", fm.priority);
     }
     if (fm.buffer_id != UINT32_MAX) {
         ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
@@ -837,7 +838,7 @@ ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
     }
 
     ds_put_char(string, ' ');
-    cls_rule_format(&fr.rule, string);
+    match_format(&fr.match, string, fr.priority);
 
     ds_put_format(string, " reason=%s",
                   ofp_flow_removed_reason_to_string(fr.reason));
@@ -983,12 +984,8 @@ ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
         ofputil_format_port(fsr.out_port, string);
     }
 
-    /* A flow stats request doesn't include a priority, but cls_rule_format()
-     * will print one unless it is OFP_DEFAULT_PRIORITY. */
-    fsr.match.priority = OFP_DEFAULT_PRIORITY;
-
     ds_put_char(string, ' ');
-    cls_rule_format(&fsr.match, string);
+    match_format(&fsr.match, string, OFP_DEFAULT_PRIORITY);
 }
 
 void
@@ -1014,7 +1011,7 @@ ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs)
         ds_put_format(string, "hard_age=%d, ", fs->hard_age);
     }
 
-    cls_rule_format(&fs->rule, string);
+    match_format(&fs->match, string, fs->priority);
     if (string->string[string->length - 1] != ' ') {
         ds_put_char(string, ' ');
     }
@@ -1600,7 +1597,7 @@ ofp_print_nxst_flow_monitor_request(struct ds *string,
         }
 
         ds_put_char(string, ' ');
-        cls_rule_format(&request.match, string);
+        match_format(&request.match, string, OFP_DEFAULT_PRIORITY);
         ds_chomp(string, ' ');
     }
 }
@@ -1617,7 +1614,7 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     for (;;) {
         struct ofputil_flow_update update;
-        struct cls_rule match;
+        struct match match;
         int retval;
 
         update.match = &match;
@@ -1662,7 +1659,7 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
         ds_put_format(string, " cookie=%#"PRIx64, ntohll(update.cookie));
 
         ds_put_char(string, ' ');
-        cls_rule_format(update.match, string);
+        match_format(update.match, string, OFP_DEFAULT_PRIORITY);
 
         if (update.ofpacts_len) {
             if (string->string[string->length - 1] != ' ') {
index ce9bb74..1976b94 100644 (file)
@@ -77,102 +77,86 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
     return 32 - ip_count_cidr_bits(netmask);
 }
 
-/* A list of the FWW_* and OFPFW10_ bits that have the same value, meaning, and
- * name. */
-#define WC_INVARIANT_LIST \
-    WC_INVARIANT_BIT(IN_PORT) \
-    WC_INVARIANT_BIT(DL_TYPE) \
-    WC_INVARIANT_BIT(NW_PROTO)
-
-/* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST)
- * actually have the same names and values. */
-#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW10_##NAME);
-    WC_INVARIANT_LIST
-#undef WC_INVARIANT_BIT
-
-/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all
- * OR'd together. */
-static const flow_wildcards_t WC_INVARIANTS = 0
-#define WC_INVARIANT_BIT(NAME) | FWW_##NAME
-    WC_INVARIANT_LIST
-#undef WC_INVARIANT_BIT
-;
-
 /* Converts the OpenFlow 1.0 wildcards in 'ofpfw' (OFPFW10_*) into a
- * flow_wildcards in 'wc' for use in struct cls_rule.  It is the caller's
+ * flow_wildcards in 'wc' for use in struct match.  It is the caller's
  * responsibility to handle the special case where the flow match's dl_vlan is
  * set to OFP_VLAN_NONE. */
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
 
-    /* Initialize most of rule->wc. */
+    /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
-    wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
 
-    /* Wildcard fields that aren't defined by ofp10_match. */
-    wc->wildcards |= FWW_NW_ECN | FWW_NW_TTL;
+    if (!(ofpfw & OFPFW10_IN_PORT)) {
+        wc->masks.in_port = UINT16_MAX;
+    }
 
-    if (ofpfw & OFPFW10_NW_TOS) {
-        /* OpenFlow 1.0 defines a TOS wildcard, but it's much later in
-         * the enum than we can use. */
-        wc->wildcards |= FWW_NW_DSCP;
+    if (!(ofpfw & OFPFW10_NW_TOS)) {
+        wc->masks.nw_tos |= IP_DSCP_MASK;
     }
 
-    wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_SRC_SHIFT);
-    wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_DST_SHIFT);
+    if (!(ofpfw & OFPFW10_NW_PROTO)) {
+        wc->masks.nw_proto = UINT8_MAX;
+    }
+    wc->masks.nw_src = ofputil_wcbits_to_netmask(ofpfw
+                                                 >> OFPFW10_NW_SRC_SHIFT);
+    wc->masks.nw_dst = ofputil_wcbits_to_netmask(ofpfw
+                                                 >> OFPFW10_NW_DST_SHIFT);
 
     if (!(ofpfw & OFPFW10_TP_SRC)) {
-        wc->tp_src_mask = htons(UINT16_MAX);
+        wc->masks.tp_src = htons(UINT16_MAX);
     }
     if (!(ofpfw & OFPFW10_TP_DST)) {
-        wc->tp_dst_mask = htons(UINT16_MAX);
+        wc->masks.tp_dst = htons(UINT16_MAX);
     }
 
     if (!(ofpfw & OFPFW10_DL_SRC)) {
-        memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN);
+        memset(wc->masks.dl_src, 0xff, ETH_ADDR_LEN);
     }
     if (!(ofpfw & OFPFW10_DL_DST)) {
-        memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN);
+        memset(wc->masks.dl_dst, 0xff, ETH_ADDR_LEN);
+    }
+    if (!(ofpfw & OFPFW10_DL_TYPE)) {
+        wc->masks.dl_type = htons(UINT16_MAX);
     }
 
     /* VLAN TCI mask. */
     if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) {
-        wc->vlan_tci_mask |= htons(VLAN_PCP_MASK | VLAN_CFI);
+        wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
     }
     if (!(ofpfw & OFPFW10_DL_VLAN)) {
-        wc->vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
+        wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
     }
 }
 
-/* Converts the ofp10_match in 'match' into a cls_rule in 'rule', with the
- * given 'priority'. */
+/* Converts the ofp10_match in 'ofmatch' into a struct match in 'match'. */
 void
-ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *match,
-                                  unsigned int priority, struct cls_rule *rule)
-{
-    uint32_t ofpfw = ntohl(match->wildcards) & OFPFW10_ALL;
-
-    /* Initialize rule->priority, rule->wc. */
-    rule->priority = !ofpfw ? UINT16_MAX : priority;
-    ofputil_wildcard_from_ofpfw10(ofpfw, &rule->wc);
-
-    /* Initialize most of rule->flow. */
-    rule->flow.nw_src = match->nw_src;
-    rule->flow.nw_dst = match->nw_dst;
-    rule->flow.in_port = ntohs(match->in_port);
-    rule->flow.dl_type = ofputil_dl_type_from_openflow(match->dl_type);
-    rule->flow.tp_src = match->tp_src;
-    rule->flow.tp_dst = match->tp_dst;
-    memcpy(rule->flow.dl_src, match->dl_src, ETH_ADDR_LEN);
-    memcpy(rule->flow.dl_dst, match->dl_dst, ETH_ADDR_LEN);
-    rule->flow.nw_tos = match->nw_tos & IP_DSCP_MASK;
-    rule->flow.nw_proto = match->nw_proto;
+ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
+                               struct match *match)
+{
+    uint32_t ofpfw = ntohl(ofmatch->wildcards) & OFPFW10_ALL;
+
+    /* Initialize match->wc. */
+    memset(match->flow.zeros, 0, sizeof match->flow.zeros);
+    ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc);
+
+    /* Initialize most of match->flow. */
+    match->flow.nw_src = ofmatch->nw_src;
+    match->flow.nw_dst = ofmatch->nw_dst;
+    match->flow.in_port = ntohs(ofmatch->in_port);
+    match->flow.dl_type = ofputil_dl_type_from_openflow(ofmatch->dl_type);
+    match->flow.tp_src = ofmatch->tp_src;
+    match->flow.tp_dst = ofmatch->tp_dst;
+    memcpy(match->flow.dl_src, ofmatch->dl_src, ETH_ADDR_LEN);
+    memcpy(match->flow.dl_dst, ofmatch->dl_dst, ETH_ADDR_LEN);
+    match->flow.nw_tos = ofmatch->nw_tos & IP_DSCP_MASK;
+    match->flow.nw_proto = ofmatch->nw_proto;
 
     /* Translate VLANs. */
     if (!(ofpfw & OFPFW10_DL_VLAN) &&
-        match->dl_vlan == htons(OFP10_VLAN_NONE)) {
+        ofmatch->dl_vlan == htons(OFP10_VLAN_NONE)) {
         /* Match only packets without 802.1Q header.
          *
          * When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct.
@@ -181,93 +165,102 @@ ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *match,
          * because we can't have a specific PCP without an 802.1Q header.
          * However, older versions of OVS treated this as matching packets
          * withut an 802.1Q header, so we do here too. */
-        rule->flow.vlan_tci = htons(0);
-        rule->wc.vlan_tci_mask = htons(0xffff);
+        match->flow.vlan_tci = htons(0);
+        match->wc.masks.vlan_tci = htons(0xffff);
     } else {
         ovs_be16 vid, pcp, tci;
 
-        vid = match->dl_vlan & htons(VLAN_VID_MASK);
-        pcp = htons((match->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK);
+        vid = ofmatch->dl_vlan & htons(VLAN_VID_MASK);
+        pcp = htons((ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK);
         tci = vid | pcp | htons(VLAN_CFI);
-        rule->flow.vlan_tci = tci & rule->wc.vlan_tci_mask;
+        match->flow.vlan_tci = tci & match->wc.masks.vlan_tci;
     }
 
     /* Clean up. */
-    cls_rule_zero_wildcarded_fields(rule);
+    match_zero_wildcarded_fields(match);
 }
 
-/* Convert 'rule' into the OpenFlow 1.0 match structure 'match'. */
+/* Convert 'match' into the OpenFlow 1.0 match structure 'ofmatch'. */
 void
-ofputil_cls_rule_to_ofp10_match(const struct cls_rule *rule,
-                                struct ofp10_match *match)
+ofputil_match_to_ofp10_match(const struct match *match,
+                             struct ofp10_match *ofmatch)
 {
-    const struct flow_wildcards *wc = &rule->wc;
+    const struct flow_wildcards *wc = &match->wc;
     uint32_t ofpfw;
 
     /* Figure out most OpenFlow wildcards. */
-    ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS);
-    ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_src_mask)
+    ofpfw = 0;
+    if (!wc->masks.in_port) {
+        ofpfw |= OFPFW10_IN_PORT;
+    }
+    if (!wc->masks.dl_type) {
+        ofpfw |= OFPFW10_DL_TYPE;
+    }
+    if (!wc->masks.nw_proto) {
+        ofpfw |= OFPFW10_NW_PROTO;
+    }
+    ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_src)
               << OFPFW10_NW_SRC_SHIFT);
-    ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_dst_mask)
+    ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_dst)
               << OFPFW10_NW_DST_SHIFT);
-    if (wc->wildcards & FWW_NW_DSCP) {
+    if (!(wc->masks.nw_tos & IP_DSCP_MASK)) {
         ofpfw |= OFPFW10_NW_TOS;
     }
-    if (!wc->tp_src_mask) {
+    if (!wc->masks.tp_src) {
         ofpfw |= OFPFW10_TP_SRC;
     }
-    if (!wc->tp_dst_mask) {
+    if (!wc->masks.tp_dst) {
         ofpfw |= OFPFW10_TP_DST;
     }
-    if (eth_addr_is_zero(wc->dl_src_mask)) {
+    if (eth_addr_is_zero(wc->masks.dl_src)) {
         ofpfw |= OFPFW10_DL_SRC;
     }
-    if (eth_addr_is_zero(wc->dl_dst_mask)) {
+    if (eth_addr_is_zero(wc->masks.dl_dst)) {
         ofpfw |= OFPFW10_DL_DST;
     }
 
     /* Translate VLANs. */
-    match->dl_vlan = htons(0);
-    match->dl_vlan_pcp = 0;
-    if (rule->wc.vlan_tci_mask == htons(0)) {
+    ofmatch->dl_vlan = htons(0);
+    ofmatch->dl_vlan_pcp = 0;
+    if (match->wc.masks.vlan_tci == htons(0)) {
         ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
-    } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
-               && !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
-        match->dl_vlan = htons(OFP10_VLAN_NONE);
+    } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
+               && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+        ofmatch->dl_vlan = htons(OFP10_VLAN_NONE);
         ofpfw |= OFPFW10_DL_VLAN_PCP;
     } else {
-        if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
+        if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
             ofpfw |= OFPFW10_DL_VLAN;
         } else {
-            match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
+            ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
         }
 
-        if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
+        if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
             ofpfw |= OFPFW10_DL_VLAN_PCP;
         } else {
-            match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
+            ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
         }
     }
 
     /* Compose most of the match structure. */
-    match->wildcards = htonl(ofpfw);
-    match->in_port = htons(rule->flow.in_port);
-    memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
-    memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
-    match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
-    match->nw_src = rule->flow.nw_src;
-    match->nw_dst = rule->flow.nw_dst;
-    match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK;
-    match->nw_proto = rule->flow.nw_proto;
-    match->tp_src = rule->flow.tp_src;
-    match->tp_dst = rule->flow.tp_dst;
-    memset(match->pad1, '\0', sizeof match->pad1);
-    memset(match->pad2, '\0', sizeof match->pad2);
+    ofmatch->wildcards = htonl(ofpfw);
+    ofmatch->in_port = htons(match->flow.in_port);
+    memcpy(ofmatch->dl_src, match->flow.dl_src, ETH_ADDR_LEN);
+    memcpy(ofmatch->dl_dst, match->flow.dl_dst, ETH_ADDR_LEN);
+    ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type);
+    ofmatch->nw_src = match->flow.nw_src;
+    ofmatch->nw_dst = match->flow.nw_dst;
+    ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK;
+    ofmatch->nw_proto = match->flow.nw_proto;
+    ofmatch->tp_src = match->flow.tp_src;
+    ofmatch->tp_dst = match->flow.tp_dst;
+    memset(ofmatch->pad1, '\0', sizeof ofmatch->pad1);
+    memset(ofmatch->pad2, '\0', sizeof ofmatch->pad2);
 }
 
 enum ofperr
-ofputil_pull_ofp11_match(struct ofpbuf *buf, unsigned int priority,
-                         struct cls_rule *rule, uint16_t *padded_match_len)
+ofputil_pull_ofp11_match(struct ofpbuf *buf, struct match *match,
+                         uint16_t *padded_match_len)
 {
     struct ofp11_match_header *omh = buf->data;
     uint16_t match_len;
@@ -289,80 +282,79 @@ ofputil_pull_ofp11_match(struct ofpbuf *buf, unsigned int priority,
         if (padded_match_len) {
             *padded_match_len = match_len;
         }
-        return ofputil_cls_rule_from_ofp11_match(om, priority, rule);
+        return ofputil_match_from_ofp11_match(om, match);
     }
 
     case OFPMT_OXM:
         if (padded_match_len) {
             *padded_match_len = ROUND_UP(match_len, 8);
         }
-        return oxm_pull_match(buf, priority, rule);
+        return oxm_pull_match(buf, match);
 
     default:
         return OFPERR_OFPBMC_BAD_TYPE;
     }
 }
 
-/* Converts the ofp11_match in 'match' into a cls_rule in 'rule', with the
- * given 'priority'.  Returns 0 if successful, otherwise an OFPERR_* value. */
+/* Converts the ofp11_match in 'match' into a struct match in 'match.  Returns
+ * 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
-ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
-                                  unsigned int priority,
-                                  struct cls_rule *rule)
+ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
+                               struct match *match)
 {
-    uint16_t wc = ntohl(match->wildcards);
+    uint16_t wc = ntohl(ofmatch->wildcards);
     uint8_t dl_src_mask[ETH_ADDR_LEN];
     uint8_t dl_dst_mask[ETH_ADDR_LEN];
     bool ipv4, arp;
     int i;
 
-    cls_rule_init_catchall(rule, priority);
+    match_init_catchall(match);
 
     if (!(wc & OFPFW11_IN_PORT)) {
         uint16_t ofp_port;
         enum ofperr error;
 
-        error = ofputil_port_from_ofp11(match->in_port, &ofp_port);
+        error = ofputil_port_from_ofp11(ofmatch->in_port, &ofp_port);
         if (error) {
             return OFPERR_OFPBMC_BAD_VALUE;
         }
-        cls_rule_set_in_port(rule, ofp_port);
+        match_set_in_port(match, ofp_port);
     }
 
     for (i = 0; i < ETH_ADDR_LEN; i++) {
-        dl_src_mask[i] = ~match->dl_src_mask[i];
+        dl_src_mask[i] = ~ofmatch->dl_src_mask[i];
     }
-    cls_rule_set_dl_src_masked(rule, match->dl_src, dl_src_mask);
+    match_set_dl_src_masked(match, ofmatch->dl_src, dl_src_mask);
 
     for (i = 0; i < ETH_ADDR_LEN; i++) {
-        dl_dst_mask[i] = ~match->dl_dst_mask[i];
+        dl_dst_mask[i] = ~ofmatch->dl_dst_mask[i];
     }
-    cls_rule_set_dl_dst_masked(rule, match->dl_dst, dl_dst_mask);
+    match_set_dl_dst_masked(match, ofmatch->dl_dst, dl_dst_mask);
 
     if (!(wc & OFPFW11_DL_VLAN)) {
-        if (match->dl_vlan == htons(OFPVID11_NONE)) {
+        if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
             /* Match only packets without a VLAN tag. */
-            rule->flow.vlan_tci = htons(0);
-            rule->wc.vlan_tci_mask = htons(UINT16_MAX);
+            match->flow.vlan_tci = htons(0);
+            match->wc.masks.vlan_tci = htons(UINT16_MAX);
         } else {
-            if (match->dl_vlan == htons(OFPVID11_ANY)) {
+            if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) {
                 /* Match any packet with a VLAN tag regardless of VID. */
-                rule->flow.vlan_tci = htons(VLAN_CFI);
-                rule->wc.vlan_tci_mask = htons(VLAN_CFI);
-            } else if (ntohs(match->dl_vlan) < 4096) {
+                match->flow.vlan_tci = htons(VLAN_CFI);
+                match->wc.masks.vlan_tci = htons(VLAN_CFI);
+            } else if (ntohs(ofmatch->dl_vlan) < 4096) {
                 /* Match only packets with the specified VLAN VID. */
-                rule->flow.vlan_tci = htons(VLAN_CFI) | match->dl_vlan;
-                rule->wc.vlan_tci_mask = htons(VLAN_CFI | VLAN_VID_MASK);
+                match->flow.vlan_tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
+                match->wc.masks.vlan_tci = htons(VLAN_CFI | VLAN_VID_MASK);
             } else {
                 /* Invalid VID. */
                 return OFPERR_OFPBMC_BAD_VALUE;
             }
 
             if (!(wc & OFPFW11_DL_VLAN_PCP)) {
-                if (match->dl_vlan_pcp <= 7) {
-                    rule->flow.vlan_tci |= htons(match->dl_vlan_pcp
-                                                 << VLAN_PCP_SHIFT);
-                    rule->wc.vlan_tci_mask |= htons(VLAN_PCP_MASK);
+                if (ofmatch->dl_vlan_pcp <= 7) {
+                    match->flow.vlan_tci |= htons(ofmatch->dl_vlan_pcp
+                                                  << VLAN_PCP_SHIFT);
+                    match->wc.masks.vlan_tci |= htons(VLAN_PCP_MASK);
                 } else {
                     /* Invalid PCP. */
                     return OFPERR_OFPBMC_BAD_VALUE;
@@ -372,33 +364,33 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
     }
 
     if (!(wc & OFPFW11_DL_TYPE)) {
-        cls_rule_set_dl_type(rule,
-                             ofputil_dl_type_from_openflow(match->dl_type));
+        match_set_dl_type(match,
+                          ofputil_dl_type_from_openflow(ofmatch->dl_type));
     }
 
-    ipv4 = rule->flow.dl_type == htons(ETH_TYPE_IP);
-    arp = rule->flow.dl_type == htons(ETH_TYPE_ARP);
+    ipv4 = match->flow.dl_type == htons(ETH_TYPE_IP);
+    arp = match->flow.dl_type == htons(ETH_TYPE_ARP);
 
     if (ipv4 && !(wc & OFPFW11_NW_TOS)) {
-        if (match->nw_tos & ~IP_DSCP_MASK) {
+        if (ofmatch->nw_tos & ~IP_DSCP_MASK) {
             /* Invalid TOS. */
             return OFPERR_OFPBMC_BAD_VALUE;
         }
 
-        cls_rule_set_nw_dscp(rule, match->nw_tos);
+        match_set_nw_dscp(match, ofmatch->nw_tos);
     }
 
     if (ipv4 || arp) {
         if (!(wc & OFPFW11_NW_PROTO)) {
-            cls_rule_set_nw_proto(rule, match->nw_proto);
+            match_set_nw_proto(match, ofmatch->nw_proto);
         }
-        cls_rule_set_nw_src_masked(rule, match->nw_src, ~match->nw_src_mask);
-        cls_rule_set_nw_dst_masked(rule, match->nw_dst, ~match->nw_dst_mask);
+        match_set_nw_src_masked(match, ofmatch->nw_src, ~ofmatch->nw_src_mask);
+        match_set_nw_dst_masked(match, ofmatch->nw_dst, ~ofmatch->nw_dst_mask);
     }
 
 #define OFPFW11_TP_ALL (OFPFW11_TP_SRC | OFPFW11_TP_DST)
     if (ipv4 && (wc & OFPFW11_TP_ALL) != OFPFW11_TP_ALL) {
-        switch (rule->flow.nw_proto) {
+        switch (match->flow.nw_proto) {
         case IPPROTO_ICMP:
             /* "A.2.3 Flow Match Structures" in OF1.1 says:
              *
@@ -408,17 +400,17 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
              * but I'm pretty sure we should support ICMP too, otherwise
              * that's a regression from OF1.0. */
             if (!(wc & OFPFW11_TP_SRC)) {
-                uint16_t icmp_type = ntohs(match->tp_src);
+                uint16_t icmp_type = ntohs(ofmatch->tp_src);
                 if (icmp_type < 0x100) {
-                    cls_rule_set_icmp_type(rule, icmp_type);
+                    match_set_icmp_type(match, icmp_type);
                 } else {
                     return OFPERR_OFPBMC_BAD_FIELD;
                 }
             }
             if (!(wc & OFPFW11_TP_DST)) {
-                uint16_t icmp_code = ntohs(match->tp_dst);
+                uint16_t icmp_code = ntohs(ofmatch->tp_dst);
                 if (icmp_code < 0x100) {
-                    cls_rule_set_icmp_code(rule, icmp_code);
+                    match_set_icmp_code(match, icmp_code);
                 } else {
                     return OFPERR_OFPBMC_BAD_FIELD;
                 }
@@ -428,10 +420,10 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
         case IPPROTO_TCP:
         case IPPROTO_UDP:
             if (!(wc & (OFPFW11_TP_SRC))) {
-                cls_rule_set_tp_src(rule, match->tp_src);
+                match_set_tp_src(match, ofmatch->tp_src);
             }
             if (!(wc & (OFPFW11_TP_DST))) {
-                cls_rule_set_tp_dst(rule, match->tp_dst);
+                match_set_tp_dst(match, ofmatch->tp_dst);
             }
             break;
 
@@ -446,8 +438,8 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
         }
     }
 
-    if (rule->flow.dl_type == htons(ETH_TYPE_MPLS) ||
-        rule->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+    if (match->flow.dl_type == htons(ETH_TYPE_MPLS) ||
+        match->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
         enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC };
 
         if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) {
@@ -456,105 +448,103 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
         }
     }
 
-    if (match->metadata_mask != htonll(UINT64_MAX)) {
-        cls_rule_set_metadata_masked(rule, match->metadata,
-                                     ~match->metadata_mask);
-    }
+    match_set_metadata_masked(match, ofmatch->metadata,
+                              ~ofmatch->metadata_mask);
 
     return 0;
 }
 
-/* Convert 'rule' into the OpenFlow 1.1 match structure 'match'. */
+/* Convert 'match' into the OpenFlow 1.1 match structure 'ofmatch'. */
 void
-ofputil_cls_rule_to_ofp11_match(const struct cls_rule *rule,
-                                struct ofp11_match *match)
+ofputil_match_to_ofp11_match(const struct match *match,
+                             struct ofp11_match *ofmatch)
 {
     uint32_t wc = 0;
     int i;
 
-    memset(match, 0, sizeof *match);
-    match->omh.type = htons(OFPMT_STANDARD);
-    match->omh.length = htons(OFPMT11_STANDARD_LENGTH);
+    memset(ofmatch, 0, sizeof *ofmatch);
+    ofmatch->omh.type = htons(OFPMT_STANDARD);
+    ofmatch->omh.length = htons(OFPMT11_STANDARD_LENGTH);
 
-    if (rule->wc.wildcards & FWW_IN_PORT) {
+    if (!match->wc.masks.in_port) {
         wc |= OFPFW11_IN_PORT;
     } else {
-        match->in_port = ofputil_port_to_ofp11(rule->flow.in_port);
+        ofmatch->in_port = ofputil_port_to_ofp11(match->flow.in_port);
     }
 
-    memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
+    memcpy(ofmatch->dl_src, match->flow.dl_src, ETH_ADDR_LEN);
     for (i = 0; i < ETH_ADDR_LEN; i++) {
-        match->dl_src_mask[i] = ~rule->wc.dl_src_mask[i];
+        ofmatch->dl_src_mask[i] = ~match->wc.masks.dl_src[i];
     }
 
-    memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
+    memcpy(ofmatch->dl_dst, match->flow.dl_dst, ETH_ADDR_LEN);
     for (i = 0; i < ETH_ADDR_LEN; i++) {
-        match->dl_dst_mask[i] = ~rule->wc.dl_dst_mask[i];
+        ofmatch->dl_dst_mask[i] = ~match->wc.masks.dl_dst[i];
     }
 
-    if (rule->wc.vlan_tci_mask == htons(0)) {
+    if (match->wc.masks.vlan_tci == htons(0)) {
         wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
-    } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
-               && !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
-        match->dl_vlan = htons(OFPVID11_NONE);
+    } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
+               && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+        ofmatch->dl_vlan = htons(OFPVID11_NONE);
         wc |= OFPFW11_DL_VLAN_PCP;
     } else {
-        if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
-            match->dl_vlan = htons(OFPVID11_ANY);
+        if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
+            ofmatch->dl_vlan = htons(OFPVID11_ANY);
         } else {
-            match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
+            ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
         }
 
-        if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
+        if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
             wc |= OFPFW11_DL_VLAN_PCP;
         } else {
-            match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
+            ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
         }
     }
 
-    if (rule->wc.wildcards & FWW_DL_TYPE) {
+    if (!match->wc.masks.dl_type) {
         wc |= OFPFW11_DL_TYPE;
     } else {
-        match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
+        ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type);
     }
 
-    if (rule->wc.wildcards & FWW_NW_DSCP) {
+    if (!(match->wc.masks.nw_tos & IP_DSCP_MASK)) {
         wc |= OFPFW11_NW_TOS;
     } else {
-        match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK;
+        ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK;
     }
 
-    if (rule->wc.wildcards & FWW_NW_PROTO) {
+    if (!match->wc.masks.nw_proto) {
         wc |= OFPFW11_NW_PROTO;
     } else {
-        match->nw_proto = rule->flow.nw_proto;
+        ofmatch->nw_proto = match->flow.nw_proto;
     }
 
-    match->nw_src = rule->flow.nw_src;
-    match->nw_src_mask = ~rule->wc.nw_src_mask;
-    match->nw_dst = rule->flow.nw_dst;
-    match->nw_dst_mask = ~rule->wc.nw_dst_mask;
+    ofmatch->nw_src = match->flow.nw_src;
+    ofmatch->nw_src_mask = ~match->wc.masks.nw_src;
+    ofmatch->nw_dst = match->flow.nw_dst;
+    ofmatch->nw_dst_mask = ~match->wc.masks.nw_dst;
 
-    if (!rule->wc.tp_src_mask) {
+    if (!match->wc.masks.tp_src) {
         wc |= OFPFW11_TP_SRC;
     } else {
-        match->tp_src = rule->flow.tp_src;
+        ofmatch->tp_src = match->flow.tp_src;
     }
 
-    if (!rule->wc.tp_dst_mask) {
+    if (!match->wc.masks.tp_dst) {
         wc |= OFPFW11_TP_DST;
     } else {
-        match->tp_dst = rule->flow.tp_dst;
+        ofmatch->tp_dst = match->flow.tp_dst;
     }
 
     /* MPLS not supported. */
     wc |= OFPFW11_MPLS_LABEL;
     wc |= OFPFW11_MPLS_TC;
 
-    match->metadata = rule->flow.metadata;
-    match->metadata_mask = ~rule->wc.metadata_mask;
+    ofmatch->metadata = match->flow.metadata;
+    ofmatch->metadata_mask = ~match->wc.masks.metadata;
 
-    match->wildcards = htonl(wc);
+    ofmatch->wildcards = htonl(wc);
 }
 
 /* Given a 'dl_type' value in the format used in struct flow, returns the
@@ -894,48 +884,47 @@ regs_fully_wildcarded(const struct flow_wildcards *wc)
     int i;
 
     for (i = 0; i < FLOW_N_REGS; i++) {
-        if (wc->reg_masks[i] != 0) {
+        if (wc->masks.regs[i] != 0) {
             return false;
         }
     }
     return true;
 }
 
-/* Returns a bit-mask of ofputil_protocols that can be used for sending 'rule'
+/* Returns a bit-mask of ofputil_protocols that can be used for sending 'match'
  * to a switch (e.g. to add or remove a flow).  Only NXM can handle tunnel IDs,
  * registers, or fixing the Ethernet multicast bit.  Otherwise, it's better to
  * use OpenFlow 1.0 protocol for backward compatibility. */
 enum ofputil_protocol
-ofputil_usable_protocols(const struct cls_rule *rule)
+ofputil_usable_protocols(const struct match *match)
 {
-    const struct flow_wildcards *wc = &rule->wc;
+    const struct flow_wildcards *wc = &match->wc;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
 
     /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
-    if (!eth_mask_is_exact(wc->dl_src_mask)
-        && !eth_addr_is_zero(wc->dl_src_mask)) {
+    if (!eth_mask_is_exact(wc->masks.dl_src)
+        && !eth_addr_is_zero(wc->masks.dl_src)) {
         return OFPUTIL_P_NXM_ANY;
     }
-    if (!eth_mask_is_exact(wc->dl_dst_mask)
-        && !eth_addr_is_zero(wc->dl_dst_mask)) {
+    if (!eth_mask_is_exact(wc->masks.dl_dst)
+        && !eth_addr_is_zero(wc->masks.dl_dst)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* NXM and OF1.1+ support matching metadata. */
-    if (wc->metadata_mask != htonll(0)) {
+    if (wc->masks.metadata != htonll(0)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports matching ARP hardware addresses. */
-    if (!eth_addr_is_zero(wc->arp_sha_mask) ||
-        !eth_addr_is_zero(wc->arp_tha_mask)) {
+    if (!eth_addr_is_zero(wc->masks.arp_sha) ||
+        !eth_addr_is_zero(wc->masks.arp_tha)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports matching IPv6 traffic. */
-    if (!(wc->wildcards & FWW_DL_TYPE)
-            && (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) {
+    if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
@@ -945,38 +934,38 @@ ofputil_usable_protocols(const struct cls_rule *rule)
     }
 
     /* Only NXM supports matching tun_id. */
-    if (wc->tun_id_mask != htonll(0)) {
+    if (wc->masks.tun_id != htonll(0)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports matching fragments. */
-    if (wc->nw_frag_mask) {
+    if (wc->masks.nw_frag) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports matching IPv6 flow label. */
-    if (wc->ipv6_label_mask) {
+    if (wc->masks.ipv6_label) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports matching IP ECN bits. */
-    if (!(wc->wildcards & FWW_NW_ECN)) {
+    if (wc->masks.nw_tos & IP_ECN_MASK) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports matching IP TTL/hop limit. */
-    if (!(wc->wildcards & FWW_NW_TTL)) {
+    if (wc->masks.nw_ttl) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports non-CIDR IPv4 address masks. */
-    if (!ip_is_cidr(wc->nw_src_mask) || !ip_is_cidr(wc->nw_dst_mask)) {
+    if (!ip_is_cidr(wc->masks.nw_src) || !ip_is_cidr(wc->masks.nw_dst)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports bitwise matching on transport port. */
-    if ((wc->tp_src_mask && wc->tp_src_mask != htons(UINT16_MAX)) ||
-        (wc->tp_dst_mask && wc->tp_dst_mask != htons(UINT16_MAX))) {
+    if ((wc->masks.tp_src && wc->masks.tp_src != htons(UINT16_MAX)) ||
+        (wc->masks.tp_dst && wc->masks.tp_dst != htons(UINT16_MAX))) {
         return OFPUTIL_P_NXM_ANY;
     }
 
@@ -1150,8 +1139,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 
         ofm = ofpbuf_pull(&b, sizeof *ofm);
 
-        error = ofputil_pull_ofp11_match(&b, ntohs(ofm->priority), &fm->cr,
-                                         NULL);
+        error = ofputil_pull_ofp11_match(&b, &fm->match, NULL);
         if (error) {
             return error;
         }
@@ -1162,6 +1150,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         }
 
         /* Translate the message. */
+        fm->priority = ntohs(ofm->priority);
         if (ofm->command == OFPFC_ADD) {
             fm->cookie = htonll(0);
             fm->cookie_mask = htonll(0);
@@ -1182,31 +1171,21 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             return error;
         }
         if (ofm->out_group != htonl(OFPG_ANY)) {
-            return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED;
+            return OFPERR_OFPFMFC_UNKNOWN;
         }
         fm->flags = ntohs(ofm->flags);
     } else {
         if (raw == OFPRAW_OFPT10_FLOW_MOD) {
             /* Standard OpenFlow 1.0 flow_mod. */
             const struct ofp10_flow_mod *ofm;
-            uint16_t priority;
             enum ofperr error;
 
             /* Get the ofp10_flow_mod. */
             ofm = ofpbuf_pull(&b, sizeof *ofm);
 
-            /* Set priority based on original wildcards.  Normally we'd allow
-             * ofputil_cls_rule_from_match() to do this for us, but
-             * ofputil_normalize_rule() can put wildcards where the original
-             * flow didn't have them. */
-            priority = ntohs(ofm->priority);
-            if (!(ofm->match.wildcards & htonl(OFPFW10_ALL))) {
-                priority = UINT16_MAX;
-            }
-
             /* Translate the rule. */
-            ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
-            ofputil_normalize_rule(&fm->cr);
+            ofputil_match_from_ofp10_match(&ofm->match, &fm->match);
+            ofputil_normalize_match(&fm->match);
 
             /* Now get the actions. */
             error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
@@ -1214,6 +1193,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                 return error;
             }
 
+            /* OpenFlow 1.0 says that exact-match rules have to have the
+             * highest possible priority. */
+            fm->priority = (ofm->match.wildcards & htonl(OFPFW10_ALL)
+                            ? ntohs(ofm->priority)
+                            : UINT16_MAX);
+
             /* Translate the message. */
             command = ntohs(ofm->command);
             fm->cookie = htonll(0);
@@ -1231,8 +1216,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 
             /* Dissect the message. */
             nfm = ofpbuf_pull(&b, sizeof *nfm);
-            error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
-                                  &fm->cr, &fm->cookie, &fm->cookie_mask);
+            error = nx_pull_match(&b, ntohs(nfm->match_len),
+                                  &fm->match, &fm->cookie, &fm->cookie_mask);
             if (error) {
                 return error;
             }
@@ -1248,6 +1233,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                  * existing cookie. */
                 return OFPERR_NXBRC_NXM_INVALID;
             }
+            fm->priority = ntohs(nfm->priority);
             fm->new_cookie = nfm->cookie;
             fm->idle_timeout = ntohs(nfm->idle_timeout);
             fm->hard_timeout = ntohs(nfm->hard_timeout);
@@ -1303,12 +1289,12 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         ofm->command = fm->command;
         ofm->idle_timeout = htons(fm->idle_timeout);
         ofm->hard_timeout = htons(fm->hard_timeout);
-        ofm->priority = htons(fm->cr.priority);
+        ofm->priority = htons(fm->priority);
         ofm->buffer_id = htonl(fm->buffer_id);
         ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
         ofm->out_group = htonl(OFPG11_ANY);
         ofm->flags = htons(fm->flags);
-        oxm_put_match(msg, &fm->cr);
+        oxm_put_match(msg, &fm->match);
         ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
         break;
     }
@@ -1320,12 +1306,12 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION,
                            fm->ofpacts_len);
         ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
-        ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match);
+        ofputil_match_to_ofp10_match(&fm->match, &ofm->match);
         ofm->cookie = fm->new_cookie;
         ofm->command = ofputil_tid_command(fm, protocol);
         ofm->idle_timeout = htons(fm->idle_timeout);
         ofm->hard_timeout = htons(fm->hard_timeout);
-        ofm->priority = htons(fm->cr.priority);
+        ofm->priority = htons(fm->priority);
         ofm->buffer_id = htonl(fm->buffer_id);
         ofm->out_port = htons(fm->out_port);
         ofm->flags = htons(fm->flags);
@@ -1343,11 +1329,11 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
         nfm->command = ofputil_tid_command(fm, protocol);
         nfm->cookie = fm->new_cookie;
-        match_len = nx_put_match(msg, &fm->cr, fm->cookie, fm->cookie_mask);
+        match_len = nx_put_match(msg, &fm->match, fm->cookie, fm->cookie_mask);
         nfm = msg->l3;
         nfm->idle_timeout = htons(fm->idle_timeout);
         nfm->hard_timeout = htons(fm->hard_timeout);
-        nfm->priority = htons(fm->cr.priority);
+        nfm->priority = htons(fm->priority);
         nfm->buffer_id = htonl(fm->buffer_id);
         nfm->out_port = htons(fm->out_port);
         nfm->flags = htons(fm->flags);
@@ -1380,7 +1366,7 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
     for (i = 0; i < n_fms; i++) {
         const struct ofputil_flow_mod *fm = &fms[i];
 
-        usable_protocols &= ofputil_usable_protocols(&fm->cr);
+        usable_protocols &= ofputil_usable_protocols(&fm->match);
         if (fm->table_id != 0xff) {
             usable_protocols &= OFPUTIL_P_TID;
         }
@@ -1401,7 +1387,7 @@ ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
                                     bool aggregate)
 {
     fsr->aggregate = aggregate;
-    ofputil_cls_rule_from_ofp10_match(&ofsr->match, 0, &fsr->match);
+    ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match);
     fsr->out_port = ntohs(ofsr->out_port);
     fsr->table_id = ofsr->table_id;
     fsr->cookie = fsr->cookie_mask = htonll(0);
@@ -1424,11 +1410,11 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
         return error;
     }
     if (ofsr->out_group != htonl(OFPG11_ANY)) {
-        return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED;
+        return OFPERR_OFPFMFC_UNKNOWN;
     }
     fsr->cookie = ofsr->cookie;
     fsr->cookie_mask = ofsr->cookie_mask;
-    error = ofputil_pull_ofp11_match(b, 0, &fsr->match, NULL);
+    error = ofputil_pull_ofp11_match(b, &fsr->match, NULL);
     if (error) {
         return error;
     }
@@ -1444,7 +1430,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
     enum ofperr error;
 
     nfsr = ofpbuf_pull(b, sizeof *nfsr);
-    error = nx_pull_match(b, ntohs(nfsr->match_len), 0, &fsr->match,
+    error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match,
                           &fsr->cookie, &fsr->cookie_mask);
     if (error) {
         return error;
@@ -1534,7 +1520,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
                : OFPRAW_OFPST10_FLOW_REQUEST);
         msg = ofpraw_alloc(raw, OFP10_VERSION, 0);
         ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
-        ofputil_cls_rule_to_ofp10_match(&fsr->match, &ofsr->match);
+        ofputil_match_to_ofp10_match(&fsr->match, &ofsr->match);
         ofsr->table_id = fsr->table_id;
         ofsr->out_port = htons(fsr->out_port);
         break;
@@ -1641,8 +1627,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
-        if (ofputil_pull_ofp11_match(msg, ntohs(ofs->priority), &fs->rule,
-                                     &padded_match_len)) {
+        if (ofputil_pull_ofp11_match(msg, &fs->match, &padded_match_len)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
             return EINVAL;
         }
@@ -1653,6 +1638,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
+        fs->priority = ntohs(ofs->priority);
         fs->table_id = ofs->table_id;
         fs->duration_sec = ntohl(ofs->duration_sec);
         fs->duration_nsec = ntohl(ofs->duration_nsec);
@@ -1686,8 +1672,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         }
 
         fs->cookie = get_32aligned_be64(&ofs->cookie);
-        ofputil_cls_rule_from_ofp10_match(&ofs->match, ntohs(ofs->priority),
-                                          &fs->rule);
+        ofputil_match_from_ofp10_match(&ofs->match, &fs->match);
+        fs->priority = ntohs(ofs->priority);
         fs->table_id = ofs->table_id;
         fs->duration_sec = ntohl(ofs->duration_sec);
         fs->duration_nsec = ntohl(ofs->duration_nsec);
@@ -1715,8 +1701,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
                          "claims invalid length %zu", match_len, length);
             return EINVAL;
         }
-        if (nx_pull_match(msg, match_len, ntohs(nfs->priority), &fs->rule,
-                          NULL, NULL)) {
+        if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL)) {
             return EINVAL;
         }
 
@@ -1729,6 +1714,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         fs->table_id = nfs->table_id;
         fs->duration_sec = ntohl(nfs->duration_sec);
         fs->duration_nsec = ntohl(nfs->duration_nsec);
+        fs->priority = ntohs(nfs->priority);
         fs->idle_timeout = ntohs(nfs->idle_timeout);
         fs->hard_timeout = ntohs(nfs->hard_timeout);
         fs->idle_age = -1;
@@ -1779,7 +1765,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         struct ofp11_flow_stats *ofs;
 
         ofpbuf_put_uninit(reply, sizeof *ofs);
-        oxm_put_match(reply, &fs->rule);
+        oxm_put_match(reply, &fs->match);
         ofpacts_put_openflow11_instructions(fs->ofpacts, fs->ofpacts_len,
                                             reply);
 
@@ -1789,7 +1775,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         ofs->pad = 0;
         ofs->duration_sec = htonl(fs->duration_sec);
         ofs->duration_nsec = htonl(fs->duration_nsec);
-        ofs->priority = htons(fs->rule.priority);
+        ofs->priority = htons(fs->priority);
         ofs->idle_timeout = htons(fs->idle_timeout);
         ofs->hard_timeout = htons(fs->hard_timeout);
         memset(ofs->pad2, 0, sizeof ofs->pad2);
@@ -1806,10 +1792,10 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         ofs->length = htons(reply->size - start_ofs);
         ofs->table_id = fs->table_id;
         ofs->pad = 0;
-        ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match);
+        ofputil_match_to_ofp10_match(&fs->match, &ofs->match);
         ofs->duration_sec = htonl(fs->duration_sec);
         ofs->duration_nsec = htonl(fs->duration_nsec);
-        ofs->priority = htons(fs->rule.priority);
+        ofs->priority = htons(fs->priority);
         ofs->idle_timeout = htons(fs->idle_timeout);
         ofs->hard_timeout = htons(fs->hard_timeout);
         memset(ofs->pad2, 0, sizeof ofs->pad2);
@@ -1823,7 +1809,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         int match_len;
 
         ofpbuf_put_uninit(reply, sizeof *nfs);
-        match_len = nx_put_match(reply, &fs->rule, 0, 0);
+        match_len = nx_put_match(reply, &fs->match, 0, 0);
         ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
 
         nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
@@ -1832,7 +1818,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         nfs->pad = 0;
         nfs->duration_sec = htonl(fs->duration_sec);
         nfs->duration_nsec = htonl(fs->duration_nsec);
-        nfs->priority = htons(fs->rule.priority);
+        nfs->priority = htons(fs->priority);
         nfs->idle_timeout = htons(fs->idle_timeout);
         nfs->hard_timeout = htons(fs->hard_timeout);
         nfs->idle_age = htons(fs->idle_age < 0 ? 0
@@ -1919,12 +1905,12 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
 
         ofr = ofpbuf_pull(&b, sizeof *ofr);
 
-        error = ofputil_pull_ofp11_match(&b, ntohs(ofr->priority),
-                                         &fr->rule, NULL);
+        error = ofputil_pull_ofp11_match(&b, &fr->match, NULL);
         if (error) {
             return error;
         }
 
+        fr->priority = ntohs(ofr->priority);
         fr->cookie = ofr->cookie;
         fr->reason = ofr->reason;
         /* XXX: ofr->table_id is ignored */
@@ -1939,8 +1925,8 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
 
         ofr = ofpbuf_pull(&b, sizeof *ofr);
 
-        ofputil_cls_rule_from_ofp10_match(&ofr->match, ntohs(ofr->priority),
-                                          &fr->rule);
+        ofputil_match_from_ofp10_match(&ofr->match, &fr->match);
+        fr->priority = ntohs(ofr->priority);
         fr->cookie = ofr->cookie;
         fr->reason = ofr->reason;
         fr->duration_sec = ntohl(ofr->duration_sec);
@@ -1954,8 +1940,8 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
         int error;
 
         nfr = ofpbuf_pull(&b, sizeof *nfr);
-        error = nx_pull_match(&b, ntohs(nfr->match_len), ntohs(nfr->priority),
-                              &fr->rule, NULL, NULL);
+        error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match,
+                              NULL, NULL);
         if (error) {
             return error;
         }
@@ -1963,6 +1949,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
             return OFPERR_OFPBRC_BAD_LEN;
         }
 
+        fr->priority = ntohs(nfr->priority);
         fr->cookie = nfr->cookie;
         fr->reason = nfr->reason;
         fr->duration_sec = ntohl(nfr->duration_sec);
@@ -1996,7 +1983,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
                                htonl(0), NXM_TYPICAL_LEN);
         ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
         ofr->cookie = fr->cookie;
-        ofr->priority = htons(fr->rule.priority);
+        ofr->priority = htons(fr->priority);
         ofr->reason = fr->reason;
         ofr->table_id = 0;
         ofr->duration_sec = htonl(fr->duration_sec);
@@ -2005,7 +1992,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         ofr->hard_timeout = htons(fr->hard_timeout);
         ofr->packet_count = htonll(fr->packet_count);
         ofr->byte_count = htonll(fr->byte_count);
-        oxm_put_match(msg, &fr->rule);
+        oxm_put_match(msg, &fr->match);
         break;
     }
 
@@ -2016,9 +2003,9 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION,
                                htonl(0), 0);
         ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
-        ofputil_cls_rule_to_ofp10_match(&fr->rule, &ofr->match);
+        ofputil_match_to_ofp10_match(&fr->match, &ofr->match);
         ofr->cookie = fr->cookie;
-        ofr->priority = htons(fr->rule.priority);
+        ofr->priority = htons(fr->priority);
         ofr->reason = fr->reason;
         ofr->duration_sec = htonl(fr->duration_sec);
         ofr->duration_nsec = htonl(fr->duration_nsec);
@@ -2036,11 +2023,11 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION,
                                htonl(0), NXM_TYPICAL_LEN);
         nfr = ofpbuf_put_zeros(msg, sizeof *nfr);
-        match_len = nx_put_match(msg, &fr->rule, 0, 0);
+        match_len = nx_put_match(msg, &fr->match, 0, 0);
 
         nfr = msg->l3;
         nfr->cookie = fr->cookie;
-        nfr->priority = htons(fr->rule.priority);
+        nfr->priority = htons(fr->priority);
         nfr->reason = fr->reason;
         nfr->duration_sec = htonl(fr->duration_sec);
         nfr->duration_nsec = htonl(fr->duration_nsec);
@@ -2060,16 +2047,15 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
 
 static void
 ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
-                                struct cls_rule *rule,
-                                struct ofpbuf *b)
+                                struct match *match, struct ofpbuf *b)
 {
     pin->packet = b->data;
     pin->packet_len = b->size;
 
-    pin->fmd.in_port = rule->flow.in_port;
-    pin->fmd.tun_id = rule->flow.tun_id;
-    pin->fmd.metadata = rule->flow.metadata;
-    memcpy(pin->fmd.regs, rule->flow.regs, sizeof pin->fmd.regs);
+    pin->fmd.in_port = match->flow.in_port;
+    pin->fmd.tun_id = match->flow.tun_id;
+    pin->fmd.metadata = match->flow.metadata;
+    memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs);
 }
 
 enum ofperr
@@ -2085,11 +2071,11 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
     raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPT12_PACKET_IN) {
         const struct ofp12_packet_in *opi;
-        struct cls_rule rule;
+        struct match match;
         int error;
 
         opi = ofpbuf_pull(&b, sizeof *opi);
-        error = oxm_pull_match_loose(&b, 0, &rule);
+        error = oxm_pull_match_loose(&b, &match);
         if (error) {
             return error;
         }
@@ -2104,7 +2090,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->buffer_id = ntohl(opi->buffer_id);
         pin->total_len = ntohs(opi->total_len);
 
-        ofputil_decode_packet_in_finish(pin, &rule, &b);
+        ofputil_decode_packet_in_finish(pin, &match, &b);
     } else if (raw == OFPRAW_OFPT10_PACKET_IN) {
         const struct ofp_packet_in *opi;
 
@@ -2119,11 +2105,11 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->total_len = ntohs(opi->total_len);
     } else if (raw == OFPRAW_NXT_PACKET_IN) {
         const struct nx_packet_in *npi;
-        struct cls_rule rule;
+        struct match match;
         int error;
 
         npi = ofpbuf_pull(&b, sizeof *npi);
-        error = nx_pull_match_loose(&b, ntohs(npi->match_len), 0, &rule, NULL,
+        error = nx_pull_match_loose(&b, ntohs(npi->match_len), &match, NULL,
                                     NULL);
         if (error) {
             return error;
@@ -2140,7 +2126,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->buffer_id = ntohl(npi->buffer_id);
         pin->total_len = ntohs(npi->total_len);
 
-        ofputil_decode_packet_in_finish(pin, &rule, &b);
+        ofputil_decode_packet_in_finish(pin, &match, &b);
     } else {
         NOT_REACHED();
     }
@@ -2149,26 +2135,26 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
 }
 
 static void
-ofputil_packet_in_to_rule(const struct ofputil_packet_in *pin,
-                          struct cls_rule *rule)
+ofputil_packet_in_to_match(const struct ofputil_packet_in *pin,
+                           struct match *match)
 {
     int i;
 
-    cls_rule_init_catchall(rule, 0);
+    match_init_catchall(match);
     if (pin->fmd.tun_id != htonll(0)) {
-        cls_rule_set_tun_id(rule, pin->fmd.tun_id);
+        match_set_tun_id(match, pin->fmd.tun_id);
     }
     if (pin->fmd.metadata != htonll(0)) {
-        cls_rule_set_metadata(rule, pin->fmd.metadata);
+        match_set_metadata(match, pin->fmd.metadata);
     }
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if (pin->fmd.regs[i]) {
-            cls_rule_set_reg(rule, i, pin->fmd.regs[i]);
+            match_set_reg(match, i, pin->fmd.regs[i]);
         }
     }
 
-    cls_rule_set_in_port(rule, pin->fmd.in_port);
+    match_set_in_port(match, pin->fmd.in_port);
 }
 
 /* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message
@@ -2184,16 +2170,16 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
     /* Add OFPT_PACKET_IN. */
     if (protocol == OFPUTIL_P_OF12) {
         struct ofp12_packet_in *opi;
-        struct cls_rule rule;
+        struct match match;
 
-        ofputil_packet_in_to_rule(pin, &rule);
+        ofputil_packet_in_to_match(pin, &match);
 
         /* The final argument is just an estimate of the space required. */
         packet = ofpraw_alloc_xid(OFPRAW_OFPT12_PACKET_IN, OFP12_VERSION,
                                   htonl(0), (sizeof(struct flow_metadata) * 2
                                              + 2 + send_len));
         ofpbuf_put_zeros(packet, sizeof *opi);
-        oxm_put_match(packet, &rule);
+        oxm_put_match(packet, &match);
         ofpbuf_put_zeros(packet, 2);
         ofpbuf_put(packet, pin->packet, send_len);
 
@@ -2216,17 +2202,17 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         ofpbuf_put(packet, pin->packet, send_len);
     } else if (packet_in_format == NXPIF_NXM) {
         struct nx_packet_in *npi;
-        struct cls_rule rule;
+        struct match match;
         size_t match_len;
 
-        ofputil_packet_in_to_rule(pin, &rule);
+        ofputil_packet_in_to_match(pin, &match);
 
         /* The final argument is just an estimate of the space required. */
         packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
                                   htonl(0), (sizeof(struct flow_metadata) * 2
                                              + 2 + send_len));
         ofpbuf_put_zeros(packet, sizeof *npi);
-        match_len = nx_put_match(packet, &rule, 0, 0);
+        match_len = nx_put_match(packet, &match, 0, 0);
         ofpbuf_put_zeros(packet, 2);
         ofpbuf_put(packet, pin->packet, send_len);
 
@@ -2293,7 +2279,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
                           const struct ofp_header *oh,
                           struct ofpbuf *ofpacts)
 {
-    enum ofperr bad_in_port_err;
     enum ofpraw raw;
     struct ofpbuf b;
 
@@ -2315,8 +2300,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         if (error) {
             return error;
         }
-
-        bad_in_port_err = OFPERR_OFPBMC_BAD_VALUE;
     } else if (raw == OFPRAW_OFPT10_PACKET_OUT) {
         enum ofperr error;
         const struct ofp_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
@@ -2328,8 +2311,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         if (error) {
             return error;
         }
-
-        bad_in_port_err = OFPERR_NXBRC_BAD_IN_PORT;
     } else {
         NOT_REACHED();
     }
@@ -2338,7 +2319,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16,
                      po->in_port);
-        return bad_in_port_err;
+        return OFPERR_OFPBRC_BAD_PORT;
     }
 
     po->ofpacts = ofpacts->data;
@@ -3005,8 +2986,7 @@ ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq,
     rq->out_port = ntohs(nfmr->out_port);
     rq->table_id = nfmr->table_id;
 
-    return nx_pull_match(msg, ntohs(nfmr->match_len), OFP_DEFAULT_PRIORITY,
-                         &rq->match, NULL, NULL);
+    return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL, NULL);
 }
 
 void
@@ -3035,7 +3015,7 @@ ofputil_append_flow_monitor_request(
 
 /* Converts an NXST_FLOW_MONITOR reply (also known as a flow update) in 'msg'
  * into an abstract ofputil_flow_update in 'update'.  The caller must have
- * initialized update->match to point to space allocated for a cls_rule.
+ * initialized update->match to point to space allocated for a match.
  *
  * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's
  * actions (except for NXFME_ABBREV, which never includes actions).  The caller
@@ -3109,9 +3089,9 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update,
         update->hard_timeout = ntohs(nfuf->hard_timeout);
         update->table_id = nfuf->table_id;
         update->cookie = nfuf->cookie;
+        update->priority = ntohs(nfuf->priority);
 
-        error = nx_pull_match(msg, match_len, ntohs(nfuf->priority),
-                              update->match, NULL, NULL);
+        error = nx_pull_match(msg, match_len, update->match, NULL, NULL);
         if (error) {
             return error;
         }
@@ -3196,7 +3176,7 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update,
 
         nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
         nfuf->reason = htons(update->reason);
-        nfuf->priority = htons(update->match->priority);
+        nfuf->priority = htons(update->priority);
         nfuf->idle_timeout = htons(update->idle_timeout);
         nfuf->hard_timeout = htons(update->hard_timeout);
         nfuf->match_len = htons(match_len);
@@ -3514,6 +3494,7 @@ ofputil_action_code_from_name(const char *name)
         NULL,
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)           NAME,
 #define OFPAT11_ACTION(ENUM, STRUCT, NAME)           NAME,
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME)           NAME,
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
 #include "ofp-util.def"
     };
@@ -3543,6 +3524,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)                    \
     case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
 #define OFPAT11_ACTION OFPAT10_ACTION
+#define OFPAT12_ACTION OFPAT10_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)        \
     case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
 #include "ofp-util.def"
@@ -3567,6 +3549,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
         return s;                                               \
     }
 #define OFPAT11_ACTION OFPAT10_ACTION
+#define OFPAT12_ACTION OFPAT10_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)            \
     void                                                        \
     ofputil_init_##ENUM(struct STRUCT *s)                       \
@@ -3588,7 +3571,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
 #include "ofp-util.def"
 
 static void
-ofputil_normalize_rule__(struct cls_rule *rule, bool may_log)
+ofputil_normalize_match__(struct match *match, bool may_log)
 {
     enum {
         MAY_NW_ADDR     = 1 << 0, /* nw_src, nw_dst */
@@ -3604,72 +3587,71 @@ ofputil_normalize_rule__(struct cls_rule *rule, bool may_log)
     struct flow_wildcards wc;
 
     /* Figure out what fields may be matched. */
-    if (rule->flow.dl_type == htons(ETH_TYPE_IP)) {
+    if (match->flow.dl_type == htons(ETH_TYPE_IP)) {
         may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
-        if (rule->flow.nw_proto == IPPROTO_TCP ||
-            rule->flow.nw_proto == IPPROTO_UDP ||
-            rule->flow.nw_proto == IPPROTO_ICMP) {
+        if (match->flow.nw_proto == IPPROTO_TCP ||
+            match->flow.nw_proto == IPPROTO_UDP ||
+            match->flow.nw_proto == IPPROTO_ICMP) {
             may_match |= MAY_TP_ADDR;
         }
-    } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6)) {
+    } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
         may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
-        if (rule->flow.nw_proto == IPPROTO_TCP ||
-            rule->flow.nw_proto == IPPROTO_UDP) {
+        if (match->flow.nw_proto == IPPROTO_TCP ||
+            match->flow.nw_proto == IPPROTO_UDP) {
             may_match |= MAY_TP_ADDR;
-        } else if (rule->flow.nw_proto == IPPROTO_ICMPV6) {
+        } else if (match->flow.nw_proto == IPPROTO_ICMPV6) {
             may_match |= MAY_TP_ADDR;
-            if (rule->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+            if (match->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
                 may_match |= MAY_ND_TARGET | MAY_ARP_SHA;
-            } else if (rule->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+            } else if (match->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) {
                 may_match |= MAY_ND_TARGET | MAY_ARP_THA;
             }
         }
-    } else if (rule->flow.dl_type == htons(ETH_TYPE_ARP)) {
+    } else if (match->flow.dl_type == htons(ETH_TYPE_ARP)) {
         may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
     } else {
         may_match = 0;
     }
 
     /* Clear the fields that may not be matched. */
-    wc = rule->wc;
+    wc = match->wc;
     if (!(may_match & MAY_NW_ADDR)) {
-        wc.nw_src_mask = wc.nw_dst_mask = htonl(0);
+        wc.masks.nw_src = wc.masks.nw_dst = htonl(0);
     }
     if (!(may_match & MAY_TP_ADDR)) {
-        wc.tp_src_mask = wc.tp_dst_mask = htons(0);
+        wc.masks.tp_src = wc.masks.tp_dst = htons(0);
     }
     if (!(may_match & MAY_NW_PROTO)) {
-        wc.wildcards |= FWW_NW_PROTO;
+        wc.masks.nw_proto = 0;
     }
     if (!(may_match & MAY_IPVx)) {
-        wc.wildcards |= FWW_NW_DSCP;
-        wc.wildcards |= FWW_NW_ECN;
-        wc.wildcards |= FWW_NW_TTL;
+        wc.masks.nw_tos = 0;
+        wc.masks.nw_ttl = 0;
     }
     if (!(may_match & MAY_ARP_SHA)) {
-        memset(wc.arp_sha_mask, 0, ETH_ADDR_LEN);
+        memset(wc.masks.arp_sha, 0, ETH_ADDR_LEN);
     }
     if (!(may_match & MAY_ARP_THA)) {
-        memset(wc.arp_tha_mask, 0, ETH_ADDR_LEN);
+        memset(wc.masks.arp_tha, 0, ETH_ADDR_LEN);
     }
     if (!(may_match & MAY_IPV6)) {
-        wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any;
-        wc.ipv6_label_mask = htonl(0);
+        wc.masks.ipv6_src = wc.masks.ipv6_dst = in6addr_any;
+        wc.masks.ipv6_label = htonl(0);
     }
     if (!(may_match & MAY_ND_TARGET)) {
-        wc.nd_target_mask = in6addr_any;
+        wc.masks.nd_target = in6addr_any;
     }
 
     /* Log any changes. */
-    if (!flow_wildcards_equal(&wc, &rule->wc)) {
+    if (!flow_wildcards_equal(&wc, &match->wc)) {
         bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
-        char *pre = log ? cls_rule_to_string(rule) : NULL;
+        char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL;
 
-        rule->wc = wc;
-        cls_rule_zero_wildcarded_fields(rule);
+        match->wc = wc;
+        match_zero_wildcarded_fields(match);
 
         if (log) {
-            char *post = cls_rule_to_string(rule);
+            char *post = match_to_string(match, OFP_DEFAULT_PRIORITY);
             VLOG_INFO("normalization changed ofp_match, details:");
             VLOG_INFO(" pre: %s", pre);
             VLOG_INFO("post: %s", post);
@@ -3679,37 +3661,37 @@ ofputil_normalize_rule__(struct cls_rule *rule, bool may_log)
     }
 }
 
-/* "Normalizes" the wildcards in 'rule'.  That means:
+/* "Normalizes" the wildcards in 'match'.  That means:
  *
  *    1. If the type of level N is known, then only the valid fields for that
  *       level may be specified.  For example, ARP does not have a TOS field,
- *       so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
+ *       so nw_tos must be wildcarded if 'match' specifies an ARP flow.
  *       Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
- *       ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
+ *       ipv6_dst (and other fields) must be wildcarded if 'match' specifies an
  *       IPv4 flow.
  *
  *    2. If the type of level N is not known (or not understood by Open
  *       vSwitch), then no fields at all for that level may be specified.  For
  *       example, Open vSwitch does not understand SCTP, an L4 protocol, so the
- *       L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
+ *       L4 fields tp_src and tp_dst must be wildcarded if 'match' specifies an
  *       SCTP flow.
  *
- * If this function changes 'rule', it logs a rate-limited informational
+ * If this function changes 'match', it logs a rate-limited informational
  * message. */
 void
-ofputil_normalize_rule(struct cls_rule *rule)
+ofputil_normalize_match(struct match *match)
 {
-    ofputil_normalize_rule__(rule, true);
+    ofputil_normalize_match__(match, true);
 }
 
-/* Same as ofputil_normalize_rule() without the logging.  Thus, this function
- * is suitable for a program's internal use, whereas ofputil_normalize_rule()
+/* Same as ofputil_normalize_match() without the logging.  Thus, this function
+ * is suitable for a program's internal use, whereas ofputil_normalize_match()
  * sense for use on flows received from elsewhere (so that a bug in the program
  * that sent them can be reported and corrected). */
 void
-ofputil_normalize_rule_quiet(struct cls_rule *rule)
+ofputil_normalize_match_quiet(struct match *match)
 {
-    ofputil_normalize_rule__(rule, false);
+    ofputil_normalize_match__(match, false);
 }
 
 /* Parses a key or a key-value pair from '*stringp'.
index 391c14b..6f5113e 100644 (file)
@@ -36,6 +36,28 @@ OFPAT11_ACTION(OFPAT11_SET_TP_DST,   ofp_action_tp_port,  "mod_tp_dst")
 //OFPAT11_ACTION(OFPAT11_SET_NW_TTL,   ofp11_action_nw_ttl, "set_nw_ttl")
 //OFPAT11_ACTION(OFPAT11_DEC_NW_TTL,   ofp_action_header,   "dec_ttl")
 
+#ifndef OFPAT12_ACTION
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME)
+#endif
+//OFPAT12_ACTION(OFPAT12_OUTPUT, , "output")
+//OFPAT12_ACTION(OFPAT12_COPY_TTL_OUT, ofp_action_header, "copy_ttl_out")
+//OFPAT12_ACTION(OFPAT12_COPY_TTL_IN, ofp_action_header, "copy_ttl_in")
+//OFPAT12_ACTION(OFPAT12_SET_MPLS_TTL, , "set_mpls_ttl")
+//OFPAT12_ACTION(OFPAT12_DEC_MPLS_TTL, ofp_action_header, "dec_mpls_ttl")
+//OFPAT12_ACTION(OFPAT12_PUSH_VLAN, , "push_vlan")
+//OFPAT12_ACTION(OFPAT12_POP_VLAN, ofp_action_header, "pop_vlan")
+//OFPAT12_ACTION(OFPAT12_PUSH_MPLS, , "push_mpls")
+//OFPAT12_ACTION(OFPAT12_POP_MPLS, , "pop_mpls")
+//OFPAT12_ACTION(OFPAT12_SET_QUEUE, , "set_queue")
+//OFPAT12_ACTION(OFPAT12_GROUP, , "group")
+//OFPAT12_ACTION(OFPAT12_SET_NW_TTL, , "set_nw_ttl")
+//OFPAT12_ACTION(OFPAT12_DEC_NW_TTL, ofp_action_header, "dec_ttl")
+//Use non-NULL name for OFPAT12_SET_FIELD once the code for
+//the OFPUTIL_OFPAT12_SET_FIELD case in parse_named_action() is implemented
+//OFPAT12_ACTION(OFPAT12_SET_FIELD, ofp12_action_set_field, "set_field")
+OFPAT12_ACTION(OFPAT12_SET_FIELD, ofp12_action_set_field, NULL)
+//OFPAT12_ACTION(OFPAT12_EXPERIMENTER, , )
+
 #ifndef NXAST_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
 #endif
@@ -62,4 +84,5 @@ NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids,      1, NULL)
 
 #undef OFPAT10_ACTION
 #undef OFPAT11_ACTION
+#undef OFPAT12_ACTION
 #undef NXAST_ACTION
index 9cc3028..e3a93c9 100644 (file)
 #include "classifier.h"
 #include "compiler.h"
 #include "flow.h"
+#include "match.h"
 #include "netdev.h"
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
 
-struct cls_rule;
 struct ofpbuf;
 
 /* Port numbers. */
@@ -96,7 +96,7 @@ enum ofputil_protocol ofputil_protocol_set_base(
 const char *ofputil_protocol_to_string(enum ofputil_protocol);
 char *ofputil_protocols_to_string(enum ofputil_protocol);
 enum ofputil_protocol ofputil_protocols_from_string(const char *);
-enum ofputil_protocol ofputil_usable_protocols(const struct cls_rule *);
+enum ofputil_protocol ofputil_usable_protocols(const struct match *);
 
 struct ofpbuf *ofputil_encode_set_protocol(enum ofputil_protocol current,
                                            enum ofputil_protocol want,
@@ -110,23 +110,18 @@ const char *ofputil_nx_flow_format_to_string(enum nx_flow_format);
 
 /* Work with ofp10_match. */
 void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *);
-void ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *,
-                                       unsigned int priority,
-                                       struct cls_rule *);
-void ofputil_normalize_rule(struct cls_rule *);
-void ofputil_normalize_rule_quiet(struct cls_rule *);
-void ofputil_cls_rule_to_ofp10_match(const struct cls_rule *,
-                                     struct ofp10_match *);
+void ofputil_match_from_ofp10_match(const struct ofp10_match *,
+                                    struct match *);
+void ofputil_normalize_match(struct match *);
+void ofputil_normalize_match_quiet(struct match *);
+void ofputil_match_to_ofp10_match(const struct match *, struct ofp10_match *);
 
 /* Work with ofp11_match. */
-enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, unsigned int priority,
-                                     struct cls_rule *,
+enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, struct match *,
                                      uint16_t *padded_match_len);
-enum ofperr ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *,
-                                              unsigned int priority,
-                                              struct cls_rule *);
-void ofputil_cls_rule_to_ofp11_match(const struct cls_rule *,
-                                     struct ofp11_match *);
+enum ofperr ofputil_match_from_ofp11_match(const struct ofp11_match *,
+                                           struct match *);
+void ofputil_match_to_ofp11_match(const struct match *, struct ofp11_match *);
 
 /* dl_type translation between OpenFlow and 'struct flow' format. */
 ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type);
@@ -160,7 +155,8 @@ struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
  * NXM Delete    <used>     <used>        -
  */
 struct ofputil_flow_mod {
-    struct cls_rule cr;
+    struct match match;
+    unsigned int priority;
     ovs_be64 cookie;         /* Cookie bits to match. */
     ovs_be64 cookie_mask;    /* 1-bit in each 'cookie' bit to match. */
     ovs_be64 new_cookie;     /* New cookie to install or -1. */
@@ -188,7 +184,7 @@ enum ofputil_protocol ofputil_flow_mod_usable_protocols(
 /* Flow stats or aggregate stats request, independent of protocol. */
 struct ofputil_flow_stats_request {
     bool aggregate;             /* Aggregate results? */
-    struct cls_rule match;
+    struct match match;
     ovs_be64 cookie;
     ovs_be64 cookie_mask;
     uint16_t out_port;
@@ -204,11 +200,12 @@ enum ofputil_protocol ofputil_flow_stats_request_usable_protocols(
 
 /* Flow stats reply, independent of protocol. */
 struct ofputil_flow_stats {
-    struct cls_rule rule;
+    struct match match;
     ovs_be64 cookie;
     uint8_t table_id;
     uint32_t duration_sec;
     uint32_t duration_nsec;
+    uint16_t priority;
     uint16_t idle_timeout;
     uint16_t hard_timeout;
     int idle_age;               /* Seconds since last packet, -1 if unknown. */
@@ -242,7 +239,8 @@ enum ofperr ofputil_decode_aggregate_stats_reply(
 
 /* Flow removed message, independent of protocol. */
 struct ofputil_flow_removed {
-    struct cls_rule rule;
+    struct match match;
+    uint16_t priority;
     ovs_be64 cookie;
     uint8_t reason;             /* One of OFPRR_*. */
     uint32_t duration_sec;
@@ -399,6 +397,7 @@ enum ofputil_action_bitmap {
     OFPUTIL_A_GROUP          = 1 << 24,
     OFPUTIL_A_SET_NW_TTL     = 1 << 25,
     OFPUTIL_A_DEC_NW_TTL     = 1 << 26,
+    OFPUTIL_A_SET_FIELD      = 1 << 27,
 };
 
 /* Abstract ofp_switch_features. */
@@ -457,7 +456,7 @@ struct ofputil_flow_monitor_request {
     enum nx_flow_monitor_flags flags;
     uint16_t out_port;
     uint8_t table_id;
-    struct cls_rule match;
+    struct match match;
 };
 
 int ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *,
@@ -475,7 +474,8 @@ struct ofputil_flow_update {
     uint16_t hard_timeout;
     uint8_t table_id;
     ovs_be64 cookie;
-    struct cls_rule *match;
+    struct match *match;
+    uint16_t priority;
     struct ofpact *ofpacts;
     size_t ofpacts_len;
 
@@ -551,6 +551,7 @@ enum OVS_PACKED_ENUM ofputil_action_code {
     OFPUTIL_ACTION_INVALID,
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)           OFPUTIL_##ENUM,
 #define OFPAT11_ACTION(ENUM, STRUCT, NAME)           OFPUTIL_##ENUM,
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME)           OFPUTIL_##ENUM,
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM,
 #include "ofp-util.def"
 };
@@ -559,6 +560,7 @@ enum OVS_PACKED_ENUM ofputil_action_code {
 enum {
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)           + 1
 #define OFPAT11_ACTION(ENUM, STRUCT, NAME)           + 1
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME)           + 1
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
     OFPUTIL_N_ACTIONS = 1
 #include "ofp-util.def"
@@ -588,6 +590,9 @@ void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
 #define OFPAT11_ACTION(ENUM, STRUCT, NAME)              \
     void ofputil_init_##ENUM(struct STRUCT *);          \
     struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
+#define OFPAT12_ACTION(ENUM, STRUCT, NAME)              \
+    void ofputil_init_##ENUM(struct STRUCT *);          \
+    struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)    \
     void ofputil_init_##ENUM(struct STRUCT *);          \
     struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
index 546acbb..7ea5ce6 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@ struct ovsdb_idl_row {
 struct ovsdb_idl_column {
     char *name;
     struct ovsdb_type type;
+    bool mutable;
     void (*parse)(struct ovsdb_idl_row *, const struct ovsdb_datum *);
     void (*unparse)(struct ovsdb_idl_row *);
 };
index 0333d96..0f1e062 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -58,7 +58,7 @@ struct reconnect {
     enum state state;
     long long int state_entered;
     int backoff;
-    long long int last_received;
+    long long int last_activity;
     long long int last_connected;
     long long int last_disconnected;
     unsigned int max_tries;
@@ -105,7 +105,7 @@ reconnect_create(long long int now)
     fsm->state = S_VOID;
     fsm->state_entered = now;
     fsm->backoff = 0;
-    fsm->last_received = now;
+    fsm->last_activity = now;
     fsm->last_connected = LLONG_MAX;
     fsm->last_disconnected = LLONG_MAX;
     fsm->max_tries = UINT_MAX;
@@ -176,9 +176,9 @@ reconnect_get_max_backoff(const struct reconnect *fsm)
 
 /* Returns the "probe interval" for 'fsm' in milliseconds.  If this is zero, it
  * disables the connection keepalive feature.  If it is nonzero, then if the
- * interval passes while 'fsm' is connected and without reconnect_received()
+ * interval passes while 'fsm' is connected and without reconnect_activity()
  * being called for 'fsm', reconnect_run() returns RECONNECT_PROBE.  If the
- * interval passes again without reconnect_received() being called,
+ * interval passes again without reconnect_activity() being called,
  * reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'. */
 int
 reconnect_get_probe_interval(const struct reconnect *fsm)
@@ -233,8 +233,8 @@ reconnect_set_backoff(struct reconnect *fsm, int min_backoff, int max_backoff)
 /* Sets the "probe interval" for 'fsm' to 'probe_interval', in milliseconds.
  * If this is zero, it disables the connection keepalive feature.  If it is
  * nonzero, then if the interval passes while 'fsm' is connected and without
- * reconnect_received() being called for 'fsm', reconnect_run() returns
- * RECONNECT_PROBE.  If the interval passes again without reconnect_received()
+ * reconnect_activity() being called for 'fsm', reconnect_run() returns
+ * RECONNECT_PROBE.  If the interval passes again without reconnect_activity()
  * being called, reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'.
  *
  * If 'probe_interval' is nonzero, then it will be forced to a value of at
@@ -360,7 +360,7 @@ reconnect_disconnected(struct reconnect *fsm, long long int now, int error)
         }
         /* Back off. */
         if (fsm->state & (S_ACTIVE | S_IDLE)
-             && (fsm->last_received - fsm->last_connected >= fsm->backoff
+             && (fsm->last_activity - fsm->last_connected >= fsm->backoff
                  || fsm->passive)) {
             fsm->backoff = fsm->passive ? 0 : fsm->min_backoff;
         } else {
@@ -441,7 +441,7 @@ reconnect_listen_error(struct reconnect *fsm, long long int now, int error)
 /* Tell 'fsm' that the connection was successful.
  *
  * The FSM will start the probe interval timer, which is reset by
- * reconnect_received().  If the timer expires, a probe will be sent (by
+ * reconnect_activity().  If the timer expires, a probe will be sent (by
  * returning RECONNECT_PROBE from reconnect_run()).  If the timer expires
  * again without being reset, the connection will be aborted (by returning
  * RECONNECT_DISCONNECT from reconnect_run()). */
@@ -467,15 +467,15 @@ reconnect_connect_failed(struct reconnect *fsm, long long int now, int error)
     reconnect_disconnected(fsm, now, error);
 }
 
-/* Tell 'fsm' that some data was received.  This resets the probe interval
- * timer, so that the connection is known not to be idle. */
+/* Tell 'fsm' that some activity has occurred on the connection.  This resets
+ * the probe interval timer, so that the connection is known not to be idle. */
 void
-reconnect_received(struct reconnect *fsm, long long int now)
+reconnect_activity(struct reconnect *fsm, long long int now)
 {
     if (fsm->state != S_ACTIVE) {
         reconnect_transition__(fsm, now, S_ACTIVE);
     }
-    fsm->last_received = now;
+    fsm->last_activity = now;
 }
 
 static void
@@ -517,7 +517,7 @@ reconnect_deadline__(const struct reconnect *fsm)
 
     case S_ACTIVE:
         if (fsm->probe_interval) {
-            long long int base = MAX(fsm->last_received, fsm->state_entered);
+            long long int base = MAX(fsm->last_activity, fsm->state_entered);
             return base + fsm->probe_interval;
         }
         return LLONG_MAX;
@@ -587,7 +587,7 @@ reconnect_run(struct reconnect *fsm, long long int now)
 
         case S_ACTIVE:
             VLOG_DBG("%s: idle %lld ms, sending inactivity probe", fsm->name,
-                     now - MAX(fsm->last_received, fsm->state_entered));
+                     now - MAX(fsm->last_activity, fsm->state_entered));
             reconnect_transition__(fsm, now, S_IDLE);
             return RECONNECT_PROBE;
 
@@ -673,7 +673,7 @@ reconnect_get_stats(const struct reconnect *fsm, long long int now,
                     struct reconnect_stats *stats)
 {
     stats->creation_time = fsm->creation_time;
-    stats->last_received = fsm->last_received;
+    stats->last_activity = fsm->last_activity;
     stats->last_connected = fsm->last_connected;
     stats->last_disconnected = fsm->last_disconnected;
     stats->backoff = fsm->backoff;
index e935d0a..4446713 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -79,7 +79,7 @@ void reconnect_listen_error(struct reconnect *, long long int now, int error);
 void reconnect_connected(struct reconnect *, long long int now);
 void reconnect_connect_failed(struct reconnect *, long long int now,
                               int error);
-void reconnect_received(struct reconnect *, long long int now);
+void reconnect_activity(struct reconnect *, long long int now);
 
 enum reconnect_action {
     RECONNECT_CONNECT = 1,
@@ -93,7 +93,7 @@ int reconnect_timeout(struct reconnect *, long long int now);
 struct reconnect_stats {
     /* All times and durations in this structure are in milliseconds. */
     long long int creation_time;     /* Time reconnect_create() called. */
-    long long int last_received;     /* Last call to reconnect_received(). */
+    long long int last_activity;     /* Last call to reconnect_activity(). */
     long long int last_connected;    /* Last call to reconnect_connected(). */
     long long int last_disconnected; /* Last call to reconnect_disconnected(). */
     int backoff;                     /* Current backoff duration.  */
index cd99142..c90d556 100644 (file)
@@ -790,37 +790,67 @@ log_2_ceil(uint32_t n)
     return log_2_floor(n) + !IS_POW2(n);
 }
 
-/* Returns the number of trailing 0-bits in 'n', or 32 if 'n' is 0. */
-int
-ctz(uint32_t n)
-{
-    if (!n) {
-        return 32;
-    } else {
+/* Returns the number of trailing 0-bits in 'n'.  Undefined if 'n' == 0. */
 #if !defined(UINT_MAX) || !defined(UINT32_MAX)
 #error "Someone screwed up the #includes."
 #elif __GNUC__ >= 4 && UINT_MAX == UINT32_MAX
-        return __builtin_ctz(n);
+/* Defined inline in util.h. */
 #else
-        unsigned int k;
-        int count = 31;
+static int
+raw_ctz(uint32_t n)
+{
+    unsigned int k;
+    int count = 31;
 
 #define CTZ_STEP(X)                             \
-        k = n << (X);                           \
-        if (k) {                                \
-            count -= X;                         \
-            n = k;                              \
-        }
-        CTZ_STEP(16);
-        CTZ_STEP(8);
-        CTZ_STEP(4);
-        CTZ_STEP(2);
-        CTZ_STEP(1);
+    k = n << (X);                               \
+    if (k) {                                    \
+        count -= X;                             \
+        n = k;                                  \
+    }
+    CTZ_STEP(16);
+    CTZ_STEP(8);
+    CTZ_STEP(4);
+    CTZ_STEP(2);
+    CTZ_STEP(1);
 #undef CTZ_STEP
 
-        return count;
+    return count;
+}
 #endif
-    }
+
+/* Returns the number of 1-bits in 'x', between 0 and 32 inclusive. */
+int
+popcount(uint32_t x)
+{
+    /* In my testing, this implementation is over twice as fast as any other
+     * portable implementation that I tried, including GCC 4.4
+     * __builtin_popcount(), although nonportable asm("popcnt") was over 50%
+     * faster. */
+#define INIT1(X)                                \
+    ((((X) & (1 << 0)) != 0) +                  \
+     (((X) & (1 << 1)) != 0) +                  \
+     (((X) & (1 << 2)) != 0) +                  \
+     (((X) & (1 << 3)) != 0) +                  \
+     (((X) & (1 << 4)) != 0) +                  \
+     (((X) & (1 << 5)) != 0) +                  \
+     (((X) & (1 << 6)) != 0) +                  \
+     (((X) & (1 << 7)) != 0))
+#define INIT2(X)   INIT1(X),  INIT1((X) +  1)
+#define INIT4(X)   INIT2(X),  INIT2((X) +  2)
+#define INIT8(X)   INIT4(X),  INIT4((X) +  4)
+#define INIT16(X)  INIT8(X),  INIT8((X) +  8)
+#define INIT32(X) INIT16(X), INIT16((X) + 16)
+#define INIT64(X) INIT32(X), INIT32((X) + 32)
+
+    static const uint8_t popcount8[256] = {
+        INIT64(0), INIT64(64), INIT64(128), INIT64(192)
+    };
+
+    return (popcount8[x & 0xff] +
+            popcount8[(x >> 8) & 0xff] +
+            popcount8[(x >> 16) & 0xff] +
+            popcount8[x >> 24]);
 }
 
 /* Returns true if the 'n' bytes starting at 'p' are zeros. */
index 57527fc..a1f4bd7 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef UTIL_H
 #define UTIL_H 1
 
+#include <limits.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stddef.h>
@@ -103,6 +104,14 @@ rightmost_1bit(uintmax_t x)
     return x & -x;
 }
 
+/* Returns 'x' with its rightmost 1-bit changed to a zero (e.g. 01011000 =>
+ * 01010000), or 0 if 'x' is 0. */
+static inline uintmax_t
+zero_rightmost_1bit(uintmax_t x)
+{
+    return x & (x - 1);
+}
+
 #ifndef MIN
 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
 #endif
@@ -233,9 +242,33 @@ char *xreadlink(const char *filename);
 char *follow_symlinks(const char *filename);
 
 void ignore(bool x OVS_UNUSED);
+
+/* Returns the number of trailing 0-bits in 'n'.  Undefined if 'n' == 0.
+ *
+ * This compiles to a single machine instruction ("bsf") with GCC on x86. */
+#if !defined(UINT_MAX) || !defined(UINT32_MAX)
+#error "Someone screwed up the #includes."
+#elif __GNUC__ >= 4 && UINT_MAX == UINT32_MAX
+static inline int
+raw_ctz(uint32_t n)
+{
+    return __builtin_ctz(n);
+}
+#else
+/* Defined in util.c. */
+int raw_ctz(uint32_t n);
+#endif
+
+/* Returns the number of trailing 0-bits in 'n', or 32 if 'n' is 0. */
+static inline int
+ctz(uint32_t n)
+{
+    return n ? raw_ctz(n) : 32;
+}
+
 int log_2_floor(uint32_t);
 int log_2_ceil(uint32_t);
-int ctz(uint32_t);
+int popcount(uint32_t);
 
 bool is_all_zeros(const uint8_t *, size_t);
 bool is_all_ones(const uint8_t *, size_t);
index aaf9b3e..391995e 100644 (file)
@@ -1600,14 +1600,14 @@ connmgr_flushed(struct connmgr *mgr)
     if (!connmgr_has_controllers(mgr)
         && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
         struct ofpbuf ofpacts;
-        struct cls_rule rule;
+        struct match match;
 
         ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
         ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
         ofpact_pad(&ofpacts);
 
-        cls_rule_init_catchall(&rule, 0);
-        ofproto_add_flow(mgr->ofproto, &rule, ofpacts.data, ofpacts.size);
+        match_init_catchall(&match);
+        ofproto_add_flow(mgr->ofproto, &match, 0, ofpacts.data, ofpacts.size);
 
         ofpbuf_uninit(&ofpacts);
     }
@@ -1717,7 +1717,7 @@ ofmonitor_create(const struct ofputil_flow_monitor_request *request,
     m->flags = request->flags;
     m->out_port = request->out_port;
     m->table_id = request->table_id;
-    m->match = request->match;
+    minimatch_init(&m->match, &request->match);
 
     *monitorp = m;
     return 0;
@@ -1805,6 +1805,7 @@ ofmonitor_report(struct connmgr *mgr, struct rule *rule,
 
             if (ofconn != abbrev_ofconn || ofconn->monitor_paused) {
                 struct ofputil_flow_update fu;
+                struct match match;
 
                 fu.event = event;
                 fu.reason = event == NXFME_DELETED ? reason : 0;
@@ -1812,7 +1813,8 @@ ofmonitor_report(struct connmgr *mgr, struct rule *rule,
                 fu.hard_timeout = rule->hard_timeout;
                 fu.table_id = rule->table_id;
                 fu.cookie = rule->flow_cookie;
-                fu.match = &rule->cr;
+                minimatch_expand(&rule->cr.match, &match);
+                fu.match = &match;
                 if (flags & NXFMF_ACTIONS) {
                     fu.ofpacts = rule->ofpacts;
                     fu.ofpacts_len = rule->ofpacts_len;
index 24a33fb..9a080f2 100644 (file)
@@ -20,6 +20,7 @@
 #include "classifier.h"
 #include "hmap.h"
 #include "list.h"
+#include "match.h"
 #include "ofp-errors.h"
 #include "ofproto.h"
 #include "openflow/nicira-ext.h"
@@ -172,7 +173,7 @@ struct ofmonitor {
     /* Matching. */
     uint16_t out_port;
     uint8_t table_id;
-    struct cls_rule match;
+    struct minimatch match;
 };
 
 struct ofputil_flow_monitor_request;
index 495197e..2c0a8f3 100644 (file)
@@ -191,14 +191,14 @@ fail_open_maybe_recover(struct fail_open *fo)
 static void
 fail_open_recover(struct fail_open *fo)
 {
-    struct cls_rule rule;
+    struct match match;
 
     VLOG_WARN("No longer in fail-open mode");
     fo->last_disconn_secs = 0;
     fo->next_bogus_packet_in = LLONG_MAX;
 
-    cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
-    ofproto_delete_flow(fo->ofproto, &rule);
+    match_init_catchall(&match);
+    ofproto_delete_flow(fo->ofproto, &match, FAIL_OPEN_PRIORITY);
 }
 
 void
@@ -216,7 +216,7 @@ fail_open_flushed(struct fail_open *fo)
     bool open = disconn_secs >= trigger_duration(fo);
     if (open) {
         struct ofpbuf ofpacts;
-        struct cls_rule rule;
+        struct match match;
 
         /* Set up a flow that matches every packet and directs them to
          * OFPP_NORMAL. */
@@ -224,8 +224,9 @@ fail_open_flushed(struct fail_open *fo)
         ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
         ofpact_pad(&ofpacts);
 
-        cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
-        ofproto_add_flow(fo->ofproto, &rule, ofpacts.data, ofpacts.size);
+        match_init_catchall(&match);
+        ofproto_add_flow(fo->ofproto, &match, FAIL_OPEN_PRIORITY,
+                         ofpacts.data, ofpacts.size);
 
         ofpbuf_uninit(&ofpacts);
     }
index 43461ad..0e4754c 100644 (file)
@@ -81,7 +81,9 @@ enum in_band_op {
 
 /* A rule to add to or delete from ofproto's flow table.  */
 struct in_band_rule {
-    struct cls_rule cls_rule;
+    struct hmap_node hmap_node; /* In struct in_band's "rules" hmap. */
+    struct match match;
+    unsigned int priority;
     enum in_band_op op;
 };
 
@@ -279,22 +281,23 @@ in_band_rule_check(const struct flow *flow,
 }
 
 static void
-add_rule(struct in_band *ib, const struct cls_rule *cls_rule)
+add_rule(struct in_band *ib, const struct match *match, unsigned int priority)
 {
-    uint32_t hash = cls_rule_hash(cls_rule, 0);
+    uint32_t hash = match_hash(match, 0);
     struct in_band_rule *rule;
 
-    HMAP_FOR_EACH_WITH_HASH (rule, cls_rule.hmap_node, hash, &ib->rules) {
-        if (cls_rule_equal(&rule->cls_rule, cls_rule)) {
+    HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &ib->rules) {
+        if (match_equal(&rule->match, match)) {
             rule->op = ADD;
             return;
         }
     }
 
     rule = xmalloc(sizeof *rule);
-    rule->cls_rule = *cls_rule;
+    rule->match = *match;
+    rule->priority = priority;
     rule->op = ADD;
-    hmap_insert(&ib->rules, &rule->cls_rule.hmap_node, hash);
+    hmap_insert(&ib->rules, &rule->hmap_node, hash);
 }
 
 static void
@@ -302,38 +305,38 @@ update_rules(struct in_band *ib)
 {
     struct in_band_rule *ib_rule;
     struct in_band_remote *r;
-    struct cls_rule rule;
+    struct match match;
 
     /* Mark all the existing rules for deletion.  (Afterward we will re-add any
      * rules that are still valid.) */
-    HMAP_FOR_EACH (ib_rule, cls_rule.hmap_node, &ib->rules) {
+    HMAP_FOR_EACH (ib_rule, hmap_node, &ib->rules) {
         ib_rule->op = DELETE;
     }
 
     if (ib->n_remotes && !eth_addr_is_zero(ib->local_mac)) {
         /* (a) Allow DHCP requests sent from the local port. */
-        cls_rule_init_catchall(&rule, IBR_FROM_LOCAL_DHCP);
-        cls_rule_set_in_port(&rule, OFPP_LOCAL);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
-        cls_rule_set_dl_src(&rule, ib->local_mac);
-        cls_rule_set_nw_proto(&rule, IPPROTO_UDP);
-        cls_rule_set_tp_src(&rule, htons(DHCP_CLIENT_PORT));
-        cls_rule_set_tp_dst(&rule, htons(DHCP_SERVER_PORT));
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_in_port(&match, OFPP_LOCAL);
+        match_set_dl_type(&match, htons(ETH_TYPE_IP));
+        match_set_dl_src(&match, ib->local_mac);
+        match_set_nw_proto(&match, IPPROTO_UDP);
+        match_set_tp_src(&match, htons(DHCP_CLIENT_PORT));
+        match_set_tp_dst(&match, htons(DHCP_SERVER_PORT));
+        add_rule(ib, &match, IBR_FROM_LOCAL_DHCP);
 
         /* (b) Allow ARP replies to the local port's MAC address. */
-        cls_rule_init_catchall(&rule, IBR_TO_LOCAL_ARP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_dl_dst(&rule, ib->local_mac);
-        cls_rule_set_nw_proto(&rule, ARP_OP_REPLY);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_ARP));
+        match_set_dl_dst(&match, ib->local_mac);
+        match_set_nw_proto(&match, ARP_OP_REPLY);
+        add_rule(ib, &match, IBR_TO_LOCAL_ARP);
 
         /* (c) Allow ARP requests from the local port's MAC address.  */
-        cls_rule_init_catchall(&rule, IBR_FROM_LOCAL_ARP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_dl_src(&rule, ib->local_mac);
-        cls_rule_set_nw_proto(&rule, ARP_OP_REQUEST);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_ARP));
+        match_set_dl_src(&match, ib->local_mac);
+        match_set_nw_proto(&match, ARP_OP_REQUEST);
+        add_rule(ib, &match, IBR_FROM_LOCAL_ARP);
     }
 
     for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) {
@@ -344,18 +347,18 @@ update_rules(struct in_band *ib)
         }
 
         /* (d) Allow ARP replies to the next hop's MAC address. */
-        cls_rule_init_catchall(&rule, IBR_TO_NEXT_HOP_ARP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_dl_dst(&rule, remote_mac);
-        cls_rule_set_nw_proto(&rule, ARP_OP_REPLY);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_ARP));
+        match_set_dl_dst(&match, remote_mac);
+        match_set_nw_proto(&match, ARP_OP_REPLY);
+        add_rule(ib, &match, IBR_TO_NEXT_HOP_ARP);
 
         /* (e) Allow ARP requests from the next hop's MAC address. */
-        cls_rule_init_catchall(&rule, IBR_FROM_NEXT_HOP_ARP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_dl_src(&rule, remote_mac);
-        cls_rule_set_nw_proto(&rule, ARP_OP_REQUEST);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_ARP));
+        match_set_dl_src(&match, remote_mac);
+        match_set_nw_proto(&match, ARP_OP_REQUEST);
+        add_rule(ib, &match, IBR_FROM_NEXT_HOP_ARP);
     }
 
     for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) {
@@ -363,35 +366,35 @@ update_rules(struct in_band *ib)
 
         /* (f) Allow ARP replies containing the remote's IP address as a
          * target. */
-        cls_rule_init_catchall(&rule, IBR_TO_REMOTE_ARP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_nw_proto(&rule, ARP_OP_REPLY);
-        cls_rule_set_nw_dst(&rule, a->sin_addr.s_addr);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_ARP));
+        match_set_nw_proto(&match, ARP_OP_REPLY);
+        match_set_nw_dst(&match, a->sin_addr.s_addr);
+        add_rule(ib, &match, IBR_TO_REMOTE_ARP);
 
         /* (g) Allow ARP requests containing the remote's IP address as a
          * source. */
-        cls_rule_init_catchall(&rule, IBR_FROM_REMOTE_ARP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_ARP));
-        cls_rule_set_nw_proto(&rule, ARP_OP_REQUEST);
-        cls_rule_set_nw_src(&rule, a->sin_addr.s_addr);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_ARP));
+        match_set_nw_proto(&match, ARP_OP_REQUEST);
+        match_set_nw_src(&match, a->sin_addr.s_addr);
+        add_rule(ib, &match, IBR_FROM_REMOTE_ARP);
 
         /* (h) Allow TCP traffic to the remote's IP and port. */
-        cls_rule_init_catchall(&rule, IBR_TO_REMOTE_TCP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
-        cls_rule_set_nw_proto(&rule, IPPROTO_TCP);
-        cls_rule_set_nw_dst(&rule, a->sin_addr.s_addr);
-        cls_rule_set_tp_dst(&rule, a->sin_port);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_IP));
+        match_set_nw_proto(&match, IPPROTO_TCP);
+        match_set_nw_dst(&match, a->sin_addr.s_addr);
+        match_set_tp_dst(&match, a->sin_port);
+        add_rule(ib, &match, IBR_TO_REMOTE_TCP);
 
         /* (i) Allow TCP traffic from the remote's IP and port. */
-        cls_rule_init_catchall(&rule, IBR_FROM_REMOTE_TCP);
-        cls_rule_set_dl_type(&rule, htons(ETH_TYPE_IP));
-        cls_rule_set_nw_proto(&rule, IPPROTO_TCP);
-        cls_rule_set_nw_src(&rule, a->sin_addr.s_addr);
-        cls_rule_set_tp_src(&rule, a->sin_port);
-        add_rule(ib, &rule);
+        match_init_catchall(&match);
+        match_set_dl_type(&match, htons(ETH_TYPE_IP));
+        match_set_nw_proto(&match, IPPROTO_TCP);
+        match_set_nw_src(&match, a->sin_addr.s_addr);
+        match_set_tp_src(&match, a->sin_port);
+        add_rule(ib, &match, IBR_FROM_REMOTE_TCP);
     }
 }
 
@@ -420,18 +423,19 @@ in_band_run(struct in_band *ib)
 
     update_rules(ib);
 
-    HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) {
+    HMAP_FOR_EACH_SAFE (rule, next, hmap_node, &ib->rules) {
         switch (rule->op) {
         case ADD:
-            ofproto_add_flow(ib->ofproto, &rule->cls_rule,
+            ofproto_add_flow(ib->ofproto, &rule->match, rule->priority,
                              ofpacts.data, ofpacts.size);
             break;
 
         case DELETE:
-            if (ofproto_delete_flow(ib->ofproto, &rule->cls_rule)) {
+            if (ofproto_delete_flow(ib->ofproto,
+                                    &rule->match, rule->priority)) {
                 /* ofproto doesn't have the rule anymore so there's no reason
                  * for us to track it any longer. */
-                hmap_remove(&ib->rules, &rule->cls_rule.hmap_node);
+                hmap_remove(&ib->rules, &rule->hmap_node);
                 free(rule);
             }
             break;
@@ -486,8 +490,8 @@ in_band_destroy(struct in_band *ib)
     if (ib) {
         struct in_band_rule *rule, *next;
 
-        HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) {
-            hmap_remove(&ib->rules, &rule->cls_rule.hmap_node);
+        HMAP_FOR_EACH_SAFE (rule, next, hmap_node, &ib->rules) {
+            hmap_remove(&ib->rules, &rule->hmap_node);
             free(rule);
         }
         hmap_destroy(&ib->rules);
index 78cb186..1289025 100644 (file)
@@ -118,8 +118,7 @@ static void rule_credit_stats(struct rule_dpif *,
 static void flow_push_stats(struct rule_dpif *, const struct flow *,
                             const struct dpif_flow_stats *);
 static tag_type rule_calculate_tag(const struct flow *,
-                                   const struct flow_wildcards *,
-                                   uint32_t basis);
+                                   const struct minimask *, uint32_t basis);
 static void rule_invalidate(const struct rule_dpif *);
 
 #define MAX_MIRRORS 32
@@ -598,7 +597,6 @@ struct ofproto_dpif {
     struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
     struct ofproto up;
     struct dpif *dpif;
-    int max_ports;
 
     /* Special OpenFlow rules. */
     struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
@@ -744,6 +742,7 @@ construct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     const char *name = ofproto->up.name;
+    int max_ports;
     int error;
     int i;
 
@@ -753,7 +752,9 @@ construct(struct ofproto *ofproto_)
         return error;
     }
 
-    ofproto->max_ports = dpif_get_max_ports(ofproto->dpif);
+    max_ports = dpif_get_max_ports(ofproto->dpif);
+    ofproto_init_max_ports(ofproto_, MIN(max_ports, OFPP_MAX));
+
     ofproto->n_matches = 0;
 
     dpif_flow_flush(ofproto->dpif);
@@ -820,8 +821,9 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
     struct ofputil_flow_mod fm;
     int error;
 
-    cls_rule_init_catchall(&fm.cr, 0);
-    cls_rule_set_reg(&fm.cr, 0, id);
+    match_init_catchall(&fm.match);
+    fm.priority = 0;
+    match_set_reg(&fm.match, 0, id);
     fm.new_cookie = htonll(0);
     fm.cookie = htonll(0);
     fm.cookie_mask = htonll(0);
@@ -842,7 +844,7 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
         return error;
     }
 
-    *rulep = rule_dpif_lookup__(ofproto, &fm.cr.flow, TBL_INTERNAL);
+    *rulep = rule_dpif_lookup__(ofproto, &fm.match.flow, TBL_INTERNAL);
     assert(*rulep != NULL);
 
     return 0;
@@ -4636,13 +4638,6 @@ rule_construct(struct rule *rule_)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
     struct rule_dpif *victim;
     uint8_t table_id;
-    enum ofperr error;
-
-    error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
-                          &rule->up.cr.flow, ofproto->max_ports);
-    if (error) {
-        return error;
-    }
 
     rule->packet_count = 0;
     rule->byte_count = 0;
@@ -4669,10 +4664,17 @@ rule_construct(struct rule *rule_)
     }
 
     table_id = rule->up.table_id;
-    rule->tag = (victim ? victim->tag
-                 : table_id == 0 ? 0
-                 : rule_calculate_tag(&rule->up.cr.flow, &rule->up.cr.wc,
-                                      ofproto->tables[table_id].basis));
+    if (victim) {
+        rule->tag = victim->tag;
+    } else if (table_id == 0) {
+        rule->tag = 0;
+    } else {
+        struct flow flow;
+
+        miniflow_expand(&rule->up.cr.match.flow, &flow);
+        rule->tag = rule_calculate_tag(&flow, &rule->up.cr.match.mask,
+                                       ofproto->tables[table_id].basis);
+    }
 
     complete_operation(rule);
     return 0;
@@ -4745,15 +4747,6 @@ static void
 rule_modify_actions(struct rule *rule_)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-    enum ofperr error;
-
-    error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
-                          &rule->up.cr.flow, ofproto->max_ports);
-    if (error) {
-        ofoperation_complete(rule->up.pending, error);
-        return;
-    }
 
     complete_operation(rule);
 }
@@ -5027,7 +5020,7 @@ xlate_table_action(struct action_xlate_ctx *ctx,
                 ctx->tags |= (rule && rule->tag
                               ? rule->tag
                               : rule_calculate_tag(&ctx->flow,
-                                                   &table->other_table->wc,
+                                                   &table->other_table->mask,
                                                    table->basis));
             }
         }
@@ -6015,7 +6008,7 @@ add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow)
         m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
 
         if (!vlan_is_mirrored(m, vlan)) {
-            mirrors &= mirrors - 1;
+            mirrors = zero_rightmost_1bit(mirrors);
             continue;
         }
 
@@ -6045,7 +6038,7 @@ update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors,
         return;
     }
 
-    for (; mirrors; mirrors &= mirrors - 1) {
+    for (; mirrors; mirrors = zero_rightmost_1bit(mirrors)) {
         struct ofmirror *m;
 
         m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
@@ -6328,18 +6321,17 @@ xlate_normal(struct action_xlate_ctx *ctx)
  * a few more, but not all of the facets or even all of the facets that
  * resubmit to the table modified by MAC learning). */
 
-/* Calculates the tag to use for 'flow' and wildcards 'wc' when it is inserted
+/* Calculates the tag to use for 'flow' and mask 'mask' when it is inserted
  * into an OpenFlow table with the given 'basis'. */
 static tag_type
-rule_calculate_tag(const struct flow *flow, const struct flow_wildcards *wc,
+rule_calculate_tag(const struct flow *flow, const struct minimask *mask,
                    uint32_t secret)
 {
-    if (flow_wildcards_is_catchall(wc)) {
+    if (minimask_is_catchall(mask)) {
         return 0;
     } else {
-        struct flow tag_flow = *flow;
-        flow_zero_wildcards(&tag_flow, wc);
-        return tag_create_deterministic(flow_hash(&tag_flow, secret));
+        uint32_t hash = flow_hash_in_minimask(flow, mask, secret);
+        return tag_create_deterministic(hash);
     }
 }
 
@@ -6441,40 +6433,32 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
            const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    enum ofperr error;
-
-    if (flow->in_port >= ofproto->max_ports && flow->in_port < OFPP_MAX) {
-        return OFPERR_NXBRC_BAD_IN_PORT;
-    }
+    struct odputil_keybuf keybuf;
+    struct dpif_flow_stats stats;
 
-    error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports);
-    if (!error) {
-        struct odputil_keybuf keybuf;
-        struct dpif_flow_stats stats;
+    struct ofpbuf key;
 
-        struct ofpbuf key;
+    struct action_xlate_ctx ctx;
+    uint64_t odp_actions_stub[1024 / 8];
+    struct ofpbuf odp_actions;
 
-        struct action_xlate_ctx ctx;
-        uint64_t odp_actions_stub[1024 / 8];
-        struct ofpbuf odp_actions;
+    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+    odp_flow_key_from_flow(&key, flow);
 
-        ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
-        odp_flow_key_from_flow(&key, flow);
+    dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
 
-        dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
+    action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, NULL,
+                          packet_get_tcp_flags(packet, flow), packet);
+    ctx.resubmit_stats = &stats;
 
-        action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, NULL,
-                              packet_get_tcp_flags(packet, flow), packet);
-        ctx.resubmit_stats = &stats;
+    ofpbuf_use_stub(&odp_actions,
+                    odp_actions_stub, sizeof odp_actions_stub);
+    xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
+    dpif_execute(ofproto->dpif, key.data, key.size,
+                 odp_actions.data, odp_actions.size, packet);
+    ofpbuf_uninit(&odp_actions);
 
-        ofpbuf_use_stub(&odp_actions,
-                        odp_actions_stub, sizeof odp_actions_stub);
-        xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
-        dpif_execute(ofproto->dpif, key.data, key.size,
-                     odp_actions.data, odp_actions.size, packet);
-        ofpbuf_uninit(&odp_actions);
-    }
-    return error;
+    return 0;
 }
 \f
 /* NetFlow. */
index 15dc347..fb7db23 100644 (file)
@@ -29,6 +29,7 @@
 #include "shash.h"
 #include "timeval.h"
 
+struct match;
 struct ofpact;
 struct ofputil_flow_mod;
 struct simap;
@@ -61,6 +62,7 @@ struct ofproto {
     /* Datapath. */
     struct hmap ports;          /* Contains "struct ofport"s. */
     struct shash port_by_name;
+    uint16_t max_ports;         /* Max possible OpenFlow port num, plus one. */
 
     /* Flow tables. */
     struct oftable *tables;
@@ -93,6 +95,7 @@ struct ofproto {
 };
 
 void ofproto_init_tables(struct ofproto *, int n_tables);
+void ofproto_init_max_ports(struct ofproto *, uint16_t max_ports);
 
 struct ofproto *ofproto_lookup(const char *name);
 struct ofport *ofproto_get_port(const struct ofproto *, uint16_t ofp_port);
@@ -365,6 +368,11 @@ struct ofproto_class {
      * ->construct() should delete flows from the underlying datapath, if
      * necessary, rather than populating the tables.
      *
+     * If the ofproto knows the maximum port number that the datapath can have,
+     * then it can call ofproto_init_max_ports().  If it does so, then the
+     * client will ensure that the actions it allows to be used through
+     * OpenFlow do not refer to ports above that maximum number.
+     *
      * Only one ofproto instance needs to be supported for any given datapath.
      * If a datapath is already open as part of one "ofproto", then another
      * attempt to "construct" the same datapath as part of another ofproto is
@@ -694,24 +702,22 @@ struct ofproto_class {
 /* ## OpenFlow Rule Functions ## */
 /* ## ----------------------- ## */
 
-
-
-    /* Chooses an appropriate table for 'cls_rule' within 'ofproto'.  On
+    /* Chooses an appropriate table for 'match' within 'ofproto'.  On
      * success, stores the table ID into '*table_idp' and returns 0.  On
      * failure, returns an OpenFlow error code.
      *
-     * The choice of table should be a function of 'cls_rule' and 'ofproto''s
+     * The choice of table should be a function of 'match' and 'ofproto''s
      * datapath capabilities.  It should not depend on the flows already in
      * 'ofproto''s flow tables.  Failure implies that an OpenFlow rule with
-     * 'cls_rule' as its matching condition can never be inserted into
-     * 'ofproto', even starting from an empty flow table.
+     * 'match' as its matching condition can never be inserted into 'ofproto',
+     * even starting from an empty flow table.
      *
      * If multiple tables are candidates for inserting the flow, the function
      * should choose one arbitrarily (but deterministically).
      *
      * If this function is NULL then table 0 is always chosen. */
     enum ofperr (*rule_choose_table)(const struct ofproto *ofproto,
-                                     const struct cls_rule *cls_rule,
+                                     const struct match *match,
                                      uint8_t *table_idp);
 
     /* Life-cycle functions for a "struct rule" (see "Life Cycle" above).
@@ -796,11 +802,7 @@ struct ofproto_class {
      *     registers, then it is an error if 'rule->cr' does not wildcard all
      *     registers.
      *
-     *   - Validate that 'rule->ofpacts' is a sequence of well-formed actions
-     *     that the datapath can correctly implement.  If your ofproto
-     *     implementation only implements a subset of the actions that Open
-     *     vSwitch understands, then you should implement your own action
-     *     validation.
+     *   - Validate that the datapath can correctly implement 'rule->ofpacts'.
      *
      *   - If the rule is valid, update the datapath flow table, adding the new
      *     rule or replacing the existing one.
@@ -882,8 +884,8 @@ struct ofproto_class {
      *
      * ->rule_modify_actions() should set the following in motion:
      *
-     *   - Validate that the actions now in 'rule' are well-formed OpenFlow
-     *     actions that the datapath can correctly implement.
+     *   - Validate that the datapath can correctly implement the actions now
+     *     in 'rule'.
      *
      *   - Update the datapath flow table with the new actions.
      *
@@ -936,8 +938,8 @@ struct ofproto_class {
      * The caller retains ownership of 'packet' and of 'ofpacts', so
      * ->packet_out() should not modify or free them.
      *
-     * This function must validate that it can implement 'ofpacts'.  If not,
-     * then it should return an OpenFlow error code.
+     * This function must validate that it can correctly implement 'ofpacts'.
+     * If not, then it should return an OpenFlow error code.
      *
      * 'flow' reflects the flow information for 'packet'.  All of the
      * information in 'flow' is extracted from 'packet', except for
@@ -946,17 +948,18 @@ struct ofproto_class {
      *
      * flow->in_port comes from the OpenFlow OFPT_PACKET_OUT message.  The
      * implementation should reject invalid flow->in_port values by returning
-     * OFPERR_NXBRC_BAD_IN_PORT.  For consistency, the implementation should
-     * consider valid for flow->in_port any value that could possibly be seen
-     * in a packet that it passes to connmgr_send_packet_in().  Ideally, even
-     * an implementation that never generates packet-ins (e.g. due to hardware
-     * limitations) should still allow flow->in_port values for every possible
-     * physical port and OFPP_LOCAL.  The only virtual ports (those above
-     * OFPP_MAX) that the caller will ever pass in as flow->in_port, other than
-     * OFPP_LOCAL, are OFPP_NONE and OFPP_CONTROLLER.  The implementation
-     * should allow both of these, treating each of them as packets generated
-     * by the controller as opposed to packets originating from some switch
-     * port.
+     * OFPERR_OFPBRC_BAD_PORT.  (If the implementation called
+     * ofproto_init_max_ports(), then the client will reject these ports
+     * itself.)  For consistency, the implementation should consider valid for
+     * flow->in_port any value that could possibly be seen in a packet that it
+     * passes to connmgr_send_packet_in().  Ideally, even an implementation
+     * that never generates packet-ins (e.g. due to hardware limitations)
+     * should still allow flow->in_port values for every possible physical port
+     * and OFPP_LOCAL.  The only virtual ports (those above OFPP_MAX) that the
+     * caller will ever pass in as flow->in_port, other than OFPP_LOCAL, are
+     * OFPP_NONE and OFPP_CONTROLLER.  The implementation should allow both of
+     * these, treating each of them as packets generated by the controller as
+     * opposed to packets originating from some switch port.
      *
      * (Ordinarily the only effect of flow->in_port is on output actions that
      * involve the input port, such as actions that output to OFPP_IN_PORT,
@@ -1215,9 +1218,11 @@ enum { OFPROTO_POSTPONE = 1 << 16 };
 BUILD_ASSERT_DECL(OFPROTO_POSTPONE < OFPERR_OFS);
 
 int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *);
-void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
+void ofproto_add_flow(struct ofproto *, const struct match *,
+                      unsigned int priority,
                       const struct ofpact *ofpacts, size_t ofpacts_len);
-bool ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
+bool ofproto_delete_flow(struct ofproto *,
+                         const struct match *, unsigned int priority);
 void ofproto_flush_flows(struct ofproto *);
 
 #endif /* ofproto/ofproto-provider.h */
index 5c9ab9d..9b235e9 100644 (file)
@@ -390,6 +390,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->frag_handling = OFPC_FRAG_NORMAL;
     hmap_init(&ofproto->ports);
     shash_init(&ofproto->port_by_name);
+    ofproto->max_ports = OFPP_MAX;
     ofproto->tables = NULL;
     ofproto->n_tables = 0;
     ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
@@ -422,6 +423,9 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     return 0;
 }
 
+/* Must be called (only) by an ofproto implementation in its constructor
+ * function.  See the large comment on 'construct' in struct ofproto_class for
+ * details. */
 void
 ofproto_init_tables(struct ofproto *ofproto, int n_tables)
 {
@@ -437,6 +441,24 @@ ofproto_init_tables(struct ofproto *ofproto, int n_tables)
     }
 }
 
+/* To be optionally called (only) by an ofproto implementation in its
+ * constructor function.  See the large comment on 'construct' in struct
+ * ofproto_class for details.
+ *
+ * Sets the maximum number of ports to 'max_ports'.  The ofproto generic layer
+ * will then ensure that actions passed into the ofproto implementation will
+ * not refer to OpenFlow ports numbered 'max_ports' or higher.  If this
+ * function is not called, there will be no such restriction.
+ *
+ * Reserved ports numbered OFPP_MAX and higher are special and not subject to
+ * the 'max_ports' restriction. */
+void
+ofproto_init_max_ports(struct ofproto *ofproto, uint16_t max_ports)
+{
+    assert(max_ports <= OFPP_MAX);
+    ofproto->max_ports = max_ports;
+}
+
 uint64_t
 ofproto_get_datapath_id(const struct ofproto *ofproto)
 {
@@ -1397,19 +1419,21 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
  *
  * This is a helper function for in-band control and fail-open. */
 void
-ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
+ofproto_add_flow(struct ofproto *ofproto, const struct match *match,
+                 unsigned int priority,
                  const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     const struct rule *rule;
 
-    rule = rule_from_cls_rule(classifier_find_rule_exactly(
-                                    &ofproto->tables[0].cls, cls_rule));
+    rule = rule_from_cls_rule(classifier_find_match_exactly(
+                                  &ofproto->tables[0].cls, match, priority));
     if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
                                 ofpacts, ofpacts_len)) {
         struct ofputil_flow_mod fm;
 
         memset(&fm, 0, sizeof fm);
-        fm.cr = *cls_rule;
+        fm.match = *match;
+        fm.priority = priority;
         fm.buffer_id = UINT32_MAX;
         fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
         fm.ofpacts_len = ofpacts_len;
@@ -1434,12 +1458,13 @@ ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm)
  *
  * This is a helper function for in-band control and fail-open. */
 bool
-ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
+ofproto_delete_flow(struct ofproto *ofproto,
+                    const struct match *target, unsigned int priority)
 {
     struct rule *rule;
 
-    rule = rule_from_cls_rule(classifier_find_rule_exactly(
-                                  &ofproto->tables[0].cls, target));
+    rule = rule_from_cls_rule(classifier_find_match_exactly(
+                                  &ofproto->tables[0].cls, target, priority));
     if (!rule) {
         /* No such rule -> success. */
         return true;
@@ -1876,6 +1901,7 @@ static void
 ofproto_rule_destroy__(struct rule *rule)
 {
     if (rule) {
+        cls_rule_destroy(&rule->cr);
         free(rule->ofpacts);
         rule->ofproto->ofproto_class->rule_dealloc(rule);
     }
@@ -2103,6 +2129,11 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     if (error) {
         goto exit_free_ofpacts;
     }
+    if (po.in_port >= p->max_ports && po.in_port < OFPP_MAX) {
+        error = OFPERR_OFPBRC_BAD_PORT;
+        goto exit_free_ofpacts;
+    }
+
 
     /* Get payload. */
     if (po.buffer_id != UINT32_MAX) {
@@ -2115,10 +2146,13 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
         ofpbuf_use_const(payload, po.packet, po.packet_len);
     }
 
-    /* Send out packet. */
+    /* Verify actions against packet, then send packet if successful. */
     flow_extract(payload, 0, 0, po.in_port, &flow);
-    error = p->ofproto_class->packet_out(p, payload, &flow,
-                                         po.ofpacts, po.ofpacts_len);
+    error = ofpacts_check(po.ofpacts, po.ofpacts_len, &flow, p->max_ports);
+    if (!error) {
+        error = p->ofproto_class->packet_out(p, payload, &flow,
+                                             po.ofpacts, po.ofpacts_len);
+    }
     ofpbuf_delete(payload);
 
 exit_free_ofpacts:
@@ -2329,7 +2363,7 @@ check_table_id(const struct ofproto *ofproto, uint8_t table_id)
 {
     return (table_id == 0xff || table_id < ofproto->n_tables
             ? 0
-            : OFPERR_NXBRC_BAD_TABLE_ID);
+            : OFPERR_OFPBRC_BAD_TABLE_ID);
 
 }
 
@@ -2403,11 +2437,12 @@ next_matching_table(const struct ofproto *ofproto,
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
-                    const struct cls_rule *match,
+                    const struct match *match,
                     ovs_be64 cookie, ovs_be64 cookie_mask,
                     uint16_t out_port, struct list *rules)
 {
     struct oftable *table;
+    struct cls_rule cr;
     enum ofperr error;
 
     error = check_table_id(ofproto, table_id);
@@ -2416,14 +2451,16 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
     }
 
     list_init(rules);
+    cls_rule_init(&cr, match, 0);
     FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
         struct cls_cursor cursor;
         struct rule *rule;
 
-        cls_cursor_init(&cursor, &table->cls, match);
+        cls_cursor_init(&cursor, &table->cls, &cr);
         CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
             if (rule->pending) {
-                return OFPROTO_POSTPONE;
+                error = OFPROTO_POSTPONE;
+                goto exit;
             }
             if (!ofproto_rule_is_hidden(rule)
                 && ofproto_rule_has_out_port(rule, out_port)
@@ -2432,7 +2469,10 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
             }
         }
     }
-    return 0;
+
+exit:
+    cls_rule_destroy(&cr);
+    return error;
 }
 
 /* Searches 'ofproto' for rules in table 'table_id' (or in all tables, if
@@ -2448,11 +2488,12 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
-                     const struct cls_rule *match,
+                     const struct match *match, unsigned int priority,
                      ovs_be64 cookie, ovs_be64 cookie_mask,
                      uint16_t out_port, struct list *rules)
 {
     struct oftable *table;
+    struct cls_rule cr;
     int error;
 
     error = check_table_id(ofproto, table_id);
@@ -2461,14 +2502,16 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
     }
 
     list_init(rules);
+    cls_rule_init(&cr, match, priority);
     FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
         struct rule *rule;
 
         rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls,
-                                                               match));
+                                                               &cr));
         if (rule) {
             if (rule->pending) {
-                return OFPROTO_POSTPONE;
+                error = OFPROTO_POSTPONE;
+                goto exit;
             }
             if (!ofproto_rule_is_hidden(rule)
                 && ofproto_rule_has_out_port(rule, out_port)
@@ -2477,6 +2520,9 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
             }
         }
     }
+
+exit:
+    cls_rule_destroy(&cr);
     return 0;
 }
 
@@ -2518,7 +2564,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
         long long int now = time_msec();
         struct ofputil_flow_stats fs;
 
-        fs.rule = rule->cr;
+        minimatch_expand(&rule->cr.match, &fs.match);
+        fs.priority = rule->cr.priority;
         fs.cookie = rule->flow_cookie;
         fs.table_id = rule->table_id;
         calc_flow_duration__(rule->created, now, &fs.duration_sec,
@@ -2840,6 +2887,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     struct oftable *table;
     struct ofopgroup *group;
     struct rule *victim;
+    struct cls_rule cr;
     struct rule *rule;
     int error;
 
@@ -2852,7 +2900,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     if (fm->table_id == 0xff) {
         uint8_t table_id;
         if (ofproto->ofproto_class->rule_choose_table) {
-            error = ofproto->ofproto_class->rule_choose_table(ofproto, &fm->cr,
+            error = ofproto->ofproto_class->rule_choose_table(ofproto,
+                                                              &fm->match,
                                                               &table_id);
             if (error) {
                 return error;
@@ -2865,33 +2914,38 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     } else if (fm->table_id < ofproto->n_tables) {
         table = &ofproto->tables[fm->table_id];
     } else {
-        return OFPERR_NXFMFC_BAD_TABLE_ID;
+        return OFPERR_OFPBRC_BAD_TABLE_ID;
     }
 
     if (table->flags & OFTABLE_READONLY) {
         return OFPERR_OFPBRC_EPERM;
     }
 
-    /* Check for overlap, if requested. */
-    if (fm->flags & OFPFF_CHECK_OVERLAP
-        && classifier_rule_overlaps(&table->cls, &fm->cr)) {
-        return OFPERR_OFPFMFC_OVERLAP;
+    /* Allocate new rule and initialize classifier rule. */
+    rule = ofproto->ofproto_class->rule_alloc();
+    if (!rule) {
+        VLOG_WARN_RL(&rl, "%s: failed to create rule (%s)",
+                     ofproto->name, strerror(error));
+        return ENOMEM;
     }
+    cls_rule_init(&rule->cr, &fm->match, fm->priority);
 
     /* Serialize against pending deletion. */
-    if (is_flow_deletion_pending(ofproto, &fm->cr, table - ofproto->tables)) {
+    if (is_flow_deletion_pending(ofproto, &cr, table - ofproto->tables)) {
+        cls_rule_destroy(&rule->cr);
+        ofproto->ofproto_class->rule_dealloc(rule);
         return OFPROTO_POSTPONE;
     }
 
-    /* Allocate new rule. */
-    rule = ofproto->ofproto_class->rule_alloc();
-    if (!rule) {
-        VLOG_WARN_RL(&rl, "%s: failed to create rule (%s)",
-                     ofproto->name, strerror(error));
-        return ENOMEM;
+    /* Check for overlap, if requested. */
+    if (fm->flags & OFPFF_CHECK_OVERLAP
+        && classifier_rule_overlaps(&table->cls, &rule->cr)) {
+        cls_rule_destroy(&rule->cr);
+        ofproto->ofproto_class->rule_dealloc(rule);
+        return OFPERR_OFPFMFC_OVERLAP;
     }
+
     rule->ofproto = ofproto;
-    rule->cr = fm->cr;
     rule->pending = NULL;
     rule->flow_cookie = fm->new_cookie;
     rule->created = rule->modified = rule->used = time_msec();
@@ -3031,7 +3085,7 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
     struct list rules;
     int error;
 
-    error = collect_rules_loose(ofproto, fm->table_id, &fm->cr,
+    error = collect_rules_loose(ofproto, fm->table_id, &fm->match,
                                 fm->cookie, fm->cookie_mask,
                                 OFPP_NONE, &rules);
     if (error) {
@@ -3056,8 +3110,8 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
     struct list rules;
     int error;
 
-    error = collect_rules_strict(ofproto, fm->table_id, &fm->cr,
-                                 fm->cookie, fm->cookie_mask,
+    error = collect_rules_strict(ofproto, fm->table_id, &fm->match,
+                                 fm->priority, fm->cookie, fm->cookie_mask,
                                  OFPP_NONE, &rules);
 
     if (error) {
@@ -3113,7 +3167,7 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
     struct list rules;
     enum ofperr error;
 
-    error = collect_rules_loose(ofproto, fm->table_id, &fm->cr,
+    error = collect_rules_loose(ofproto, fm->table_id, &fm->match,
                                 fm->cookie, fm->cookie_mask,
                                 fm->out_port, &rules);
     return (error ? error
@@ -3131,8 +3185,8 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
     struct list rules;
     enum ofperr error;
 
-    error = collect_rules_strict(ofproto, fm->table_id, &fm->cr,
-                                 fm->cookie, fm->cookie_mask,
+    error = collect_rules_strict(ofproto, fm->table_id, &fm->match,
+                                 fm->priority, fm->cookie, fm->cookie_mask,
                                  fm->out_port, &rules);
     return (error ? error
             : list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn,
@@ -3149,7 +3203,8 @@ ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
         return;
     }
 
-    fr.rule = rule->cr;
+    minimatch_expand(&rule->cr.match, &fr.match);
+    fr.priority = rule->cr.priority;
     fr.cookie = rule->flow_cookie;
     fr.reason = reason;
     calc_flow_duration__(rule->created, time_msec(),
@@ -3231,7 +3286,12 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
          * dropped from OpenFlow in the near future.  There is no good error
          * code, so just state that the flow table is full. */
         error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
-    } else {
+    }
+    if (!error) {
+        error = ofpacts_check(fm.ofpacts, fm.ofpacts_len,
+                              &fm.match.flow, ofproto->max_ports);
+    }
+    if (!error) {
         error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
     }
     if (error) {
@@ -3455,6 +3515,7 @@ ofproto_compose_flow_refresh_update(const struct rule *rule,
 {
     struct ofoperation *op = rule->pending;
     struct ofputil_flow_update fu;
+    struct match match;
 
     if (op && op->type == OFOPERATION_ADD && !op->victim) {
         /* We'll report the final flow when the operation completes.  Reporting
@@ -3469,7 +3530,8 @@ ofproto_compose_flow_refresh_update(const struct rule *rule,
     fu.hard_timeout = rule->hard_timeout;
     fu.table_id = rule->table_id;
     fu.cookie = rule->flow_cookie;
-    fu.match = CONST_CAST(struct cls_rule *, &rule->cr);
+    minimatch_expand(&rule->cr.match, &match);
+    fu.match = &match;
     if (!(flags & NXFMF_ACTIONS)) {
         fu.ofpacts = NULL;
         fu.ofpacts_len = 0;
@@ -3572,12 +3634,14 @@ ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m,
     const struct ofproto *ofproto = ofconn_get_ofproto(m->ofconn);
     const struct ofoperation *op;
     const struct oftable *table;
+    struct cls_rule target;
 
+    cls_rule_init_from_minimatch(&target, &m->match, 0);
     FOR_EACH_MATCHING_TABLE (table, m->table_id, ofproto) {
         struct cls_cursor cursor;
         struct rule *rule;
 
-        cls_cursor_init(&cursor, &table->cls, &m->match);
+        cls_cursor_init(&cursor, &table->cls, &target);
         CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
             assert(!rule->pending); /* XXX */
             ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules);
@@ -3590,10 +3654,11 @@ ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m,
         if (((m->table_id == 0xff
               ? !(ofproto->tables[rule->table_id].flags & OFTABLE_HIDDEN)
               : m->table_id == rule->table_id))
-            && cls_rule_is_loose_match(&rule->cr, &m->match)) {
+            && cls_rule_is_loose_match(&rule->cr, &target.match)) {
             ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules);
         }
     }
+    cls_rule_destroy(&target);
 }
 
 static void
@@ -3957,12 +4022,13 @@ ofopgroup_complete(struct ofopgroup *group)
         switch (op->type) {
         case OFOPERATION_ADD:
             if (!op->error) {
+                uint16_t vid_mask;
+
                 ofproto_rule_destroy__(op->victim);
-                if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
-                    == htons(VLAN_VID_MASK)) {
+                vid_mask = minimask_get_vid_mask(&rule->cr.match.mask);
+                if (vid_mask == VLAN_VID_MASK) {
                     if (ofproto->vlan_bitmap) {
-                        uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
-
+                        uint16_t vid = miniflow_get_vid(&rule->cr.match.flow);
                         if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
                             bitmap_set1(ofproto->vlan_bitmap, vid);
                             ofproto->vlans_changed = true;
@@ -4296,17 +4362,19 @@ eviction_group_hash_rule(struct rule *rule)
 {
     struct oftable *table = &rule->ofproto->tables[rule->table_id];
     const struct mf_subfield *sf;
+    struct flow flow;
     uint32_t hash;
 
     hash = table->eviction_group_id_basis;
+    miniflow_expand(&rule->cr.match.flow, &flow);
     for (sf = table->eviction_fields;
          sf < &table->eviction_fields[table->n_eviction_fields];
          sf++)
     {
-        if (mf_are_prereqs_ok(sf->field, &rule->cr.flow)) {
+        if (mf_are_prereqs_ok(sf->field, &flow)) {
             union mf_value value;
 
-            mf_get_value(sf->field, &rule->cr.flow, &value);
+            mf_get_value(sf->field, &flow, &value);
             if (sf->ofs) {
                 bitwise_zero(&value, sf->field->n_bytes, 0, sf->ofs);
             }
@@ -4612,12 +4680,11 @@ ofproto_get_vlan_usage(struct ofproto *ofproto, unsigned long int *vlan_bitmap)
         const struct cls_table *table;
 
         HMAP_FOR_EACH (table, hmap_node, &oftable->cls.tables) {
-            if ((table->wc.vlan_tci_mask & htons(VLAN_VID_MASK))
-                == htons(VLAN_VID_MASK)) {
+            if (minimask_get_vid_mask(&table->mask) == VLAN_VID_MASK) {
                 const struct cls_rule *rule;
 
                 HMAP_FOR_EACH (rule, hmap_node, &table->rules) {
-                    uint16_t vid = vlan_tci_to_vid(rule->flow.vlan_tci);
+                    uint16_t vid = miniflow_get_vid(&rule->match.flow);
                     bitmap_set1(vlan_bitmap, vid);
                     bitmap_set1(ofproto->vlan_bitmap, vid);
                 }
index 1aff0c5..300c247 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -434,6 +434,22 @@ ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
     if (!error) {
         error = parse_row(row_json, table, x->symtab, &row, &columns);
     }
+    if (!error) {
+        size_t i;
+
+        for (i = 0; i < columns.n_columns; i++) {
+            const struct ovsdb_column *column = columns.columns[i];
+
+            if (!column->mutable) {
+                error = ovsdb_syntax_error(parser->json,
+                                           "constraint violation",
+                                           "Cannot update immutable column %s "
+                                           "in table %s.",
+                                           column->name, table->schema->name);
+                break;
+            }
+        }
+    }
     if (!error) {
         error = ovsdb_condition_from_json(table->schema, where, x->symtab,
                                           &condition);
index 0dcd16f..5fd983a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -95,6 +95,12 @@ ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
                                   "No column %s in table %s.",
                                   column_name, ts->name);
     }
+    if (!m->column->mutable) {
+        return ovsdb_syntax_error(json, "constraint violation",
+                                  "Cannot mutate immutable column %s in "
+                                  "table %s.", column_name, ts->name);
+    }
+
     ovsdb_type_clone(&m->type, &m->column->type);
 
     mutator_name = json_string(array->elems[1]);
index 0b1933b..478109a 100755 (executable)
@@ -577,11 +577,16 @@ static void\n%s_columns_init(void)
         for columnName, column in sorted(table.columns.iteritems()):
             cs = "%s_col_%s" % (structName, columnName)
             d = {'cs': cs, 'c': columnName, 's': structName}
+            if column.mutable:
+                mutable = "true"
+            else:
+                mutable = "false"
             print
             print "    /* Initialize %(cs)s. */" % d
             print "    c = &%(cs)s;" % d
             print "    c->name = \"%(c)s\";" % d
             print column.type.cInitType("    ", "c->type")
+            print "    c->mutable = %s;" % mutable
             print "    c->parse = %(s)s_parse_%(c)s;" % d
             print "    c->unparse = %(s)s_unparse_%(c)s;" % d
         print "}"
index e4b98e8..3a8dec2 100644 (file)
@@ -758,6 +758,8 @@ class Transaction(object):
                 row = self._txn_rows.get(uuid, None)
                 if row and row._data is None:
                     return ["named-uuid", _uuid_name_from_uuid(uuid)]
+            else:
+                return [self._substitute_uuids(elem) for elem in json]
         return json
 
     def __disassemble(self):
@@ -1198,13 +1200,22 @@ class SchemaHelper(object):
     The location on disk of the schema used may be found in the
     'schema_location' variable."""
 
-    def __init__(self, location=None):
-        """Creates a new Schema object."""
+    def __init__(self, location=None, schema_json=None):
+        """Creates a new Schema object.
+
+        'location' file path to ovs schema. None means default location
+        'schema_json' schema in json preresentation in memory
+        """
 
-        if location is None:
-            location = "%s/vswitch.ovsschema" % ovs.dirs.PKGDATADIR
+        if location and schema_json:
+            raise ValueError("both location and schema_json can't be "
+                             "specified. it's ambiguous.")
+        if schema_json is None:
+            if location is None:
+                location = "%s/vswitch.ovsschema" % ovs.dirs.PKGDATADIR
+            schema_json = ovs.json.from_file(location)
 
-        self.schema_location = location
+        self.schema_json = schema_json
         self._tables = {}
         self._all = False
 
@@ -1224,6 +1235,15 @@ class SchemaHelper(object):
         columns = set(columns) | self._tables.get(table, set())
         self._tables[table] = columns
 
+    def register_table(self, table):
+        """Registers interest in the given all columns of 'table'. Future calls
+        to get_idl_schema() will include all columns of 'table'.
+
+        'table' must be a string
+        """
+        assert type(table) is str
+        self._tables[table] = set()  # empty set means all columns in the table
+
     def register_all(self):
         """Registers interest in every column of every table."""
         self._all = True
@@ -1233,8 +1253,8 @@ class SchemaHelper(object):
         object based on columns registered using the register_columns()
         function."""
 
-        schema = ovs.db.schema.DbSchema.from_json(
-            ovs.json.from_file(self.schema_location))
+        schema = ovs.db.schema.DbSchema.from_json(self.schema_json)
+        self.schema_json = None
 
         if not self._all:
             schema_tables = {}
@@ -1249,6 +1269,10 @@ class SchemaHelper(object):
         assert table_name in schema.tables
         table = schema.tables[table_name]
 
+        if not columns:
+            # empty set means all columns in the table
+            return table
+
         new_columns = {}
         for column_name in columns:
             assert type(column_name) is str
index a054401..fa66aab 100644 (file)
@@ -186,6 +186,7 @@ class Connection(object):
         self.input = ""
         self.output = ""
         self.parser = None
+        self.received_bytes = 0
 
     def close(self):
         self.stream.close()
@@ -221,6 +222,9 @@ class Connection(object):
         else:
             return len(self.output)
 
+    def get_received_bytes(self):
+        return self.received_bytes
+
     def __log_msg(self, title, msg):
         vlog.dbg("%s: %s %s" % (self.name, title, msg))
 
@@ -271,6 +275,7 @@ class Connection(object):
                     return EOF, None
                 else:
                     self.input += data
+                    self.received_bytes += len(data)
             else:
                 if self.parser is None:
                     self.parser = ovs.json.Parser()
@@ -444,7 +449,16 @@ class Session(object):
                 self.pstream = None
 
         if self.rpc:
+            received_bytes = self.rpc.get_received_bytes()
             self.rpc.run()
+            if received_bytes != self.rpc.get_received_bytes():
+                # Data was successfully received.
+                #
+                # Previously we only counted receiving a full message as
+                # activity, but with large messages or a slow connection that
+                # policy could time out the session mid-message.
+                self.reconnect.activity(ovs.timeval.msec())
+
             error = self.rpc.get_status()
             if error != 0:
                 self.reconnect.disconnected(ovs.timeval.msec(), error)
@@ -502,9 +516,19 @@ class Session(object):
 
     def recv(self):
         if self.rpc is not None:
+            backlog = self.rpc.get_backlog()
             error, msg = self.rpc.recv()
+            if self.rpc.get_backlog() < backlog:
+                # Data previously caught in a queue was successfully sent (or
+                # there's an error, which we'll catch below).
+                #
+                # We don't count data that is successfully sent immediately as
+                # activity, because there's a lot of queuing downstream from
+                # us, which means that we can push a lot of data into a
+                # connection that has stalled and won't ever recover.
+                self.reconnect.activity(ovs.timeval.msec())
+
             if not error:
-                self.reconnect.received(ovs.timeval.msec())
                 if msg.type == Message.T_REQUEST and msg.method == "echo":
                     # Echo request.  Send reply.
                     self.send(Message.create_reply(msg.params, msg.id))
index e459c58..c04c9b3 100644 (file)
 # limitations under the License.
 
 import errno
-import select
 import ovs.timeval
 import ovs.vlog
+import select
+import socket
 
 vlog = ovs.vlog.Vlog("poller")
 
 
+# eventlet/gevent doesn't support select.poll. If select.poll is used,
+# python interpreter is blocked as a whole instead of switching from the
+# current thread that is about to block to other runnable thread.
+# So emulate select.poll by select.select because using python means that
+# performance isn't so important.
+class _SelectSelect(object):
+    """ select.poll emulation by using select.select.
+    Only register and poll are needed at the moment.
+    """
+    def __init__(self):
+        self.rlist = []
+        self.wlist = []
+        self.xlist = []
+
+    def register(self, fd, events):
+        if isinstance(fd, socket.socket):
+            fd = fd.fileno()
+        assert isinstance(fd, int)
+        if events & select.POLLIN:
+            self.rlist.append(fd)
+            events &= ~select.POLLIN
+        if events & select.POLLOUT:
+            self.wlist.append(fd)
+            events &= ~select.POLLOUT
+        if events:
+            self.xlist.append(fd)
+
+    def poll(self, timeout):
+        if timeout == -1:
+            # epoll uses -1 for infinite timeout, select uses None.
+            timeout = None
+        else:
+            timeout = float(timeout) / 1000
+
+        rlist, wlist, xlist = select.select(self.rlist, self.wlist, self.xlist,
+                                            timeout)
+        # collections.defaultdict is introduced by python 2.5 and
+        # XenServer uses python 2.4. We don't use it for XenServer.
+        # events_dict = collections.defaultdict(int)
+        # events_dict[fd] |= event
+        events_dict = {}
+        for fd in rlist:
+            events_dict[fd] = events_dict.get(fd, 0) | select.POLLIN
+        for fd in wlist:
+            events_dict[fd] = events_dict.get(fd, 0) | select.POLLOUT
+        for fd in xlist:
+            events_dict[fd] = events_dict.get(fd, 0) | (select.POLLERR |
+                                                        select.POLLHUP |
+                                                        select.POLLNVAL)
+        return events_dict.items()
+
+
+SelectPoll = _SelectSelect
+# If eventlet/gevent isn't used, we can use select.poll by replacing
+# _SelectPoll with select.poll class
+# _SelectPoll = select.poll
+
+
 class Poller(object):
     """High-level wrapper around the "poll" system call.
 
@@ -122,5 +181,5 @@ class Poller(object):
                     vlog.dbg("%s on fd %d" % (s, fd))
 
     def __reset(self):
-        self.poll = select.poll()
+        self.poll = SelectPoll()
         self.timeout = -1
index 4599573..39dd556 100644 (file)
@@ -94,7 +94,7 @@ class Reconnect(object):
         @staticmethod
         def deadline(fsm):
             if fsm.probe_interval:
-                base = max(fsm.last_received, fsm.state_entered)
+                base = max(fsm.last_activity, fsm.state_entered)
                 return base + fsm.probe_interval
             return None
 
@@ -102,7 +102,7 @@ class Reconnect(object):
         def run(fsm, now):
             vlog.dbg("%s: idle %d ms, sending inactivity probe"
                      % (fsm.name,
-                        now - max(fsm.last_received, fsm.state_entered)))
+                        now - max(fsm.last_activity, fsm.state_entered)))
             fsm._transition(now, Reconnect.Idle)
             return PROBE
 
@@ -150,7 +150,7 @@ class Reconnect(object):
         self.state = Reconnect.Void
         self.state_entered = now
         self.backoff = 0
-        self.last_received = now
+        self.last_activity = now
         self.last_connected = None
         self.last_disconnected = None
         self.max_tries = None
@@ -204,8 +204,8 @@ class Reconnect(object):
         """Returns the "probe interval" in milliseconds.  If this is zero, it
         disables the connection keepalive feature.  If it is nonzero, then if
         the interval passes while the FSM is connected and without
-        self.received() being called, self.run() returns ovs.reconnect.PROBE.
-        If the interval passes again without self.received() being called,
+        self.activity() being called, self.run() returns ovs.reconnect.PROBE.
+        If the interval passes again without self.activity() being called,
         self.run() returns ovs.reconnect.DISCONNECT."""
         return self.probe_interval
 
@@ -246,9 +246,9 @@ class Reconnect(object):
         """Sets the "probe interval" to 'probe_interval', in milliseconds.  If
         this is zero, it disables the connection keepalive feature.  If it is
         nonzero, then if the interval passes while this FSM is connected and
-        without self.received() being called, self.run() returns
+        without self.activity() being called, self.run() returns
         ovs.reconnect.PROBE.  If the interval passes again without
-        self.received() being called, self.run() returns
+        self.activity() being called, self.run() returns
         ovs.reconnect.DISCONNECT.
 
         If 'probe_interval' is nonzero, then it will be forced to a value of at
@@ -354,7 +354,7 @@ class Reconnect(object):
 
             # Back off
             if (self.state in (Reconnect.Active, Reconnect.Idle) and
-                (self.last_received - self.last_connected >= self.backoff or
+                (self.last_activity - self.last_connected >= self.backoff or
                  self.passive)):
                 if self.passive:
                     self.backoff = 0
@@ -426,7 +426,7 @@ class Reconnect(object):
         """Tell this FSM that the connection was successful.
 
         The FSM will start the probe interval timer, which is reset by
-        self.received().  If the timer expires, a probe will be sent (by
+        self.activity().  If the timer expires, a probe will be sent (by
         returning ovs.reconnect.PROBE from self.run().  If the timer expires
         again without being reset, the connection will be aborted (by returning
         ovs.reconnect.DISCONNECT from self.run()."""
@@ -444,12 +444,13 @@ class Reconnect(object):
         self.connecting(now)
         self.disconnected(now, error)
 
-    def received(self, now):
-        """Tell this FSM that some data was received.  This resets the probe
-        interval timer, so that the connection is known not to be idle."""
+    def activity(self, now):
+        """Tell this FSM that some activity occurred on the connection.  This
+        resets the probe interval timer, so that the connection is known not to
+        be idle."""
         if self.state != Reconnect.Active:
             self._transition(now, Reconnect.Active)
-        self.last_received = now
+        self.last_activity = now
 
     def _transition(self, now, state):
         if self.state == Reconnect.ConnectInProgress:
@@ -561,7 +562,7 @@ class Reconnect(object):
         stats.creation_time = self.creation_time
         stats.last_connected = self.last_connected
         stats.last_disconnected = self.last_disconnected
-        stats.last_received = self.last_received
+        stats.last_activity = self.last_activity
         stats.backoff = self.backoff
         stats.seqno = self.seqno
         stats.is_connected = self.is_connected()
index a8e8d92..a978707 100644 (file)
@@ -19,6 +19,7 @@ import socket
 import sys
 
 import ovs.fatal_signal
+import ovs.poller
 import ovs.vlog
 
 vlog = ovs.vlog.Vlog("socket_util")
@@ -75,7 +76,7 @@ def make_unix_socket(style, nonblock, bind_path, connect_path):
 
 
 def check_connection_completion(sock):
-    p = select.poll()
+    p = ovs.poller.SelectPoll()
     p.register(sock, select.POLLOUT)
     if len(p.poll(0)) == 1:
         return get_socket_error(sock)
index 26ca079..cf0cc44 100644 (file)
@@ -12,3 +12,13 @@ m4_foreach(
   [AT_SETUP([flow classifier - m4_bpatsubst(testname, [-], [ ])])
    AT_CHECK([test-classifier testname], [0], [], [])
    AT_CLEANUP])])
+
+AT_BANNER([miniflow unit tests])
+m4_foreach(
+  [testname],
+  [[miniflow],
+   [minimask_has_extra],
+   [minimask_combine]],
+  [AT_SETUP([miniflow - m4_bpatsubst(testname, [-], [ ])])
+   AT_CHECK([test-classifier testname], [0], [], [])
+   AT_CLEANUP])])
index 70660a2..0765c3f 100644 (file)
@@ -103,12 +103,14 @@ AT_CLEANUP
 m4_foreach(
   [testname],
   [[ctz],
+   [popcount],
    [log_2_floor],
    [bitwise_copy],
    [bitwise_zero],
    [bitwise_one],
    [bitwise_is_all_zeros]],
   [AT_SETUP([testname[()] function])
+   AT_KEYWORDS([testname])
    AT_CHECK([test-util testname], [0], [], [])
    AT_CLEANUP])
 
index 877cc87..2644d3f 100644 (file)
@@ -5,16 +5,47 @@ m4_define([CHECK_LOCKFILE],
    AT_KEYWORDS([lockfile])
    AT_CHECK([test-lockfile $1], [0], [$1: success (m4_if(
      [$2], [1], [$2 child], [$2 children]))
-])
+], [stderr])
+   AT_CHECK([sed 's/pid [[0-9]]*/pid <pid>/' stderr], [0], [$3])
    AT_CLEANUP])
 
 CHECK_LOCKFILE([lock_and_unlock], [0])
+
 CHECK_LOCKFILE([lock_and_unlock_twice], [0])
-CHECK_LOCKFILE([lock_blocks_same_process], [0])
-CHECK_LOCKFILE([lock_blocks_same_process_twice], [0])
-CHECK_LOCKFILE([lock_blocks_other_process], [1])
-CHECK_LOCKFILE([lock_twice_blocks_other_process], [1])
+
+CHECK_LOCKFILE([lock_blocks_same_process], [0],
+  [lockfile|WARN|.file.~lock~: failed to lock file: Resource deadlock avoided
+])
+
+CHECK_LOCKFILE([lock_blocks_same_process_twice], [0],
+  [lockfile|WARN|.file.~lock~: failed to lock file: Resource deadlock avoided
+lockfile|WARN|.file.~lock~: failed to lock file: Resource deadlock avoided
+])
+
+CHECK_LOCKFILE([lock_blocks_other_process], [1],
+  [lockfile|WARN|.file.~lock~: child does not inherit lock
+lockfile|WARN|.file.~lock~: cannot lock file because it is already locked by pid <pid>
+])
+
+CHECK_LOCKFILE([lock_twice_blocks_other_process], [1],
+  [lockfile|WARN|.file.~lock~: failed to lock file: Resource deadlock avoided
+lockfile|WARN|.file.~lock~: child does not inherit lock
+lockfile|WARN|.file.~lock~: cannot lock file because it is already locked by pid <pid>
+])
+
 CHECK_LOCKFILE([lock_and_unlock_allows_other_process], [1])
-CHECK_LOCKFILE([lock_multiple], [0])
-CHECK_LOCKFILE([lock_symlink], [0])
-CHECK_LOCKFILE([lock_symlink_to_dir], [0])
+
+CHECK_LOCKFILE([lock_multiple], [0],
+  [lockfile|WARN|.a.~lock~: failed to lock file: Resource deadlock avoided
+])
+
+CHECK_LOCKFILE([lock_symlink], [0],
+  [lockfile|WARN|.a.~lock~: failed to lock file: Resource deadlock avoided
+lockfile|WARN|.b.~lock~: failed to lock file: Resource deadlock avoided
+lockfile|WARN|.b.~lock~: failed to lock file: Resource deadlock avoided
+lockfile|WARN|.a.~lock~: failed to lock file: Resource deadlock avoided
+])
+
+CHECK_LOCKFILE([lock_symlink_to_dir], [0],
+  [lockfile|WARN|dir/.b.~lock~: failed to lock file: Resource deadlock avoided
+])
index ba5003a..7ba9277 100644 (file)
@@ -261,7 +261,8 @@ dnl Check that an empty Apply-Actions instruction gets dropped.
 #  7: 00 -> (none)
 0004 0008 00000000
 
-# bad OF1.1 instructions: NXBIC_DUP_TYPE
+dnl Duplicate instruction type:
+# bad OF1.1 instructions: OFPIT_BAD_INSTRUCTION
 0004 0008 00000000 0004 0008 00000000
 
 dnl Instructions not multiple of 8 in length.
index 3c55d91..9844592 100644 (file)
@@ -455,8 +455,8 @@ AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 OFPT_FLOW_MOD (xid=0x0): ADD priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2 idle:5 buf:0x10e out_port:0 actions=output:3
 ], [dnl
 ofp_util|INFO|normalization changed ofp_match, details:
-ofp_util|INFO| pre: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
-ofp_util|INFO|post: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2
+ofp_util|INFO| pre: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
+ofp_util|INFO|post: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2
 ])
 AT_CLEANUP
 
@@ -493,8 +493,8 @@ AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 OFPT_FLOW_MOD (xid=0x0): ADD arp,in_port=1,dl_vlan=65535,dl_vlan_pcp=0,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 idle:5 pri:65535 buf:0x10e out_port:0 actions=output:3
 ], [dnl
 ofp_util|INFO|normalization changed ofp_match, details:
-ofp_util|INFO| pre: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
-ofp_util|INFO|post: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2
+ofp_util|INFO| pre: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
+ofp_util|INFO|post: arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2
 ])
 AT_CLEANUP
 
index 84cc272..e903619 100644 (file)
@@ -736,6 +736,18 @@ AT_CHECK([RUN_OVS_VSCTL([clear netflow `cat netflow-uuid` targets])],
 AT_CHECK([RUN_OVS_VSCTL([destroy b br2])], 
   [1], [], [ovs-vsctl: no row "br2" in table Bridge
 ], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL([add i br1 name x])],
+  [1], [], [ovs-vsctl: cannot modify read-only column name in table Interface
+], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL([set port br1 name br2])],
+  [1], [], [ovs-vsctl: cannot modify read-only column name in table Port
+], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL([remove b br1 name br1])],
+  [1], [], [ovs-vsctl: cannot modify read-only column name in table Bridge
+], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL([clear b br1 name])],
+  [1], [], [ovs-vsctl: cannot modify read-only column name in table Bridge
+], [OVS_VSCTL_CLEANUP])
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
index 480f610..6a3b5d1 100644 (file)
@@ -111,6 +111,15 @@ gc_schema () {
          "isRoot": false}}}
 EOF
 }
+
+immutable_schema () {
+    cat <<'EOF'
+{"name": "immutable",
+ "tables": {
+    "a": {
+        "columns": {"i": {"type": "integer", "mutable": false}}}}}
+EOF
+}
 ]
 m4_divert_pop([PREPARE_TESTS])
 
@@ -908,6 +917,40 @@ OVSDB_CHECK_EXECUTION([weak references],
 [{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["set",[]]}]}]
 ]])
 
+OVSDB_CHECK_EXECUTION([immutable columns],
+  [immutable_schema],
+  [[[["immutable",
+      {"op": "insert",
+       "table": "a",
+       "row": {"i": 5},
+       "uuid-name": "row1"}]]],
+   [[["immutable",
+      {"op": "update",
+       "table": "a",
+       "row": {"i": 10},
+       "where": []}]]],
+   [[["immutable",
+      {"op": "update",
+       "table": "a",
+       "row": {"i": 5},
+       "where": []}]]],
+   [[["immutable",
+      {"op": "mutate",
+       "table": "a",
+       "where": [],
+       "mutations": [["i", "-=", 5]]}]]],
+   [[["immutable",
+      {"op": "mutate",
+       "table": "a",
+       "where": [],
+       "mutations": [["i", "*=", 1]]}]]]],
+  [[[{"uuid":["uuid","<0>"]}]
+[{"details":"Cannot update immutable column i in table a.","error":"constraint violation","syntax":"{\"op\":\"update\",\"row\":{\"i\":10},\"table\":\"a\",\"where\":[]}"}]
+[{"details":"Cannot update immutable column i in table a.","error":"constraint violation","syntax":"{\"op\":\"update\",\"row\":{\"i\":5},\"table\":\"a\",\"where\":[]}"}]
+[{"details":"Cannot mutate immutable column i in table a.","error":"constraint violation","syntax":"[\"i\",\"-=\",5]"}]
+[{"details":"Cannot mutate immutable column i in table a.","error":"constraint violation","syntax":"[\"i\",\"*=\",1]"}]
+]])
+
 OVSDB_CHECK_EXECUTION([garbage collection],
   [gc_schema],
   [dnl Check that inserting a row without any references is a no-op.
index 48e7489..68fe868 100644 (file)
@@ -429,3 +429,13 @@ OVSDB_CHECK_IDL([external-linking idl, consistent ops],
 002: i=1 k=1 ka=[] l2=0 uuid=<1>
 003: done
 ]])
+
+OVSDB_CHECK_IDL_PY([external-linking idl, insert ops],
+  [],
+  [['linktest']],
+  [[000: empty
+001: commit, status=success
+002: i=1 k=1 ka=[1] l2= uuid=<0>
+002: i=2 k=1 ka=[1 2] l2= uuid=<1>
+003: done
+]])
index 3d75317..fc898b5 100644 (file)
@@ -99,6 +99,18 @@ test-ovsdb: syntax "["u","delete",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"
 ]])
 AT_CLEANUP
 
+AT_SETUP([disallowed mutations on immutable columns])
+AT_KEYWORDS([ovsdb negative mutation])
+AT_CHECK([[test-ovsdb parse-mutations \
+    '{"columns":
+        {"i": {"type": "integer", "mutable": false}}}' \
+    '[["i", "+=", 1]]'
+]],
+  [1], [],
+  [[test-ovsdb: syntax "["i","+=",1]": constraint violation: Cannot mutate immutable column i in table mytable.
+]])
+AT_CLEANUP
+
 OVSDB_CHECK_POSITIVE([mutations on sets],
   [[parse-mutations \
     '{"columns":
index bb37170..551e8a3 100644 (file)
@@ -59,7 +59,7 @@ run
   should connect
 connected
   in ACTIVE for 0 ms (0 ms backoff)
-  created 1000, last received 1000, last connected 1000
+  created 1000, last activity 1000, last connected 1000
   1 successful connections out of 1 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
@@ -129,7 +129,7 @@ advance 500
 run
 connected
   in ACTIVE for 0 ms (0 ms backoff)
-  created 1000, last received 1000, last connected 1500
+  created 1000, last activity 1000, last connected 1500
   1 successful connections out of 1 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
@@ -408,7 +408,7 @@ run
   should connect
 connected
   in ACTIVE for 0 ms (0 ms backoff)
-  created 1000, last received 1000, last connected 1000
+  created 1000, last activity 1000, last connected 1000
   1 successful connections out of 1 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
@@ -448,7 +448,7 @@ run
   should connect
 connected
   in ACTIVE for 0 ms (1000 ms backoff)
-  created 1000, last received 1000, last connected 12000
+  created 1000, last activity 1000, last connected 12000
   2 successful connections out of 2 attempts, seqno 3
   connected
   last connected 0 ms ago, connected 10000 ms total
@@ -488,7 +488,7 @@ run
   should connect
 connected
   in ACTIVE for 0 ms (2000 ms backoff)
-  created 1000, last received 1000, last connected 24000
+  created 1000, last activity 1000, last connected 24000
   3 successful connections out of 3 attempts, seqno 5
   connected
   last connected 0 ms ago, connected 20000 ms total
@@ -625,7 +625,7 @@ advance 500
 run
 connected
   in ACTIVE for 0 ms (2000 ms backoff)
-  created 1000, last received 1000, last connected 6500
+  created 1000, last activity 1000, last connected 6500
   1 successful connections out of 3 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
@@ -688,13 +688,13 @@ connected
 # Connection receives 3 chunks of data spaced 250 ms apart.
 advance 250
 run
-received
+activity
 advance 250
 run
-received
+activity
 advance 250
 run
-received
+activity
 
 # Connection drops.
 disconnected
@@ -767,7 +767,7 @@ advance 500
 run
 connected
   in ACTIVE for 0 ms (2000 ms backoff)
-  created 1000, last received 1000, last connected 6500
+  created 1000, last activity 1000, last connected 6500
   1 successful connections out of 3 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
@@ -778,22 +778,22 @@ advance 250
 ### t=6750 ###
   in ACTIVE for 250 ms (2000 ms backoff)
 run
-received
-  created 1000, last received 6750, last connected 6500
+activity
+  created 1000, last activity 6750, last connected 6500
 advance 250
 
 ### t=7000 ###
   in ACTIVE for 500 ms (2000 ms backoff)
 run
-received
-  created 1000, last received 7000, last connected 6500
+activity
+  created 1000, last activity 7000, last connected 6500
 advance 250
 
 ### t=7250 ###
   in ACTIVE for 750 ms (2000 ms backoff)
 run
-received
-  created 1000, last received 7250, last connected 6500
+activity
+  created 1000, last activity 7250, last connected 6500
 
 # Connection drops.
 disconnected
@@ -849,13 +849,13 @@ connected
 # Connection receives 3 chunks of data spaced 2000 ms apart.
 advance 2000
 run
-received
+activity
 advance 2000
 run
-received
+activity
 advance 2000
 run
-received
+activity
 
 # Connection drops.
 disconnected
@@ -928,7 +928,7 @@ advance 500
 run
 connected
   in ACTIVE for 0 ms (2000 ms backoff)
-  created 1000, last received 1000, last connected 6500
+  created 1000, last activity 1000, last connected 6500
   1 successful connections out of 3 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
@@ -939,22 +939,22 @@ advance 2000
 ### t=8500 ###
   in ACTIVE for 2000 ms (2000 ms backoff)
 run
-received
-  created 1000, last received 8500, last connected 6500
+activity
+  created 1000, last activity 8500, last connected 6500
 advance 2000
 
 ### t=10500 ###
   in ACTIVE for 4000 ms (2000 ms backoff)
 run
-received
-  created 1000, last received 10500, last connected 6500
+activity
+  created 1000, last activity 10500, last connected 6500
 advance 2000
 
 ### t=12500 ###
   in ACTIVE for 6000 ms (2000 ms backoff)
 run
-received
-  created 1000, last received 12500, last connected 6500
+activity
+  created 1000, last activity 12500, last connected 6500
 
 # Connection drops.
 disconnected
@@ -1060,7 +1060,7 @@ run
   should connect
 connected
   in ACTIVE for 0 ms (0 ms backoff)
-  created 1000, last received 1000, last connected 1000
+  created 1000, last activity 1000, last connected 1000
   1 successful connections out of 1 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
@@ -1128,9 +1128,9 @@ listening
 
 # Connection accepted.
 connected
-received
+activity
 advance 1000
-received
+activity
 
 # Connection times out.
 timeout
@@ -1178,18 +1178,18 @@ listening
 # Connection accepted.
 connected
   in ACTIVE for 0 ms (1000 ms backoff)
-  created 1000, last received 1000, last connected 2000
+  created 1000, last activity 1000, last connected 2000
   1 successful connections out of 1 attempts, seqno 1
   connected
   last connected 0 ms ago, connected 0 ms total
-received
-  created 1000, last received 2000, last connected 2000
+activity
+  created 1000, last activity 2000, last connected 2000
 advance 1000
 
 ### t=3000 ###
   in ACTIVE for 1000 ms (1000 ms backoff)
-received
-  created 1000, last received 3000, last connected 2000
+activity
+  created 1000, last activity 3000, last connected 2000
 
 # Connection times out.
 timeout
index f2d9b82..aa8b6f0 100644 (file)
@@ -136,6 +136,7 @@ main(int argc, char *argv[])
     flows = xmalloc(N_FLOWS * sizeof *flows);
     for (i = 0; i < N_FLOWS; i++) {
         random_bytes(&flows[i], sizeof flows[i]);
+        memset(flows[i].zeros, 0, sizeof flows[i].zeros);
         flows[i].regs[0] = OFPP_NONE;
     }
 
index e7cf734..a7daa94 100644 (file)
 
 /* Fields in a rule. */
 #define CLS_FIELDS                                                  \
-    /*                                    struct flow  all-caps */  \
-    /*        FWW_* bit(s)                member name  name     */  \
-    /*        --------------------------  -----------  -------- */  \
-    CLS_FIELD(0,                          tun_id,      TUN_ID)      \
-    CLS_FIELD(0,                          metadata,    METADATA)    \
-    CLS_FIELD(0,                          nw_src,      NW_SRC)      \
-    CLS_FIELD(0,                          nw_dst,      NW_DST)      \
-    CLS_FIELD(FWW_IN_PORT,                in_port,     IN_PORT)     \
-    CLS_FIELD(0,                          vlan_tci,    VLAN_TCI)    \
-    CLS_FIELD(FWW_DL_TYPE,                dl_type,     DL_TYPE)     \
-    CLS_FIELD(0,                          tp_src,      TP_SRC)      \
-    CLS_FIELD(0,                          tp_dst,      TP_DST)      \
-    CLS_FIELD(0,                          dl_src,      DL_SRC)      \
-    CLS_FIELD(0,                          dl_dst,      DL_DST)      \
-    CLS_FIELD(FWW_NW_PROTO,               nw_proto,    NW_PROTO)    \
-    CLS_FIELD(FWW_NW_DSCP,                nw_tos,      NW_DSCP)
+    /*        struct flow  all-caps */  \
+    /*        member name  name     */  \
+    /*        -----------  -------- */  \
+    CLS_FIELD(tun_id,      TUN_ID)      \
+    CLS_FIELD(metadata,    METADATA)    \
+    CLS_FIELD(nw_src,      NW_SRC)      \
+    CLS_FIELD(nw_dst,      NW_DST)      \
+    CLS_FIELD(in_port,     IN_PORT)     \
+    CLS_FIELD(vlan_tci,    VLAN_TCI)    \
+    CLS_FIELD(dl_type,     DL_TYPE)     \
+    CLS_FIELD(tp_src,      TP_SRC)      \
+    CLS_FIELD(tp_dst,      TP_DST)      \
+    CLS_FIELD(dl_src,      DL_SRC)      \
+    CLS_FIELD(dl_dst,      DL_DST)      \
+    CLS_FIELD(nw_proto,    NW_PROTO)    \
+    CLS_FIELD(nw_tos,      NW_DSCP)
 
 /* Field indexes.
  *
  * (These are also indexed into struct classifier's 'tables' array.) */
 enum {
-#define CLS_FIELD(WILDCARDS, MEMBER, NAME) CLS_F_IDX_##NAME,
+#define CLS_FIELD(MEMBER, NAME) CLS_F_IDX_##NAME,
     CLS_FIELDS
 #undef CLS_FIELD
     CLS_N_FIELDS
@@ -72,15 +72,13 @@ enum {
 struct cls_field {
     int ofs;                    /* Offset in struct flow. */
     int len;                    /* Length in bytes. */
-    flow_wildcards_t wildcards; /* FWW_* bit or bits for this field. */
     const char *name;           /* Name (for debugging). */
 };
 
 static const struct cls_field cls_fields[CLS_N_FIELDS] = {
-#define CLS_FIELD(WILDCARDS, MEMBER, NAME)      \
+#define CLS_FIELD(MEMBER, NAME)                 \
     { offsetof(struct flow, MEMBER),            \
       sizeof ((struct flow *)0)->MEMBER,        \
-      WILDCARDS,                                \
       #NAME },
     CLS_FIELDS
 #undef CLS_FIELD
@@ -97,6 +95,11 @@ test_rule_from_cls_rule(const struct cls_rule *rule)
     return rule ? CONTAINER_OF(rule, struct test_rule, cls_rule) : NULL;
 }
 
+static struct test_rule *make_rule(int wc_fields, unsigned int priority,
+                                   int value_pat);
+static void free_rule(struct test_rule *);
+static struct test_rule *clone_rule(const struct test_rule *);
+
 /* Trivial (linear) classifier. */
 struct tcls {
     size_t n_rules;
@@ -136,14 +139,12 @@ tcls_insert(struct tcls *tcls, const struct test_rule *rule)
 {
     size_t i;
 
-    assert(!flow_wildcards_is_exact(&rule->cls_rule.wc)
-           || rule->cls_rule.priority == UINT_MAX);
     for (i = 0; i < tcls->n_rules; i++) {
         const struct cls_rule *pos = &tcls->rules[i]->cls_rule;
         if (cls_rule_equal(pos, &rule->cls_rule)) {
             /* Exact match. */
-            free(tcls->rules[i]);
-            tcls->rules[i] = xmemdup(rule, sizeof *rule);
+            free_rule(tcls->rules[i]);
+            tcls->rules[i] = clone_rule(rule);
             return tcls->rules[i];
         } else if (pos->priority < rule->cls_rule.priority) {
             break;
@@ -158,7 +159,7 @@ tcls_insert(struct tcls *tcls, const struct test_rule *rule)
         memmove(&tcls->rules[i + 1], &tcls->rules[i],
                 sizeof *tcls->rules * (tcls->n_rules - i));
     }
-    tcls->rules[i] = xmemdup(rule, sizeof *rule);
+    tcls->rules[i] = clone_rule(rule);
     tcls->n_rules++;
     return tcls->rules[i];
 }
@@ -182,43 +183,54 @@ tcls_remove(struct tcls *cls, const struct test_rule *rule)
 }
 
 static bool
-match(const struct cls_rule *wild, const struct flow *fixed)
+match(const struct cls_rule *wild_, const struct flow *fixed)
 {
+    struct match wild;
     int f_idx;
 
+    minimatch_expand(&wild_->match, &wild);
     for (f_idx = 0; f_idx < CLS_N_FIELDS; f_idx++) {
-        const struct cls_field *f = &cls_fields[f_idx];
         bool eq;
 
-        if (f->wildcards) {
-            void *wild_field = (char *) &wild->flow + f->ofs;
-            void *fixed_field = (char *) fixed + f->ofs;
-            eq = ((wild->wc.wildcards & f->wildcards) == f->wildcards
-                  || !memcmp(wild_field, fixed_field, f->len));
-        } else if (f_idx == CLS_F_IDX_NW_SRC) {
-            eq = !((fixed->nw_src ^ wild->flow.nw_src) & wild->wc.nw_src_mask);
+        if (f_idx == CLS_F_IDX_NW_SRC) {
+            eq = !((fixed->nw_src ^ wild.flow.nw_src)
+                   & wild.wc.masks.nw_src);
         } else if (f_idx == CLS_F_IDX_NW_DST) {
-            eq = !((fixed->nw_dst ^ wild->flow.nw_dst) & wild->wc.nw_dst_mask);
+            eq = !((fixed->nw_dst ^ wild.flow.nw_dst)
+                   & wild.wc.masks.nw_dst);
         } else if (f_idx == CLS_F_IDX_TP_SRC) {
-            eq = !((fixed->tp_src ^ wild->flow.tp_src) & wild->wc.tp_src_mask);
+            eq = !((fixed->tp_src ^ wild.flow.tp_src)
+                   & wild.wc.masks.tp_src);
         } else if (f_idx == CLS_F_IDX_TP_DST) {
-            eq = !((fixed->tp_dst ^ wild->flow.tp_dst) & wild->wc.tp_dst_mask);
+            eq = !((fixed->tp_dst ^ wild.flow.tp_dst)
+                   & wild.wc.masks.tp_dst);
         } else if (f_idx == CLS_F_IDX_DL_SRC) {
-            eq = eth_addr_equal_except(fixed->dl_src, wild->flow.dl_src,
-                                       wild->wc.dl_src_mask);
+            eq = eth_addr_equal_except(fixed->dl_src, wild.flow.dl_src,
+                                       wild.wc.masks.dl_src);
         } else if (f_idx == CLS_F_IDX_DL_DST) {
-            eq = eth_addr_equal_except(fixed->dl_dst, wild->flow.dl_dst,
-                                       wild->wc.dl_dst_mask);
+            eq = eth_addr_equal_except(fixed->dl_dst, wild.flow.dl_dst,
+                                       wild.wc.masks.dl_dst);
         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
-            eq = !((fixed->vlan_tci ^ wild->flow.vlan_tci)
-                   & wild->wc.vlan_tci_mask);
+            eq = !((fixed->vlan_tci ^ wild.flow.vlan_tci)
+                   & wild.wc.masks.vlan_tci);
         } else if (f_idx == CLS_F_IDX_TUN_ID) {
-            eq = !((fixed->tun_id ^ wild->flow.tun_id) & wild->wc.tun_id_mask);
+            eq = !((fixed->tun_id ^ wild.flow.tun_id)
+                   & wild.wc.masks.tun_id);
         } else if (f_idx == CLS_F_IDX_METADATA) {
-            eq = !((fixed->metadata ^ wild->flow.metadata)
-                   & wild->wc.metadata_mask);
+            eq = !((fixed->metadata ^ wild.flow.metadata)
+                   & wild.wc.masks.metadata);
         } else if (f_idx == CLS_F_IDX_NW_DSCP) {
-            eq = !((fixed->nw_tos ^ wild->flow.nw_tos) & IP_DSCP_MASK);
+            eq = !((fixed->nw_tos ^ wild.flow.nw_tos) &
+                   (wild.wc.masks.nw_tos & IP_DSCP_MASK));
+        } else if (f_idx == CLS_F_IDX_NW_PROTO) {
+            eq = !((fixed->nw_proto ^ wild.flow.nw_proto)
+                   & wild.wc.masks.nw_proto);
+        } else if (f_idx == CLS_F_IDX_DL_TYPE) {
+            eq = !((fixed->dl_type ^ wild.flow.dl_type)
+                   & wild.wc.masks.dl_type);
+        } else if (f_idx == CLS_F_IDX_IN_PORT) {
+            eq = !((fixed->in_port ^ wild.flow.in_port)
+                   & wild.wc.masks.in_port);
         } else {
             NOT_REACHED();
         }
@@ -251,12 +263,17 @@ tcls_delete_matches(struct tcls *cls, const struct cls_rule *target)
 
     for (i = 0; i < cls->n_rules; ) {
         struct test_rule *pos = cls->rules[i];
-        if (!flow_wildcards_has_extra(&pos->cls_rule.wc, &target->wc)
-            && match(target, &pos->cls_rule.flow)) {
-            tcls_remove(cls, pos);
-        } else {
-            i++;
+        if (!minimask_has_extra(&pos->cls_rule.match.mask,
+                                &target->match.mask)) {
+            struct flow flow;
+
+            miniflow_expand(&pos->cls_rule.match.flow, &flow);
+            if (match(target, &flow)) {
+                tcls_remove(cls, pos);
+                continue;
+            }
         }
+        i++;
     }
 }
 \f
@@ -377,6 +394,7 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
         unsigned int x;
 
         x = rand () % N_FLOW_VALUES;
+        memset(&flow, 0, sizeof flow);
         flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
         flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
         flow.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)];
@@ -415,7 +433,7 @@ destroy_classifier(struct classifier *cls)
     cls_cursor_init(&cursor, cls, NULL);
     CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cls_rule, &cursor) {
         classifier_remove(cls, &rule->cls_rule);
-        free(rule);
+        free_rule(rule);
     }
     classifier_destroy(cls);
 }
@@ -470,42 +488,69 @@ make_rule(int wc_fields, unsigned int priority, int value_pat)
 {
     const struct cls_field *f;
     struct test_rule *rule;
+    struct match match;
 
-    rule = xzalloc(sizeof *rule);
-    cls_rule_init_catchall(&rule->cls_rule, wc_fields ? priority : UINT_MAX);
+    match_init_catchall(&match);
     for (f = &cls_fields[0]; f < &cls_fields[CLS_N_FIELDS]; f++) {
         int f_idx = f - cls_fields;
         int value_idx = (value_pat & (1u << f_idx)) != 0;
-        memcpy((char *) &rule->cls_rule.flow + f->ofs,
+        memcpy((char *) &match.flow + f->ofs,
                values[f_idx][value_idx], f->len);
 
-        if (f->wildcards) {
-            rule->cls_rule.wc.wildcards &= ~f->wildcards;
-        } else if (f_idx == CLS_F_IDX_NW_SRC) {
-            rule->cls_rule.wc.nw_src_mask = htonl(UINT32_MAX);
+        if (f_idx == CLS_F_IDX_NW_SRC) {
+            match.wc.masks.nw_src = htonl(UINT32_MAX);
         } else if (f_idx == CLS_F_IDX_NW_DST) {
-            rule->cls_rule.wc.nw_dst_mask = htonl(UINT32_MAX);
+            match.wc.masks.nw_dst = htonl(UINT32_MAX);
         } else if (f_idx == CLS_F_IDX_TP_SRC) {
-            rule->cls_rule.wc.tp_src_mask = htons(UINT16_MAX);
+            match.wc.masks.tp_src = htons(UINT16_MAX);
         } else if (f_idx == CLS_F_IDX_TP_DST) {
-            rule->cls_rule.wc.tp_dst_mask = htons(UINT16_MAX);
+            match.wc.masks.tp_dst = htons(UINT16_MAX);
         } else if (f_idx == CLS_F_IDX_DL_SRC) {
-            memset(rule->cls_rule.wc.dl_src_mask, 0xff, ETH_ADDR_LEN);
+            memset(match.wc.masks.dl_src, 0xff, ETH_ADDR_LEN);
         } else if (f_idx == CLS_F_IDX_DL_DST) {
-            memset(rule->cls_rule.wc.dl_dst_mask, 0xff, ETH_ADDR_LEN);
+            memset(match.wc.masks.dl_dst, 0xff, ETH_ADDR_LEN);
         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
-            rule->cls_rule.wc.vlan_tci_mask = htons(UINT16_MAX);
+            match.wc.masks.vlan_tci = htons(UINT16_MAX);
         } else if (f_idx == CLS_F_IDX_TUN_ID) {
-            rule->cls_rule.wc.tun_id_mask = htonll(UINT64_MAX);
+            match.wc.masks.tun_id = htonll(UINT64_MAX);
         } else if (f_idx == CLS_F_IDX_METADATA) {
-            rule->cls_rule.wc.metadata_mask = htonll(UINT64_MAX);
+            match.wc.masks.metadata = htonll(UINT64_MAX);
+        } else if (f_idx == CLS_F_IDX_NW_DSCP) {
+            match.wc.masks.nw_tos |= IP_DSCP_MASK;
+        } else if (f_idx == CLS_F_IDX_NW_PROTO) {
+            match.wc.masks.nw_proto = UINT8_MAX;
+        } else if (f_idx == CLS_F_IDX_DL_TYPE) {
+            match.wc.masks.dl_type = htons(UINT16_MAX);
+        } else if (f_idx == CLS_F_IDX_IN_PORT) {
+            match.wc.masks.in_port = UINT16_MAX;
         } else {
             NOT_REACHED();
         }
     }
+
+    rule = xzalloc(sizeof *rule);
+    cls_rule_init(&rule->cls_rule, &match, wc_fields ? priority : UINT_MAX);
     return rule;
 }
 
+static struct test_rule *
+clone_rule(const struct test_rule *src)
+{
+    struct test_rule *dst;
+
+    dst = xmalloc(sizeof *dst);
+    dst->aux = src->aux;
+    cls_rule_clone(&dst->cls_rule, &src->cls_rule);
+    return dst;
+}
+
+static void
+free_rule(struct test_rule *rule)
+{
+    cls_rule_destroy(&rule->cls_rule);
+    free(rule);
+}
+
 static void
 shuffle(unsigned int *p, size_t n)
 {
@@ -516,7 +561,20 @@ shuffle(unsigned int *p, size_t n)
         *q = tmp;
     }
 }
+
+static void
+shuffle_u32s(uint32_t *p, size_t n)
+{
+    for (; n > 1; n--, p++) {
+        uint32_t *q = &p[rand() % n];
+        uint32_t tmp = *p;
+        *p = *q;
+        *q = tmp;
+    }
+}
 \f
+/* Classifier tests. */
+
 /* Tests an empty classifier. */
 static void
 test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
@@ -568,7 +626,7 @@ test_single_rule(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         assert(tcls_is_empty(&tcls));
         compare_classifiers(&cls, &tcls);
 
-        free(rule);
+        free_rule(rule);
         classifier_destroy(&cls);
         tcls_destroy(&tcls);
     }
@@ -603,7 +661,7 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         tcls_insert(&tcls, rule2);
         assert(test_rule_from_cls_rule(
                    classifier_replace(&cls, &rule2->cls_rule)) == rule1);
-        free(rule1);
+        free_rule(rule1);
         check_tables(&cls, 1, 1, 0);
         compare_classifiers(&cls, &tcls);
         tcls_destroy(&tcls);
@@ -744,7 +802,7 @@ test_many_rules_in_one_list (int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
             tcls_destroy(&tcls);
 
             for (i = 0; i < N_RULES; i++) {
-                free(rules[i]);
+                free_rule(rules[i]);
             }
         } while (next_permutation(ops, ARRAY_SIZE(ops)));
         assert(n_permutations == (factorial(N_RULES * 2) >> N_RULES));
@@ -757,7 +815,7 @@ count_ones(unsigned long int x)
     int n = 0;
 
     while (x) {
-        x &= x - 1;
+        x = zero_rightmost_1bit(x);
         n++;
     }
 
@@ -822,7 +880,7 @@ test_many_rules_in_one_table(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         for (i = 0; i < N_RULES; i++) {
             tcls_remove(&tcls, tcls_rules[i]);
             classifier_remove(&cls, &rules[i]->cls_rule);
-            free(rules[i]);
+            free_rule(rules[i]);
 
             check_tables(&cls, i < N_RULES - 1, N_RULES - (i + 1), 0);
             compare_classifiers(&cls, &tcls);
@@ -881,18 +939,17 @@ test_many_rules_in_n_tables(int n_tables)
             struct test_rule *target;
             struct cls_cursor cursor;
 
-            target = xmemdup(tcls.rules[rand() % tcls.n_rules],
-                             sizeof(struct test_rule));
+            target = clone_rule(tcls.rules[rand() % tcls.n_rules]);
 
             cls_cursor_init(&cursor, &cls, &target->cls_rule);
             CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cls_rule, &cursor) {
                 classifier_remove(&cls, &rule->cls_rule);
-                free(rule);
+                free_rule(rule);
             }
             tcls_delete_matches(&tcls, &target->cls_rule);
             compare_classifiers(&cls, &tcls);
             check_tables(&cls, -1, -1, -1);
-            free(target);
+            free_rule(target);
         }
 
         destroy_classifier(&cls);
@@ -912,7 +969,301 @@ test_many_rules_in_five_tables(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     test_many_rules_in_n_tables(5);
 }
 \f
+/* Miniflow tests. */
+
+static uint32_t
+random_value(void)
+{
+    static const uint32_t values[] =
+        { 0xffffffff, 0xaaaaaaaa, 0x55555555, 0x80000000,
+          0x00000001, 0xface0000, 0x00d00d1e, 0xdeadbeef };
+
+    return values[random_uint32() % ARRAY_SIZE(values)];
+}
+
+static bool
+choose(unsigned int n, unsigned int *idxp)
+{
+    if (*idxp < n) {
+        return true;
+    } else {
+        *idxp -= n;
+        return false;
+    }
+}
+
+static bool
+init_consecutive_values(int n_consecutive, struct flow *flow,
+                        unsigned int *idxp)
+{
+    uint32_t *flow_u32 = (uint32_t *) flow;
+
+    if (choose(FLOW_U32S - n_consecutive + 1, idxp)) {
+        int i;
+
+        for (i = 0; i < n_consecutive; i++) {
+            flow_u32[*idxp + i] = random_value();
+        }
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static bool
+next_random_flow(struct flow *flow, unsigned int idx)
+{
+    uint32_t *flow_u32 = (uint32_t *) flow;
+    int i;
+
+    memset(flow, 0, sizeof *flow);
+
+    /* Empty flow. */
+    if (choose(1, &idx)) {
+        return true;
+    }
+
+    /* All flows with a small number of consecutive nonzero values. */
+    for (i = 1; i <= 4; i++) {
+        if (init_consecutive_values(i, flow, &idx)) {
+            return true;
+        }
+    }
+
+    /* All flows with a large number of consecutive nonzero values. */
+    for (i = FLOW_U32S - 4; i <= FLOW_U32S; i++) {
+        if (init_consecutive_values(i, flow, &idx)) {
+            return true;
+        }
+    }
+
+    /* All flows with exactly two nonconsecutive nonzero values. */
+    if (choose((FLOW_U32S - 1) * (FLOW_U32S - 2) / 2, &idx)) {
+        int ofs1;
+
+        for (ofs1 = 0; ofs1 < FLOW_U32S - 2; ofs1++) {
+            int ofs2;
+
+            for (ofs2 = ofs1 + 2; ofs2 < FLOW_U32S; ofs2++) {
+                if (choose(1, &idx)) {
+                    flow_u32[ofs1] = random_value();
+                    flow_u32[ofs2] = random_value();
+                    return true;
+                }
+            }
+        }
+        NOT_REACHED();
+    }
+
+    /* 16 randomly chosen flows with N >= 3 nonzero values. */
+    if (choose(16 * (FLOW_U32S - 4), &idx)) {
+        int n = idx / 16 + 3;
+        int i;
+
+        for (i = 0; i < n; i++) {
+            flow_u32[i] = random_value();
+        }
+        shuffle_u32s(flow_u32, FLOW_U32S);
+
+        return true;
+    }
+
+    return false;
+}
+
+static void
+any_random_flow(struct flow *flow)
+{
+    static unsigned int max;
+    if (!max) {
+        while (next_random_flow(flow, max)) {
+            max++;
+        }
+    }
+
+    next_random_flow(flow, random_range(max));
+}
+
+static void
+toggle_masked_flow_bits(struct flow *flow, const struct flow_wildcards *mask)
+{
+    const uint32_t *mask_u32 = (const uint32_t *) &mask->masks;
+    uint32_t *flow_u32 = (uint32_t *) flow;
+    int i;
+
+    for (i = 0; i < FLOW_U32S; i++) {
+        if (mask_u32[i] != 0) {
+            uint32_t bit;
+
+            do {
+                bit = 1u << random_range(32);
+            } while (!(bit & mask_u32[i]));
+            flow_u32[i] ^= bit;
+        }
+    }
+}
+
+static void
+wildcard_extra_bits(struct flow_wildcards *mask)
+{
+    uint32_t *mask_u32 = (uint32_t *) &mask->masks;
+    int i;
+
+    for (i = 0; i < FLOW_U32S; i++) {
+        if (mask_u32[i] != 0) {
+            uint32_t bit;
+
+            do {
+                bit = 1u << random_range(32);
+            } while (!(bit & mask_u32[i]));
+            mask_u32[i] &= ~bit;
+        }
+    }
+}
+
+static void
+test_miniflow(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct flow flow;
+    unsigned int idx;
+
+    random_set_seed(0xb3faca38);
+    for (idx = 0; next_random_flow(&flow, idx); idx++) {
+        const uint32_t *flow_u32 = (const uint32_t *) &flow;
+        struct miniflow miniflow, miniflow2, miniflow3;
+        struct flow flow2, flow3;
+        struct flow_wildcards mask;
+        struct minimask minimask;
+        int i;
+
+        /* Convert flow to miniflow. */
+        miniflow_init(&miniflow, &flow);
+
+        /* Check that the flow equals its miniflow. */
+        assert(miniflow_get_vid(&miniflow) == vlan_tci_to_vid(flow.vlan_tci));
+        for (i = 0; i < FLOW_U32S; i++) {
+            assert(miniflow_get(&miniflow, i) == flow_u32[i]);
+        }
+
+        /* Check that the miniflow equals itself. */
+        assert(miniflow_equal(&miniflow, &miniflow));
+
+        /* Convert miniflow back to flow and verify that it's the same. */
+        miniflow_expand(&miniflow, &flow2);
+        assert(flow_equal(&flow, &flow2));
+
+        /* Check that copying a miniflow works properly. */
+        miniflow_clone(&miniflow2, &miniflow);
+        assert(miniflow_equal(&miniflow, &miniflow2));
+        assert(miniflow_hash(&miniflow, 0) == miniflow_hash(&miniflow2, 0));
+        miniflow_expand(&miniflow2, &flow3);
+        assert(flow_equal(&flow, &flow3));
+
+        /* Check that masked matches work as expected for identical flows and
+         * miniflows. */
+        do {
+            next_random_flow(&mask.masks, 1);
+        } while (flow_wildcards_is_catchall(&mask));
+        minimask_init(&minimask, &mask);
+        assert(minimask_is_catchall(&minimask)
+               == flow_wildcards_is_catchall(&mask));
+        assert(miniflow_equal_in_minimask(&miniflow, &miniflow2, &minimask));
+        assert(miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
+        assert(miniflow_hash_in_minimask(&miniflow, &minimask, 0x12345678) ==
+               flow_hash_in_minimask(&flow, &minimask, 0x12345678));
+
+        /* Check that masked matches work as expected for differing flows and
+         * miniflows. */
+        toggle_masked_flow_bits(&flow2, &mask);
+        assert(!miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
+        miniflow_init(&miniflow3, &flow2);
+        assert(!miniflow_equal_in_minimask(&miniflow, &miniflow3, &minimask));
+
+        /* Clean up. */
+        miniflow_destroy(&miniflow);
+        miniflow_destroy(&miniflow2);
+        miniflow_destroy(&miniflow3);
+        minimask_destroy(&minimask);
+    }
+}
+
+static void
+test_minimask_has_extra(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct flow_wildcards catchall;
+    struct minimask minicatchall;
+    struct flow flow;
+    unsigned int idx;
+
+    flow_wildcards_init_catchall(&catchall);
+    minimask_init(&minicatchall, &catchall);
+    assert(minimask_is_catchall(&minicatchall));
+
+    random_set_seed(0x2ec7905b);
+    for (idx = 0; next_random_flow(&flow, idx); idx++) {
+        struct flow_wildcards mask;
+        struct minimask minimask;
+
+        mask.masks = flow;
+        minimask_init(&minimask, &mask);
+        assert(!minimask_has_extra(&minimask, &minimask));
+        assert(minimask_has_extra(&minicatchall, &minimask)
+               == !minimask_is_catchall(&minimask));
+        if (!minimask_is_catchall(&minimask)) {
+            struct minimask minimask2;
+
+            wildcard_extra_bits(&mask);
+            minimask_init(&minimask2, &mask);
+            assert(minimask_has_extra(&minimask2, &minimask));
+            assert(!minimask_has_extra(&minimask, &minimask2));
+            minimask_destroy(&minimask2);
+        }
+
+        minimask_destroy(&minimask);
+    }
+}
+
+static void
+test_minimask_combine(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct flow_wildcards catchall;
+    struct minimask minicatchall;
+    struct flow flow;
+    unsigned int idx;
+
+    flow_wildcards_init_catchall(&catchall);
+    minimask_init(&minicatchall, &catchall);
+    assert(minimask_is_catchall(&minicatchall));
+
+    random_set_seed(0x181bf0cd);
+    for (idx = 0; next_random_flow(&flow, idx); idx++) {
+        struct minimask minimask, minimask2, minicombined;
+        struct flow_wildcards mask, mask2, combined, combined2;
+        uint32_t storage[FLOW_U32S];
+        struct flow flow2;
+
+        mask.masks = flow;
+        minimask_init(&minimask, &mask);
+
+        minimask_combine(&minicombined, &minimask, &minicatchall, storage);
+        assert(minimask_is_catchall(&minicombined));
+
+        any_random_flow(&flow2);
+        mask2.masks = flow2;
+        minimask_init(&minimask2, &mask2);
+
+        minimask_combine(&minicombined, &minimask, &minimask2, storage);
+        flow_wildcards_combine(&combined, &mask, &mask2);
+        minimask_expand(&minicombined, &combined2);
+        assert(flow_wildcards_equal(&combined, &combined2));
+
+        minimask_destroy(&minimask);
+        minimask_destroy(&minimask2);
+    }
+}
+\f
 static const struct command commands[] = {
+    /* Classifier tests. */
     {"empty", 0, 0, test_empty},
     {"destroy-null", 0, 0, test_destroy_null},
     {"single-rule", 0, 0, test_single_rule},
@@ -921,6 +1272,12 @@ static const struct command commands[] = {
     {"many-rules-in-one-table", 0, 0, test_many_rules_in_one_table},
     {"many-rules-in-two-tables", 0, 0, test_many_rules_in_two_tables},
     {"many-rules-in-five-tables", 0, 0, test_many_rules_in_five_tables},
+
+    /* Miniflow and minimask tests. */
+    {"miniflow", 0, 0, test_miniflow},
+       {"minimask_has_extra", 0, 0, test_minimask_has_extra},
+       {"minimask_combine", 0, 0, test_minimask_combine},
+
     {NULL, 0, 0, NULL},
 };
 
index 33417e0..a4d7c09 100644 (file)
@@ -56,7 +56,7 @@ main(int argc OVS_UNUSED, char *argv[])
     while (fread(&expected_match, sizeof expected_match, 1, flows)) {
         struct ofpbuf *packet;
         struct ofp10_match extracted_match;
-        struct cls_rule rule;
+        struct match match;
         struct flow flow;
 
         n++;
@@ -69,8 +69,8 @@ main(int argc OVS_UNUSED, char *argv[])
         }
 
         flow_extract(packet, 0, 0, 1, &flow);
-        cls_rule_init_exact(&flow, 0, &rule);
-        ofputil_cls_rule_to_ofp10_match(&rule, &extracted_match);
+        match_init_exact(&match, &flow);
+        ofputil_match_to_ofp10_match(&match, &extracted_match);
 
         if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) {
             char *exp_s = ofp10_match_to_string(&expected_match, 2);
@@ -80,7 +80,7 @@ main(int argc OVS_UNUSED, char *argv[])
             printf("Packet:\n");
             ofp_print_packet(stdout, packet->data, packet->size);
             ovs_hex_dump(stdout, packet->data, packet->size, 0, true);
-            cls_rule_print(&rule);
+            match_print(&match);
             printf("Expected flow:\n%s\n", exp_s);
             printf("Actually extracted flow:\n%s\n", got_s);
             ovs_hex_dump(stdout, &expected_match, sizeof expected_match, 0, false);
index bdf1435..d53ba2e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira, Inc.
+ * Copyright (c) 2009, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,6 +40,12 @@ hash_words_cb(uint32_t input)
     return hash_words(&input, 1, 0);
 }
 
+static uint32_t
+mhash_words_cb(uint32_t input)
+{
+    return mhash_words(&input, 1, 0);
+}
+
 static uint32_t
 hash_int_cb(uint32_t input)
 {
@@ -76,11 +82,37 @@ check_word_hash(uint32_t (*hash)(uint32_t), const char *name,
     }
 }
 
-int
-main(void)
+static void
+check_3word_hash(uint32_t (*hash)(const uint32_t[], size_t, uint32_t),
+                 const char *name)
 {
     int i, j;
 
+    for (i = 0; i <= 96; i++) {
+        for (j = i + 1; j <= 96; j++) {
+            uint32_t in1[3], in2[3];
+            uint32_t out1, out2;
+            const int min_unique = 12;
+            const uint32_t unique_mask = (UINT32_C(1) << min_unique) - 1;
+
+            set_bit(in1, i);
+            set_bit(in2, j);
+            out1 = hash(in1, 3, 0);
+            out2 = hash(in2, 3, 0);
+            if ((out1 & unique_mask) == (out2 & unique_mask)) {
+                printf("%s has a partial collision:\n", name);
+                printf("hash(1 << %d) == %08"PRIx32"\n", i, out1);
+                printf("hash(1 << %d) == %08"PRIx32"\n", j, out2);
+                printf("The low-order %d bits of output are both "
+                       "0x%"PRIx32"\n", min_unique, out1 & unique_mask);
+            }
+        }
+    }
+}
+
+int
+main(void)
+{
     /* Check that all hashes computed with hash_words with one 1-bit (or no
      * 1-bits) set within a single 32-bit word have different values in all
      * 11-bit consecutive runs.
@@ -98,6 +130,7 @@ main(void)
      * independence must be a bad assumption :-)
      */
     check_word_hash(hash_words_cb, "hash_words", 11);
+    check_word_hash(mhash_words_cb, "mhash_words", 11);
 
     /* Check that all hash functions of with one 1-bit (or no 1-bits) set
      * within three 32-bit words have different values in their lowest 12
@@ -112,27 +145,8 @@ main(void)
      *
      * so we are doing pretty well to not have any collisions in 12 bits.
      */
-    for (i = 0; i <= 96; i++) {
-        for (j = i + 1; j <= 96; j++) {
-            uint32_t in1[3], in2[3];
-            uint32_t out1, out2;
-            const int min_unique = 12;
-            const uint32_t unique_mask = (UINT32_C(1) << min_unique) - 1;
-
-            set_bit(in1, i);
-            set_bit(in2, j);
-            out1 = hash_words(in1, 3, 0);
-            out2 = hash_words(in2, 3, 0);
-            if ((out1 & unique_mask) == (out2 & unique_mask)) {
-                printf("Partial collision:\n");
-                printf("hash(1 << %d) == %08"PRIx32"\n", i, out1);
-                printf("hash(1 << %d) == %08"PRIx32"\n", j, out2);
-                printf("The low-order %d bits of output are both "
-                       "0x%"PRIx32"\n", min_unique, out1 & unique_mask);
-                exit(1);
-            }
-        }
-    }
+    check_3word_hash(hash_words, "hash_words");
+    check_3word_hash(mhash_words, "mhash_words");
 
     /* Check that all hashes computed with hash_int with one 1-bit (or no
      * 1-bits) set within a single 32-bit word have different values in all
index b37fd22..fe258c2 100644 (file)
@@ -270,11 +270,11 @@ static const struct test tests[] = {
 int
 main(int argc, char *argv[])
 {
-    extern struct vlog_module VLM_lockfile;
     size_t i;
 
     set_program_name(argv[0]);
-    vlog_set_levels(&VLM_lockfile, VLF_ANY_FACILITY, VLL_ERR);
+    vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m");
+    vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF);
 
     if (argc != 2) {
         ovs_fatal(0, "exactly one argument required; use \"%s help\" for help",
index 8a35567..b990c13 100644 (file)
@@ -60,6 +60,7 @@ main(int argc, char *argv[])
             struct flow flow;
 
             random_bytes(&flow, sizeof flow);
+            memset(flow.zeros, 0, sizeof flow.zeros);
 
             mp.max_link = n - 1;
             multipath_execute(&mp, &flow);
index 1350ccd..170476d 100644 (file)
@@ -180,7 +180,7 @@ def print_idl(idl, step):
     for row in l2.itervalues():
         s = ["%03d: i=%s l1=" % (step, row.i)]
         if row.l1:
-            s.append(str(row.l1.i))
+            s.append(str(row.l1[0].i))
         s.append(" uuid=%s" % row.uuid)
         print(''.join(s))
         n += 1
@@ -312,6 +312,15 @@ def idl_set(idl, commands, step):
             sys.stdout.flush()
             txn.abort()
             return
+        elif name == "linktest":
+            l1_0 = txn.insert(idl.tables["link1"])
+            l1_0.i = 1
+            l1_0.k = [l1_0]
+            l1_0.ka = [l1_0]
+            l1_1 = txn.insert(idl.tables["link1"])
+            l1_1.i = 2
+            l1_1.k = [l1_0]
+            l1_1.ka = [l1_0, l1_1]
         else:
             sys.stderr.write("unknown command %s\n" % name)
             sys.exit(1)
index 14569db..020ae48 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -148,9 +148,9 @@ do_connected(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 }
 
 static void
-do_received(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+do_activity(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
-    reconnect_received(reconnect, now);
+    reconnect_activity(reconnect, now);
 }
 
 static void
@@ -220,10 +220,10 @@ diff_stats(const struct reconnect_stats *old,
                new->state, new->state_elapsed, new->backoff);
     }
     if (old->creation_time != new->creation_time
-        || old->last_received != new->last_received
+        || old->last_activity != new->last_activity
         || old->last_connected != new->last_connected) {
-        printf("  created %lld, last received %lld, last connected %lld\n",
-               new->creation_time, new->last_received, new->last_connected);
+        printf("  created %lld, last activity %lld, last connected %lld\n",
+               new->creation_time, new->last_activity, new->last_connected);
     }
     if (old->n_successful_connections != new->n_successful_connections
         || old->n_attempted_connections != new->n_attempted_connections
@@ -280,7 +280,7 @@ static const struct command commands[] = {
     { "connecting", 0, 0, do_connecting },
     { "connect-failed", 0, 1, do_connect_failed },
     { "connected", 0, 0, do_connected },
-    { "received", 0, 0, do_received },
+    { "activity", 0, 0, do_activity },
     { "run", 0, 1, do_run },
     { "advance", 1, 1, do_advance },
     { "timeout", 0, 0, do_timeout },
index 44b4723..c478c2a 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010 Nicira, Inc.
+# Copyright (c) 2009, 2010, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -61,8 +61,8 @@ def do_connected(_):
     r.connected(now)
 
 
-def do_received(_):
-    r.received(now)
+def do_activity(_):
+    r.activity(now)
 
 
 def do_run(arg):
@@ -110,10 +110,10 @@ def diff_stats(old, new, delta):
               % (new.state, new.state_elapsed, new.backoff))
 
     if (old.creation_time != new.creation_time or
-        old.last_received != new.last_received or
+        old.last_activity != new.last_activity or
         old.last_connected != new.last_connected):
-        print("  created %d, last received %d, last connected %d"
-              % (new.creation_time, new.last_received, new.last_connected))
+        print("  created %d, last activity %d, last connected %d"
+              % (new.creation_time, new.last_activity, new.last_connected))
 
     if (old.n_successful_connections != new.n_successful_connections or
         old.n_attempted_connections != new.n_attempted_connections or
@@ -166,7 +166,7 @@ def main():
         "connecting": do_connecting,
         "connect-failed": do_connect_failed,
         "connected": do_connected,
-        "received": do_received,
+        "activity": do_activity,
         "run": do_run,
         "advance": do_advance,
         "timeout": do_timeout,
index 71a7f21..b98fc79 100644 (file)
@@ -26,6 +26,9 @@
 #include "random.h"
 #include "util.h"
 
+#undef NDEBUG
+#include <assert.h>
+
 static void
 check_log_2_floor(uint32_t x, int n)
 {
@@ -85,6 +88,59 @@ test_ctz(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     check_ctz(0, 32);
 }
 
+static void
+shuffle(unsigned int *p, size_t n)
+{
+    for (; n > 1; n--, p++) {
+        unsigned int *q = &p[rand() % n];
+        unsigned int tmp = *p;
+        *p = *q;
+        *q = tmp;
+    }
+}
+
+static void
+check_popcount(uint32_t x, int n)
+{
+    if (popcount(x) != n) {
+        fprintf(stderr, "popcount(%#"PRIx32") is %d but should be %d\n",
+                x, popcount(x), n);
+        abort();
+    }
+}
+
+static void
+test_popcount(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    unsigned int bits[32];
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(bits); i++) {
+        bits[i] = 1u << i;
+    }
+
+    check_popcount(0, 0);
+
+    for (i = 0; i < 1000; i++) {
+        uint32_t x = 0;
+        int j;
+
+        shuffle(bits, ARRAY_SIZE(bits));
+        for (j = 0; j < 32; j++) {
+            x |= bits[j];
+            check_popcount(x, j + 1);
+        }
+        assert(x == UINT32_MAX);
+
+        shuffle(bits, ARRAY_SIZE(bits));
+        for (j = 31; j >= 0; j--) {
+            x &= ~bits[j];
+            check_popcount(x, j);
+        }
+        assert(x == 0);
+    }
+}
+
 /* Returns the sum of the squares of the first 'n' positive integers. */
 static unsigned int
 sum_of_squares(int n)
@@ -282,6 +338,7 @@ test_follow_symlinks(int argc, char *argv[])
 \f
 static const struct command commands[] = {
     {"ctz", 0, 0, test_ctz},
+    {"popcount", 0, 0, test_popcount},
     {"log_2_floor", 0, 0, test_log_2_floor},
     {"bitwise_copy", 0, 0, test_bitwise_copy},
     {"bitwise_zero", 0, 0, test_bitwise_zero},
index 15f4d64..0f0e8a9 100644 (file)
@@ -4,14 +4,14 @@ debugging.
 tcpdump
 -------
 The "ofp-tcpdump.patch" patch adds the ability to parse OpenFlow
-messages to tcpdump.  These instructions assume that tcpdump 3.9.8 
+messages to tcpdump.  These instructions assume that tcpdump 4.3.0
 is going to be used, but it should work with other versions that are not
 substantially different.  To begin, download tcpdump and apply the
 patch:
 
-    wget http://www.tcpdump.org/release/tcpdump-3.9.8.tar.gz
-    tar xzf tcpdump-3.9.8.tar.gz
-    ln -s tcpdump-3.9.8 tcpdump
+    wget http://www.tcpdump.org/release/tcpdump-4.3.0.tar.gz
+    tar xzf tcpdump-4.3.0.tar.gz
+    ln -s tcpdump-4.3.0 tcpdump
     patch -p0 < ofp-tcpdump.patch
 
 Then build the new version of tcpdump:
index 73bb344..5c47061 100644 (file)
@@ -1,6 +1,6 @@
 --- tcpdump/interface.h        2007-06-13 18:03:20.000000000 -0700
 +++ tcpdump/interface.h        2008-04-15 18:28:55.000000000 -0700
-@@ -148,7 +148,8 @@
+@@ -130,7 +130,8 @@
  
  extern const char *dnaddr_string(u_short);
  
      __attribute__((noreturn, format (printf, 1, 2)));
  extern void warning(const char *, ...) __attribute__ ((format (printf, 1, 2)));
  
-@@ -176,6 +177,7 @@
+@@ -163,6 +164,7 @@
  extern void hex_print_with_offset(const char *, const u_char *, u_int, u_int);
  extern void hex_print(const char *, const u_char *, u_int);
  extern void telnet_print(const u_char *, u_int);
 +extern void openflow_print(const u_char *, u_int);
- extern int ether_encap_print(u_short, const u_char *, u_int, u_int, u_short *);
  extern int llc_print(const u_char *, u_int, u_int, const u_char *,
        const u_char *, u_short *);
---- tcpdump/Makefile.in        2007-09-25 18:59:52.000000000 -0700
-+++ tcpdump/Makefile.in        2009-05-11 15:59:28.000000000 -0700
-@@ -49,10 +49,10 @@
- CFLAGS = $(CCOPT) $(DEFS) $(INCLS)
+ extern int snap_print(const u_char *, u_int, u_int, u_int);
+--- tcpdump/Makefile.in        2012-06-13 04:56:20.000000000 +1200
++++ tcpdump/Makefile.in        2012-08-29 21:36:37.000000000 +1200
+@@ -43,7 +43,7 @@
+ CC = @CC@
+ PROG = tcpdump
+ CCOPT = @V_CCOPT@
+-INCLS = -I. @V_INCLS@
++INCLS = -I. @V_INCLS@ -I../../include
+ DEFS = @DEFS@ @CPPFLAGS@ @V_DEFS@
+
+ # Standard CFLAGS
+@@ -51,10 +51,10 @@
+ FULL_CFLAGS = $(CCOPT) $(DEFS) $(INCLS) $(CFLAGS)
  
  # Standard LDFLAGS
 -LDFLAGS = @LDFLAGS@
  
  # Standard LIBS
 -LIBS = @LIBS@
-+LIBS = @LIBS@ -lopenvswitch
++LIBS = @LIBS@ -lopenvswitch -lssl -lrt -lm
  
  INSTALL = @INSTALL@
  INSTALL_PROGRAM = @INSTALL_PROGRAM@
-@@ -87,7 +87,8 @@
-       print-slow.c print-snmp.c print-stp.c print-sunatm.c print-sunrpc.c \
+@@ -93,7 +93,8 @@
        print-symantec.c print-syslog.c print-tcp.c print-telnet.c print-tftp.c \
-       print-timed.c print-token.c print-udp.c print-vjc.c print-vrrp.c \
--      print-wb.c print-zephyr.c setsignal.c tcpdump.c util.c
-+      print-wb.c print-zephyr.c setsignal.c tcpdump.c util.c \
+       print-timed.c print-tipc.c print-token.c print-udld.c print-udp.c \
+       print-usb.c print-vjc.c print-vqp.c print-vrrp.c print-vtp.c \
+-      print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c
++      print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c \
 +      print-openflow.c
  
- LOCALSRC = @LOCALSRC@
- GENSRC = version.c
+ LIBNETDISSECT_SRC=print-isakmp.c
+ LIBNETDISSECT_OBJ=$(LIBNETDISSECT_SRC:.c=.o)
 --- tcpdump/print-openflow.c   1969-12-31 16:00:00.000000000 -0800
 +++ tcpdump/print-openflow.c   2009-05-11 15:38:41.000000000 -0700
-@@ -0,0 +1,46 @@
+@@ -0,0 +1,45 @@
 +/* Copyright (C) 2007, 2008, 2009 Nicira, Inc.
 +
 +   Redistribution and use in source and binary forms, with or without
@@ -66,7 +75,6 @@
 +   IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 +   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 + */
-+ *
 +
 +#ifdef HAVE_CONFIG_H
 +#include "config.h"
@@ -75,7 +83,7 @@
 +#include <stdlib.h>
 +
 +#include "interface.h"
-+#include "../../include/openflow/openflow.h"
++#include "openflow/openflow.h"
 +#include "../../lib/ofp-print.h"
 +
 +void
 +}
 --- tcpdump/print-tcp.c        2006-09-19 12:07:57.000000000 -0700
 +++ tcpdump/print-tcp.c        2009-05-11 15:38:25.000000000 -0700
-@@ -52,6 +52,8 @@
+@@ -56,6 +56,8 @@
  
  #include "nameser.h"
  
-+#include "../../include/openflow/openflow.h"
++#include "openflow/openflow.h"
 +
  #ifdef HAVE_LIBCRYPTO
  #include <openssl/md5.h>
+ #include <signature.h>
+@@ -669,7 +672,9 @@
+         }
+         else if (length > 0 && (sport == LDP_PORT || dport == LDP_PORT)) {
+                 ldp_print(bp, length);
+-        }
++        } else if (sport == OFP_TCP_PORT || dport == OFP_TCP_PORT) {
++                   openflow_print(bp, length);
++        }
  
-@@ -680,7 +682,8 @@
-               }
-                 else if (length > 0 && (sport == LDP_PORT || dport == LDP_PORT)) {
-                         ldp_print(bp, length);
--              }
-+              } else if (sport == OFP_TCP_PORT || dport == OFP_TCP_PORT)
-+                      openflow_print(bp, length);
-       }
-       return;
- bad:
+         return;
+  bad:
index 5d10b37..5f61fd6 100644 (file)
@@ -841,8 +841,8 @@ compare_flows(const void *afs_, const void *bfs_)
 {
     const struct ofputil_flow_stats *afs = afs_;
     const struct ofputil_flow_stats *bfs = bfs_;
-    const struct cls_rule *a = &afs->rule;
-    const struct cls_rule *b = &bfs->rule;
+    const struct match *a = &afs->match;
+    const struct match *b = &bfs->match;
     const struct sort_criterion *sc;
 
     for (sc = criteria; sc < &criteria[n_criteria]; sc++) {
@@ -850,7 +850,9 @@ compare_flows(const void *afs_, const void *bfs_)
         int ret;
 
         if (!f) {
-            ret = a->priority < b->priority ? -1 : a->priority > b->priority;
+            unsigned int a_pri = afs->priority;
+            unsigned int b_pri = bfs->priority;
+            ret = a_pri < b_pri ? -1 : a_pri > b_pri;
         } else {
             bool ina, inb;
 
@@ -1776,6 +1778,7 @@ fte_free(struct fte *fte)
     if (fte) {
         fte_version_free(fte->versions[0]);
         fte_version_free(fte->versions[1]);
+        cls_rule_destroy(&fte->rule);
         free(fte);
     }
 }
@@ -1801,19 +1804,20 @@ fte_free_all(struct classifier *cls)
  *
  * Takes ownership of 'version'. */
 static void
-fte_insert(struct classifier *cls, const struct cls_rule *rule,
-           struct fte_version *version, int index)
+fte_insert(struct classifier *cls, const struct match *match,
+           unsigned int priority, struct fte_version *version, int index)
 {
     struct fte *old, *fte;
 
     fte = xzalloc(sizeof *fte);
-    fte->rule = *rule;
+    cls_rule_init(&fte->rule, match, priority);
     fte->versions[index] = version;
 
     old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
     if (old) {
         fte_version_free(old->versions[index]);
         fte->versions[!index] = old->versions[!index];
+        cls_rule_destroy(&old->rule);
         free(old);
     }
 }
@@ -1849,9 +1853,9 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
         version->ofpacts = fm.ofpacts;
         version->ofpacts_len = fm.ofpacts_len;
 
-        usable_protocols &= ofputil_usable_protocols(&fm.cr);
+        usable_protocols &= ofputil_usable_protocols(&fm.match);
 
-        fte_insert(cls, &fm.cr, version, index);
+        fte_insert(cls, &fm.match, fm.priority, version, index);
     }
     ds_destroy(&s);
 
@@ -1931,7 +1935,7 @@ read_flows_from_switch(struct vconn *vconn,
     ovs_be32 send_xid;
 
     fsr.aggregate = false;
-    cls_rule_init_catchall(&fsr.match, 0);
+    match_init_catchall(&fsr.match);
     fsr.out_port = OFPP_NONE;
     fsr.table_id = 0xff;
     fsr.cookie = fsr.cookie_mask = htonll(0);
@@ -1952,7 +1956,7 @@ read_flows_from_switch(struct vconn *vconn,
         version->ofpacts_len = fs.ofpacts_len;
         version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len);
 
-        fte_insert(cls, &fs.rule, version, index);
+        fte_insert(cls, &fs.match, fs.priority, version, index);
     }
     ofpbuf_uninit(&ofpacts);
 }
@@ -1965,7 +1969,8 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
     struct ofputil_flow_mod fm;
     struct ofpbuf *ofm;
 
-    fm.cr = fte->rule;
+    minimatch_expand(&fte->rule.match, &fm.match);
+    fm.priority = fte->rule.priority;
     fm.cookie = htonll(0);
     fm.cookie_mask = htonll(0);
     fm.new_cookie = version->cookie;
@@ -2177,7 +2182,7 @@ ofctl_parse_nxm__(bool oxm)
     ds_init(&in);
     while (!ds_get_test_line(&in, stdin)) {
         struct ofpbuf nx_match;
-        struct cls_rule rule;
+        struct match match;
         ovs_be64 cookie, cookie_mask;
         enum ofperr error;
         int match_len;
@@ -2190,19 +2195,19 @@ ofctl_parse_nxm__(bool oxm)
             match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
         }
 
-        /* Convert nx_match to cls_rule. */
+        /* Convert nx_match to match. */
         if (strict) {
             if (oxm) {
-                error = oxm_pull_match(&nx_match, 0, &rule);
+                error = oxm_pull_match(&nx_match, &match);
             } else {
-                error = nx_pull_match(&nx_match, match_len, 0, &rule,
+                error = nx_pull_match(&nx_match, match_len, &match,
                                       &cookie, &cookie_mask);
             }
         } else {
             if (oxm) {
-                error = oxm_pull_match_loose(&nx_match, 0, &rule);
+                error = oxm_pull_match_loose(&nx_match, &match);
             } else {
-                error = nx_pull_match_loose(&nx_match, match_len, 0, &rule,
+                error = nx_pull_match_loose(&nx_match, match_len, &match,
                                             &cookie, &cookie_mask);
             }
         }
@@ -2211,14 +2216,14 @@ ofctl_parse_nxm__(bool oxm)
         if (!error) {
             char *out;
 
-            /* Convert cls_rule back to nx_match. */
+            /* Convert match back to nx_match. */
             ofpbuf_uninit(&nx_match);
             ofpbuf_init(&nx_match, 0);
             if (oxm) {
-                match_len = oxm_put_match(&nx_match, &rule);
+                match_len = oxm_put_match(&nx_match, &match);
                 out = oxm_match_to_string(nx_match.data, match_len);
             } else {
-                match_len = nx_put_match(&nx_match, &rule,
+                match_len = nx_put_match(&nx_match, &match,
                                          cookie, cookie_mask);
                 out = nx_match_to_string(nx_match.data, match_len);
             }
@@ -2354,7 +2359,7 @@ ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         struct ofpbuf match_in, match_expout;
         struct ofp10_match match_out;
         struct ofp10_match match_normal;
-        struct cls_rule rule;
+        struct match match;
         char *p;
 
         /* Parse hex bytes to use for expected output. */
@@ -2390,18 +2395,17 @@ ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         }
 
         /* Convert to cls_rule and print. */
-        ofputil_cls_rule_from_ofp10_match(match_in.data, OFP_DEFAULT_PRIORITY,
-                                          &rule);
-        cls_rule_print(&rule);
+        ofputil_match_from_ofp10_match(match_in.data, &match);
+        match_print(&match);
 
         /* Convert back to ofp10_match and print differences from input. */
-        ofputil_cls_rule_to_ofp10_match(&rule, &match_out);
+        ofputil_match_to_ofp10_match(&match, &match_out);
         print_differences("", match_expout.data, match_expout.size,
                           &match_out, sizeof match_out);
 
         /* Normalize, then convert and compare again. */
-        ofputil_normalize_rule(&rule);
-        ofputil_cls_rule_to_ofp10_match(&rule, &match_normal);
+        ofputil_normalize_match(&match);
+        ofputil_match_to_ofp10_match(&match, &match_normal);
         print_differences("normal: ", &match_out, sizeof match_out,
                           &match_normal, sizeof match_normal);
         putchar('\n');
@@ -2414,9 +2418,9 @@ ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 }
 
 /* "parse-ofp11-match": reads a series of ofp11_match specifications as hex
- * bytes from stdin, converts them to cls_rules, prints them as strings on
- * stdout, and then converts them back to hex bytes and prints any differences
- * from the input. */
+ * bytes from stdin, converts them to "struct match"es, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
 static void
 ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
@@ -2426,7 +2430,7 @@ ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     while (!ds_get_preprocessed_line(&in, stdin)) {
         struct ofpbuf match_in;
         struct ofp11_match match_out;
-        struct cls_rule rule;
+        struct match match;
         enum ofperr error;
 
         /* Parse hex bytes. */
@@ -2439,20 +2443,19 @@ ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
                       match_in.size, sizeof(struct ofp11_match));
         }
 
-        /* Convert to cls_rule. */
-        error = ofputil_cls_rule_from_ofp11_match(match_in.data,
-                                                  OFP_DEFAULT_PRIORITY, &rule);
+        /* Convert to match. */
+        error = ofputil_match_from_ofp11_match(match_in.data, &match);
         if (error) {
             printf("bad ofp11_match: %s\n\n", ofperr_get_name(error));
             ofpbuf_uninit(&match_in);
             continue;
         }
 
-        /* Print cls_rule. */
-        cls_rule_print(&rule);
+        /* Print match. */
+        match_print(&match);
 
         /* Convert back to ofp11_match and print differences from input. */
-        ofputil_cls_rule_to_ofp11_match(&rule, &match_out);
+        ofputil_match_to_ofp11_match(&match, &match_out);
 
         print_differences("", match_in.data, match_in.size,
                           &match_out, sizeof match_out);
@@ -2586,71 +2589,71 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 static void
 ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
 {
-    struct cls_rule rule;
+    struct match match;
 
     char *string_s;
     struct ofputil_flow_mod fm;
 
     struct ofpbuf nxm;
-    struct cls_rule nxm_rule;
+    struct match nxm_match;
     int nxm_match_len;
     char *nxm_s;
 
-    struct ofp10_match of10_match;
-    struct cls_rule of10_rule;
+    struct ofp10_match of10_raw;
+    struct match of10_match;
 
-    struct ofp11_match of11_match;
-    struct cls_rule of11_rule;
+    struct ofp11_match of11_raw;
+    struct match of11_match;
 
     enum ofperr error;
 
-    cls_rule_init_catchall(&rule, OFP_DEFAULT_PRIORITY);
-    rule.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
-    rule.wc.vlan_tci_mask = htons(strtoul(argv[2], NULL, 16));
+    match_init_catchall(&match);
+    match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
+    match.wc.masks.vlan_tci = htons(strtoul(argv[2], NULL, 16));
 
     /* Convert to and from string. */
-    string_s = cls_rule_to_string(&rule);
+    string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
     printf("%s -> ", string_s);
     fflush(stdout);
     parse_ofp_str(&fm, -1, string_s, false);
     printf("%04"PRIx16"/%04"PRIx16"\n",
-           ntohs(fm.cr.flow.vlan_tci),
-           ntohs(fm.cr.wc.vlan_tci_mask));
+           ntohs(fm.match.flow.vlan_tci),
+           ntohs(fm.match.wc.masks.vlan_tci));
     free(string_s);
 
     /* Convert to and from NXM. */
     ofpbuf_init(&nxm, 0);
-    nxm_match_len = nx_put_match(&nxm, &rule, htonll(0), htonll(0));
+    nxm_match_len = nx_put_match(&nxm, &match, htonll(0), htonll(0));
     nxm_s = nx_match_to_string(nxm.data, nxm_match_len);
-    error = nx_pull_match(&nxm, nxm_match_len, 0, &nxm_rule, NULL, NULL);
+    error = nx_pull_match(&nxm, nxm_match_len, &nxm_match, NULL, NULL);
     printf("NXM: %s -> ", nxm_s);
     if (error) {
         printf("%s\n", ofperr_to_string(error));
     } else {
         printf("%04"PRIx16"/%04"PRIx16"\n",
-               ntohs(nxm_rule.flow.vlan_tci),
-               ntohs(nxm_rule.wc.vlan_tci_mask));
+               ntohs(nxm_match.flow.vlan_tci),
+               ntohs(nxm_match.wc.masks.vlan_tci));
     }
     free(nxm_s);
     ofpbuf_uninit(&nxm);
 
     /* Convert to and from OXM. */
     ofpbuf_init(&nxm, 0);
-    nxm_match_len = oxm_put_match(&nxm, &rule);
+    nxm_match_len = oxm_put_match(&nxm, &match);
     nxm_s = oxm_match_to_string(nxm.data, nxm_match_len);
-    error = oxm_pull_match(&nxm, 0, &nxm_rule);
+    error = oxm_pull_match(&nxm, &nxm_match);
     printf("OXM: %s -> ", nxm_s);
     if (error) {
         printf("%s\n", ofperr_to_string(error));
     } else {
-        uint16_t vid = ntohs(nxm_rule.flow.vlan_tci) &
+        uint16_t vid = ntohs(nxm_match.flow.vlan_tci) &
             (VLAN_VID_MASK | VLAN_CFI);
-        uint16_t mask = ntohs(nxm_rule.wc.vlan_tci_mask) &
+        uint16_t mask = ntohs(nxm_match.wc.masks.vlan_tci) &
             (VLAN_VID_MASK | VLAN_CFI);
 
         printf("%04"PRIx16"/%04"PRIx16",", vid, mask);
-        if (vid && vlan_tci_to_pcp(nxm_rule.wc.vlan_tci_mask)) {
-            printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_rule.flow.vlan_tci));
+        if (vid && vlan_tci_to_pcp(nxm_match.wc.masks.vlan_tci)) {
+            printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_match.flow.vlan_tci));
         } else {
             printf("--\n");
         }
@@ -2659,26 +2662,26 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     ofpbuf_uninit(&nxm);
 
     /* Convert to and from OpenFlow 1.0. */
-    ofputil_cls_rule_to_ofp10_match(&rule, &of10_match);
-    ofputil_cls_rule_from_ofp10_match(&of10_match, 0, &of10_rule);
+    ofputil_match_to_ofp10_match(&match, &of10_raw);
+    ofputil_match_from_ofp10_match(&of10_raw, &of10_match);
     printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
-           ntohs(of10_match.dl_vlan),
-           (of10_match.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
-           of10_match.dl_vlan_pcp,
-           (of10_match.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
-           ntohs(of10_rule.flow.vlan_tci),
-           ntohs(of10_rule.wc.vlan_tci_mask));
+           ntohs(of10_raw.dl_vlan),
+           (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
+           of10_raw.dl_vlan_pcp,
+           (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
+           ntohs(of10_match.flow.vlan_tci),
+           ntohs(of10_match.wc.masks.vlan_tci));
 
     /* Convert to and from OpenFlow 1.1. */
-    ofputil_cls_rule_to_ofp11_match(&rule, &of11_match);
-    ofputil_cls_rule_from_ofp11_match(&of11_match, 0, &of11_rule);
+    ofputil_match_to_ofp11_match(&match, &of11_raw);
+    ofputil_match_from_ofp11_match(&of11_raw, &of11_match);
     printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
-           ntohs(of11_match.dl_vlan),
-           (of11_match.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
-           of11_match.dl_vlan_pcp,
-           (of11_match.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
-           ntohs(of11_rule.flow.vlan_tci),
-           ntohs(of11_rule.wc.vlan_tci_mask));
+           ntohs(of11_raw.dl_vlan),
+           (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
+           of11_raw.dl_vlan_pcp,
+           (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
+           ntohs(of11_match.flow.vlan_tci),
+           ntohs(of11_match.wc.masks.vlan_tci));
 }
 
 /* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
index 0a7c2c6..e6bd63b 100644 (file)
@@ -2763,7 +2763,7 @@ error:
     return error;
 }
 
-static void
+static const struct ovsdb_idl_column *
 pre_parse_column_key_value(struct vsctl_context *ctx,
                            const char *arg,
                            const struct vsctl_table_class *table)
@@ -2780,6 +2780,18 @@ pre_parse_column_key_value(struct vsctl_context *ctx,
 
     pre_get_column(ctx, table, column_name, &column);
     free(column_name);
+
+    return column;
+}
+
+static void
+check_mutable(const struct vsctl_table_class *table,
+              const struct ovsdb_idl_column *column)
+{
+    if (!column->mutable) {
+        vsctl_fatal("cannot modify read-only column %s in table %s",
+                    column->name, table->class->name);
+    }
 }
 
 static void
@@ -3112,7 +3124,10 @@ pre_cmd_set(struct vsctl_context *ctx)
 
     table = pre_get_table(ctx, table_name);
     for (i = 3; i < ctx->argc; i++) {
-        pre_parse_column_key_value(ctx, ctx->argv[i], table);
+        const struct ovsdb_idl_column *column;
+
+        column = pre_parse_column_key_value(ctx, ctx->argv[i], table);
+        check_mutable(table, column);
     }
 }
 
@@ -3195,6 +3210,7 @@ pre_cmd_add(struct vsctl_context *ctx)
 
     table = pre_get_table(ctx, table_name);
     pre_get_column(ctx, table, column_name, &column);
+    check_mutable(table, column);
 }
 
 static void
@@ -3251,6 +3267,7 @@ pre_cmd_remove(struct vsctl_context *ctx)
 
     table = pre_get_table(ctx, table_name);
     pre_get_column(ctx, table, column_name, &column);
+    check_mutable(table, column);
 }
 
 static void
@@ -3316,6 +3333,7 @@ pre_cmd_clear(struct vsctl_context *ctx)
         const struct ovsdb_idl_column *column;
 
         pre_get_column(ctx, table, ctx->argv[i], &column);
+        check_mutable(table, column);
     }
 }