diff mbox

[ovs-dev,01/10] userspace: Add OXM field MFF_PACKET_TYPE

Message ID AM2PR07MB104201BAF41D4631B21342518A190@AM2PR07MB1042.eurprd07.prod.outlook.com
State Deferred
Headers show

Commit Message

Zoltan Balogh April 18, 2017, 11:19 a.m. UTC
From: Jan Scheurich <jan.scheurich@ericsson.com>

Allow packet type namespace OFPHTN_ETHERTYPE as alternative pre-requisite
for matching L3 protocols (MPLS, IP, IPv6, ARP etc).

Scan packet_type in odp_flow string.

Added dummy entry for MFF_PACKET_TYPE to lib/meta-flow.xml

Added packet_type to the matching properties in tests/ofproto.at. Should be
removed later, when packet_type_aware bridge attribute will be introduced.

Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
---
 include/openvswitch/meta-flow.h | 16 +++++++++++++-
 lib/flow.h                      | 26 ++++++++++++++++------
 lib/meta-flow.c                 | 30 ++++++++++++++++++++------
 lib/meta-flow.xml               |  4 ++++
 lib/nx-match.c                  | 16 ++++++++++----
 lib/odp-util.c                  | 48 ++++++++++++++++++++++++++++++++++++++---
 lib/ofp-parse.c                 |  6 ++++++
 lib/ofp-util.c                  | 42 +++++++++++++++++++++++++-----------
 tests/dpif-netdev.at            | 14 ++++++------
 tests/odp.at                    |  1 +
 tests/ofproto-dpif.at           | 20 ++++++++---------
 tests/ofproto.at                |  1 +
 tests/pmd.at                    |  2 +-
 tests/tunnel-push-pop-ipv6.at   |  2 +-
 tests/tunnel-push-pop.at        |  2 +-
 15 files changed, 177 insertions(+), 53 deletions(-)

Comments

