Thank you for the reviews.  I pushed this to master, with the
improvement you pointed out in patch 2.

On Thu, Feb 02, 2012 at 12:46:56PM -0800, Ethan Jackson wrote:
> Looks good,
> 
> Ethan
> 
> On Fri, Jan 27, 2012 at 17:18, Ben Pfaff <b...@nicira.com> wrote:
> > Bug #8827.
> > Signed-off-by: Ben Pfaff <b...@nicira.com>
> > ---
> >  NEWS                          |    3 ++
> >  include/openflow/nicira-ext.h |   10 ++++-
> >  lib/classifier.c              |   77 
> > +++++++++++++++++++++++----------------
> >  lib/classifier.h              |    6 +++-
> >  lib/flow.c                    |   44 ++++++++++++++---------
> >  lib/flow.h                    |   22 ++++++------
> >  lib/meta-flow.c               |   81 
> > +++++++++++++++++++++++++----------------
> >  lib/nx-match.c                |   22 ++++--------
> >  lib/ofp-util.c                |   29 ++++++++++++---
> >  tests/ovs-ofctl.at            |   12 +++++-
> >  tests/test-classifier.c       |   14 ++++++--
> >  utilities/ovs-ofctl.8.in      |   64 ++++++++++++++++++++++++++++++++-
> >  12 files changed, 264 insertions(+), 120 deletions(-)
> >
> > diff --git a/NEWS b/NEWS
> > index d7332f8..d7750ae 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -6,6 +6,9 @@ post-v1.5.0
> >         - The default bond_mode changed from SLB to active-backup, to 
> > protect
> >           unsuspecting users from the significant risks of SLB bonds (which 
> > are
> >           documented in vswitchd/INTERNALS).
> > +    - OpenFlow:
> > +        - Added support for bitwise matching on TCP and UDP ports.
> > +          See ovs-ofctl(8) for more information.
> >
> >
> >  v1.5.0 - xx xxx xxxx
> > diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
> > index 6921803..6d24c6c 100644
> > --- a/include/openflow/nicira-ext.h
> > +++ b/include/openflow/nicira-ext.h
> > @@ -1397,9 +1397,12 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 
> > 24);
> >  *
> >  * Format: 16-bit integer in network byte order.
> >  *
> > - * Masking: Not maskable. */
> > + * Masking: Fully maskable, in Open vSwitch 1.6 and later.  Not maskable, 
> > in
> > + *   earlier versions. */
> >  #define NXM_OF_TCP_SRC    NXM_HEADER  (0x0000,  9, 2)
> > +#define NXM_OF_TCP_SRC_W  NXM_HEADER_W(0x0000,  9, 2)
> >  #define NXM_OF_TCP_DST    NXM_HEADER  (0x0000, 10, 2)
> > +#define NXM_OF_TCP_DST_W  NXM_HEADER_W(0x0000, 10, 2)
> >
> >  /* The source or destination port in the UDP header.
> >  *
> > @@ -1409,9 +1412,12 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 
> > 24);
> >  *
> >  * Format: 16-bit integer in network byte order.
> >  *
> > - * Masking: Not maskable. */
> > + * Masking: Fully maskable, in Open vSwitch 1.6 and later.  Not maskable, 
> > in
> > + *   earlier versions. */
> >  #define NXM_OF_UDP_SRC    NXM_HEADER  (0x0000, 11, 2)
> > +#define NXM_OF_UDP_SRC_W  NXM_HEADER_W(0x0000, 11, 2)
> >  #define NXM_OF_UDP_DST    NXM_HEADER  (0x0000, 12, 2)
> > +#define NXM_OF_UDP_DST_W  NXM_HEADER_W(0x0000, 12, 2)
> >
> >  /* The type or code in the ICMP header.
> >  *
> > diff --git a/lib/classifier.c b/lib/classifier.c
> > index 26751ca..18958a8 100644
> > --- a/lib/classifier.c
> > +++ b/lib/classifier.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright (c) 2009, 2010, 2011 Nicira Networks.
> > + * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
> >  *
> >  * Licensed under the Apache License, Version 2.0 (the "License");
> >  * you may not use this file except in compliance with the License.
> > @@ -252,15 +252,27 @@ cls_rule_set_dl_vlan_pcp(struct cls_rule *rule, 
> > uint8_t dl_vlan_pcp)
> >  void
> >  cls_rule_set_tp_src(struct cls_rule *rule, ovs_be16 tp_src)
> >  {
> > -    rule->wc.wildcards &= ~FWW_TP_SRC;
> > -    rule->flow.tp_src = 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)
> >  {
> > -    rule->wc.wildcards &= ~FWW_TP_DST;
> > -    rule->flow.tp_dst = 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
> > @@ -340,15 +352,13 @@ cls_rule_set_nw_frag_masked(struct cls_rule *rule,
> >  void
> >  cls_rule_set_icmp_type(struct cls_rule *rule, uint8_t icmp_type)
> >  {
> > -    rule->wc.wildcards &= ~FWW_TP_SRC;
> > -    rule->flow.tp_src = htons(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)
> >  {
> > -    rule->wc.wildcards &= ~FWW_TP_DST;
> > -    rule->flow.tp_dst = htons(icmp_code);
> > +    cls_rule_set_tp_dst(rule, htons(icmp_code));
> >  }
> >
> >  void
> > @@ -452,6 +462,23 @@ format_ipv6_netmask(struct ds *s, const char *name,
> >     }
> >  }
> >
> > +
> > +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, ',');
> > +    }
> > +}
> > +
> >  void
> >  cls_rule_format(const struct cls_rule *rule, struct ds *s)
> >  {
> > @@ -464,7 +491,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds 
> > *s)
> >
> >     int i;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     if (rule->priority != OFP_DEFAULT_PRIORITY) {
> >         ds_put_format(s, "priority=%d,", rule->priority);
> > @@ -637,19 +664,11 @@ cls_rule_format(const struct cls_rule *rule, struct 
> > ds *s)
> >         break;
> >     }
> >     if (f->nw_proto == IPPROTO_ICMP) {
> > -        if (!(w & FWW_TP_SRC)) {
> > -            ds_put_format(s, "icmp_type=%"PRIu16",", ntohs(f->tp_src));
> > -        }
> > -        if (!(w & FWW_TP_DST)) {
> > -            ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->tp_dst));
> > -        }
> > +        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) {
> > -        if (!(w & FWW_TP_SRC)) {
> > -            ds_put_format(s, "icmp_type=%"PRIu16",", ntohs(f->tp_src));
> > -        }
> > -        if (!(w & FWW_TP_DST)) {
> > -            ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->tp_dst));
> > -        }
> > +        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);
> >         if (!(w & FWW_ND_TARGET)) {
> >             ds_put_cstr(s, "nd_target=");
> >             print_ipv6_addr(s, &f->nd_target);
> > @@ -664,12 +683,8 @@ cls_rule_format(const struct cls_rule *rule, struct ds 
> > *s)
> >                     ETH_ADDR_ARGS(f->arp_tha));
> >         }
> >    } else {
> > -        if (!(w & FWW_TP_SRC)) {
> > -            ds_put_format(s, "tp_src=%"PRIu16",", ntohs(f->tp_src));
> > -        }
> > -        if (!(w & FWW_TP_DST)) {
> > -            ds_put_format(s, "tp_dst=%"PRIu16",", ntohs(f->tp_dst));
> > -        }
> > +        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) == ',') {
> > @@ -1149,7 +1164,7 @@ flow_equal_except(const struct flow *a, const struct 
> > flow *b,
> >     const flow_wildcards_t wc = wildcards->wildcards;
> >     int i;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     for (i = 0; i < FLOW_N_REGS; i++) {
> >         if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
> > @@ -1163,8 +1178,8 @@ flow_equal_except(const struct flow *a, const struct 
> > flow *b,
> >             && (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)
> > -            && (wc & FWW_TP_SRC || a->tp_src == b->tp_src)
> > -            && (wc & FWW_TP_DST || a->tp_dst == b->tp_dst)
> > +            && !((a->tp_src ^ b->tp_src) & wildcards->tp_src_mask)
> > +            && !((a->tp_dst ^ b->tp_dst) & wildcards->tp_dst_mask)
> >             && (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)
> > diff --git a/lib/classifier.h b/lib/classifier.h
> > index d55475d..f9bcabb 100644
> > --- a/lib/classifier.h
> > +++ b/lib/classifier.h
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright (c) 2009, 2010, 2011 Nicira Networks.
> > + * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
> >  *
> >  * Licensed under the Apache License, Version 2.0 (the "License");
> >  * you may not use this file except in compliance with the License.
> > @@ -110,7 +110,11 @@ void cls_rule_set_dl_vlan(struct cls_rule *, ovs_be16);
> >  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);
> > diff --git a/lib/flow.c b/lib/flow.c
> > index dc2bb1c..9f83b2c 100644
> > --- a/lib/flow.c
> > +++ b/lib/flow.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
> > + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
> >  *
> >  * Licensed under the Apache License, Version 2.0 (the "License");
> >  * you may not use this file except in compliance with the License.
> > @@ -444,7 +444,7 @@ 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 == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     for (i = 0; i < FLOW_N_REGS; i++) {
> >         flow->regs[i] &= wildcards->reg_masks[i];
> > @@ -459,12 +459,8 @@ flow_zero_wildcards(struct flow *flow, const struct 
> > flow_wildcards *wildcards)
> >     if (wc & FWW_DL_TYPE) {
> >         flow->dl_type = htons(0);
> >     }
> > -    if (wc & FWW_TP_SRC) {
> > -        flow->tp_src = htons(0);
> > -    }
> > -    if (wc & FWW_TP_DST) {
> > -        flow->tp_dst = htons(0);
> > -    }
> > +    flow->tp_src &= wildcards->tp_src_mask;
> > +    flow->tp_dst &= wildcards->tp_dst_mask;
> >     if (wc & FWW_DL_SRC) {
> >         memset(flow->dl_src, 0, sizeof flow->dl_src);
> >     }
> > @@ -598,7 +594,7 @@ flow_print(FILE *stream, const struct flow *flow)
> >  void
> >  flow_wildcards_init_catchall(struct flow_wildcards *wc)
> >  {
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     wc->wildcards = FWW_ALL;
> >     wc->tun_id_mask = htonll(0);
> > @@ -609,6 +605,8 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
> >     memset(wc->reg_masks, 0, sizeof wc->reg_masks);
> >     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->zeros, 0, sizeof wc->zeros);
> >  }
> >
> > @@ -617,7 +615,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
> >  void
> >  flow_wildcards_init_exact(struct flow_wildcards *wc)
> >  {
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     wc->wildcards = 0;
> >     wc->tun_id_mask = htonll(UINT64_MAX);
> > @@ -628,6 +626,8 @@ flow_wildcards_init_exact(struct flow_wildcards *wc)
> >     memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
> >     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->zeros, 0, sizeof wc->zeros);
> >  }
> >
> > @@ -638,12 +638,14 @@ flow_wildcards_is_exact(const struct flow_wildcards 
> > *wc)
> >  {
> >     int i;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     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)
> >         || !ipv6_mask_is_exact(&wc->ipv6_src_mask)
> >         || !ipv6_mask_is_exact(&wc->ipv6_dst_mask)
> > @@ -667,12 +669,14 @@ flow_wildcards_is_catchall(const struct 
> > flow_wildcards *wc)
> >  {
> >     int i;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     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)
> >         || !ipv6_mask_is_any(&wc->ipv6_src_mask)
> >         || !ipv6_mask_is_any(&wc->ipv6_dst_mask)
> > @@ -699,7 +703,7 @@ flow_wildcards_combine(struct flow_wildcards *dst,
> >  {
> >     int i;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     dst->wildcards = src1->wildcards | src2->wildcards;
> >     dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
> > @@ -713,6 +717,8 @@ flow_wildcards_combine(struct flow_wildcards *dst,
> >         dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
> >     }
> >     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;
> >  }
> >
> >  /* Returns a hash of the wildcards in 'wc'. */
> > @@ -734,7 +740,7 @@ flow_wildcards_equal(const struct flow_wildcards *a,
> >  {
> >     int i;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     if (a->wildcards != b->wildcards
> >         || a->tun_id_mask != b->tun_id_mask
> > @@ -742,7 +748,9 @@ flow_wildcards_equal(const struct flow_wildcards *a,
> >         || a->nw_dst_mask != b->nw_dst_mask
> >         || a->vlan_tci_mask != b->vlan_tci_mask
> >         || !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
> > -        || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)) {
> > +        || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)
> > +        || a->tp_src_mask != b->tp_src_mask
> > +        || a->tp_dst_mask != b->tp_dst_mask) {
> >         return false;
> >     }
> >
> > @@ -764,7 +772,7 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
> >     int i;
> >     struct in6_addr ipv6_masked;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     for (i = 0; i < FLOW_N_REGS; i++) {
> >         if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
> > @@ -786,7 +794,9 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
> >             || (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->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_mask);
> > +            || (a->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_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);
> >  }
> >
> >  /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
> > diff --git a/lib/flow.h b/lib/flow.h
> > index 44eb977..7b001a6 100644
> > --- a/lib/flow.h
> > +++ b/lib/flow.h
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
> > + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
> >  *
> >  * Licensed under the Apache License, Version 2.0 (the "License");
> >  * you may not use this file except in compliance with the License.
> > @@ -35,7 +35,7 @@ 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 7
> > +#define FLOW_WC_SEQ 8
> >
> >  #define FLOW_N_REGS 5
> >  BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
> > @@ -100,7 +100,7 @@ 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 == 130 && FLOW_WC_SEQ == 7);
> > +BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 130 && FLOW_WC_SEQ == 8);
> >
> >  void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
> >                   uint16_t in_port, struct flow *);
> > @@ -152,22 +152,20 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
> >                                               /* 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))
> >  /* No corresponding OFPFW_* bits. */
> >  #define FWW_ETH_MCAST   ((OVS_FORCE flow_wildcards_t) (1 << 1))
> >                                                        /* multicast bit 
> > only */
> > +#define FWW_NW_DSCP     ((OVS_FORCE flow_wildcards_t) (1 << 6))
> > +#define FWW_NW_ECN      ((OVS_FORCE flow_wildcards_t) (1 << 7))
> >  #define FWW_ARP_SHA     ((OVS_FORCE flow_wildcards_t) (1 << 8))
> >  #define FWW_ARP_THA     ((OVS_FORCE flow_wildcards_t) (1 << 9))
> >  #define FWW_ND_TARGET   ((OVS_FORCE flow_wildcards_t) (1 << 10))
> >  #define FWW_IPV6_LABEL  ((OVS_FORCE flow_wildcards_t) (1 << 11))
> >  #define FWW_NW_TTL      ((OVS_FORCE flow_wildcards_t) (1 << 12))
> > -#define FWW_NW_DSCP     ((OVS_FORCE flow_wildcards_t) (1 << 13))
> > -#define FWW_NW_ECN      ((OVS_FORCE flow_wildcards_t) (1 << 14))
> > -#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 15)) - 1))
> > +#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 13)) - 1))
> >
> >  /* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */
> > -BUILD_ASSERT_DECL(FWW_ALL == ((1 << 15) - 1) && FLOW_WC_SEQ == 7);
> > +BUILD_ASSERT_DECL(FWW_ALL == ((1 << 13) - 1) && FLOW_WC_SEQ == 8);
> >
> >  /* Information on wildcards for a flow, as a supplement to "struct flow".
> >  *
> > @@ -182,12 +180,14 @@ struct flow_wildcards {
> >     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. */
> >     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 zeros[5];           /* Padding field set to zero. */
> > +    uint8_t zeros[1];           /* Padding field set to zero. */
> >  };
> >
> >  /* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */
> > -BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 7);
> > +BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 8);
> >
> >  void flow_wildcards_init_catchall(struct flow_wildcards *);
> >  void flow_wildcards_init_exact(struct flow_wildcards *);
> > diff --git a/lib/meta-flow.c b/lib/meta-flow.c
> > index 41cbf49..8d649e4 100644
> > --- a/lib/meta-flow.c
> > +++ b/lib/meta-flow.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright (c) 2011 Nicira Networks.
> > + * Copyright (c) 2011, 2012 Nicira Networks.
> >  *
> >  * Licensed under the Apache License, Version 2.0 (the "License");
> >  * you may not use this file except in compliance with the License.
> > @@ -286,7 +286,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     {
> >         MFF_TCP_SRC, "tcp_src", "tp_src",
> >         MF_FIELD_SIZES(be16),
> > -        MFM_NONE, FWW_TP_SRC,
> > +        MFM_FULLY, 0,
> >         MFS_DECIMAL,
> >         MFP_TCP,
> >         true,
> > @@ -294,7 +294,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     }, {
> >         MFF_TCP_DST, "tcp_dst", "tp_dst",
> >         MF_FIELD_SIZES(be16),
> > -        MFM_NONE, FWW_TP_DST,
> > +        MFM_FULLY, 0,
> >         MFS_DECIMAL,
> >         MFP_TCP,
> >         true,
> > @@ -304,7 +304,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     {
> >         MFF_UDP_SRC, "udp_src", NULL,
> >         MF_FIELD_SIZES(be16),
> > -        MFM_NONE, FWW_TP_SRC,
> > +        MFM_FULLY, 0,
> >         MFS_DECIMAL,
> >         MFP_UDP,
> >         true,
> > @@ -312,7 +312,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     }, {
> >         MFF_UDP_DST, "udp_dst", NULL,
> >         MF_FIELD_SIZES(be16),
> > -        MFM_NONE, FWW_TP_DST,
> > +        MFM_FULLY, 0,
> >         MFS_DECIMAL,
> >         MFP_UDP,
> >         true,
> > @@ -322,7 +322,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     {
> >         MFF_ICMPV4_TYPE, "icmp_type", NULL,
> >         MF_FIELD_SIZES(u8),
> > -        MFM_NONE, FWW_TP_SRC,
> > +        MFM_NONE, 0,
> >         MFS_DECIMAL,
> >         MFP_ICMPV4,
> >         false,
> > @@ -330,7 +330,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     }, {
> >         MFF_ICMPV4_CODE, "icmp_code", NULL,
> >         MF_FIELD_SIZES(u8),
> > -        MFM_NONE, FWW_TP_DST,
> > +        MFM_NONE, 0,
> >         MFS_DECIMAL,
> >         MFP_ICMPV4,
> >         false,
> > @@ -340,7 +340,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     {
> >         MFF_ICMPV6_TYPE, "icmpv6_type", NULL,
> >         MF_FIELD_SIZES(u8),
> > -        MFM_NONE, FWW_TP_SRC,
> > +        MFM_NONE, 0,
> >         MFS_DECIMAL,
> >         MFP_ICMPV6,
> >         false,
> > @@ -348,7 +348,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
> >     }, {
> >         MFF_ICMPV6_CODE, "icmpv6_code", NULL,
> >         MF_FIELD_SIZES(u8),
> > -        MFM_NONE, FWW_TP_DST,
> > +        MFM_NONE, 0,
> >         MFS_DECIMAL,
> >         MFP_ICMPV6,
> >         false,
> > @@ -501,14 +501,6 @@ mf_is_all_wild(const struct mf_field *mf, const struct 
> > flow_wildcards *wc)
> >     case MFF_ARP_OP:
> >     case MFF_ARP_SHA:
> >     case MFF_ARP_THA:
> > -    case MFF_TCP_SRC:
> > -    case MFF_TCP_DST:
> > -    case MFF_UDP_SRC:
> > -    case MFF_UDP_DST:
> > -    case MFF_ICMPV4_TYPE:
> > -    case MFF_ICMPV4_CODE:
> > -    case MFF_ICMPV6_TYPE:
> > -    case MFF_ICMPV6_CODE:
> >     case MFF_ND_TARGET:
> >     case MFF_ND_SLL:
> >     case MFF_ND_TLL:
> > @@ -567,6 +559,17 @@ mf_is_all_wild(const struct mf_field *mf, const struct 
> > flow_wildcards *wc)
> >     case MFF_ARP_TPA:
> >         return !wc->nw_dst_mask;
> >
> > +    case MFF_TCP_SRC:
> > +    case MFF_UDP_SRC:
> > +    case MFF_ICMPV4_TYPE:
> > +    case MFF_ICMPV6_TYPE:
> > +        return !wc->tp_src_mask;
> > +    case MFF_TCP_DST:
> > +    case MFF_UDP_DST:
> > +    case MFF_ICMPV4_CODE:
> > +    case MFF_ICMPV6_CODE:
> > +        return !wc->tp_dst_mask;
> > +
> >     case MFF_N_IDS:
> >     default:
> >         NOT_REACHED();
> > @@ -595,14 +598,6 @@ mf_get_mask(const struct mf_field *mf, const struct 
> > flow_wildcards *wc,
> >     case MFF_ARP_OP:
> >     case MFF_ARP_SHA:
> >     case MFF_ARP_THA:
> > -    case MFF_TCP_SRC:
> > -    case MFF_TCP_DST:
> > -    case MFF_UDP_SRC:
> > -    case MFF_UDP_DST:
> > -    case MFF_ICMPV4_TYPE:
> > -    case MFF_ICMPV4_CODE:
> > -    case MFF_ICMPV6_TYPE:
> > -    case MFF_ICMPV6_CODE:
> >     case MFF_ND_TARGET:
> >     case MFF_ND_SLL:
> >     case MFF_ND_TLL:
> > @@ -675,6 +670,24 @@ mf_get_mask(const struct mf_field *mf, const struct 
> > flow_wildcards *wc,
> >         mask->be32 = wc->nw_dst_mask;
> >         break;
> >
> > +    case MFF_TCP_SRC:
> > +    case MFF_UDP_SRC:
> > +        mask->be16 = wc->tp_src_mask;
> > +        break;
> > +    case MFF_TCP_DST:
> > +    case MFF_UDP_DST:
> > +        mask->be16 = wc->tp_dst_mask;
> > +        break;
> > +
> > +    case MFF_ICMPV4_TYPE:
> > +    case MFF_ICMPV6_TYPE:
> > +        mask->u8 = ntohs(wc->tp_src_mask);
> > +        break;
> > +    case MFF_ICMPV4_CODE:
> > +    case MFF_ICMPV6_CODE:
> > +        mask->u8 = ntohs(wc->tp_dst_mask);
> > +        break;
> > +
> >     case MFF_N_IDS:
> >     default:
> >         NOT_REACHED();
> > @@ -1477,7 +1490,7 @@ mf_set_wild(const struct mf_field *mf, struct 
> > cls_rule *rule)
> >     case MFF_UDP_SRC:
> >     case MFF_ICMPV4_TYPE:
> >     case MFF_ICMPV6_TYPE:
> > -        rule->wc.wildcards |= FWW_TP_SRC;
> > +        rule->wc.tp_src_mask = htons(0);
> >         rule->flow.tp_src = htons(0);
> >         break;
> >
> > @@ -1485,7 +1498,7 @@ mf_set_wild(const struct mf_field *mf, struct 
> > cls_rule *rule)
> >     case MFF_UDP_DST:
> >     case MFF_ICMPV4_CODE:
> >     case MFF_ICMPV6_CODE:
> > -        rule->wc.wildcards |= FWW_TP_DST;
> > +        rule->wc.tp_dst_mask = htons(0);
> >         rule->flow.tp_dst = htons(0);
> >         break;
> >
> > @@ -1538,10 +1551,6 @@ mf_set(const struct mf_field *mf,
> >     case MFF_ARP_OP:
> >     case MFF_ARP_SHA:
> >     case MFF_ARP_THA:
> > -    case MFF_TCP_SRC:
> > -    case MFF_TCP_DST:
> > -    case MFF_UDP_SRC:
> > -    case MFF_UDP_DST:
> >     case MFF_ICMPV4_TYPE:
> >     case MFF_ICMPV4_CODE:
> >     case MFF_ICMPV6_TYPE:
> > @@ -1615,6 +1624,16 @@ mf_set(const struct mf_field *mf,
> >         cls_rule_set_nw_dst_masked(rule, value->be32, mask->be32);
> >         break;
> >
> > +    case MFF_TCP_SRC:
> > +    case MFF_UDP_SRC:
> > +        cls_rule_set_tp_src_masked(rule, value->be16, mask->be16);
> > +        break;
> > +
> > +    case MFF_TCP_DST:
> > +    case MFF_UDP_DST:
> > +        cls_rule_set_tp_dst_masked(rule, value->be16, mask->be16);
> > +        break;
> > +
> >     case MFF_N_IDS:
> >     default:
> >         NOT_REACHED();
> > diff --git a/lib/nx-match.c b/lib/nx-match.c
> > index f42bb9a..a43ed6a 100644
> > --- a/lib/nx-match.c
> > +++ b/lib/nx-match.c
> > @@ -433,24 +433,16 @@ nxm_put_ip(struct ofpbuf *b, const struct cls_rule 
> > *cr,
> >     }
> >
> >     if (flow->nw_proto == IPPROTO_TCP) {
> > -        if (!(wc & FWW_TP_SRC)) {
> > -            nxm_put_16(b, NXM_OF_TCP_SRC, flow->tp_src);
> > -        }
> > -        if (!(wc & FWW_TP_DST)) {
> > -            nxm_put_16(b, NXM_OF_TCP_DST, flow->tp_dst);
> > -        }
> > +        nxm_put_16m(b, NXM_OF_TCP_SRC, flow->tp_src, cr->wc.tp_src_mask);
> > +        nxm_put_16m(b, NXM_OF_TCP_DST, flow->tp_dst, cr->wc.tp_dst_mask);
> >     } else if (flow->nw_proto == IPPROTO_UDP) {
> > -        if (!(wc & FWW_TP_SRC)) {
> > -            nxm_put_16(b, NXM_OF_UDP_SRC, flow->tp_src);
> > -        }
> > -        if (!(wc & FWW_TP_DST)) {
> > -            nxm_put_16(b, NXM_OF_UDP_DST, flow->tp_dst);
> > -        }
> > +        nxm_put_16m(b, NXM_OF_UDP_SRC, flow->tp_src, cr->wc.tp_src_mask);
> > +        nxm_put_16m(b, NXM_OF_UDP_DST, flow->tp_dst, cr->wc.tp_dst_mask);
> >     } else if (flow->nw_proto == icmp_proto) {
> > -        if (!(wc & FWW_TP_SRC)) {
> > +        if (cr->wc.tp_src_mask) {
> >             nxm_put_8(b, icmp_type, ntohs(flow->tp_src));
> >         }
> > -        if (!(wc & FWW_TP_DST)) {
> > +        if (cr->wc.tp_dst_mask) {
> >             nxm_put_8(b, icmp_code, ntohs(flow->tp_dst));
> >         }
> >     }
> > @@ -478,7 +470,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule 
> > *cr,
> >     int match_len;
> >     int i;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     /* Metadata. */
> >     if (!(wc & FWW_IN_PORT)) {
> > diff --git a/lib/ofp-util.c b/lib/ofp-util.c
> > index 6fe1611..876abaf 100644
> > --- a/lib/ofp-util.c
> > +++ b/lib/ofp-util.c
> > @@ -77,9 +77,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
> >     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)
> > +    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. */
> > @@ -101,7 +99,7 @@ static const flow_wildcards_t WC_INVARIANTS = 0
> >  void
> >  ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
> >  {
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     /* Initialize most of rule->wc. */
> >     flow_wildcards_init_catchall(wc);
> > @@ -120,6 +118,13 @@ ofputil_wildcard_from_openflow(uint32_t ofpfw, struct 
> > flow_wildcards *wc)
> >     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 & OFPFW_TP_SRC)) {
> > +        wc->tp_src_mask = htons(UINT16_MAX);
> > +    }
> > +    if (!(ofpfw & OFPFW_TP_DST)) {
> > +        wc->tp_dst_mask = htons(UINT16_MAX);
> > +    }
> > +
> >     if (ofpfw & OFPFW_DL_DST) {
> >         /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, 
> > but
> >          * Open vSwitch breaks the Ethernet destination into bits as 
> > FWW_DL_DST
> > @@ -199,6 +204,12 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, 
> > struct ofp_match *match)
> >     if (wc->wildcards & FWW_NW_DSCP) {
> >         ofpfw |= OFPFW_NW_TOS;
> >     }
> > +    if (!wc->tp_src_mask) {
> > +        ofpfw |= OFPFW_TP_SRC;
> > +    }
> > +    if (!wc->tp_dst_mask) {
> > +        ofpfw |= OFPFW_TP_DST;
> > +    }
> >
> >     /* Translate VLANs. */
> >     match->dl_vlan = htons(0);
> > @@ -904,7 +915,7 @@ ofputil_min_flow_format(const struct cls_rule *rule)
> >  {
> >     const struct flow_wildcards *wc = &rule->wc;
> >
> > -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7);
> > +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8);
> >
> >     /* Only NXM supports separately wildcards the Ethernet multicast bit. */
> >     if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
> > @@ -952,6 +963,12 @@ ofputil_min_flow_format(const struct cls_rule *rule)
> >         return NXFF_NXM;
> >     }
> >
> > +    /* 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))) {
> > +        return NXFF_NXM;
> > +    }
> > +
> >     /* Other formats can express this rule. */
> >     return NXFF_OPENFLOW10;
> >  }
> > @@ -2733,7 +2750,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum 
> > nx_flow_format flow_format)
> >         wc.nw_src_mask = wc.nw_dst_mask = htonl(0);
> >     }
> >     if (!(may_match & MAY_TP_ADDR)) {
> > -        wc.wildcards |= FWW_TP_SRC | FWW_TP_DST;
> > +        wc.tp_src_mask = wc.tp_dst_mask = htons(0);
> >     }
> >     if (!(may_match & MAY_NW_PROTO)) {
> >         wc.wildcards |= FWW_NW_PROTO;
> > diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
> > index 0b58b3d..4869dd7 100644
> > --- a/tests/ovs-ofctl.at
> > +++ b/tests/ovs-ofctl.at
> > @@ -10,7 +10,7 @@ tcp,nw_src=192.168.0.3,tp_dst=80 
> > actions=set_queue:37,output:1
> >  udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
> >  cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
> >  actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
> > -tun_id=0x1234,cookie=0x5678,actions=flood
> > +tcp,tp_src=0x1230/0xfff0,tun_id=0x1234,cookie=0x5678,actions=flood
> >  actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789
> >  actions=multipath(eth_src, 50, hrw, 12, 0, 
> > NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, 
> > NXM_NX_REG0[0..12])
> >  table=1,actions=drop
> > @@ -40,7 +40,7 @@ OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 
> > actions=pop_queue,output:1
> >  OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 
> > actions=CONTROLLER:65535
> >  OFPT_FLOW_MOD: ADD 
> > actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
> >  NXT_SET_FLOW_FORMAT: format=nxm
> > -NXT_FLOW_MOD: ADD tun_id=0x1234 cookie:0x5678 actions=FLOOD
> > +NXT_FLOW_MOD: ADD tcp,tun_id=0x1234,tp_src=0x1230/0xfff0 cookie:0x5678 
> > actions=FLOOD
> >  NXT_FLOW_MOD: ADD 
> > actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789
> >  NXT_FLOW_MOD: ADD 
> > actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
> >  NXT_FLOW_MOD_TABLE_ID: enable
> > @@ -243,18 +243,22 @@ NXM_OF_ETH_TYPE(0806) 
> > NXM_OF_IP_DST_W(C0D80000/FFFF0000)
> >
> >  # TCP source port
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC(4231)
> > +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/F0F0)
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_SRC(4231)
> >
> >  # TCP destination port
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST(4231)
> > +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/FFF0)
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231)
> >
> >  # UDP source port
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732)
> > +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/01FF)
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_UDP_SRC(7823)
> >
> >  # UDP destination port
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST(1782)
> > +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/F00F)
> >  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_OF_UDP_DST(1293)
> >
> >  # ICMP type
> > @@ -436,18 +440,22 @@ nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
> >
> >  # TCP source port
> >  NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(4231)
> > +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC_W(5050/f0f0)
> >  nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
> >
> >  # TCP destination port
> >  NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(4231)
> > +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST_W(fde0/fff0)
> >  nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
> >
> >  # UDP source port
> >  NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732)
> > +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC_W(0132/01ff)
> >  nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
> >
> >  # UDP destination port
> >  NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(1782)
> > +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST_W(5005/f00f)
> >  nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
> >
> >  # ICMP type
> > diff --git a/tests/test-classifier.c b/tests/test-classifier.c
> > index 8dfe016..1a07790 100644
> > --- a/tests/test-classifier.c
> > +++ b/tests/test-classifier.c
> > @@ -1,5 +1,5 @@
> >  /*
> > - * Copyright (c) 2009, 2010, 2011 Nicira Networks.
> > + * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
> >  *
> >  * Licensed under the Apache License, Version 2.0 (the "License");
> >  * you may not use this file except in compliance with the License.
> > @@ -50,8 +50,8 @@
> >     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(FWW_TP_SRC,                 tp_src,      TP_SRC)      \
> > -    CLS_FIELD(FWW_TP_DST,                 tp_dst,      TP_DST)      \
> > +    CLS_FIELD(0,                          tp_src,      TP_SRC)      \
> > +    CLS_FIELD(0,                          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)    \
> > @@ -198,6 +198,10 @@ match(const struct cls_rule *wild, const struct flow 
> > *fixed)
> >             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 if (f_idx == CLS_F_IDX_TP_SRC) {
> > +            eq = !((fixed->tp_src ^ wild->flow.tp_src) & 
> > wild->wc.tp_src_mask);
> > +        } else if (f_idx == CLS_F_IDX_TP_DST) {
> > +            eq = !((fixed->tp_dst ^ wild->flow.tp_dst) & 
> > wild->wc.tp_dst_mask);
> >         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
> >             eq = !((fixed->vlan_tci ^ wild->flow.vlan_tci)
> >                    & wild->wc.vlan_tci_mask);
> > @@ -463,6 +467,10 @@ make_rule(int wc_fields, unsigned int priority, int 
> > value_pat)
> >             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 if (f_idx == CLS_F_IDX_TP_SRC) {
> > +            rule->cls_rule.wc.tp_src_mask = htons(UINT16_MAX);
> > +        } else if (f_idx == CLS_F_IDX_TP_DST) {
> > +            rule->cls_rule.wc.tp_dst_mask = htons(UINT16_MAX);
> >         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
> >             rule->cls_rule.wc.vlan_tci_mask = htons(UINT16_MAX);
> >         } else if (f_idx == CLS_F_IDX_TUN_ID) {
> > diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
> > index e9e2e6f..e592953 100644
> > --- a/utilities/ovs-ofctl.8.in
> > +++ b/utilities/ovs-ofctl.8.in
> > @@ -443,13 +443,75 @@ above).
> >  .IQ \fBtp_dst=\fIport\fR
> >  When \fBdl_type\fR and \fBnw_proto\fR specify TCP or UDP, \fBtp_src\fR
> >  and \fBtp_dst\fR match the UDP or TCP source or destination port
> > -\fIport\fR, respectively. which is specified as a decimal number
> > +\fIport\fR, respectively, which is specified as a decimal number
> >  between 0 and 65535, inclusive (e.g. 80 to match packets originating
> >  from a HTTP server).
> >  .IP
> >  When \fBdl_type\fR and \fBnw_proto\fR take other values, the values of
> >  these settings are ignored (see \fBFlow Syntax\fR above).
> >  .
> > +.IP \fBtp_src=\fIport\fB/\fImask\fR
> > +.IQ \fBtp_dst=\fIport\fB/\fImask\fR
> > +Bitwise match on TCP (or UDP) source or destination port,
> > +respectively.  The \fIport\fR and \fImask\fR are 16-bit numbers
> > +written in decimal or in hexadecimal prefixed by \fB0x\fR.  Each 1-bit
> > +in \fImask\fR requires that the corresponding bit in \fIport\fR must
> > +match.  Each 0-bit in \fImask\fR causes the corresponding bit to be
> > +ignored.
> > +.IP
> > +Bitwise matches on transport ports are rarely useful in isolation, but
> > +a group of them can be used to reduce the number of flows required to
> > +match on a range of transport ports.  For example, suppose that the
> > +goal is to match TCP source ports 1000 to 1999, inclusive.  One way is
> > +to insert 1001 flows, each of which matches on a single source port.
> > +Another way is to look at the binary representations of 1000 and 1999,
> > +as follows:
> > +.br
> > +.B "01111101000"
> > +.br
> > +.B "11111001111"
> > +.br
> > +and then to transform those into a series of bitwise matches that
> > +accomplish the same results:
> > +.br
> > +.B "01111101xxx"
> > +.br
> > +.B "0111111xxxx"
> > +.br
> > +.B "10xxxxxxxxx"
> > +.br
> > +.B "110xxxxxxxx"
> > +.br
> > +.B "1110xxxxxxx"
> > +.br
> > +.B "11110xxxxxx"
> > +.br
> > +.B "1111100xxxx"
> > +.br
> > +which become the following when written in the syntax required by
> > +\fBovs\-ofctl\fR:
> > +.br
> > +.B "tcp,tp_src=0x03e8/0xfff8"
> > +.br
> > +.B "tcp,tp_src=0x03f0/0xfff0"
> > +.br
> > +.B "tcp,tp_src=0x0400/0xfe00"
> > +.br
> > +.B "tcp,tp_src=0x0600/0xff00"
> > +.br
> > +.B "tcp,tp_src=0x0700/0xff80"
> > +.br
> > +.B "tcp,tp_src=0x0780/0xffc0"
> > +.br
> > +.B "tcp,tp_src=0x07c0/0xfff0"
> > +.IP
> > +Only Open vSwitch 1.6 and later supports bitwise matching on transport
> > +ports.
> > +.IP
> > +Like the exact-match forms of \fBtp_src\fR and \fBtp_dst\fR described
> > +above, the bitwise match forms apply only when When \fBdl_type\fR and
> > +\fBnw_proto\fR specify TCP or UDP.
> > +.
> >  .IP \fBicmp_type=\fItype\fR
> >  .IQ \fBicmp_code=\fIcode\fR
> >  When \fBdl_type\fR and \fBnw_proto\fR specify ICMP or ICMPv6, \fItype\fR
> > --
> > 1.7.2.5
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > http://openvswitch.org/mailman/listinfo/dev
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to