[ovs-dev] [generic tci mask 6/8] flow: Fully separate flow_wildcards from OpenFlow wildcard bits.

Justin Pettit jpettit at nicira.com
Sat Nov 20 02:36:27 UTC 2010


Looks good.

--Justin


On Nov 10, 2010, at 3:38 PM, Ben Pfaff wrote:

> Originally, wildcards were just the OpenFlow OFPFW_* bits.  Then, when
> OpenFlow added CIDR masks for IP addresses, struct flow_wildcards was born
> with additional members for those masks, derived from the wildcard bits.
> Then, when OVS added support for tunnels, we added another bit
> NXFW_TUN_ID that coexisted with the OFPFW_*.  Later we added even more bits
> that do not appear in the OpenFlow 1.0 match structure at all.  This had
> become really confusing, and the difficulties were especially visible in
> the long list of invariants in comments on struct flow_wildcards.
> 
> This commit cleanly separates the OpenFlow 1.0 wildcard bits from the
> bits used inside Open vSwitch, by defining a new set of bits that are
> used only internally to Open vSwitch and converting to and from those
> wildcard bits at the point where data comes off or goes onto the wire.
> It also moves those functions into ofp-util.[ch] since they are only for
> dealing with OpenFlow wire protocol now.
> ---
> include/openvswitch/types.h |    2 +
> lib/classifier.c            |  156 ++++++++++++------------------------------
> lib/classifier.h            |    5 --
> lib/flow.c                  |  108 +++++++++---------------------
> lib/flow.h                  |   64 +++++++-----------
> lib/nx-match.c              |   67 +++++++++---------
> lib/nx-match.def            |   50 +++++++-------
> lib/ofp-parse.c             |   28 ++++----
> lib/ofp-util.c              |  136 +++++++++++++++++++++++++++++++++++++
> lib/ofp-util.h              |   13 +++-
> ofproto/ofproto.c           |   16 +++--
> tests/test-classifier.c     |  100 +++++++++++++---------------
> tests/test-flows.c          |    2 +-
> utilities/ovs-ofctl.c       |    4 +-
> 14 files changed, 383 insertions(+), 368 deletions(-)
> 
> diff --git a/include/openvswitch/types.h b/include/openvswitch/types.h
> index 2f670c0..fbd2997 100644
> --- a/include/openvswitch/types.h
> +++ b/include/openvswitch/types.h
> @@ -21,8 +21,10 @@
> 
> #ifdef __CHECKER__
> #define OVS_BITWISE __attribute__((bitwise))
> +#define OVS_FORCE __attribute__((force))
> #else
> #define OVS_BITWISE
> +#define OVS_FORCE
> #endif
> 
> /* The ovs_be<N> types indicate that an object is in big-endian, not
> diff --git a/lib/classifier.c b/lib/classifier.c
> index fb87b63..5c09f77 100644
> --- a/lib/classifier.c
> +++ b/lib/classifier.c
> @@ -23,6 +23,7 @@
> #include "dynamic-string.h"
> #include "flow.h"
> #include "hash.h"
> +#include "ofp-util.h"
> #include "packets.h"
> 
> static struct cls_table *find_table(const struct classifier *,
> @@ -91,78 +92,10 @@ void
> cls_rule_init_catchall(struct cls_rule *rule, unsigned int priority)
> {
>     memset(&rule->flow, 0, sizeof rule->flow);
> -    flow_wildcards_init(&rule->wc, OVSFW_ALL | FWW_ALL);
> +    flow_wildcards_init_catchall(&rule->wc);
>     rule->priority = priority;
> }
> 
> -/* Converts the ofp_match in 'match' (with format 'flow_format', one of NXFF_*)
> - * into a cls_rule in 'rule', with the given 'priority'.  'cookie' is used
> - * when 'flow_format' is NXFF_TUN_ID_FROM_COOKIE. */
> -void
> -cls_rule_from_match(const struct ofp_match *match, unsigned int priority,
> -                    int flow_format, uint64_t cookie,
> -                    struct cls_rule *rule)
> -{
> -    uint32_t wildcards = ntohl(match->wildcards) & OVSFW_ALL;
> -
> -    rule->priority = !wildcards ? UINT16_MAX : priority;
> -
> -    rule->flow.tun_id = 0;
> -    if (flow_format != NXFF_TUN_ID_FROM_COOKIE) {
> -        wildcards |= NXFW_TUN_ID;
> -    } else {
> -        if (!(wildcards & NXFW_TUN_ID)) {
> -            rule->flow.tun_id = htonl(ntohll(cookie) >> 32);
> -        }
> -    }
> -    if (wildcards & OFPFW_DL_DST) {
> -        wildcards |= FWW_ETH_MCAST;
> -    }
> -    flow_wildcards_init(&rule->wc, wildcards);
> -
> -    rule->flow.nw_src = match->nw_src;
> -    rule->flow.nw_dst = match->nw_dst;
> -    rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
> -                     : ntohs(match->in_port));
> -    rule->flow.dl_vlan = match->dl_vlan;
> -    rule->flow.dl_vlan_pcp = match->dl_vlan_pcp;
> -    rule->flow.dl_type = 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;
> -    rule->flow.nw_proto = match->nw_proto;
> -
> -    cls_rule_zero_wildcarded_fields(rule);
> -}
> -
> -/* Conerts 'rule' into an OpenFlow match structure 'match' with the given flow
> - * format 'flow_format' (one of NXFF_*). */
> -void
> -cls_rule_to_match(const struct cls_rule *rule, int flow_format,
> -                  struct ofp_match *match)
> -{
> -    match->wildcards = htonl(rule->wc.wildcards
> -                             & (flow_format == NXFF_TUN_ID_FROM_COOKIE
> -                                ? OVSFW_ALL : OFPFW_ALL));
> -    match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL
> -                           : rule->flow.in_port);
> -    match->dl_vlan = rule->flow.dl_vlan;
> -    match->dl_vlan_pcp = rule->flow.dl_vlan_pcp;
> -    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 = 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;
> -    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);
> -}
> -
> /* 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.
> @@ -180,28 +113,28 @@ cls_rule_zero_wildcarded_fields(struct cls_rule *rule)
> void
> cls_rule_set_in_port(struct cls_rule *rule, uint16_t odp_port)
> {
> -    rule->wc.wildcards &= ~OFPFW_IN_PORT;
> +    rule->wc.wildcards &= ~FWW_IN_PORT;
>     rule->flow.in_port = odp_port;
> }
> 
> void
> cls_rule_set_dl_type(struct cls_rule *rule, ovs_be16 dl_type)
> {
> -    rule->wc.wildcards &= ~OFPFW_DL_TYPE;
> +    rule->wc.wildcards &= ~FWW_DL_TYPE;
>     rule->flow.dl_type = dl_type;
> }
> 
> void
> cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN])
> {
> -    rule->wc.wildcards &= ~OFPFW_DL_SRC;
> +    rule->wc.wildcards &= ~FWW_DL_SRC;
>     memcpy(rule->flow.dl_src, dl_src, ETH_ADDR_LEN);
> }
> 
> void
> cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
> {
> -    rule->wc.wildcards &= ~(OFPFW_DL_DST | FWW_ETH_MCAST);
> +    rule->wc.wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
>     memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN);
> }
> 
> @@ -218,13 +151,13 @@ cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
>     case 0xffff:
>         if (tci == htons(0)) {
>             /* Match only packets that have no 802.1Q header. */
> -            rule->wc.wildcards &= ~(OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP);
> +            rule->wc.wildcards &= ~(FWW_DL_VLAN | FWW_DL_VLAN_PCP);
>             rule->flow.dl_vlan = htons(OFP_VLAN_NONE);
>             rule->flow.dl_vlan_pcp = 0;
>             return true;
>         } else if (tci & htons(VLAN_CFI)) {
>             /* Match only packets that have a specific 802.1Q VID and PCP. */
> -            rule->wc.wildcards &= ~(OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP);
> +            rule->wc.wildcards &= ~(FWW_DL_VLAN | FWW_DL_VLAN_PCP);
>             rule->flow.dl_vlan = htons(vlan_tci_to_vid(tci));
>             rule->flow.dl_vlan_pcp = vlan_tci_to_pcp(tci);
>             return true;
> @@ -239,7 +172,7 @@ cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
>         } else {
>             /* Match only packets that have a specific 802.1Q VID. */
>             cls_rule_set_dl_vlan(rule, tci & htons(VLAN_VID_MASK));
> -            rule->wc.wildcards |= OFPFW_DL_VLAN_PCP;
> +            rule->wc.wildcards |= FWW_DL_VLAN_PCP;
>             rule->flow.dl_vlan_pcp = 0;
>             return true;
>         }
> @@ -250,14 +183,14 @@ cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
>         } else {
>             /* Match only packets that have a specific 802.1Q PCP. */
>             cls_rule_set_dl_vlan_pcp(rule, vlan_tci_to_pcp(tci));
> -            rule->wc.wildcards |= OFPFW_DL_VLAN;
> +            rule->wc.wildcards |= FWW_DL_VLAN;
>             rule->flow.dl_vlan = 0;
>             return true;
>         }
> 
>     case 0x0000:
>         /* Match anything. */
> -        rule->wc.wildcards |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP;
> +        rule->wc.wildcards |= FWW_DL_VLAN | FWW_DL_VLAN_PCP;
>         rule->flow.dl_vlan = htons(0);
>         rule->flow.dl_vlan_pcp = 0;
>         return true;
> @@ -274,35 +207,35 @@ cls_rule_set_dl_vlan(struct cls_rule *rule, ovs_be16 dl_vlan)
>         dl_vlan &= htons(VLAN_VID_MASK);
>     }
> 
> -    rule->wc.wildcards &= ~OFPFW_DL_VLAN;
> +    rule->wc.wildcards &= ~FWW_DL_VLAN;
>     rule->flow.dl_vlan = dl_vlan;
> }
> 
> void
> cls_rule_set_dl_vlan_pcp(struct cls_rule *rule, uint8_t dl_vlan_pcp)
> {
> -    rule->wc.wildcards &= ~OFPFW_DL_VLAN_PCP;
> +    rule->wc.wildcards &= ~FWW_DL_VLAN_PCP;
>     rule->flow.dl_vlan_pcp = dl_vlan_pcp & 0x07;
> }
> 
> void
> cls_rule_set_tp_src(struct cls_rule *rule, ovs_be16 tp_src)
> {
> -    rule->wc.wildcards &= ~OFPFW_TP_SRC;
> +    rule->wc.wildcards &= ~FWW_TP_SRC;
>     rule->flow.tp_src = tp_src;
> }
> 
> void
> cls_rule_set_tp_dst(struct cls_rule *rule, ovs_be16 tp_dst)
> {
> -    rule->wc.wildcards &= ~OFPFW_TP_DST;
> +    rule->wc.wildcards &= ~FWW_TP_DST;
>     rule->flow.tp_dst = tp_dst;
> }
> 
> void
> cls_rule_set_nw_proto(struct cls_rule *rule, uint8_t nw_proto)
> {
> -    rule->wc.wildcards &= ~OFPFW_NW_PROTO;
> +    rule->wc.wildcards &= ~FWW_NW_PROTO;
>     rule->flow.nw_proto = nw_proto;
> }
> 
> @@ -343,14 +276,14 @@ cls_rule_set_nw_dst_masked(struct cls_rule *rule, ovs_be32 ip, ovs_be32 mask)
> void
> cls_rule_set_nw_tos(struct cls_rule *rule, uint8_t nw_tos)
> {
> -    rule->wc.wildcards &= ~OFPFW_NW_TOS;
> +    rule->wc.wildcards &= ~FWW_NW_TOS;
>     rule->flow.nw_tos = nw_tos & IP_DSCP_MASK;
> }
> 
> void
> cls_rule_set_icmp_type(struct cls_rule *rule, uint8_t icmp_type)
> {
> -    rule->wc.wildcards &= ~OFPFW_ICMP_TYPE;
> +    rule->wc.wildcards &= ~FWW_TP_SRC;
>     rule->flow.icmp_type = htons(icmp_type);
> 
> }
> @@ -358,7 +291,7 @@ cls_rule_set_icmp_type(struct cls_rule *rule, uint8_t icmp_type)
> void
> cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t icmp_code)
> {
> -    rule->wc.wildcards &= ~OFPFW_ICMP_CODE;
> +    rule->wc.wildcards &= ~FWW_TP_DST;
>     rule->flow.icmp_code = htons(icmp_code);
> }
> 
> @@ -820,7 +753,7 @@ static bool
> flow_equal_except(const struct flow *a, const struct flow *b,
>                   const struct flow_wildcards *wildcards)
> {
> -    const uint32_t wc = wildcards->wildcards;
> +    const flow_wildcards_t wc = wildcards->wildcards;
>     int i;
> 
>     BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + FLOW_N_REGS * 4);
> @@ -831,32 +764,33 @@ flow_equal_except(const struct flow *a, const struct flow *b,
>         }
>     }
> 
> -    return ((wc & NXFW_TUN_ID || a->tun_id == b->tun_id)
> +    return ((wc & FWW_TUN_ID || a->tun_id == b->tun_id)
>             && !((a->nw_src ^ b->nw_src) & wildcards->nw_src_mask)
>             && !((a->nw_dst ^ b->nw_dst) & wildcards->nw_dst_mask)
> -            && (wc & OFPFW_IN_PORT || a->in_port == b->in_port)
> -            && (wc & OFPFW_DL_VLAN || a->dl_vlan == b->dl_vlan)
> -            && (wc & OFPFW_DL_TYPE || a->dl_type == b->dl_type)
> -            && (wc & OFPFW_TP_SRC || a->tp_src == b->tp_src)
> -            && (wc & OFPFW_TP_DST || a->tp_dst == b->tp_dst)
> -            && (wc & OFPFW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src))
> -            && (wc & OFPFW_DL_DST
> +            && (wc & FWW_IN_PORT || a->in_port == b->in_port)
> +            && (wc & FWW_DL_VLAN || a->dl_vlan == b->dl_vlan)
> +            && (wc & FWW_DL_TYPE || a->dl_type == b->dl_type)
> +            && (wc & FWW_TP_SRC || a->tp_src == b->tp_src)
> +            && (wc & FWW_TP_DST || a->tp_dst == b->tp_dst)
> +            && (wc & FWW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src))
> +            && (wc & FWW_DL_DST
>                 || (!((a->dl_dst[0] ^ b->dl_dst[0]) & 0xfe)
>                     && a->dl_dst[1] == b->dl_dst[1]
>                     && a->dl_dst[2] == b->dl_dst[2]
>                     && a->dl_dst[3] == b->dl_dst[3]
>                     && a->dl_dst[4] == b->dl_dst[4]
>                     && a->dl_dst[5] == b->dl_dst[5]))
> -            && (wc & FWW_ETH_MCAST || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
> -            && (wc & OFPFW_NW_PROTO || a->nw_proto == b->nw_proto)
> -            && (wc & OFPFW_DL_VLAN_PCP || a->dl_vlan_pcp == b->dl_vlan_pcp)
> -            && (wc & OFPFW_NW_TOS || a->nw_tos == b->nw_tos));
> +            && (wc & FWW_ETH_MCAST
> +                || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
> +            && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
> +            && (wc & FWW_DL_VLAN_PCP || a->dl_vlan_pcp == b->dl_vlan_pcp)
> +            && (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos));
> }
> 
> static void
> zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
> {
> -    const uint32_t wc = wildcards->wildcards;
> +    const flow_wildcards_t wc = wildcards->wildcards;
>     int i;
> 
>     BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + 4 * FLOW_N_REGS);
> @@ -864,43 +798,43 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
>     for (i = 0; i < FLOW_N_REGS; i++) {
>         flow->regs[i] &= wildcards->reg_masks[i];
>     }
> -    if (wc & NXFW_TUN_ID) {
> +    if (wc & FWW_TUN_ID) {
>         flow->tun_id = 0;
>     }
>     flow->nw_src &= wildcards->nw_src_mask;
>     flow->nw_dst &= wildcards->nw_dst_mask;
> -    if (wc & OFPFW_IN_PORT) {
> +    if (wc & FWW_IN_PORT) {
>         flow->in_port = 0;
>     }
> -    if (wc & OFPFW_DL_VLAN) {
> +    if (wc & FWW_DL_VLAN) {
>         flow->dl_vlan = 0;
>     }
> -    if (wc & OFPFW_DL_TYPE) {
> +    if (wc & FWW_DL_TYPE) {
>         flow->dl_type = 0;
>     }
> -    if (wc & OFPFW_TP_SRC) {
> +    if (wc & FWW_TP_SRC) {
>         flow->tp_src = 0;
>     }
> -    if (wc & OFPFW_TP_DST) {
> +    if (wc & FWW_TP_DST) {
>         flow->tp_dst = 0;
>     }
> -    if (wc & OFPFW_DL_SRC) {
> +    if (wc & FWW_DL_SRC) {
>         memset(flow->dl_src, 0, sizeof flow->dl_src);
>     }
> -    if (wc & OFPFW_DL_DST) {
> +    if (wc & FWW_DL_DST) {
>         flow->dl_dst[0] &= 0x01;
>         memset(&flow->dl_dst[1], 0, 5);
>     }
>     if (wc & FWW_ETH_MCAST) {
>         flow->dl_dst[0] &= 0xfe;
>     }
> -    if (wc & OFPFW_NW_PROTO) {
> +    if (wc & FWW_NW_PROTO) {
>         flow->nw_proto = 0;
>     }
> -    if (wc & OFPFW_DL_VLAN_PCP) {
> +    if (wc & FWW_DL_VLAN_PCP) {
>         flow->dl_vlan_pcp = 0;
>     }
> -    if (wc & OFPFW_NW_TOS) {
> +    if (wc & FWW_NW_TOS) {
>         flow->nw_tos = 0;
>     }
> }
> diff --git a/lib/classifier.h b/lib/classifier.h
> index d8135e9..e4b7f5f 100644
> --- a/lib/classifier.h
> +++ b/lib/classifier.h
> @@ -72,11 +72,6 @@ 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_from_match(const struct ofp_match *, unsigned int priority,
> -                         int flow_format, uint64_t cookie, struct cls_rule *);
> -void cls_rule_to_match(const struct cls_rule *, int flow_format,
> -                       struct ofp_match *);
> -
> void cls_rule_zero_wildcarded_fields(struct cls_rule *);
> 
> void cls_rule_set_in_port(struct cls_rule *, uint16_t odp_port);
> diff --git a/lib/flow.c b/lib/flow.c
> index 7c3ad51..8146519 100644
> --- a/lib/flow.c
> +++ b/lib/flow.c
> @@ -294,38 +294,13 @@ flow_print(FILE *stream, const struct flow *flow)
> 
> /* flow_wildcards functions. */
> 
> -/* Return 'wildcards' in "normal form":
> - *
> - *   - Forces unknown bits to 0.
> - *
> - *   - Forces nw_src and nw_dst masks greater than 32 to exactly 32.
> - */
> -static inline uint32_t
> -flow_wildcards_normalize(uint32_t wildcards)
> -{
> -    wildcards &= wildcards & (OVSFW_ALL | FWW_ALL);
> -    if (wildcards & (0x20 << OFPFW_NW_SRC_SHIFT)) {
> -        wildcards &= ~(0x1f << OFPFW_NW_SRC_SHIFT);
> -    }
> -    if (wildcards & (0x20 << OFPFW_NW_DST_SHIFT)) {
> -        wildcards &= ~(0x1f << OFPFW_NW_DST_SHIFT);
> -    }
> -    return wildcards;
> -}
> -
> -/* Initializes 'wc' from 'wildcards', which may be any combination of the
> - * OFPFW_* and OVSFW_* wildcard bits.
> - *
> - * All registers (NXM_NX_REG*) are always completely wildcarded, because
> - * 'wildcards' doesn't have enough bits to give the details on which
> - * particular bits should be wildcarded (if any).  The caller may use
> - * flow_wildcards_set_reg_mask() to update the register wildcard masks. */
> +/* Initializes 'wc' as a set of wildcards that matches every packet. */
> void
> -flow_wildcards_init(struct flow_wildcards *wc, uint32_t wildcards)
> +flow_wildcards_init_catchall(struct flow_wildcards *wc)
> {
> -    wc->wildcards = flow_wildcards_normalize(wildcards) | FWW_REGS;
> -    wc->nw_src_mask = ofputil_wcbits_to_netmask(wildcards >> OFPFW_NW_SRC_SHIFT);
> -    wc->nw_dst_mask = ofputil_wcbits_to_netmask(wildcards >> OFPFW_NW_DST_SHIFT);
> +    wc->wildcards = FWW_ALL;
> +    wc->nw_src_mask = htonl(0);
> +    wc->nw_dst_mask = htonl(0);
>     memset(wc->reg_masks, 0, sizeof wc->reg_masks);
> }
> 
> @@ -345,15 +320,21 @@ flow_wildcards_init_exact(struct flow_wildcards *wc)
> bool
> flow_wildcards_is_exact(const struct flow_wildcards *wc)
> {
> -    return !wc->wildcards;
> -}
> +    int i;
> 
> -static inline uint32_t
> -combine_nw_bits(uint32_t wb1, uint32_t wb2, int shift)
> -{
> -    uint32_t sb1 = (wb1 >> shift) & 0x3f;
> -    uint32_t sb2 = (wb2 >> shift) & 0x3f;
> -    return MAX(sb1, sb2) << shift;
> +    if (wc->wildcards
> +        || wc->nw_src_mask != htonl(UINT32_MAX)
> +        || wc->nw_dst_mask != htonl(UINT32_MAX)) {
> +        return false;
> +    }
> +
> +    for (i = 0; i < FLOW_N_REGS; i++) {
> +        if (wc->reg_masks[i] != htonl(UINT32_MAX)) {
> +            return false;
> +        }
> +    }
> +
> +    return true;
> }
> 
> /* Initializes 'dst' as the combination of wildcards in 'src1' and 'src2'.
> @@ -364,13 +345,9 @@ flow_wildcards_combine(struct flow_wildcards *dst,
>                        const struct flow_wildcards *src1,
>                        const struct flow_wildcards *src2)
> {
> -    uint32_t wb1 = src1->wildcards;
> -    uint32_t wb2 = src2->wildcards;
>     int i;
> 
> -    dst->wildcards = (wb1 | wb2) & ~(OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK);
> -    dst->wildcards |= combine_nw_bits(wb1, wb2, OFPFW_NW_SRC_SHIFT);
> -    dst->wildcards |= combine_nw_bits(wb1, wb2, OFPFW_NW_DST_SHIFT);
> +    dst->wildcards = src1->wildcards | src2->wildcards;
>     dst->nw_src_mask = src1->nw_src_mask & src2->nw_src_mask;
>     dst->nw_dst_mask = src1->nw_dst_mask & src2->nw_dst_mask;
>     for (i = 0; i < FLOW_N_REGS; i++) {
> @@ -382,13 +359,11 @@ flow_wildcards_combine(struct flow_wildcards *dst,
> uint32_t
> flow_wildcards_hash(const struct flow_wildcards *wc)
> {
> -    /* There is no need to include nw_src_mask or nw_dst_mask because they do
> -     * not add any information (they can be computed from wc->wildcards).  */
> -    BUILD_ASSERT_DECL(sizeof wc->wildcards == 4);
> -    BUILD_ASSERT_DECL(sizeof wc->reg_masks == 4 * FLOW_N_REGS);
> -    BUILD_ASSERT_DECL(offsetof(struct flow_wildcards, wildcards) == 0);
> -    BUILD_ASSERT_DECL(offsetof(struct flow_wildcards, reg_masks) == 4);
> -    return hash_words((const uint32_t *) wc, 1 + FLOW_N_REGS, 0);
> +    /* 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 == 12 + FLOW_N_REGS * 4);
> +    return hash_bytes(wc, sizeof *wc, 0);
> }
> 
> /* Returns true if 'a' and 'b' represent the same wildcards, false if they are
> @@ -399,7 +374,9 @@ flow_wildcards_equal(const struct flow_wildcards *a,
> {
>     int i;
> 
> -    if (a->wildcards != b->wildcards) {
> +    if (a->wildcards != b->wildcards
> +        || a->nw_src_mask != b->nw_src_mask
> +        || a->nw_dst_mask != b->nw_dst_mask) {
>         return false;
>     }
> 
> @@ -426,19 +403,15 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
>         }
>     }
> 
> -#define OFPFW_NW_MASK (OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK)
> -    return ((a->wildcards & ~(b->wildcards | OFPFW_NW_MASK))
> +    return (a->wildcards & ~b->wildcards
>             || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
>             || (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask);
> }
> 
> static bool
> -set_nw_mask(struct flow_wildcards *wc, ovs_be32 mask,
> -            ovs_be32 *maskp, int shift)
> +set_nw_mask(ovs_be32 *maskp, ovs_be32 mask)
> {
>     if (ip_is_cidr(mask)) {
> -        wc->wildcards &= ~(0x3f << shift);
> -        wc->wildcards |= ofputil_netmask_to_wcbits(mask) << shift;
>         *maskp = mask;
>         return true;
>     } else {
> @@ -452,7 +425,7 @@ set_nw_mask(struct flow_wildcards *wc, ovs_be32 mask,
> bool
> flow_wildcards_set_nw_src_mask(struct flow_wildcards *wc, ovs_be32 mask)
> {
> -    return set_nw_mask(wc, mask, &wc->nw_src_mask, OFPFW_NW_SRC_SHIFT);
> +    return set_nw_mask(&wc->nw_src_mask, mask);
> }
> 
> /* Sets the IP (or ARP) destination wildcard mask to CIDR 'mask' (consisting of
> @@ -461,7 +434,7 @@ flow_wildcards_set_nw_src_mask(struct flow_wildcards *wc, ovs_be32 mask)
> bool
> flow_wildcards_set_nw_dst_mask(struct flow_wildcards *wc, ovs_be32 mask)
> {
> -    return set_nw_mask(wc, mask, &wc->nw_dst_mask, OFPFW_NW_DST_SHIFT);
> +    return set_nw_mask(&wc->nw_dst_mask, mask);
> }
> 
> /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
> @@ -469,20 +442,5 @@ flow_wildcards_set_nw_dst_mask(struct flow_wildcards *wc, ovs_be32 mask)
> void
> flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
> {
> -    if (mask != wc->reg_masks[idx]) {
> -        wc->reg_masks[idx] = mask;
> -        if (mask != UINT32_MAX) {
> -            wc->wildcards |= FWW_REGS;
> -        } else {
> -            int i;
> -
> -            for (i = 0; i < FLOW_N_REGS; i++) {
> -                if (wc->reg_masks[i] != UINT32_MAX) {
> -                    wc->wildcards |= FWW_REGS;
> -                    return;
> -                }
> -            }
> -            wc->wildcards &= ~FWW_REGS;
> -        }
> -    }
> +    wc->reg_masks[idx] = mask;
> }
> diff --git a/lib/flow.h b/lib/flow.h
> index 058b5e8..f772936 100644
> --- a/lib/flow.h
> +++ b/lib/flow.h
> @@ -89,58 +89,46 @@ flow_hash(const struct flow *flow, uint32_t basis)
>     return hash_bytes(flow, FLOW_SIG_SIZE, basis);
> }
> 
> -/* Open vSwitch internal-only wildcard bits.
> +/* 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. */
> 
> -/* Set to 1 if any bits in any of the reg_masks are wildcarded.  This maintains
> - * the invariant that 'wildcards' is nonzero if and only if any bits are
> - * wildcarded. */
> -#define FWW_REGS (1u << 31)
> -
> -/* Set to 1 if bit 0 (the multicast bit) of the flow's dl_dst is wildcarded.
> - *
> - * (We reinterpret OFPFW_DL_DST as excluding bit 0.) */
> -#define FWW_ETH_MCAST (1u << 30)
> -
> -/* Avoid collisions. */
> -#define FWW_ALL (FWW_REGS | FWW_ETH_MCAST)
> -BUILD_ASSERT_DECL(!(FWW_ALL & OVSFW_ALL));
> +typedef unsigned int OVS_BITWISE flow_wildcards_t;
> +
> +/* Same values and meanings as corresponding OFPFW_* bits. */
> +#define FWW_IN_PORT     ((OVS_FORCE flow_wildcards_t) (1 << 0))
> +#define FWW_DL_VLAN     ((OVS_FORCE flow_wildcards_t) (1 << 1))
> +#define FWW_DL_SRC      ((OVS_FORCE flow_wildcards_t) (1 << 2))
> +#define FWW_DL_DST      ((OVS_FORCE flow_wildcards_t) (1 << 3))
> +                                              /* excluding the multicast bit */
> +#define FWW_DL_TYPE     ((OVS_FORCE flow_wildcards_t) (1 << 4))
> +#define FWW_NW_PROTO    ((OVS_FORCE flow_wildcards_t) (1 << 5))
> +#define FWW_TP_SRC      ((OVS_FORCE flow_wildcards_t) (1 << 6))
> +#define FWW_TP_DST      ((OVS_FORCE flow_wildcards_t) (1 << 7))
> +/* Same meanings as corresponding OFPFW_* bits, but differ in value. */
> +#define FWW_DL_VLAN_PCP ((OVS_FORCE flow_wildcards_t) (1 << 8))
> +#define FWW_NW_TOS      ((OVS_FORCE flow_wildcards_t) (1 << 9))
> +/* No OFPFW_* bits, but they do have corresponding OVSFW_* bits. */
> +#define FWW_TUN_ID      ((OVS_FORCE flow_wildcards_t) (1 << 10))
> +/* No corresponding OFPFW_* or OVSFW_* bits. */
> +#define FWW_ETH_MCAST   ((OVS_FORCE flow_wildcards_t) (1 << 11))
> +                                                       /* multicast bit only */
> +#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1))
> 
> /* Information on wildcards for a flow, as a supplement to "struct flow".
>  *
> - * The flow_wildcards_*() functions below both depend on and maintain the
> - * following important invariants:
> - *
> - * 1. 'wildcards' is nonzero if and only if at least one bit or field is
> - *    wildcarded.
> - *
> - * 2. Bits in 'wildcards' not included in OVSFW_ALL or FWW_ALL are set to 0.
> - *    (This is a corollary to invariant #1.)
> - *
> - * 3. The fields in 'wildcards' masked by OFPFW_NW_SRC_MASK and
> - *    OFPFW_NW_DST_MASK have values between 0 and 32, inclusive.
> - *
> - * 4. The fields masked by OFPFW_NW_SRC_MASK and OFPFW_NW_DST_MASK correspond
> - *    correctly to the masks in 'nw_src_mask' and 'nw_dst_mask', respectively.
> - *
> - * 5. FWW_REGS is set to 1 in 'wildcards' if and only if at least one bit in
> - *    'reg_masks[]' is nonzero.  (This allows wildcarded 'reg_masks[]' to
> - *    satisfy invariant #1.)
> - *
> - * 6. If FWW_REGS is set to 0 in 'wildcards', then the values of all of the
> - *    other members can be correctly predicted based on 'wildcards' alone.
> - */
> + * Note that the meaning of 1-bits in 'wildcards' is opposite that of 1-bits in
> + * the rest of the members. */
> struct flow_wildcards {
> -    uint32_t wildcards;         /* OFPFW_* | OVSFW_* | FWW_*. */
> +    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. */
> };
> 
> -void flow_wildcards_init(struct flow_wildcards *, uint32_t wildcards);
> +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 *);
> diff --git a/lib/nx-match.c b/lib/nx-match.c
> index 0ba33f7..210ca9a 100644
> --- a/lib/nx-match.c
> +++ b/lib/nx-match.c
> @@ -55,7 +55,7 @@ struct nxm_field {
>     struct hmap_node hmap_node;
>     enum nxm_field_index index; /* NFI_* value. */
>     uint32_t header;            /* NXM_* value. */
> -    uint32_t wildcard;          /* Wildcard bit, if exactly one. */
> +    flow_wildcards_t wildcard;  /* FWW_* bit, if exactly one. */
>     ovs_be16 dl_type;           /* dl_type prerequisite, if nonzero. */
>     uint8_t nw_proto;           /* nw_proto prerequisite, if nonzero. */
>     const char *name;           /* "NXM_*" string. */
> @@ -143,8 +143,9 @@ nxm_field_bits(uint32_t header)
> static int
> parse_tci(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
> {
> -    enum { OFPFW_DL_TCI = OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP };
> -    if ((rule->wc.wildcards & OFPFW_DL_TCI) != OFPFW_DL_TCI) {
> +    const flow_wildcards_t FWW_DL_TCI = FWW_DL_VLAN | FWW_DL_VLAN_PCP;
> +
> +    if ((rule->wc.wildcards & FWW_DL_TCI) != FWW_DL_TCI) {
>         return NXM_DUP_TYPE;
>     } else {
>         return cls_rule_set_dl_tci_masked(rule, tci, mask) ? 0 : NXM_INVALID;
> @@ -188,29 +189,29 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
> 
>         /* Ethernet header. */
>     case NFI_NXM_OF_ETH_DST:
> -        if ((wc->wildcards & (OFPFW_DL_DST | FWW_ETH_MCAST))
> -            != (OFPFW_DL_DST | FWW_ETH_MCAST)) {
> +        if ((wc->wildcards & (FWW_DL_DST | FWW_ETH_MCAST))
> +            != (FWW_DL_DST | FWW_ETH_MCAST)) {
>             return NXM_DUP_TYPE;
>         } else {
> -            wc->wildcards &= ~(OFPFW_DL_DST | FWW_ETH_MCAST);
> +            wc->wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
>             memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
>             return 0;
>         }
>     case NFI_NXM_OF_ETH_DST_W:
> -        if ((wc->wildcards & (OFPFW_DL_DST | FWW_ETH_MCAST))
> -            != (OFPFW_DL_DST | FWW_ETH_MCAST)) {
> +        if ((wc->wildcards & (FWW_DL_DST | FWW_ETH_MCAST))
> +            != (FWW_DL_DST | FWW_ETH_MCAST)) {
>             return NXM_DUP_TYPE;
>         } else if (eth_addr_equals(mask, eth_mcast_1)) {
>             wc->wildcards &= ~FWW_ETH_MCAST;
>             flow->dl_dst[0] = *(uint8_t *) value & 0x01;
>         } else if (eth_addr_equals(mask, eth_mcast_0)) {
> -            wc->wildcards &= ~OFPFW_DL_DST;
> +            wc->wildcards &= ~FWW_DL_DST;
>             memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
>             flow->dl_dst[0] &= 0xfe;
>         } else if (eth_addr_equals(mask, eth_all_0s)) {
>             return 0;
>         } else if (eth_addr_equals(mask, eth_all_1s)) {
> -            wc->wildcards &= ~(OFPFW_DL_DST | FWW_ETH_MCAST);
> +            wc->wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
>             memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
>             return 0;
>         } else {
> @@ -528,10 +529,10 @@ static void
> nxm_put_eth_dst(struct ofpbuf *b,
>                 uint32_t wc, const uint8_t value[ETH_ADDR_LEN])
> {
> -    switch (wc & (OFPFW_DL_DST | FWW_ETH_MCAST)) {
> -    case OFPFW_DL_DST | FWW_ETH_MCAST:
> +    switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) {
> +    case FWW_DL_DST | FWW_ETH_MCAST:
>         break;
> -    case OFPFW_DL_DST:
> +    case FWW_DL_DST:
>         nxm_put_header(b, NXM_OF_ETH_DST_W);
>         ofpbuf_put(b, value, ETH_ADDR_LEN);
>         ofpbuf_put(b, eth_mcast_1, ETH_ADDR_LEN);
> @@ -551,7 +552,7 @@ nxm_put_eth_dst(struct ofpbuf *b,
> int
> nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
> {
> -    const uint32_t wc = cr->wc.wildcards;
> +    const flow_wildcards_t wc = cr->wc.wildcards;
>     const struct flow *flow = &cr->flow;
>     const size_t start_len = b->size;
>     ovs_be16 vid, pcp;
> @@ -559,7 +560,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
>     int i;
> 
>     /* Metadata. */
> -    if (!(wc & OFPFW_IN_PORT)) {
> +    if (!(wc & FWW_IN_PORT)) {
>         uint16_t in_port = flow->in_port;
>         if (in_port == ODPP_LOCAL) {
>             in_port = OFPP_LOCAL;
> @@ -569,24 +570,24 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
> 
>     /* Ethernet. */
>     nxm_put_eth_dst(b, wc, flow->dl_dst);
> -    if (!(wc & OFPFW_DL_SRC)) {
> +    if (!(wc & FWW_DL_SRC)) {
>         nxm_put_eth(b, NXM_OF_ETH_SRC, flow->dl_src);
>     }
> -    if (!(wc & OFPFW_DL_TYPE)) {
> +    if (!(wc & FWW_DL_TYPE)) {
>         nxm_put_16(b, NXM_OF_ETH_TYPE, flow->dl_type);
>     }
> 
>     /* 802.1Q. */
>     vid = flow->dl_vlan & htons(VLAN_VID_MASK);
>     pcp = htons((flow->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK);
> -    switch (wc & (OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP)) {
> -    case OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP:
> +    switch (wc & (FWW_DL_VLAN | FWW_DL_VLAN_PCP)) {
> +    case FWW_DL_VLAN | FWW_DL_VLAN_PCP:
>         break;
> -    case OFPFW_DL_VLAN:
> +    case FWW_DL_VLAN:
>         nxm_put_16w(b, NXM_OF_VLAN_TCI_W, pcp | htons(VLAN_CFI),
>                      htons(VLAN_PCP_MASK | VLAN_CFI));
>         break;
> -    case OFPFW_DL_VLAN_PCP:
> +    case FWW_DL_VLAN_PCP:
>         if (flow->dl_vlan == htons(OFP_VLAN_NONE)) {
>             nxm_put_16(b, NXM_OF_VLAN_TCI, 0);
>         } else {
> @@ -603,51 +604,51 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
>         break;
>     }
> 
> -    if (!(wc & OFPFW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IP)) {
> +    if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IP)) {
>         /* IP. */
> -        if (!(wc & OFPFW_NW_TOS)) {
> +        if (!(wc & FWW_NW_TOS)) {
>             nxm_put_8(b, NXM_OF_IP_TOS, flow->nw_tos & 0xfc);
>         }
>         nxm_put_32m(b, NXM_OF_IP_SRC, flow->nw_src, cr->wc.nw_src_mask);
>         nxm_put_32m(b, NXM_OF_IP_DST, flow->nw_dst, cr->wc.nw_dst_mask);
> 
> -        if (!(wc & OFPFW_NW_PROTO)) {
> +        if (!(wc & FWW_NW_PROTO)) {
>             nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto);
>             switch (flow->nw_proto) {
>                 /* TCP. */
>             case IP_TYPE_TCP:
> -                if (!(wc & OFPFW_TP_SRC)) {
> +                if (!(wc & FWW_TP_SRC)) {
>                     nxm_put_16(b, NXM_OF_TCP_SRC, flow->tp_src);
>                 }
> -                if (!(wc & OFPFW_TP_DST)) {
> +                if (!(wc & FWW_TP_DST)) {
>                     nxm_put_16(b, NXM_OF_TCP_DST, flow->tp_dst);
>                 }
>                 break;
> 
>                 /* UDP. */
>             case IP_TYPE_UDP:
> -                if (!(wc & OFPFW_TP_SRC)) {
> +                if (!(wc & FWW_TP_SRC)) {
>                     nxm_put_16(b, NXM_OF_UDP_SRC, flow->tp_src);
>                 }
> -                if (!(wc & OFPFW_TP_DST)) {
> +                if (!(wc & FWW_TP_DST)) {
>                     nxm_put_16(b, NXM_OF_UDP_DST, flow->tp_dst);
>                 }
>                 break;
> 
>                 /* ICMP. */
>             case IP_TYPE_ICMP:
> -                if (!(wc & OFPFW_TP_SRC)) {
> +                if (!(wc & FWW_TP_SRC)) {
>                     nxm_put_8(b, NXM_OF_ICMP_TYPE, ntohs(flow->tp_src));
>                 }
> -                if (!(wc & OFPFW_TP_DST)) {
> +                if (!(wc & FWW_TP_DST)) {
>                     nxm_put_8(b, NXM_OF_ICMP_CODE, ntohs(flow->tp_dst));
>                 }
>                 break;
>             }
>         }
> -    } else if (!(wc & OFPFW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_ARP)) {
> +    } else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_ARP)) {
>         /* ARP. */
> -        if (!(wc & OFPFW_NW_PROTO)) {
> +        if (!(wc & FWW_NW_PROTO)) {
>             nxm_put_16(b, NXM_OF_ARP_OP, htons(flow->nw_proto));
>         }
>         nxm_put_32m(b, NXM_OF_ARP_SPA, flow->nw_src, cr->wc.nw_src_mask);
> @@ -655,7 +656,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
>     }
> 
>     /* Tunnel ID. */
> -    if (!(wc & NXFW_TUN_ID)) {
> +    if (!(wc & FWW_TUN_ID)) {
>         nxm_put_64(b, NXM_NX_TUN_ID, htonll(ntohl(flow->tun_id)));
>     }
> 
> diff --git a/lib/nx-match.def b/lib/nx-match.def
> index f6167af..e045d0f 100644
> --- a/lib/nx-match.def
> +++ b/lib/nx-match.def
> @@ -18,37 +18,37 @@
>         DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO)               \
>         DEFINE_FIELD(HEADER##_W, WILDCARD, DL_TYPE, NW_PROTO)
> 
> -/*             NXM_ bit      OFPFW_* bit     dl_type       nw_proto      */
> -/*             ------------  --------------  -----------   ------------- */
> -DEFINE_FIELD  (OF_IN_PORT,   OFPFW_IN_PORT,  0,            0)
> -DEFINE_FIELD_M(OF_ETH_DST,   0,              0,            0)
> -DEFINE_FIELD  (OF_ETH_SRC,   OFPFW_DL_SRC,   0,            0)
> -DEFINE_FIELD  (OF_ETH_TYPE,  OFPFW_DL_TYPE,  0,            0)
> -DEFINE_FIELD_M(OF_VLAN_TCI,  0,              0,            0)
> -DEFINE_FIELD  (OF_IP_TOS,    OFPFW_NW_TOS,   ETH_TYPE_IP,  0)
> -DEFINE_FIELD  (OF_IP_PROTO,  OFPFW_NW_PROTO, ETH_TYPE_IP,  0)
> -DEFINE_FIELD_M(OF_IP_SRC,    0,              ETH_TYPE_IP,  0)
> -DEFINE_FIELD_M(OF_IP_DST,    0,              ETH_TYPE_IP,  0)
> -DEFINE_FIELD  (OF_TCP_SRC,   OFPFW_TP_SRC,   ETH_TYPE_IP,  IP_TYPE_TCP)
> -DEFINE_FIELD  (OF_TCP_DST,   OFPFW_TP_DST,   ETH_TYPE_IP,  IP_TYPE_TCP)
> -DEFINE_FIELD  (OF_UDP_SRC,   OFPFW_TP_SRC,   ETH_TYPE_IP,  IP_TYPE_UDP)
> -DEFINE_FIELD  (OF_UDP_DST,   OFPFW_TP_DST,   ETH_TYPE_IP,  IP_TYPE_UDP)
> -DEFINE_FIELD  (OF_ICMP_TYPE, OFPFW_TP_SRC,   ETH_TYPE_IP,  IP_TYPE_ICMP)
> -DEFINE_FIELD  (OF_ICMP_CODE, OFPFW_TP_DST,   ETH_TYPE_IP,  IP_TYPE_ICMP)
> -DEFINE_FIELD  (OF_ARP_OP,    OFPFW_NW_PROTO, ETH_TYPE_ARP, 0)
> -DEFINE_FIELD_M(OF_ARP_SPA,   0,              ETH_TYPE_ARP, 0)
> -DEFINE_FIELD_M(OF_ARP_TPA,   0,              ETH_TYPE_ARP, 0)
> -DEFINE_FIELD  (NX_TUN_ID,    NXFW_TUN_ID,    0,            0)
> +/*             NXM_ suffix   FWW_* bit     dl_type       nw_proto      */
> +/*             ------------  ------------  -----------   ------------- */
> +DEFINE_FIELD  (OF_IN_PORT,   FWW_IN_PORT,  0,            0)
> +DEFINE_FIELD_M(OF_ETH_DST,   0,            0,            0)
> +DEFINE_FIELD  (OF_ETH_SRC,   FWW_DL_SRC,   0,            0)
> +DEFINE_FIELD  (OF_ETH_TYPE,  FWW_DL_TYPE,  0,            0)
> +DEFINE_FIELD_M(OF_VLAN_TCI,  0,            0,            0)
> +DEFINE_FIELD  (OF_IP_TOS,    FWW_NW_TOS,   ETH_TYPE_IP,  0)
> +DEFINE_FIELD  (OF_IP_PROTO,  FWW_NW_PROTO, ETH_TYPE_IP,  0)
> +DEFINE_FIELD_M(OF_IP_SRC,    0,            ETH_TYPE_IP,  0)
> +DEFINE_FIELD_M(OF_IP_DST,    0,            ETH_TYPE_IP,  0)
> +DEFINE_FIELD  (OF_TCP_SRC,   FWW_TP_SRC,   ETH_TYPE_IP,  IP_TYPE_TCP)
> +DEFINE_FIELD  (OF_TCP_DST,   FWW_TP_DST,   ETH_TYPE_IP,  IP_TYPE_TCP)
> +DEFINE_FIELD  (OF_UDP_SRC,   FWW_TP_SRC,   ETH_TYPE_IP,  IP_TYPE_UDP)
> +DEFINE_FIELD  (OF_UDP_DST,   FWW_TP_DST,   ETH_TYPE_IP,  IP_TYPE_UDP)
> +DEFINE_FIELD  (OF_ICMP_TYPE, FWW_TP_SRC,   ETH_TYPE_IP,  IP_TYPE_ICMP)
> +DEFINE_FIELD  (OF_ICMP_CODE, FWW_TP_DST,   ETH_TYPE_IP,  IP_TYPE_ICMP)
> +DEFINE_FIELD  (OF_ARP_OP,    FWW_NW_PROTO, ETH_TYPE_ARP, 0)
> +DEFINE_FIELD_M(OF_ARP_SPA,   0,            ETH_TYPE_ARP, 0)
> +DEFINE_FIELD_M(OF_ARP_TPA,   0,            ETH_TYPE_ARP, 0)
> +DEFINE_FIELD  (NX_TUN_ID,    FWW_TUN_ID,   0,            0)
> 
> -DEFINE_FIELD_M(NX_REG0,      0,              0,            0)
> +DEFINE_FIELD_M(NX_REG0,      0,            0,            0)
> #if FLOW_N_REGS >= 2
> -DEFINE_FIELD_M(NX_REG1,      0,              0,            0)
> +DEFINE_FIELD_M(NX_REG1,      0,            0,            0)
> #endif
> #if FLOW_N_REGS >= 3
> -DEFINE_FIELD_M(NX_REG2,      0,              0,            0)
> +DEFINE_FIELD_M(NX_REG2,      0,            0,            0)
> #endif
> #if FLOW_N_REGS >= 4
> -DEFINE_FIELD_M(NX_REG3,      0,              0,            0)
> +DEFINE_FIELD_M(NX_REG3,      0,            0,            0)
> #endif
> #if FLOW_N_REGS > 4
> #error
> diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
> index f572f4e..4de9708 100644
> --- a/lib/ofp-parse.c
> +++ b/lib/ofp-parse.c
> @@ -345,20 +345,20 @@ parse_protocol(const char *name, const struct protocol **p_out)
> }
> 
> #define FIELDS                                              \
> -    FIELD(F_IN_PORT,     "in_port",     OFPFW_IN_PORT)      \
> -    FIELD(F_DL_VLAN,     "dl_vlan",     OFPFW_DL_VLAN)      \
> -    FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", OFPFW_DL_VLAN_PCP)  \
> -    FIELD(F_DL_SRC,      "dl_src",      OFPFW_DL_SRC)       \
> -    FIELD(F_DL_DST,      "dl_dst",      OFPFW_DL_DST)       \
> -    FIELD(F_DL_TYPE,     "dl_type",     OFPFW_DL_TYPE)      \
> +    FIELD(F_IN_PORT,     "in_port",     FWW_IN_PORT)        \
> +    FIELD(F_DL_VLAN,     "dl_vlan",     FWW_DL_VLAN)        \
> +    FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", FWW_DL_VLAN_PCP)    \
> +    FIELD(F_DL_SRC,      "dl_src",      FWW_DL_SRC)         \
> +    FIELD(F_DL_DST,      "dl_dst",      FWW_DL_DST)         \
> +    FIELD(F_DL_TYPE,     "dl_type",     FWW_DL_TYPE)        \
>     FIELD(F_NW_SRC,      "nw_src",      0)                  \
>     FIELD(F_NW_DST,      "nw_dst",      0)                  \
> -    FIELD(F_NW_PROTO,    "nw_proto",    OFPFW_NW_PROTO)     \
> -    FIELD(F_NW_TOS,      "nw_tos",      OFPFW_NW_TOS)       \
> -    FIELD(F_TP_SRC,      "tp_src",      OFPFW_TP_SRC)       \
> -    FIELD(F_TP_DST,      "tp_dst",      OFPFW_TP_DST)       \
> -    FIELD(F_ICMP_TYPE,   "icmp_type",   OFPFW_ICMP_TYPE)    \
> -    FIELD(F_ICMP_CODE,   "icmp_code",   OFPFW_ICMP_CODE)
> +    FIELD(F_NW_PROTO,    "nw_proto",    FWW_NW_PROTO)       \
> +    FIELD(F_NW_TOS,      "nw_tos",      FWW_NW_TOS)         \
> +    FIELD(F_TP_SRC,      "tp_src",      FWW_TP_SRC)         \
> +    FIELD(F_TP_DST,      "tp_dst",      FWW_TP_DST)         \
> +    FIELD(F_ICMP_TYPE,   "icmp_type",   FWW_TP_SRC)         \
> +    FIELD(F_ICMP_CODE,   "icmp_code",   FWW_TP_DST)
> 
> enum field_index {
> #define FIELD(ENUM, NAME, WILDCARD) ENUM,
> @@ -370,7 +370,7 @@ enum field_index {
> struct field {
>     enum field_index index;
>     const char *name;
> -    uint32_t wildcard;
> +    flow_wildcards_t wildcard;  /* FWW_* bit. */
> };
> 
> static bool
> @@ -571,7 +571,7 @@ parse_ofp_flow_mod(char *string, uint16_t command)
>     parse_ofp_str(&pf, buffer, string);
> 
>     ofm = buffer->data;
> -    cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &ofm->match);
> +    ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &ofm->match);
>     ofm->command = htons(command);
>     ofm->cookie = htonll(pf.cookie);
>     ofm->idle_timeout = htons(pf.idle_timeout);
> diff --git a/lib/ofp-util.c b/lib/ofp-util.c
> index 6712dd1..7d4cb14 100644
> --- a/lib/ofp-util.c
> +++ b/lib/ofp-util.c
> @@ -19,6 +19,7 @@
> #include <inttypes.h>
> #include <stdlib.h>
> #include "byte-order.h"
> +#include "classifier.h"
> #include "nx-match.h"
> #include "ofp-util.h"
> #include "ofpbuf.h"
> @@ -67,6 +68,141 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
> #endif
> }
> 
> +/* A list of the FWW_* and OFPFW_ bits that have the same value, meaning, and
> + * name. */
> +#define WC_INVARIANT_LIST \
> +    WC_INVARIANT_BIT(IN_PORT) \
> +    WC_INVARIANT_BIT(DL_VLAN) \
> +    WC_INVARIANT_BIT(DL_SRC) \
> +    WC_INVARIANT_BIT(DL_DST) \
> +    WC_INVARIANT_BIT(DL_TYPE) \
> +    WC_INVARIANT_BIT(NW_PROTO) \
> +    WC_INVARIANT_BIT(TP_SRC) \
> +    WC_INVARIANT_BIT(TP_DST)
> +
> +/* 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 == OFPFW_##NAME);
> +    WC_INVARIANT_LIST
> +#undef WC_INVARIANT_BIT
> +
> +/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all
> + * OR'd together. */
> +enum {
> +    WC_INVARIANTS = 0
> +#define WC_INVARIANT_BIT(NAME) | FWW_##NAME
> +    WC_INVARIANT_LIST
> +#undef WC_INVARIANT_BIT
> +};
> +
> +/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
> + * 'priority'.
> + *
> + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE.  In
> + * the latter case only, 'flow''s tun_id field will be taken from the high bits
> + * of 'cookie', if 'match''s wildcards do not indicate that tun_id is
> + * wildcarded. */
> +void
> +ofputil_cls_rule_from_match(const struct ofp_match *match,
> +                            unsigned int priority, int flow_format,
> +                            uint64_t cookie, struct cls_rule *rule)
> +{
> +    struct flow_wildcards *wc = &rule->wc;
> +    unsigned int ofpfw;
> +
> +    /* Initialize rule->priority. */
> +    ofpfw = ntohl(match->wildcards);
> +    ofpfw &= flow_format == NXFF_TUN_ID_FROM_COOKIE ? OVSFW_ALL : OFPFW_ALL;
> +    rule->priority = !ofpfw ? UINT16_MAX : priority;
> +
> +    /* Initialize most of rule->wc. */
> +    wc->wildcards = ofpfw & WC_INVARIANTS;
> +    if (ofpfw & OFPFW_DL_VLAN_PCP) {
> +        wc->wildcards |= FWW_DL_VLAN_PCP;
> +    }
> +    if (ofpfw & OFPFW_NW_TOS) {
> +        wc->wildcards |= FWW_NW_TOS;
> +    }
> +    memset(wc->reg_masks, 0, sizeof wc->reg_masks);
> +    wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT);
> +    wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT);
> +
> +    if (!(ofpfw & NXFW_TUN_ID)) {
> +        rule->flow.tun_id = htonl(ntohll(cookie) >> 32);
> +    } else {
> +        wc->wildcards |= FWW_TUN_ID;
> +        rule->flow.tun_id = 0;
> +    }
> +
> +    if (ofpfw & OFPFW_DL_DST) {
> +        wc->wildcards |= FWW_ETH_MCAST;
> +    }
> +
> +    /* Initialize rule->flow. */
> +    rule->flow.nw_src = match->nw_src;
> +    rule->flow.nw_dst = match->nw_dst;
> +    rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
> +                     : ntohs(match->in_port));
> +    rule->flow.dl_vlan = match->dl_vlan;
> +    rule->flow.dl_vlan_pcp = match->dl_vlan_pcp;
> +    rule->flow.dl_type = 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;
> +    rule->flow.nw_proto = match->nw_proto;
> +
> +    /* Clean up. */
> +    cls_rule_zero_wildcarded_fields(rule);
> +}
> +
> +/* Extract 'flow' with 'wildcards' into the OpenFlow match structure
> + * 'match'.
> + *
> + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE.  In
> + * the latter case only, 'match''s NXFW_TUN_ID bit will be filled in; otherwise
> + * it is always set to 0. */
> +void
> +ofputil_cls_rule_to_match(const struct cls_rule *rule, int flow_format,
> +                          struct ofp_match *match)
> +{
> +    const struct flow_wildcards *wc = &rule->wc;
> +    unsigned int ofpfw;
> +
> +    /* Figure out OpenFlow wildcards. */
> +    ofpfw = wc->wildcards & WC_INVARIANTS;
> +    ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
> +    ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
> +    if (wc->wildcards & FWW_DL_VLAN_PCP) {
> +        ofpfw |= OFPFW_DL_VLAN_PCP;
> +    }
> +    if (wc->wildcards & FWW_NW_TOS) {
> +        ofpfw |= OFPFW_NW_TOS;
> +    }
> +    if (flow_format == NXFF_TUN_ID_FROM_COOKIE && wc->wildcards & FWW_TUN_ID) {
> +        ofpfw |= NXFW_TUN_ID;
> +    }
> +
> +    /* Compose match structure. */
> +    match->wildcards = htonl(ofpfw);
> +    match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL
> +                           : rule->flow.in_port);
> +    match->dl_vlan = rule->flow.dl_vlan;
> +    match->dl_vlan_pcp = rule->flow.dl_vlan_pcp;
> +    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 = 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;
> +    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);
> +}
> +
> /* XXX we should really use consecutive xids to avoid probabilistic
>  * failures. */
> static inline uint32_t
> diff --git a/lib/ofp-util.h b/lib/ofp-util.h
> index d3dd692..1c02600 100644
> --- a/lib/ofp-util.h
> +++ b/lib/ofp-util.h
> @@ -23,6 +23,7 @@
> #include <stdint.h>
> #include "flow.h"
> 
> +struct cls_rule;
> struct ofpbuf;
> struct ofp_action_header;
> 
> @@ -31,6 +32,15 @@ struct ofp_action_header;
> ovs_be32 ofputil_wcbits_to_netmask(int wcbits);
> int ofputil_netmask_to_wcbits(ovs_be32 netmask);
> 
> +/* Work with OpenFlow 1.0 ofp_match. */
> +void ofputil_cls_rule_from_match(const struct ofp_match *,
> +                                 unsigned int priority, int flow_format,
> +                                 uint64_t cookie, struct cls_rule *);
> +void ofputil_cls_rule_to_match(const struct cls_rule *, int flow_format,
> +                               struct ofp_match *);
> +void normalize_match(struct ofp_match *);
> +char *ofp_match_to_literal_string(const struct ofp_match *match);
> +
> /* OpenFlow protocol utility functions. */
> void *make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **);
> void *make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **);
> @@ -86,9 +96,6 @@ int validate_actions(const union ofp_action *, size_t n_actions,
>                      const struct flow *, int max_ports);
> bool action_outputs_to_port(const union ofp_action *, uint16_t port);
> 
> -void normalize_match(struct ofp_match *);
> -char *ofp_match_to_literal_string(const struct ofp_match *match);
> -
> int ofputil_pull_actions(struct ofpbuf *, unsigned int actions_len,
>                          union ofp_action **, size_t *);
> 
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index e5b565c..9e287e7 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -3404,7 +3404,7 @@ put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
>     ofs->length = htons(len);
>     ofs->table_id = 0;
>     ofs->pad = 0;
> -    cls_rule_to_match(&rule->cr, ofconn->flow_format, &ofs->match);
> +    ofputil_cls_rule_to_match(&rule->cr, ofconn->flow_format, &ofs->match);
>     calc_flow_duration(rule->created, &ofs->duration_sec, &ofs->duration_nsec);
>     ofs->cookie = rule->flow_cookie;
>     ofs->priority = htons(rule->cr.priority);
> @@ -3443,7 +3443,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
>         struct cls_rule target;
>         struct rule *rule;
> 
> -        cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0, &target);
> +        ofputil_cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0,
> +                                    &target);
>         cls_cursor_init(&cursor, &ofconn->ofproto->cls, &target);
>         CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
>             put_ofp_flow_stats(ofconn, rule, fsr->out_port, &reply);
> @@ -3535,7 +3536,7 @@ flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds *results)
>     size_t act_len = sizeof *rule->actions * rule->n_actions;
> 
>     query_stats(ofproto, rule, &packet_count, &byte_count);
> -    cls_rule_to_match(&rule->cr, NXFF_OPENFLOW10, &match);
> +    ofputil_cls_rule_to_match(&rule->cr, NXFF_OPENFLOW10, &match);
> 
>     ds_put_format(results, "duration=%llds, ",
>                   (time_msec() - rule->created) / 1000);
> @@ -3616,7 +3617,8 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
>     }
>     request = (struct ofp_aggregate_stats_request *) osr->body;
> 
> -    cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0, &target);
> +    ofputil_cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0,
> +                                &target);
> 
>     msg = start_ofp_stats_reply(osr, sizeof *reply);
>     reply = append_ofp_stats_reply(sizeof *reply, ofconn, &msg);
> @@ -4172,8 +4174,8 @@ handle_ofpt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
>     }
> 
>     /* Translate the message. */
> -    cls_rule_from_match(&ofm->match, ntohs(ofm->priority), ofconn->flow_format,
> -                        ofm->cookie, &fm.cr);
> +    ofputil_cls_rule_from_match(&ofm->match, ntohs(ofm->priority),
> +                                ofconn->flow_format, ofm->cookie, &fm.cr);
>     fm.cookie = ofm->cookie;
>     fm.command = ntohs(ofm->command);
>     fm.idle_timeout = ntohs(ofm->idle_timeout);
> @@ -4802,7 +4804,7 @@ compose_ofp_flow_removed(struct ofconn *ofconn, const struct rule *rule,
>     struct ofpbuf *buf;
> 
>     ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
> -    cls_rule_to_match(&rule->cr, ofconn->flow_format, &ofr->match);
> +    ofputil_cls_rule_to_match(&rule->cr, ofconn->flow_format, &ofr->match);
>     ofr->cookie = rule->flow_cookie;
>     ofr->priority = htons(rule->cr.priority);
>     ofr->reason = reason;
> diff --git a/tests/test-classifier.c b/tests/test-classifier.c
> index 2de1eb3..fc1470e 100644
> --- a/tests/test-classifier.c
> +++ b/tests/test-classifier.c
> @@ -40,24 +40,23 @@
> #include <assert.h>
> 
> /* Fields in a rule. */
> -#define CLS_FIELDS                                          \
> -    /*                           struct flow  all-caps */   \
> -    /*        wildcard bit(s)    member name  name     */   \
> -    /*        -----------------  -----------  -------- */   \
> -    CLS_FIELD(NXFW_TUN_ID,       tun_id,      TUN_ID)       \
> -    CLS_FIELD(OFPFW_NW_SRC_MASK, nw_src,      NW_SRC)       \
> -    CLS_FIELD(OFPFW_NW_DST_MASK, nw_dst,      NW_DST)       \
> -    CLS_FIELD(OFPFW_IN_PORT,     in_port,     IN_PORT)      \
> -    CLS_FIELD(OFPFW_DL_VLAN,     dl_vlan,     DL_VLAN)      \
> -    CLS_FIELD(OFPFW_DL_TYPE,     dl_type,     DL_TYPE)      \
> -    CLS_FIELD(OFPFW_TP_SRC,      tp_src,      TP_SRC)       \
> -    CLS_FIELD(OFPFW_TP_DST,      tp_dst,      TP_DST)       \
> -    CLS_FIELD(OFPFW_DL_SRC,      dl_src,      DL_SRC)       \
> -    CLS_FIELD(OFPFW_DL_DST | FWW_ETH_MCAST,                 \
> -                                 dl_dst,      DL_DST)       \
> -    CLS_FIELD(OFPFW_NW_PROTO,    nw_proto,    NW_PROTO)     \
> -    CLS_FIELD(OFPFW_DL_VLAN_PCP, dl_vlan_pcp, DL_VLAN_PCP)  \
> -    CLS_FIELD(OFPFW_NW_TOS,      nw_tos,      NW_TOS)
> +#define CLS_FIELDS                                                  \
> +    /*                                    struct flow  all-caps */  \
> +    /*        FWW_* bit(s)                member name  name     */  \
> +    /*        --------------------------  -----------  -------- */  \
> +    CLS_FIELD(FWW_TUN_ID,                 tun_id,      TUN_ID)      \
> +    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(FWW_DL_VLAN,                dl_vlan,     DL_VLAN)     \
> +    CLS_FIELD(FWW_DL_TYPE,                dl_type,     DL_TYPE)     \
> +    CLS_FIELD(FWW_TP_SRC,                 tp_src,      TP_SRC)      \
> +    CLS_FIELD(FWW_TP_DST,                 tp_dst,      TP_DST)      \
> +    CLS_FIELD(FWW_DL_SRC,                 dl_src,      DL_SRC)      \
> +    CLS_FIELD(FWW_DL_DST | FWW_ETH_MCAST, dl_dst,      DL_DST)      \
> +    CLS_FIELD(FWW_NW_PROTO,               nw_proto,    NW_PROTO)    \
> +    CLS_FIELD(FWW_DL_VLAN_PCP,            dl_vlan_pcp, DL_VLAN_PCP) \
> +    CLS_FIELD(FWW_NW_TOS,                 nw_tos,      NW_TOS)
> 
> /* Field indexes.
>  *
> @@ -73,7 +72,7 @@ enum {
> struct cls_field {
>     int ofs;                    /* Offset in struct flow. */
>     int len;                    /* Length in bytes. */
> -    uint32_t wildcards;         /* OFPFW_* bit or bits for this field. */
> +    flow_wildcards_t wildcards; /* FWW_* bit or bits for this field. */
>     const char *name;           /* Name (for debugging). */
> };
> 
> @@ -189,30 +188,24 @@ match(const struct cls_rule *wild, const struct flow *fixed)
> 
>     for (f_idx = 0; f_idx < CLS_N_FIELDS; f_idx++) {
>         const struct cls_field *f = &cls_fields[f_idx];
> -        void *wild_field = (char *) &wild->flow + f->ofs;
> -        void *fixed_field = (char *) fixed + f->ofs;
> -
> -        if ((wild->wc.wildcards & f->wildcards) == f->wildcards ||
> -            !memcmp(wild_field, fixed_field, f->len)) {
> -            /* Definite match. */
> -            continue;
> +        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);
> +        } else if (f_idx == CLS_F_IDX_NW_DST) {
> +            eq = !((fixed->nw_dst ^ wild->flow.nw_dst) & wild->wc.nw_dst_mask);
> +        } else {
> +            NOT_REACHED();
>         }
> 
> -        if (wild->wc.wildcards & f->wildcards) {
> -            uint32_t test = get_unaligned_u32(wild_field);
> -            uint32_t ip = get_unaligned_u32(fixed_field);
> -            uint32_t mask;
> -            int shift;
> -
> -            shift = (f_idx == CLS_F_IDX_NW_SRC
> -                         ? OFPFW_NW_SRC_SHIFT : OFPFW_NW_DST_SHIFT);
> -            mask = ofputil_wcbits_to_netmask(wild->wc.wildcards) >> shift;
> -            if (!((test ^ ip) & mask)) {
> -                continue;
> -            }
> +        if (!eq) {
> +            return false;
>         }
> -
> -        return false;
>     }
>     return true;
> }
> @@ -445,27 +438,26 @@ static struct test_rule *
> make_rule(int wc_fields, unsigned int priority, int value_pat)
> {
>     const struct cls_field *f;
> -    struct flow_wildcards wc;
>     struct test_rule *rule;
> -    uint32_t wildcards;
> -    struct flow flow;
> 
> -    wildcards = 0;
> -    memset(&flow, 0, sizeof flow);
> +    rule = xzalloc(sizeof *rule);
> +    cls_rule_init_catchall(&rule->cls_rule, wc_fields ? priority : UINT_MAX);
>     for (f = &cls_fields[0]; f < &cls_fields[CLS_N_FIELDS]; f++) {
>         int f_idx = f - cls_fields;
> -        if (wc_fields & (1u << f_idx)) {
> -            wildcards |= f->wildcards;
> +        int value_idx = (value_pat & (1u << f_idx)) != 0;
> +        memcpy((char *) &rule->cls_rule.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);
> +        } else if (f_idx == CLS_F_IDX_NW_DST) {
> +            rule->cls_rule.wc.nw_dst_mask = htonl(UINT32_MAX);
>         } else {
> -            int value_idx = (value_pat & (1u << f_idx)) != 0;
> -            memcpy((char *) &flow + f->ofs, values[f_idx][value_idx], f->len);
> +            NOT_REACHED();
>         }
>     }
> -
> -    rule = xzalloc(sizeof *rule);
> -    flow_wildcards_init(&wc, wildcards);
> -    cls_rule_init(&flow, &wc, !wildcards ? UINT_MAX : priority,
> -                  &rule->cls_rule);
>     return rule;
> }
> 
> diff --git a/tests/test-flows.c b/tests/test-flows.c
> index b53d853..4ae198b 100644
> --- a/tests/test-flows.c
> +++ b/tests/test-flows.c
> @@ -70,7 +70,7 @@ main(int argc OVS_UNUSED, char *argv[])
> 
>         flow_extract(packet, 0, 1, &flow);
>         cls_rule_init_exact(&flow, 0, &rule);
> -        cls_rule_to_match(&rule, NXFF_OPENFLOW10, &extracted_match);
> +        ofputil_cls_rule_to_match(&rule, NXFF_OPENFLOW10, &extracted_match);
> 
>         if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) {
>             char *exp_s = ofp_match_to_string(&expected_match, 2);
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index 8581c56..4e49fd6 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -443,7 +443,7 @@ do_dump_flows(int argc, char *argv[])
> 
>     req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
>     parse_ofp_str(&pf, NULL, argc > 2 ? argv[2] : "");
> -    cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match);
> +    ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match);
>     memset(&req->pad, 0, sizeof req->pad);
>     req->out_port = htons(pf.out_port);
> 
> @@ -459,7 +459,7 @@ do_dump_aggregate(int argc, char *argv[])
> 
>     req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
>     parse_ofp_str(&pf, NULL, argc > 2 ? argv[2] : "");
> -    cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match);
> +    ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &req->match);
>     memset(&req->pad, 0, sizeof req->pad);
>     req->out_port = htons(pf.out_port);
> 
> -- 
> 1.7.1
> 
> 
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev_openvswitch.org





More information about the dev mailing list