flow: Simplify many functions for working with flows and wildcards.
[openvswitch] / lib / classifier.c
index 38f1a4feaf27838928c810ccd3a272bad854b8f3..2fd33e5a3fed4b6f0d49b84b28ed6e618bc73c01 100644 (file)
@@ -40,9 +40,6 @@ static struct cls_rule *find_equal(struct cls_table *, const struct flow *,
                                    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))
@@ -128,7 +125,7 @@ void
 cls_rule_set_metadata_masked(struct cls_rule *rule, ovs_be64 metadata,
                              ovs_be64 mask)
 {
-    rule->wc.metadata_mask = mask;
+    rule->wc.masks.metadata = mask;
     rule->flow.metadata = metadata & mask;
 }
 
@@ -142,21 +139,21 @@ 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->wc.masks.tun_id = 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->wc.masks.in_port = UINT16_MAX;
     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->wc.masks.dl_type = htons(UINT16_MAX);
     rule->flow.dl_type = dl_type;
 }
 
@@ -193,7 +190,7 @@ cls_rule_set_eth_masked(const uint8_t value_src[ETH_ADDR_LEN],
 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);
+    cls_rule_set_eth(dl_src, rule->flow.dl_src, rule->wc.masks.dl_src);
 }
 
 /* Modifies 'rule' so that the source Ethernet address
@@ -205,7 +202,7 @@ cls_rule_set_dl_src_masked(struct cls_rule *rule,
                            const uint8_t mask[ETH_ADDR_LEN])
 {
     cls_rule_set_eth_masked(dl_src, mask,
-                            rule->flow.dl_src, rule->wc.dl_src_mask);
+                            rule->flow.dl_src, rule->wc.masks.dl_src);
 }
 
 /* Modifies 'rule' so that the destination Ethernet address
@@ -213,7 +210,7 @@ cls_rule_set_dl_src_masked(struct cls_rule *rule,
 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);
+    cls_rule_set_eth(dl_dst, rule->flow.dl_dst, rule->wc.masks.dl_dst);
 }
 
 /* Modifies 'rule' so that the destination Ethernet address
@@ -225,7 +222,7 @@ cls_rule_set_dl_dst_masked(struct cls_rule *rule,
                            const uint8_t mask[ETH_ADDR_LEN])
 {
     cls_rule_set_eth_masked(dl_dst, mask,
-                            rule->flow.dl_dst, rule->wc.dl_dst_mask);
+                            rule->flow.dl_dst, rule->wc.masks.dl_dst);
 }
 
 void
@@ -238,7 +235,7 @@ 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;
+    rule->wc.masks.vlan_tci = mask;
 }
 
 /* Modifies 'rule' so that the VLAN VID is wildcarded.  If the PCP is already
@@ -247,8 +244,8 @@ cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
 void
 cls_rule_set_any_vid(struct cls_rule *rule)
 {
-    if (rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK)) {
-        rule->wc.vlan_tci_mask &= ~htons(VLAN_VID_MASK);
+    if (rule->wc.masks.vlan_tci & htons(VLAN_PCP_MASK)) {
+        rule->wc.masks.vlan_tci &= ~htons(VLAN_VID_MASK);
         rule->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
     } else {
         cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
@@ -268,9 +265,9 @@ 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);
+        rule->wc.masks.vlan_tci = htons(UINT16_MAX);
     } else {
-        rule->wc.vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
+        rule->wc.masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
     }
 }
 
@@ -296,7 +293,7 @@ cls_rule_set_vlan_vid_masked(struct cls_rule *rule,
 
     mask &= vid_mask;
     flow_set_vlan_vid(&rule->flow, vid & mask);
-    rule->wc.vlan_tci_mask = mask | (rule->wc.vlan_tci_mask & pcp_mask);
+    rule->wc.masks.vlan_tci = mask | (rule->wc.masks.vlan_tci & pcp_mask);
 }
 
 /* Modifies 'rule' so that the VLAN PCP is wildcarded.  If the VID is already
@@ -305,8 +302,8 @@ cls_rule_set_vlan_vid_masked(struct cls_rule *rule,
 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);
+    if (rule->wc.masks.vlan_tci & htons(VLAN_VID_MASK)) {
+        rule->wc.masks.vlan_tci &= ~htons(VLAN_PCP_MASK);
         rule->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
     } else {
         cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
@@ -319,7 +316,7 @@ 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);
+    rule->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
 }
 
 void
@@ -332,7 +329,7 @@ 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;
+    rule->wc.masks.tp_src = mask;
 }
 
 void
@@ -345,21 +342,21 @@ 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;
+    rule->wc.masks.tp_dst = 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;
+    rule->wc.masks.nw_proto = UINT8_MAX;
 }
 
 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);
+    rule->wc.masks.nw_src = htonl(UINT32_MAX);
 }
 
 void
@@ -367,27 +364,27 @@ 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;
+    rule->wc.masks.nw_src = 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);
+    rule->wc.masks.nw_dst = 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;
+    rule->wc.masks.nw_dst = mask;
 }
 
 void
 cls_rule_set_nw_dscp(struct cls_rule *rule, uint8_t nw_dscp)
 {
-    rule->wc.wildcards &= ~FWW_NW_DSCP;
+    rule->wc.masks.nw_tos |= IP_DSCP_MASK;
     rule->flow.nw_tos &= ~IP_DSCP_MASK;
     rule->flow.nw_tos |= nw_dscp & IP_DSCP_MASK;
 }
@@ -395,7 +392,7 @@ cls_rule_set_nw_dscp(struct cls_rule *rule, uint8_t nw_dscp)
 void
 cls_rule_set_nw_ecn(struct cls_rule *rule, uint8_t nw_ecn)
 {
-    rule->wc.wildcards &= ~FWW_NW_ECN;
+    rule->wc.masks.nw_tos |= IP_ECN_MASK;
     rule->flow.nw_tos &= ~IP_ECN_MASK;
     rule->flow.nw_tos |= nw_ecn & IP_ECN_MASK;
 }
@@ -403,14 +400,14 @@ cls_rule_set_nw_ecn(struct cls_rule *rule, uint8_t nw_ecn)
 void
 cls_rule_set_nw_ttl(struct cls_rule *rule, uint8_t nw_ttl)
 {
-    rule->wc.wildcards &= ~FWW_NW_TTL;
+    rule->wc.masks.nw_ttl = UINT8_MAX;
     rule->flow.nw_ttl = nw_ttl;
 }
 
 void
 cls_rule_set_nw_frag(struct cls_rule *rule, uint8_t nw_frag)
 {
-    rule->wc.nw_frag_mask |= FLOW_NW_FRAG_MASK;
+    rule->wc.masks.nw_frag |= FLOW_NW_FRAG_MASK;
     rule->flow.nw_frag = nw_frag;
 }
 
@@ -419,7 +416,7 @@ 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;
+    rule->wc.masks.nw_frag = mask;
 }
 
 void
@@ -437,7 +434,7 @@ cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t 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);
+    cls_rule_set_eth(sha, rule->flow.arp_sha, rule->wc.masks.arp_sha);
 }
 
 void
@@ -446,13 +443,13 @@ cls_rule_set_arp_sha_masked(struct cls_rule *rule,
                            const uint8_t mask[ETH_ADDR_LEN])
 {
     cls_rule_set_eth_masked(arp_sha, mask,
-                            rule->flow.arp_sha, rule->wc.arp_sha_mask);
+                            rule->flow.arp_sha, rule->wc.masks.arp_sha);
 }
 
 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);
+    cls_rule_set_eth(tha, rule->flow.arp_tha, rule->wc.masks.arp_tha);
 }
 
 void
@@ -461,14 +458,14 @@ cls_rule_set_arp_tha_masked(struct cls_rule *rule,
                            const uint8_t mask[ETH_ADDR_LEN])
 {
     cls_rule_set_eth_masked(arp_tha, mask,
-                            rule->flow.arp_tha, rule->wc.arp_tha_mask);
+                            rule->flow.arp_tha, rule->wc.masks.arp_tha);
 }
 
 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;
+    rule->wc.masks.ipv6_src = in6addr_exact;
 }
 
 void
@@ -476,14 +473,14 @@ 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;
+    rule->wc.masks.ipv6_src = *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;
+    rule->wc.masks.ipv6_dst = in6addr_exact;
 }
 
 void
@@ -491,7 +488,7 @@ 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;
+    rule->wc.masks.ipv6_dst = *mask;
 }
 
 void
@@ -505,14 +502,14 @@ 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;
+    rule->wc.masks.ipv6_label = 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;
+    rule->wc.masks.nd_target = in6addr_exact;
 }
 
 void
@@ -521,7 +518,7 @@ cls_rule_set_nd_target_masked(struct cls_rule *rule,
                               const struct in6_addr *mask)
 {
     rule->flow.nd_target = ipv6_addr_bitand(target, mask);
-    rule->wc.nd_target_mask = *mask;
+    rule->wc.masks.nd_target = *mask;
 }
 
 /* Returns true if 'a' and 'b' have the same priority, wildcard the same
@@ -600,23 +597,22 @@ 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);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
 
     if (rule->priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%d,", rule->priority);
     }
 
-    if (!(w & FWW_DL_TYPE)) {
+    if (wc->masks.dl_type) {
         skip_type = true;
         if (f->dl_type == htons(ETH_TYPE_IP)) {
-            if (!(w & FWW_NW_PROTO)) {
+            if (wc->masks.nw_proto) {
                 skip_proto = true;
                 if (f->nw_proto == IPPROTO_ICMP) {
                     ds_put_cstr(s, "icmp,");
@@ -632,7 +628,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
                 ds_put_cstr(s, "ip,");
             }
         } else if (f->dl_type == htons(ETH_TYPE_IPV6)) {
-            if (!(w & FWW_NW_PROTO)) {
+            if (wc->masks.nw_proto) {
                 skip_proto = true;
                 if (f->nw_proto == IPPROTO_ICMPV6) {
                     ds_put_cstr(s, "icmp6,");
@@ -654,7 +650,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         }
     }
     for (i = 0; i < FLOW_N_REGS; i++) {
-        switch (wc->reg_masks[i]) {
+        switch (wc->masks.regs[i]) {
         case 0:
             break;
         case UINT32_MAX:
@@ -662,11 +658,11 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
             break;
         default:
             ds_put_format(s, "reg%d=0x%"PRIx32"/0x%"PRIx32",",
-                          i, f->regs[i], wc->reg_masks[i]);
+                          i, f->regs[i], wc->masks.regs[i]);
             break;
         }
     }
-    switch (wc->tun_id_mask) {
+    switch (wc->masks.tun_id) {
     case 0:
         break;
     case CONSTANT_HTONLL(UINT64_MAX):
@@ -674,10 +670,10 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         break;
     default:
         ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
-                      ntohll(f->tun_id), ntohll(wc->tun_id_mask));
+                      ntohll(f->tun_id), ntohll(wc->masks.tun_id));
         break;
     }
-    switch (wc->metadata_mask) {
+    switch (wc->masks.metadata) {
     case 0:
         break;
     case CONSTANT_HTONLL(UINT64_MAX):
@@ -685,16 +681,16 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         break;
     default:
         ds_put_format(s, "metadata=%#"PRIx64"/%#"PRIx64",",
-                      ntohll(f->metadata), ntohll(wc->metadata_mask));
+                      ntohll(f->metadata), ntohll(wc->masks.metadata));
         break;
     }
-    if (!(w & FWW_IN_PORT)) {
+    if (wc->masks.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 (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))
@@ -708,36 +704,36 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
                 ds_put_format(s, "dl_vlan_pcp=%d,",
                               vlan_tci_to_pcp(f->vlan_tci));
             }
-        } else if (wc->vlan_tci_mask == htons(0xffff)) {
+        } 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->vlan_tci_mask));
+                          ntohs(f->vlan_tci), ntohs(wc->masks.vlan_tci));
         }
     }
-    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)) {
+    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->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)) {
+        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->ipv6_label_mask));
+                              ntohl(wc->masks.ipv6_label));
             }
         }
     } 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);
+        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 && !(w & FWW_NW_PROTO)) {
+    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 {
@@ -745,19 +741,19 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         }
     }
     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);
+        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 (!(w & FWW_NW_DSCP)) {
+    if (wc->masks.nw_tos & IP_DSCP_MASK) {
         ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos & IP_DSCP_MASK);
     }
-    if (!(w & FWW_NW_ECN)) {
+    if (wc->masks.nw_tos & IP_ECN_MASK) {
         ds_put_format(s, "nw_ecn=%"PRIu8",", f->nw_tos & IP_ECN_MASK);
     }
-    if (!(w & FWW_NW_TTL)) {
+    if (wc->masks.nw_ttl) {
         ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
     }
-    switch (wc->nw_frag_mask) {
+    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
@@ -776,18 +772,18 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         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);
+        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->tp_src_mask);
-        format_be16_masked(s, "icmp_code", f->tp_dst, wc->tp_dst_mask);
+        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->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);
+                            &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->tp_src_mask);
-        format_be16_masked(s, "tp_dst", f->tp_dst, wc->tp_dst_mask);
+        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) == ',') {
@@ -1259,73 +1255,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));
-}