Gregory Rose April 18, 2017, 2:09 p.m. UTC | #1
On Tue, 2017-04-18 at 11:19 +0000, Zoltán Balogh wrote:
> From: Jan Scheurich <jan.scheurich@ericsson.com>
> 
> Allow packet type namespace OFPHTN_ETHERTYPE as alternative pre-requisite
> for matching L3 protocols (MPLS, IP, IPv6, ARP etc).
> 
> Scan packet_type in odp_flow string.
> 
> Added dummy entry for MFF_PACKET_TYPE to lib/meta-flow.xml
> 
> Added packet_type to the matching properties in tests/ofproto.at. Should be
> removed later, when packet_type_aware bridge attribute will be introduced.
> 
> Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
> ---
>  include/openvswitch/meta-flow.h | 16 +++++++++++++-
>  lib/flow.h                      | 26 ++++++++++++++++------
>  lib/meta-flow.c                 | 30 ++++++++++++++++++++------
>  lib/meta-flow.xml               |  4 ++++
>  lib/nx-match.c                  | 16 ++++++++++----
>  lib/odp-util.c                  | 48 ++++++++++++++++++++++++++++++++++++++---
>  lib/ofp-parse.c                 |  6 ++++++
>  lib/ofp-util.c                  | 42 +++++++++++++++++++++++++-----------
>  tests/dpif-netdev.at            | 14 ++++++------
>  tests/odp.at                    |  1 +
>  tests/ofproto-dpif.at           | 20 ++++++++---------
>  tests/ofproto.at                |  1 +
>  tests/pmd.at                    |  2 +-
>  tests/tunnel-push-pop-ipv6.at   |  2 +-
>  tests/tunnel-push-pop.at        |  2 +-
>  15 files changed, 177 insertions(+), 53 deletions(-)
> 
> diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
> index c284ec6..172cc3b 100644
> --- a/include/openvswitch/meta-flow.h
> +++ b/include/openvswitch/meta-flow.h
> @@ -247,6 +247,20 @@ enum OVS_PACKED_ENUM mf_field_id {
>       */
>      MFF_RECIRC_ID,
>  
> +    /* "packet_type".
> +     *
> +     * Define the packet type in OpenFlow 1.3+.
> +     *
> +     * Type: be32.
> +     * Maskable: no.
> +     * Formatting: hexadecimal.
> +     * Prerequisites: none.
> +     * Access: read-only.
> +     * NXM: none.
> +     * OXM: OXM_OF_PACKET_TYPE(44) since OF1.3 and v2.7.
> +     */
> +    MFF_PACKET_TYPE,
> +
>      /* "conj_id".
>       *
>       * ID for "conjunction" actions.  Please refer to ovs-ofctl(8)
> @@ -589,7 +603,7 @@ enum OVS_PACKED_ENUM mf_field_id {
>       */
>      MFF_METADATA,
>  
> -    /* "in_port".
> +     /* "in_port".

Why the formatting change in the middle of the patch?  And even in that
case it looks off a bit.

- Greg

>       *
>       * 16-bit (OpenFlow 1.0) view of the physical or virtual port on which the
>       * packet was received.
> diff --git a/lib/flow.h b/lib/flow.h
> index a8772c5..fcc7d13 100644
> --- a/lib/flow.h
> +++ b/lib/flow.h
> @@ -959,9 +959,23 @@ static inline bool is_ethernet(const struct flow *flow,
>      return flow->packet_type == htonl(PT_ETH);
>  }
>  
> +static inline ovs_be16 get_dl_type(const struct flow *flow)
> +{
> +    if (flow->packet_type == htonl(PT_ETH)) {
> +        return flow->dl_type;
> +    } else if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
> +        return pt_ns_type_be(flow->packet_type);
> +    } else {
> +        return FLOW_DL_TYPE_NONE;
> +    }
> +}
> +
>  static inline bool is_vlan(const struct flow *flow,
>                             struct flow_wildcards *wc)
>  {
> +    if (!is_ethernet(flow, wc)) {
> +        return false;
> +    }
>      if (wc) {
>          WC_MASK_FIELD_MASK(wc, vlans[0].tci, htons(VLAN_CFI));
>      }
> @@ -970,7 +984,7 @@ static inline bool is_vlan(const struct flow *flow,
>  
>  static inline bool is_ip_any(const struct flow *flow)
>  {
> -    return dl_type_is_ip_any(flow->dl_type);
> +    return dl_type_is_ip_any(get_dl_type(flow));
>  }
>  
>  static inline bool is_ip_proto(const struct flow *flow, uint8_t ip_proto,
> @@ -1006,7 +1020,7 @@ static inline bool is_sctp(const struct flow *flow,
>  static inline bool is_icmpv4(const struct flow *flow,
>                               struct flow_wildcards *wc)
>  {
> -    if (flow->dl_type == htons(ETH_TYPE_IP)) {
> +    if (get_dl_type(flow) == htons(ETH_TYPE_IP)) {
>          if (wc) {
>              memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
>          }
> @@ -1018,7 +1032,7 @@ static inline bool is_icmpv4(const struct flow *flow,
>  static inline bool is_icmpv6(const struct flow *flow,
>                               struct flow_wildcards *wc)
>  {
> -    if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
> +    if (get_dl_type(flow) == htons(ETH_TYPE_IPV6)) {
>          if (wc) {
>              memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
>          }
> @@ -1049,7 +1063,7 @@ static inline bool is_nd(const struct flow *flow,
>  
>  static inline bool is_igmp(const struct flow *flow, struct flow_wildcards *wc)
>  {
> -    if (flow->dl_type == htons(ETH_TYPE_IP)) {
> +    if (get_dl_type(flow) == htons(ETH_TYPE_IP)) {
>          if (wc) {
>              memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
>          }
> @@ -1093,8 +1107,8 @@ static inline bool is_mld_report(const struct flow *flow,
>  
>  static inline bool is_stp(const struct flow *flow)
>  {
> -    return (eth_addr_equals(flow->dl_dst, eth_addr_stp)
> -            && flow->dl_type == htons(FLOW_DL_TYPE_NONE));
> +    return (flow->dl_type == htons(FLOW_DL_TYPE_NONE)
> +            && eth_addr_equals(flow->dl_dst, eth_addr_stp));
>  }
>  
>  #endif /* flow.h */
> diff --git a/lib/meta-flow.c b/lib/meta-flow.c
> index a963cce..eac7fbc 100644
> --- a/lib/meta-flow.c
> +++ b/lib/meta-flow.c
> @@ -205,6 +205,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
>          return !wc->masks.dp_hash;
>      case MFF_RECIRC_ID:
>          return !wc->masks.recirc_id;
> +    case MFF_PACKET_TYPE:
> +        return !wc->masks.packet_type;
>      case MFF_CONJ_ID:
>          return !wc->masks.conj_id;
>      case MFF_TUN_SRC:
> @@ -401,22 +403,24 @@ mf_are_prereqs_ok__(const struct mf_field *mf, const struct flow *flow,
>                      const struct flow_wildcards *mask,
>                      struct flow_wildcards *wc)
>  {
> +    ovs_be16 dl_type = get_dl_type(flow);
> +
>      switch (mf->prereqs) {
>      case MFP_NONE:
>          return true;
>      case MFP_ETHERNET:
>          return is_ethernet(flow, wc);
>      case MFP_ARP:
> -        return (flow->dl_type == htons(ETH_TYPE_ARP) ||
> -                flow->dl_type == htons(ETH_TYPE_RARP));
> +        return (dl_type == htons(ETH_TYPE_ARP) ||
> +                dl_type == htons(ETH_TYPE_RARP));
>      case MFP_IPV4:
> -        return flow->dl_type == htons(ETH_TYPE_IP);
> +        return dl_type == htons(ETH_TYPE_IP);
>      case MFP_IPV6:
> -        return flow->dl_type == htons(ETH_TYPE_IPV6);
> +        return dl_type == htons(ETH_TYPE_IPV6);
>      case MFP_VLAN_VID:
>          return is_vlan(flow, wc);
>      case MFP_MPLS:
> -        return eth_type_mpls(flow->dl_type);
> +        return eth_type_mpls(dl_type);
>      case MFP_IP_ANY:
>          return is_ip_any(flow);
>      case MFP_CT_VALID:
> @@ -476,6 +480,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
>      switch (mf->id) {
>      case MFF_DP_HASH:
>      case MFF_RECIRC_ID:
> +    case MFF_PACKET_TYPE:
>      case MFF_CONJ_ID:
>      case MFF_TUN_ID:
>      case MFF_TUN_SRC:
> @@ -600,6 +605,9 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
>      case MFF_RECIRC_ID:
>          value->be32 = htonl(flow->recirc_id);
>          break;
> +    case MFF_PACKET_TYPE:
> +        value->be32 = flow->packet_type;
> +        break;
>      case MFF_CONJ_ID:
>          value->be32 = htonl(flow->conj_id);
>          break;
> @@ -883,6 +891,9 @@ mf_set_value(const struct mf_field *mf,
>      case MFF_RECIRC_ID:
>          match_set_recirc_id(match, ntohl(value->be32));
>          break;
> +    case MFF_PACKET_TYPE:
> +        match_set_packet_type(match, value->be32);
> +        break;
>      case MFF_CONJ_ID:
>          match_set_conj_id(match, ntohl(value->be32));
>          break;
> @@ -1248,6 +1259,9 @@ mf_set_flow_value(const struct mf_field *mf,
>      case MFF_RECIRC_ID:
>          flow->recirc_id = ntohl(value->be32);
>          break;
> +    case MFF_PACKET_TYPE:
> +        flow->packet_type = value->be32;
> +        break;
>      case MFF_CONJ_ID:
>          flow->conj_id = ntohl(value->be32);
>          break;
> @@ -1292,7 +1306,6 @@ mf_set_flow_value(const struct mf_field *mf,
>      case MFF_IN_PORT:
>          flow->in_port.ofp_port = u16_to_ofp(ntohs(value->be16));
>          break;
> -
>      case MFF_IN_PORT_OXM:
>          ofputil_port_from_ofp11(value->be32, &flow->in_port.ofp_port);
>          break;
> @@ -1598,6 +1611,10 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
>          match->flow.recirc_id = 0;
>          match->wc.masks.recirc_id = 0;
>          break;
> +    case MFF_PACKET_TYPE:
> +        match->flow.packet_type = 0;
> +        match->wc.masks.packet_type = 0;
> +        break;
>      case MFF_CONJ_ID:
>          match->flow.conj_id = 0;
>          match->wc.masks.conj_id = 0;
> @@ -1931,6 +1948,7 @@ mf_set(const struct mf_field *mf,
>      case MFF_CT_TP_SRC:
>      case MFF_CT_TP_DST:
>      case MFF_RECIRC_ID:
> +    case MFF_PACKET_TYPE:
>      case MFF_CONJ_ID:
>      case MFF_IN_PORT:
>      case MFF_IN_PORT_OXM:
> diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
> index 5efd431..3183968 100644
> --- a/lib/meta-flow.xml
> +++ b/lib/meta-flow.xml
> @@ -2273,6 +2273,10 @@ actions=clone(load:0->NXM_OF_IN_PORT[],output:123)
>  
>      <field id="MFF_DP_HASH" title="Datapath Hash" internal="yes"/>
>      <field id="MFF_RECIRC_ID" title="Datapath Recirculation ID" internal="yes"/>
> +
> +    <field id="MFF_PACKET_TYPE" title="Packet Type">
> +    </field>
> +
>    </group>
>  
>    <group title="Connection Tracking">
> diff --git a/lib/nx-match.c b/lib/nx-match.c
> index 68e58d3..af19fb2 100644
> --- a/lib/nx-match.c
> +++ b/lib/nx-match.c
> @@ -873,8 +873,9 @@ static void
>  nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
>  {
>      const struct flow *flow = &match->flow;
> +    ovs_be16 dl_type = get_dl_type(flow);
>  
> -    if (flow->dl_type == htons(ETH_TYPE_IP)) {
> +    if (dl_type == htons(ETH_TYPE_IP)) {
>          nxm_put_32m(b, MFF_IPV4_SRC, oxm,
>                      flow->nw_src, match->wc.masks.nw_src);
>          nxm_put_32m(b, MFF_IPV4_DST, oxm,
> @@ -983,11 +984,18 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
>  {
>      const struct flow *flow = &match->flow;
>      const size_t start_len = b->size;
> +    ovs_be16 dl_type = get_dl_type(flow);
>      int match_len;
>      int i;
>  
>      BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
>  
> +    /* OpenFlow Packet Type. Must be first. */
> +    if (match->wc.masks.packet_type) {
> +        nxm_put_32m(b, MFF_PACKET_TYPE, oxm, flow->packet_type,
> +                    match->wc.masks.packet_type);
> +    }
> +
>      /* Metadata. */
>      if (match->wc.masks.dp_hash) {
>          nxm_put_32m(b, MFF_DP_HASH, oxm,
> @@ -1049,7 +1057,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
>      }
>  
>      /* MPLS. */
> -    if (eth_type_mpls(flow->dl_type)) {
> +    if (eth_type_mpls(dl_type)) {
>          if (match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
>              nxm_put_8(b, MFF_MPLS_TC, oxm,
>                        mpls_lse_to_tc(flow->mpls_lse[0]));
> @@ -1069,8 +1077,8 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
>      /* L3. */
>      if (is_ip_any(flow)) {
>          nxm_put_ip(b, match, oxm);
> -    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
> -               flow->dl_type == htons(ETH_TYPE_RARP)) {
> +    } else if (dl_type == htons(ETH_TYPE_ARP) ||
> +               dl_type == htons(ETH_TYPE_RARP)) {
>          /* ARP. */
>          if (match->wc.masks.nw_proto) {
>              nxm_put_16(b, MFF_ARP_OP, oxm,
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index f98a9e5..5445314 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -4388,6 +4388,50 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
>          return s - start;
>      }
>  
> +    /* Packet_type open-coded. */
> +    if (strncmp(s, "packet_type(", strlen("packet_type(")) == 0) {
> +        const char *start = s;
> +        ovs_be32 skey = 0;
> +        ovs_be32 smask = 0;
> +        int len;
> +        uint16_t  ns = 0;
> +        uint16_t  ns_type = 0;
> +
> +        s += strlen("packet_type(");
> +        if (ovs_scan(s, "ns=%"SCNu16",id=%n", &ns, &len)){
> +            if (len == 0) {
> +                return -EINVAL;
> +            }
> +            s += len;
> +            if (strncmp(s, "*", 1) == 0) {
> +                s++;
> +            } else if (ovs_scan(s, "0x%"SCNx16"%n", &ns_type, &len)) {
> +                s += len;
> +                skey = PACKET_TYPE_BE(ns, ns_type);
> +                if ( *s == '/' ) {
> +                    uint16_t  ns_type_mask = 0;
> +                    if (ovs_scan(s, "/0x%"SCNx16"%n", &ns_type_mask, &len)) {
> +                        s += len;
> +                        smask = PACKET_TYPE_BE(ns, ns_type_mask);
> +                    }
> +                }
> +            }
> +
> +            if (*s++ != ')') {
> +                return -EINVAL;
> +            }
> +
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_PACKET_TYPE, &skey,
> +                              sizeof(skey));
> +            if (mask) {
> +                nl_msg_put_unspec(mask, OVS_KEY_ATTR_PACKET_TYPE, &smask,
> +                                  sizeof(smask));
> +            }
> +        }
> +
> +        return s - start;
> +    }
> +
>      return -EINVAL;
>  }
>  
> @@ -4538,9 +4582,7 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
>          nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, data->in_port.odp_port);
>      }
>  
> -    if (export_mask || flow->packet_type != htonl(PT_ETH)) {
> -        nl_msg_put_be32(buf, OVS_KEY_ATTR_PACKET_TYPE, data->packet_type);
> -    }
> +    nl_msg_put_be32(buf, OVS_KEY_ATTR_PACKET_TYPE, data->packet_type);
>  
>      if (OVS_UNLIKELY(parms->probe)) {
>          max_vlans = FLOW_MAX_VLAN_HEADERS;
> diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
> index 7826bc5..bc7f7fa 100644
> --- a/lib/ofp-parse.c
> +++ b/lib/ofp-parse.c
> @@ -513,6 +513,12 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
>              return error;
>          }
>      }
> +    /* Copy ethertype to flow->dl_type for matches on packet_type
> +     * (OFPHTN_ETHERTYPE, ethertype). */
> +    if (fm->match.wc.masks.packet_type == OVS_BE32_MAX &&
> +            pt_ns(fm->match.flow.packet_type) == OFPHTN_ETHERTYPE) {
> +        fm->match.flow.dl_type = pt_ns_type_be(fm->match.flow.packet_type);
> +    }
>      /* Check for usable protocol interdependencies between match fields. */
>      if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) {
>          const struct flow_wildcards *wc = &fm->match.wc;
> diff --git a/lib/ofp-util.c b/lib/ofp-util.c
> index bdf89b6..758f905 100644
> --- a/lib/ofp-util.c
> +++ b/lib/ofp-util.c
> @@ -7431,21 +7431,37 @@ ofputil_normalize_match__(struct match *match, bool may_log)
>          MAY_IPV6        = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
>          MAY_ND_TARGET   = 1 << 7, /* nd_target */
>          MAY_MPLS        = 1 << 8, /* mpls label and tc */
> +        MAY_ETHER       = 1 << 9, /* dl_src, dl_dst */
>      } may_match;
>  
> -    struct flow_wildcards wc;
> +    struct flow_wildcards wc = match->wc;
> +    ovs_be16 dl_type;
>  
>      /* Figure out what fields may be matched. */
> -    if (match->flow.dl_type == htons(ETH_TYPE_IP)) {
> -        may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
> +    /* Check the packet_type first and extract dl_type. */
> +    if (wc.masks.packet_type == 0 ||
> +        (wc.masks.packet_type == OVS_BE32_MAX &&
> +         match->flow.packet_type == htonl(PT_ETH))) {
> +        may_match = MAY_ETHER;
> +        dl_type = match->flow.dl_type;
> +    } else if (wc.masks.packet_type == OVS_BE32_MAX &&
> +               pt_ns(match->flow.packet_type) == OFPHTN_ETHERTYPE) {
> +        may_match = 0;
> +        dl_type = pt_ns_type_be(match->flow.packet_type);
> +    } else {
> +        may_match = 0;
> +        dl_type = 0;
> +    }
> +    if (dl_type == htons(ETH_TYPE_IP)) {
> +        may_match |= MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
>          if (match->flow.nw_proto == IPPROTO_TCP ||
>              match->flow.nw_proto == IPPROTO_UDP ||
>              match->flow.nw_proto == IPPROTO_SCTP ||
>              match->flow.nw_proto == IPPROTO_ICMP) {
>              may_match |= MAY_TP_ADDR;
>          }
> -    } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
> -        may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
> +    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
> +        may_match |= MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
>          if (match->flow.nw_proto == IPPROTO_TCP ||
>              match->flow.nw_proto == IPPROTO_UDP ||
>              match->flow.nw_proto == IPPROTO_SCTP) {
> @@ -7458,17 +7474,17 @@ ofputil_normalize_match__(struct match *match, bool may_log)
>                  may_match |= MAY_ND_TARGET | MAY_ARP_THA;
>              }
>          }
> -    } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) ||
> -               match->flow.dl_type == htons(ETH_TYPE_RARP)) {
> -        may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
> -    } else if (eth_type_mpls(match->flow.dl_type)) {
> -        may_match = MAY_MPLS;
> -    } else {
> -        may_match = 0;
> +    } else if (dl_type == htons(ETH_TYPE_ARP) ||
> +               dl_type == htons(ETH_TYPE_RARP)) {
> +        may_match |= MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
> +    } else if (eth_type_mpls(dl_type)) {
> +        may_match |= MAY_MPLS;
>      }
>  
>      /* Clear the fields that may not be matched. */
> -    wc = match->wc;
> +    if (!(may_match & MAY_ETHER)) {
> +        wc.masks.dl_src = wc.masks.dl_dst = eth_addr_zero;
> +    }
>      if (!(may_match & MAY_NW_ADDR)) {
>          wc.masks.nw_src = wc.masks.nw_dst = htonl(0);
>      }
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index 586a5b1..774619d 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -99,7 +99,7 @@ m4_define([DPIF_NETDEV_MISS_FLOW_INSTALL],
>     sleep 1
>  
>     AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
> -skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
> +skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
>  ])
>     AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
>  recirc_id(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: <del>
> @@ -112,7 +112,7 @@ recirc_id(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_typ
>     sleep 1
>  
>     AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
> -skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
> +skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
>  ])
>     AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
>  recirc_id(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: <del>
> @@ -140,10 +140,10 @@ m4_define([DPIF_NETDEV_MISS_FLOW_DUMP],
>     sleep 1
>  
>     AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
> -skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
> +skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
>  ])
>     AT_CHECK([filter_flow_dump < ovs-vswitchd.log | strip_xout], [0], [dnl
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
>  ])
>  
>     # Now, the same again without megaflows.
> @@ -155,11 +155,11 @@ skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label
>     sleep 1
>  
>     AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
> -skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
> +skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
>  ])
>     AT_CHECK([filter_flow_dump < ovs-vswitchd.log | strip_xout], [0], [dnl
> -skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
> +skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
>  ])
>  
>     OVS_VSWITCHD_STOP
> diff --git a/tests/odp.at b/tests/odp.at
> index e408c9f..72167ee 100644
> --- a/tests/odp.at
> +++ b/tests/odp.at
> @@ -94,6 +94,7 @@ dnl Some fields are always printed for this test, because wildcards aren't
>  dnl specified. We can skip these.
>  sed -i 's/\(skb_mark(0)\),\(ct\)/\1,ct_state(0),ct_zone(0),\2/' odp-out.txt
>  sed -i 's/\(skb_mark([[^)]]*)\),\(recirc\)/\1,ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),\2/' odp-out.txt
> +sed -i 's/\(in_port(1)\),\(eth\)/\1,packet_type(ns=0,id=0x0),\2/' odp-out.txt
>  
>  AT_CHECK_UNQUOTED([ovstest test-odp parse-keys < odp-in.txt], [0], [`cat odp-out.txt`
>  ])
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index 0c2ea38..f8b9e46 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -7151,12 +7151,12 @@ recirc_id(0),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used
>  ])
>  
>  AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | strip_ufid | strip_used | sort], [0], [dnl
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
>  ])
>  
>  AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | strip_ufid | strip_used | sort], [0], [dnl
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
>  ])
>  
>  OVS_VSWITCHD_STOP
> @@ -7316,10 +7316,10 @@ recirc_id(0),in_port(101),eth_type(0x0800),ipv4(frag=no), actions:100,2,3
>  ])
>  
>  AT_CHECK([grep -e 'in_port(100).*packets:9' ovs-vswitchd.log | strip_ufid | filter_flow_dump], [0], [dnl
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(100),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:9, bytes:378, used:0.0s, actions:101,3,2
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(100),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:9, bytes:378, used:0.0s, actions:101,3,2
>  ])
>  AT_CHECK([grep -e 'in_port(101).*packets:4' ovs-vswitchd.log | strip_ufid | filter_flow_dump], [0], [dnl
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(101),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:4, bytes:168, used:0.0s, actions:100,2,3
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(101),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:4, bytes:168, used:0.0s, actions:100,2,3
>  ])
>  
>  AT_CHECK([ovs-ofctl dump-ports br0 pbr0], [0], [dnl
> @@ -7930,8 +7930,8 @@ skb_priority(0),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone
>  skb_priority(0),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions:drop
>  ])
>     AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_dump | grep 'packets:3'], [0], [dnl
> -skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:2
> -skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:drop
> +skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:2
> +skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:drop
>  ])
>     OVS_VSWITCHD_STOP
>     AT_CLEANUP])
> @@ -8543,7 +8543,7 @@ recirc_id(0),in_port(1),eth_type(0x1234), packets:5, bytes:70, used:0.0s, action
>  ])
>  
>  AT_CHECK([grep 'modify' ovs-vswitchd.log | strip_ufid ], [0], [dnl
> -dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:push_vlan(vid=4,pcp=0),100
> +dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:push_vlan(vid=4,pcp=0),100
>  ])
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
> @@ -8623,8 +8623,8 @@ recirc_id(0),in_port(1),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0
>  # are wildcarded.
>  AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | strip_ufid ], [0], [dnl
>  dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),eth_type(0x1234), actions:100
> -dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234)
> -dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:100
> +dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234)
> +dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:100
>  dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), actions:drop
>  ])
>  OVS_VSWITCHD_STOP
> diff --git a/tests/ofproto.at b/tests/ofproto.at
> index 5c0d076..66ee695 100644
> --- a/tests/ofproto.at
> +++ b/tests/ofproto.at
> @@ -2390,6 +2390,7 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
>      matching:
>        dp_hash: arbitrary mask
>        recirc_id: exact match or wildcard
> +      packet_type: exact match or wildcard
>        conj_id: exact match or wildcard
>        tun_id: arbitrary mask
>        tun_src: arbitrary mask
> diff --git a/tests/pmd.at b/tests/pmd.at
> index 5686bed..ab83950 100644
> --- a/tests/pmd.at
> +++ b/tests/pmd.at
> @@ -190,7 +190,7 @@ for i in `seq 0 19`;
>  ovs-appctl time/warp 100
>  
>  AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
> -skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
> +skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
>  ])
>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout], [0], [dnl
>  recirc_id(0),in_port(1),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(frag=no), actions: <del>
> diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
> index dedda8c..9757565 100644
> --- a/tests/tunnel-push-pop-ipv6.at
> +++ b/tests/tunnel-push-pop-ipv6.at
> @@ -163,7 +163,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
>  ])
>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
> -tunnel(tun_id=0x7b,ipv6_src=2001:cafe::92,ipv6_dst=2001:cafe::88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
> +tunnel(tun_id=0x7b,ipv6_src=2001:cafe::92,ipv6_dst=2001:cafe::88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0x0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
>  ])
>  
>  OVS_VSWITCHD_STOP
> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> index 7fe67aa..8dcfc38 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -203,7 +203,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
>  ])
>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0x0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
>  ])
>  
>  OVS_VSWITCHD_STOP
Zoltan Balogh April 19, 2017, 9:36 a.m. UTC | #2
> >
> > -    /* "in_port".
> > +     /* "in_port".
> 
> Why the formatting change in the middle of the patch?  And even in that
> case it looks off a bit.
> 
> - Greg

Thank you for the review! I guess, this was included mistakenly. We will exclude it from next version of the patch set.

/Zoltan
Ben Pfaff April 22, 2017, 2:05 a.m. UTC | #3
On Tue, Apr 18, 2017 at 11:19:44AM +0000, Zoltán Balogh wrote:
> From: Jan Scheurich <jan.scheurich@ericsson.com>
> 
> Allow packet type namespace OFPHTN_ETHERTYPE as alternative pre-requisite
> for matching L3 protocols (MPLS, IP, IPv6, ARP etc).
> 
> Scan packet_type in odp_flow string.
> 
> Added dummy entry for MFF_PACKET_TYPE to lib/meta-flow.xml
> 
> Added packet_type to the matching properties in tests/ofproto.at. Should be
> removed later, when packet_type_aware bridge attribute will be introduced.
> 
> Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>

Zoltán, thanks a lot for working on this feature.  But I'm seeing a lot
of patch rejects even on the first patch, so I'd appreciate very much a
rebase.

Thanks,

Ben.
Ben Pfaff April 22, 2017, 4:38 a.m. UTC | #4
On Fri, Apr 21, 2017 at 07:05:41PM -0700, Ben Pfaff wrote:
> On Tue, Apr 18, 2017 at 11:19:44AM +0000, Zoltán Balogh wrote:
> > From: Jan Scheurich <jan.scheurich@ericsson.com>
> > 
> > Allow packet type namespace OFPHTN_ETHERTYPE as alternative pre-requisite
> > for matching L3 protocols (MPLS, IP, IPv6, ARP etc).
> > 
> > Scan packet_type in odp_flow string.
> > 
> > Added dummy entry for MFF_PACKET_TYPE to lib/meta-flow.xml
> > 
> > Added packet_type to the matching properties in tests/ofproto.at. Should be
> > removed later, when packet_type_aware bridge attribute will be introduced.
> > 
> > Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
> 
> Zoltán, thanks a lot for working on this feature.  But I'm seeing a lot
> of patch rejects even on the first patch, so I'd appreciate very much a
> rebase.

Oh, it's because there's another series dependency, never mind.
diff mbox

Patch

diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index c284ec6..172cc3b 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -247,6 +247,20 @@  enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_RECIRC_ID,
 
+    /* "packet_type".
+     *
+     * Define the packet type in OpenFlow 1.3+.
+     *
+     * Type: be32.
+     * Maskable: no.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read-only.
+     * NXM: none.
+     * OXM: OXM_OF_PACKET_TYPE(44) since OF1.3 and v2.7.
+     */
+    MFF_PACKET_TYPE,
+
     /* "conj_id".
      *
      * ID for "conjunction" actions.  Please refer to ovs-ofctl(8)
@@ -589,7 +603,7 @@  enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_METADATA,
 
-    /* "in_port".
+     /* "in_port".
      *
      * 16-bit (OpenFlow 1.0) view of the physical or virtual port on which the
      * packet was received.
diff --git a/lib/flow.h b/lib/flow.h
index a8772c5..fcc7d13 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -959,9 +959,23 @@  static inline bool is_ethernet(const struct flow *flow,
     return flow->packet_type == htonl(PT_ETH);
 }
 
+static inline ovs_be16 get_dl_type(const struct flow *flow)
+{
+    if (flow->packet_type == htonl(PT_ETH)) {
+        return flow->dl_type;
+    } else if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
+        return pt_ns_type_be(flow->packet_type);
+    } else {
+        return FLOW_DL_TYPE_NONE;
+    }
+}
+
 static inline bool is_vlan(const struct flow *flow,
                            struct flow_wildcards *wc)
 {
+    if (!is_ethernet(flow, wc)) {
+        return false;
+    }
     if (wc) {
         WC_MASK_FIELD_MASK(wc, vlans[0].tci, htons(VLAN_CFI));
     }
@@ -970,7 +984,7 @@  static inline bool is_vlan(const struct flow *flow,
 
 static inline bool is_ip_any(const struct flow *flow)
 {
-    return dl_type_is_ip_any(flow->dl_type);
+    return dl_type_is_ip_any(get_dl_type(flow));
 }
 
 static inline bool is_ip_proto(const struct flow *flow, uint8_t ip_proto,
@@ -1006,7 +1020,7 @@  static inline bool is_sctp(const struct flow *flow,
 static inline bool is_icmpv4(const struct flow *flow,
                              struct flow_wildcards *wc)
 {
-    if (flow->dl_type == htons(ETH_TYPE_IP)) {
+    if (get_dl_type(flow) == htons(ETH_TYPE_IP)) {
         if (wc) {
             memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
         }
@@ -1018,7 +1032,7 @@  static inline bool is_icmpv4(const struct flow *flow,
 static inline bool is_icmpv6(const struct flow *flow,
                              struct flow_wildcards *wc)
 {
-    if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+    if (get_dl_type(flow) == htons(ETH_TYPE_IPV6)) {
         if (wc) {
             memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
         }
@@ -1049,7 +1063,7 @@  static inline bool is_nd(const struct flow *flow,
 
 static inline bool is_igmp(const struct flow *flow, struct flow_wildcards *wc)
 {
-    if (flow->dl_type == htons(ETH_TYPE_IP)) {
+    if (get_dl_type(flow) == htons(ETH_TYPE_IP)) {
         if (wc) {
             memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
         }
@@ -1093,8 +1107,8 @@  static inline bool is_mld_report(const struct flow *flow,
 
 static inline bool is_stp(const struct flow *flow)
 {
-    return (eth_addr_equals(flow->dl_dst, eth_addr_stp)
-            && flow->dl_type == htons(FLOW_DL_TYPE_NONE));
+    return (flow->dl_type == htons(FLOW_DL_TYPE_NONE)
+            && eth_addr_equals(flow->dl_dst, eth_addr_stp));
 }
 
 #endif /* flow.h */
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index a963cce..eac7fbc 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -205,6 +205,8 @@  mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
         return !wc->masks.dp_hash;
     case MFF_RECIRC_ID:
         return !wc->masks.recirc_id;
+    case MFF_PACKET_TYPE:
+        return !wc->masks.packet_type;
     case MFF_CONJ_ID:
         return !wc->masks.conj_id;
     case MFF_TUN_SRC:
@@ -401,22 +403,24 @@  mf_are_prereqs_ok__(const struct mf_field *mf, const struct flow *flow,
                     const struct flow_wildcards *mask,
                     struct flow_wildcards *wc)
 {
+    ovs_be16 dl_type = get_dl_type(flow);
+
     switch (mf->prereqs) {
     case MFP_NONE:
         return true;
     case MFP_ETHERNET:
         return is_ethernet(flow, wc);
     case MFP_ARP:
-        return (flow->dl_type == htons(ETH_TYPE_ARP) ||
-                flow->dl_type == htons(ETH_TYPE_RARP));
+        return (dl_type == htons(ETH_TYPE_ARP) ||
+                dl_type == htons(ETH_TYPE_RARP));
     case MFP_IPV4:
-        return flow->dl_type == htons(ETH_TYPE_IP);
+        return dl_type == htons(ETH_TYPE_IP);
     case MFP_IPV6:
-        return flow->dl_type == htons(ETH_TYPE_IPV6);
+        return dl_type == htons(ETH_TYPE_IPV6);
     case MFP_VLAN_VID:
         return is_vlan(flow, wc);
     case MFP_MPLS:
-        return eth_type_mpls(flow->dl_type);
+        return eth_type_mpls(dl_type);
     case MFP_IP_ANY:
         return is_ip_any(flow);
     case MFP_CT_VALID:
@@ -476,6 +480,7 @@  mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     switch (mf->id) {
     case MFF_DP_HASH:
     case MFF_RECIRC_ID:
+    case MFF_PACKET_TYPE:
     case MFF_CONJ_ID:
     case MFF_TUN_ID:
     case MFF_TUN_SRC:
@@ -600,6 +605,9 @@  mf_get_value(const struct mf_field *mf, const struct flow *flow,
     case MFF_RECIRC_ID:
         value->be32 = htonl(flow->recirc_id);
         break;
+    case MFF_PACKET_TYPE:
+        value->be32 = flow->packet_type;
+        break;
     case MFF_CONJ_ID:
         value->be32 = htonl(flow->conj_id);
         break;
@@ -883,6 +891,9 @@  mf_set_value(const struct mf_field *mf,
     case MFF_RECIRC_ID:
         match_set_recirc_id(match, ntohl(value->be32));
         break;
+    case MFF_PACKET_TYPE:
+        match_set_packet_type(match, value->be32);
+        break;
     case MFF_CONJ_ID:
         match_set_conj_id(match, ntohl(value->be32));
         break;
@@ -1248,6 +1259,9 @@  mf_set_flow_value(const struct mf_field *mf,
     case MFF_RECIRC_ID:
         flow->recirc_id = ntohl(value->be32);
         break;
+    case MFF_PACKET_TYPE:
+        flow->packet_type = value->be32;
+        break;
     case MFF_CONJ_ID:
         flow->conj_id = ntohl(value->be32);
         break;
@@ -1292,7 +1306,6 @@  mf_set_flow_value(const struct mf_field *mf,
     case MFF_IN_PORT:
         flow->in_port.ofp_port = u16_to_ofp(ntohs(value->be16));
         break;
-
     case MFF_IN_PORT_OXM:
         ofputil_port_from_ofp11(value->be32, &flow->in_port.ofp_port);
         break;
@@ -1598,6 +1611,10 @@  mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
         match->flow.recirc_id = 0;
         match->wc.masks.recirc_id = 0;
         break;
+    case MFF_PACKET_TYPE:
+        match->flow.packet_type = 0;
+        match->wc.masks.packet_type = 0;
+        break;
     case MFF_CONJ_ID:
         match->flow.conj_id = 0;
         match->wc.masks.conj_id = 0;
@@ -1931,6 +1948,7 @@  mf_set(const struct mf_field *mf,
     case MFF_CT_TP_SRC:
     case MFF_CT_TP_DST:
     case MFF_RECIRC_ID:
+    case MFF_PACKET_TYPE:
     case MFF_CONJ_ID:
     case MFF_IN_PORT:
     case MFF_IN_PORT_OXM:
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 5efd431..3183968 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -2273,6 +2273,10 @@  actions=clone(load:0->NXM_OF_IN_PORT[],output:123)
 
     <field id="MFF_DP_HASH" title="Datapath Hash" internal="yes"/>
     <field id="MFF_RECIRC_ID" title="Datapath Recirculation ID" internal="yes"/>
+
+    <field id="MFF_PACKET_TYPE" title="Packet Type">
+    </field>
+
   </group>
 
   <group title="Connection Tracking">
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 68e58d3..af19fb2 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -873,8 +873,9 @@  static void
 nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
 {
     const struct flow *flow = &match->flow;
+    ovs_be16 dl_type = get_dl_type(flow);
 
-    if (flow->dl_type == htons(ETH_TYPE_IP)) {
+    if (dl_type == htons(ETH_TYPE_IP)) {
         nxm_put_32m(b, MFF_IPV4_SRC, oxm,
                     flow->nw_src, match->wc.masks.nw_src);
         nxm_put_32m(b, MFF_IPV4_DST, oxm,
@@ -983,11 +984,18 @@  nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
 {
     const struct flow *flow = &match->flow;
     const size_t start_len = b->size;
+    ovs_be16 dl_type = get_dl_type(flow);
     int match_len;
     int i;
 
     BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
 
+    /* OpenFlow Packet Type. Must be first. */
+    if (match->wc.masks.packet_type) {
+        nxm_put_32m(b, MFF_PACKET_TYPE, oxm, flow->packet_type,
+                    match->wc.masks.packet_type);
+    }
+
     /* Metadata. */
     if (match->wc.masks.dp_hash) {
         nxm_put_32m(b, MFF_DP_HASH, oxm,
@@ -1049,7 +1057,7 @@  nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     }
 
     /* MPLS. */
-    if (eth_type_mpls(flow->dl_type)) {
+    if (eth_type_mpls(dl_type)) {
         if (match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
             nxm_put_8(b, MFF_MPLS_TC, oxm,
                       mpls_lse_to_tc(flow->mpls_lse[0]));
@@ -1069,8 +1077,8 @@  nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     /* L3. */
     if (is_ip_any(flow)) {
         nxm_put_ip(b, match, oxm);
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
-               flow->dl_type == htons(ETH_TYPE_RARP)) {
+    } else if (dl_type == htons(ETH_TYPE_ARP) ||
+               dl_type == htons(ETH_TYPE_RARP)) {
         /* ARP. */
         if (match->wc.masks.nw_proto) {
             nxm_put_16(b, MFF_ARP_OP, oxm,
diff --git a/lib/odp-util.c b/lib/odp-util.c
index f98a9e5..5445314 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -4388,6 +4388,50 @@  parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         return s - start;
     }
 
+    /* Packet_type open-coded. */
+    if (strncmp(s, "packet_type(", strlen("packet_type(")) == 0) {
+        const char *start = s;
+        ovs_be32 skey = 0;
+        ovs_be32 smask = 0;
+        int len;
+        uint16_t  ns = 0;
+        uint16_t  ns_type = 0;
+
+        s += strlen("packet_type(");
+        if (ovs_scan(s, "ns=%"SCNu16",id=%n", &ns, &len)){
+            if (len == 0) {
+                return -EINVAL;
+            }
+            s += len;
+            if (strncmp(s, "*", 1) == 0) {
+                s++;
+            } else if (ovs_scan(s, "0x%"SCNx16"%n", &ns_type, &len)) {
+                s += len;
+                skey = PACKET_TYPE_BE(ns, ns_type);
+                if ( *s == '/' ) {
+                    uint16_t  ns_type_mask = 0;
+                    if (ovs_scan(s, "/0x%"SCNx16"%n", &ns_type_mask, &len)) {
+                        s += len;
+                        smask = PACKET_TYPE_BE(ns, ns_type_mask);
+                    }
+                }
+            }
+
+            if (*s++ != ')') {
+                return -EINVAL;
+            }
+
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_PACKET_TYPE, &skey,
+                              sizeof(skey));
+            if (mask) {
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_PACKET_TYPE, &smask,
+                                  sizeof(smask));
+            }
+        }
+
+        return s - start;
+    }
+
     return -EINVAL;
 }
 
@@ -4538,9 +4582,7 @@  odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
         nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, data->in_port.odp_port);
     }
 
-    if (export_mask || flow->packet_type != htonl(PT_ETH)) {
-        nl_msg_put_be32(buf, OVS_KEY_ATTR_PACKET_TYPE, data->packet_type);
-    }
+    nl_msg_put_be32(buf, OVS_KEY_ATTR_PACKET_TYPE, data->packet_type);
 
     if (OVS_UNLIKELY(parms->probe)) {
         max_vlans = FLOW_MAX_VLAN_HEADERS;
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 7826bc5..bc7f7fa 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -513,6 +513,12 @@  parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
             return error;
         }
     }
+    /* Copy ethertype to flow->dl_type for matches on packet_type
+     * (OFPHTN_ETHERTYPE, ethertype). */
+    if (fm->match.wc.masks.packet_type == OVS_BE32_MAX &&
+            pt_ns(fm->match.flow.packet_type) == OFPHTN_ETHERTYPE) {
+        fm->match.flow.dl_type = pt_ns_type_be(fm->match.flow.packet_type);
+    }
     /* Check for usable protocol interdependencies between match fields. */
     if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) {
         const struct flow_wildcards *wc = &fm->match.wc;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index bdf89b6..758f905 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -7431,21 +7431,37 @@  ofputil_normalize_match__(struct match *match, bool may_log)
         MAY_IPV6        = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
         MAY_ND_TARGET   = 1 << 7, /* nd_target */
         MAY_MPLS        = 1 << 8, /* mpls label and tc */
+        MAY_ETHER       = 1 << 9, /* dl_src, dl_dst */
     } may_match;
 
-    struct flow_wildcards wc;
+    struct flow_wildcards wc = match->wc;
+    ovs_be16 dl_type;
 
     /* Figure out what fields may be matched. */
-    if (match->flow.dl_type == htons(ETH_TYPE_IP)) {
-        may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
+    /* Check the packet_type first and extract dl_type. */
+    if (wc.masks.packet_type == 0 ||
+        (wc.masks.packet_type == OVS_BE32_MAX &&
+         match->flow.packet_type == htonl(PT_ETH))) {
+        may_match = MAY_ETHER;
+        dl_type = match->flow.dl_type;
+    } else if (wc.masks.packet_type == OVS_BE32_MAX &&
+               pt_ns(match->flow.packet_type) == OFPHTN_ETHERTYPE) {
+        may_match = 0;
+        dl_type = pt_ns_type_be(match->flow.packet_type);
+    } else {
+        may_match = 0;
+        dl_type = 0;
+    }
+    if (dl_type == htons(ETH_TYPE_IP)) {
+        may_match |= MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
         if (match->flow.nw_proto == IPPROTO_TCP ||
             match->flow.nw_proto == IPPROTO_UDP ||
             match->flow.nw_proto == IPPROTO_SCTP ||
             match->flow.nw_proto == IPPROTO_ICMP) {
             may_match |= MAY_TP_ADDR;
         }
-    } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
-        may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
+    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
+        may_match |= MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
         if (match->flow.nw_proto == IPPROTO_TCP ||
             match->flow.nw_proto == IPPROTO_UDP ||
             match->flow.nw_proto == IPPROTO_SCTP) {
@@ -7458,17 +7474,17 @@  ofputil_normalize_match__(struct match *match, bool may_log)
                 may_match |= MAY_ND_TARGET | MAY_ARP_THA;
             }
         }
-    } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) ||
-               match->flow.dl_type == htons(ETH_TYPE_RARP)) {
-        may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
-    } else if (eth_type_mpls(match->flow.dl_type)) {
-        may_match = MAY_MPLS;
-    } else {
-        may_match = 0;
+    } else if (dl_type == htons(ETH_TYPE_ARP) ||
+               dl_type == htons(ETH_TYPE_RARP)) {
+        may_match |= MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
+    } else if (eth_type_mpls(dl_type)) {
+        may_match |= MAY_MPLS;
     }
 
     /* Clear the fields that may not be matched. */
-    wc = match->wc;
+    if (!(may_match & MAY_ETHER)) {
+        wc.masks.dl_src = wc.masks.dl_dst = eth_addr_zero;
+    }
     if (!(may_match & MAY_NW_ADDR)) {
         wc.masks.nw_src = wc.masks.nw_dst = htonl(0);
     }
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 586a5b1..774619d 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -99,7 +99,7 @@  m4_define([DPIF_NETDEV_MISS_FLOW_INSTALL],
    sleep 1
 
    AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
-skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
+skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
 ])
    AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
 recirc_id(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: <del>
@@ -112,7 +112,7 @@  recirc_id(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_typ
    sleep 1
 
    AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
-skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
+skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
 ])
    AT_CHECK([filter_flow_install < ovs-vswitchd.log | strip_xout], [0], [dnl
 recirc_id(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(frag=no), actions: <del>
@@ -140,10 +140,10 @@  m4_define([DPIF_NETDEV_MISS_FLOW_DUMP],
    sleep 1
 
    AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
-skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
+skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
 ])
    AT_CHECK([filter_flow_dump < ovs-vswitchd.log | strip_xout], [0], [dnl
-skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
 ])
 
    # Now, the same again without megaflows.
@@ -155,11 +155,11 @@  skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label
    sleep 1
 
    AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
-skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
+skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
 ])
    AT_CHECK([filter_flow_dump < ovs-vswitchd.log | strip_xout], [0], [dnl
-skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
-skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del>
 ])
 
    OVS_VSWITCHD_STOP
diff --git a/tests/odp.at b/tests/odp.at
index e408c9f..72167ee 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -94,6 +94,7 @@  dnl Some fields are always printed for this test, because wildcards aren't
 dnl specified. We can skip these.
 sed -i 's/\(skb_mark(0)\),\(ct\)/\1,ct_state(0),ct_zone(0),\2/' odp-out.txt
 sed -i 's/\(skb_mark([[^)]]*)\),\(recirc\)/\1,ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),\2/' odp-out.txt
+sed -i 's/\(in_port(1)\),\(eth\)/\1,packet_type(ns=0,id=0x0),\2/' odp-out.txt
 
 AT_CHECK_UNQUOTED([ovstest test-odp parse-keys < odp-in.txt], [0], [`cat odp-out.txt`
 ])
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 0c2ea38..f8b9e46 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7151,12 +7151,12 @@  recirc_id(0),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | strip_ufid | strip_used | sort], [0], [dnl
-skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
-skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
+skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
+skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
 ])
 
 AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | strip_ufid | strip_used | sort], [0], [dnl
-skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
+skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
 ])
 
 OVS_VSWITCHD_STOP
@@ -7316,10 +7316,10 @@  recirc_id(0),in_port(101),eth_type(0x0800),ipv4(frag=no), actions:100,2,3
 ])
 
 AT_CHECK([grep -e 'in_port(100).*packets:9' ovs-vswitchd.log | strip_ufid | filter_flow_dump], [0], [dnl
-skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(100),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:9, bytes:378, used:0.0s, actions:101,3,2
+skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(100),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:9, bytes:378, used:0.0s, actions:101,3,2
 ])
 AT_CHECK([grep -e 'in_port(101).*packets:4' ovs-vswitchd.log | strip_ufid | filter_flow_dump], [0], [dnl
-skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(101),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:4, bytes:168, used:0.0s, actions:100,2,3
+skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(101),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:4, bytes:168, used:0.0s, actions:100,2,3
 ])
 
 AT_CHECK([ovs-ofctl dump-ports br0 pbr0], [0], [dnl
@@ -7930,8 +7930,8 @@  skb_priority(0),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone
 skb_priority(0),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions:drop
 ])
    AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_dump | grep 'packets:3'], [0], [dnl
-skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:2
-skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:drop
+skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:2
+skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:126, used:0.0s, actions:drop
 ])
    OVS_VSWITCHD_STOP
    AT_CLEANUP])
@@ -8543,7 +8543,7 @@  recirc_id(0),in_port(1),eth_type(0x1234), packets:5, bytes:70, used:0.0s, action
 ])
 
 AT_CHECK([grep 'modify' ovs-vswitchd.log | strip_ufid ], [0], [dnl
-dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:push_vlan(vid=4,pcp=0),100
+dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:push_vlan(vid=4,pcp=0),100
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -8623,8 +8623,8 @@  recirc_id(0),in_port(1),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0
 # are wildcarded.
 AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | strip_ufid ], [0], [dnl
 dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),eth_type(0x1234), actions:100
-dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234)
-dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:100
+dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234)
+dpif|DBG|dummy@ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=*),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:100
 dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), actions:drop
 ])
 OVS_VSWITCHD_STOP
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 5c0d076..66ee695 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2390,6 +2390,7 @@  metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
     matching:
       dp_hash: arbitrary mask
       recirc_id: exact match or wildcard
+      packet_type: exact match or wildcard
       conj_id: exact match or wildcard
       tun_id: arbitrary mask
       tun_src: arbitrary mask
diff --git a/tests/pmd.at b/tests/pmd.at
index 5686bed..ab83950 100644
--- a/tests/pmd.at
+++ b/tests/pmd.at
@@ -190,7 +190,7 @@  for i in `seq 0 19`;
 ovs-appctl time/warp 100
 
 AT_CHECK([grep -A 1 'miss upcall' ovs-vswitchd.log | tail -n 1], [0], [dnl
-skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
+skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0x0),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
 ])
 AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout], [0], [dnl
 recirc_id(0),in_port(1),eth(src=50:54:00:00:00:77,dst=50:54:00:00:01:78),eth_type(0x0800),ipv4(frag=no), actions: <del>
diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
index dedda8c..9757565 100644
--- a/tests/tunnel-push-pop-ipv6.at
+++ b/tests/tunnel-push-pop-ipv6.at
@@ -163,7 +163,7 @@  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
   port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
 ])
 AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
-tunnel(tun_id=0x7b,ipv6_src=2001:cafe::92,ipv6_dst=2001:cafe::88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
+tunnel(tun_id=0x7b,ipv6_src=2001:cafe::92,ipv6_dst=2001:cafe::88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0x0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
 ])
 
 OVS_VSWITCHD_STOP
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index 7fe67aa..8dcfc38 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -203,7 +203,7 @@  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
   port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
 ])
 AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
-tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
+tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0x0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,slow_path(controller))
 ])
 
 OVS_VSWITCHD_STOP