diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index 58ca5848b127..da2f780c3ba5 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -23,6 +23,7 @@
 
 struct ds;
 struct ofputil_port_map;
+struct mf_field;
 
 /* A flow classification match.
  *
@@ -119,6 +120,7 @@ void match_set_ct_ipv6_dst_masked(struct match *, const struct in6_addr *,
 void match_set_packet_type(struct match *, ovs_be32 packet_type);
 void match_set_default_packet_type(struct match *);
 bool match_has_default_packet_type(const struct match *);
+void match_add_ethernet_prereq(struct match *, const struct mf_field *);
 
 void match_set_skb_priority(struct match *, uint32_t skb_priority);
 void match_set_dl_type(struct match *, ovs_be16);
diff --git a/include/openvswitch/ofp-errors.h b/include/openvswitch/ofp-errors.h
index 7d75386352d8..aeb58e012fd3 100644
--- a/include/openvswitch/ofp-errors.h
+++ b/include/openvswitch/ofp-errors.h
@@ -311,8 +311,7 @@ enum ofperr {
 /* ## OFPET_BAD_MATCH ## */
 /* ## --------------- ## */
 
-    /* NX1.0(4,264), OF1.1+(4,0).  Unsupported match type specified by the
-     * match */
+    /* OF1.1+(4,0).  Unsupported match type specified by the match */
     OFPERR_OFPBMC_BAD_TYPE,
 
     /* OF1.1+(4,1).  Length problem in match. */
diff --git a/lib/learn.c b/lib/learn.c
index bc5a6eb2d16f..4658b8611857 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -139,6 +139,7 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
         switch (spec->dst_type) {
         case NX_LEARN_DST_MATCH:
             mf_write_subfield(&spec->dst, &value, &fm->match);
+            match_add_ethernet_prereq(&fm->match, spec->dst.field);
             mf_vl_mff_set_tlv_bitmap(
                 spec->dst.field, &fm->match.flow.tunnel.metadata.present.map);
             break;
diff --git a/lib/match.c b/lib/match.c
index 0e23eb6e6069..6ea6cf0519ca 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -502,6 +502,30 @@ match_has_default_packet_type(const struct match *match)
             && match->wc.masks.packet_type == OVS_BE32_MAX);
 }
 
+/* A match on 'field' is being added to or has been added to 'match'.  If
+ * 'field' is a data field, and 'match' does not already match on packet_type,
+ * this function make it match on the Ethernet packet_type.
+ *
+ * This function is useful because OpenFlow implicitly applies to Ethernet
+ * packets when there's no explicit packet_type, but matching on a metadata
+ * field doesn't imply anything about the packet_type and falsely inferring
+ * that it does can cause harm.  A flow that matches only on metadata fields,
+ * for example, should be able to match more than just Ethernet flows.  There
+ * are also important reasons that a catch-all match (one with no field matches
+ * at all) should not imply a packet_type(0,0) match.  For example, a "flow
+ * dump" request that matches on no fields should return every flow in the
+ * switch, not just the flows that match on Ethernet.  As a second example,
+ * OpenFlow 1.2+ special-cases "table miss" flows, that is catch-all flows with
+ * priority 0, and inferring a match on packet_type(0,0) causes such a flow not
+ * to be a table miss flow.  */
+void
+match_add_ethernet_prereq(struct match *match, const struct mf_field *field)
+{
+    if (field->prereqs != MFP_NONE) {
+        match_set_default_packet_type(match);
+    }
+}
+
 void
 match_set_dl_type(struct match *match, ovs_be16 dl_type)
 {
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 283a1cb0bcb8..0c93c88c5939 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -26,19 +26,25 @@
     networking technology in use are called called <dfn>root fields</dfn>.
     Open vSwitch 2.7 and earlier considered Ethernet fields to be root fields,
     and this remains the default mode of operation for Open vSwitch bridges.
-    In this mode, when a packet is received from a non-Ethernet interfaces,
-    such as a layer-3 LISP or GRE tunnel, Open vSwitch force-fits it to this
-    Ethernet-centric point of view by pretending that an Ethernet header is
-    present whose Ethernet type that indicates the packet's actual type (and
+    When a packet is received from a non-Ethernet interfaces, such as a layer-3
+    LISP or GRE tunnel, Open vSwitch 2.7 and earlier force-fit the packet to
+    this Ethernet-centric point of view by pretending that an Ethernet header
+    is present whose Ethernet type that indicates the packet's actual type (and
     whose source and destination addresses are all-zero).
   </p>
 
   <p>
-    Open vSwitch 2.8 and later supports the ``packet type-aware pipeline''
-    concept introduced in OpenFlow 1.5.  A bridge configured to be packet
-    type-aware can handle packets of multiple networking technologies, such as
-    Ethernet, IP, ARP, MPLS, or NSH in parallel.  Such a bridge does not have
-    any root fields.
+    Open vSwitch 2.8 and later implement the ``packet type-aware pipeline''
+    concept introduced in OpenFlow 1.5.  Such a pipeline does not have any root
+    fields.  Instead, a new metadata field, <ref field="packet_type"/>,
+    indicates the basic type of the packet, which can be Ethernet, IPv4, IPv6,
+    or another type.  For backward compatibility, by default Open vSwitch 2.8
+    imitates the behavior of Open vSwitch 2.7 and earlier.  Later versions of
+    Open vSwitch may change the default, and in the meantime controllers can
+    turn off this legacy behavior by setting
+    <code>other-config:legacy-l3-tunnel</code> to <code>false</code> in the
+    <code>Bridge</code> table.  (See <code>ovs-vwitchd.conf.db</code>(5) for
+    more information.)
   </p>
 
   <p>
@@ -332,14 +338,6 @@ tcp,tp_src=0x07c0/0xfff0
     <dt><code>mplsm</code></dt>  <dd><code>eth_type=0x8848</code></dd>
   </dl>
 
-  <p>
-    These shorthand notations continue to work in packet type-aware bridges.
-    The absence of a packet_type match implies
-    <code>packet_type=ethernet</code>, so that shorthands match on Ethernet
-    packets with the implied eth_type. Please note that the shorthand
-    <code>ip</code> does not match packets of packet_type (1,0x800) for IPv4.
-  </p>
-
 
   <h2>Evolution of OpenFlow Fields</h2>
 
@@ -2374,15 +2372,14 @@ actions=clone(load:0->NXM_OF_IN_PORT[],output:123)
       </diagram>
 
       <p>
-        When the OVS bridge is configured to be packet type-aware, <ref
-        field="packet_type"/> is a pre-requisite for all data field matches.
-        As a special case to simplify the common case, and for backward
-        compatibility, when no such match is supplied, Open vSwitch acts as
-        though a match on <code>(0,0)</code> (Ethernet) had been supplied.
-      </p>
-
-      <p>
-        Open vSwitch allows any table to match this field.
+        Matching on <ref field="packet_type"/> is a pre-requisite for matching
+        on any data field, but for backward compatibility, when a match on a
+        data field is present without a <ref field="packet_type"/> match, Open
+        vSwitch acts as though a match on <code>(0,0)</code> (Ethernet) had
+        been supplied.  Similarly, when Open vSwitch sends flow match
+        information to a controller, e.g. in a reply to a request to dump the
+        flow table, Open vSwitch omits a match on packet type (0,0) if it would
+        be implied by a data field match.
       </p>
     </field>
 
diff --git a/lib/nx-match.c b/lib/nx-match.c
index daed882852aa..cb0cad8458b9 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -561,6 +561,8 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
                 free(err_str);
                 return OFPERR_OFPBMC_BAD_VALUE;
             }
+
+            match_add_ethernet_prereq(match, field);
         }
 
         if (error) {
@@ -775,6 +777,7 @@ oxm_pull_field_array(const void *fields_data, size_t fields_len,
 
 struct nxm_put_ctx {
     struct ofpbuf *output;
+    bool implied_ethernet;
 };
 
 void
@@ -795,6 +798,9 @@ nxm_put__(struct nxm_put_ctx *ctx,
           const void *value, const void *mask, size_t n_bytes)
 {
     nxm_put_entry_raw(ctx->output, field, version, value, mask, n_bytes);
+    if (!ctx->implied_ethernet && mf_from_id(field)->prereqs != MFP_NONE) {
+        ctx->implied_ethernet = true;
+    }
 }
 
 static void
@@ -1021,7 +1027,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
 
     BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
 
-    struct nxm_put_ctx ctx = { .output = b };
+    struct nxm_put_ctx ctx = { .output = b, .implied_ethernet = false };
 
     /* OpenFlow Packet Type. Must be first. */
     if (match->wc.masks.packet_type && !match_has_default_packet_type(match)) {
@@ -1206,6 +1212,16 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
         }
     }
 
+    if (match_has_default_packet_type(match) && !ctx.implied_ethernet) {
+        uint64_t pt_stub[16 / 8];
+        struct ofpbuf pt;
+        ofpbuf_use_stack(&pt, pt_stub, sizeof pt_stub);
+        nxm_put_entry_raw(&pt, MFF_PACKET_TYPE, oxm, &flow->packet_type,
+                          NULL, sizeof flow->packet_type);
+
+        ofpbuf_insert(b, start_len, pt.data, pt.size);
+    }
+
     match_len = b->size - start_len;
     return match_len;
 }
@@ -2103,15 +2119,12 @@ oxm_writable_fields(void)
 /* Returns a bitmap of fields that can be encoded in OXM and that can be
  * matched in a flow table.  */
 struct mf_bitmap
-oxm_matchable_fields(bool packet_type_aware)
+oxm_matchable_fields(void)
 {
     struct mf_bitmap b = MF_BITMAP_INITIALIZER;
     int i;
 
     for (i = 0; i < MFF_N_IDS; i++) {
-        if (i == MFF_PACKET_TYPE && !packet_type_aware) {
-            continue;
-        }
         if (mf_oxm_header(i, 0)) {
             bitmap_set1(b.bm, i);
         }
@@ -2122,15 +2135,12 @@ oxm_matchable_fields(bool packet_type_aware)
 /* Returns a bitmap of fields that can be encoded in OXM and that can be
  * matched in a flow table with an arbitrary bitmask.  */
 struct mf_bitmap
-oxm_maskable_fields(bool packet_type_aware)
+oxm_maskable_fields(void)
 {
     struct mf_bitmap b = MF_BITMAP_INITIALIZER;
     int i;
 
     for (i = 0; i < MFF_N_IDS; i++) {
-        if (i == MFF_PACKET_TYPE && !packet_type_aware) {
-            continue;
-        }
         if (mf_oxm_header(i, 0) && mf_from_id(i)->maskable == MFM_FULLY) {
             bitmap_set1(b.bm, i);
         }
diff --git a/lib/nx-match.h b/lib/nx-match.h
index c6f2b26db8ea..e304072d3532 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -149,8 +149,8 @@ ovs_be64 oxm_bitmap_from_mf_bitmap(const struct mf_bitmap *, enum ofp_version);
 struct mf_bitmap oxm_bitmap_to_mf_bitmap(ovs_be64 oxm_bitmap,
                                          enum ofp_version);
 struct mf_bitmap oxm_writable_fields(void);
-struct mf_bitmap oxm_matchable_fields(bool packet_type_aware);
-struct mf_bitmap oxm_maskable_fields(bool packet_type_aware);
+struct mf_bitmap oxm_matchable_fields(void);
+struct mf_bitmap oxm_maskable_fields(void);
 
 /* Dealing with the 'ofs_nbits' members in several Nicira extensions. */
 
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 8467be7fe2b5..8e2448b20dbd 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -251,6 +251,7 @@ parse_field(const struct mf_field *mf, const char *s,
     error = mf_parse(mf, s, port_map, &value, &mask);
     if (!error) {
         *usable_protocols &= mf_set(mf, &value, &mask, match, &error);
+        match_add_ethernet_prereq(match, mf);
     }
     return error;
 }
@@ -297,6 +298,8 @@ parse_subfield(const char *name, const char *str_value, struct match *match,
         bitwise_copy(&val, size, 0, &value, size, sf.ofs, sf.n_bits);
         bitwise_one (               &mask,  size, sf.ofs, sf.n_bits);
         *usable_protocols &= mf_set(field, &value, &mask, match, &error);
+
+        match_add_ethernet_prereq(match, sf.field);
     }
     return error;
 }
@@ -416,6 +419,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
             if (p->nw_proto) {
                 match_set_nw_proto(&fm->match, p->nw_proto);
             }
+            match_set_default_packet_type(&fm->match);
         } else if (!strcmp(name, "eth")) {
             match_set_packet_type(&fm->match, htonl(PT_ETH));
         } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) {
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 941000c24745..344b66a30d0c 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -161,6 +161,19 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
     ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc);
     memset(&match->tun_md, 0, sizeof match->tun_md);
 
+    /* If any fields, except in_port, are matched, then we also need to match
+     * on the Ethernet packet_type. */
+    const uint32_t ofpfw_data_bits = (OFPFW10_NW_TOS | OFPFW10_NW_PROTO
+                                      | OFPFW10_TP_SRC | OFPFW10_TP_DST
+                                      | OFPFW10_DL_SRC | OFPFW10_DL_DST
+                                      | OFPFW10_DL_TYPE
+                                      | OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP);
+    if ((ofpfw & ofpfw_data_bits) != ofpfw_data_bits
+        || ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_SRC_SHIFT)
+        || ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_DST_SHIFT)) {
+        match_set_default_packet_type(match);
+    }
+
     /* Initialize most of match->flow. */
     match->flow.nw_src = ofmatch->nw_src;
     match->flow.nw_dst = ofmatch->nw_dst;
@@ -328,6 +341,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
     bool ipv4, arp, rarp;
 
     match_init_catchall(match);
+    match->flow.tunnel.metadata.tab = NULL;
 
     if (!(wc & OFPFW11_IN_PORT)) {
         ofp_port_t ofp_port;
@@ -340,10 +354,13 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
         match_set_in_port(match, ofp_port);
     }
 
-    match_set_dl_src_masked(match, ofmatch->dl_src,
-                            eth_addr_invert(ofmatch->dl_src_mask));
-    match_set_dl_dst_masked(match, ofmatch->dl_dst,
-                            eth_addr_invert(ofmatch->dl_dst_mask));
+    struct eth_addr dl_src_mask = eth_addr_invert(ofmatch->dl_src_mask);
+    struct eth_addr dl_dst_mask = eth_addr_invert(ofmatch->dl_dst_mask);
+    if (!eth_addr_is_zero(dl_src_mask) || !eth_addr_is_zero(dl_dst_mask)) {
+        match_set_dl_src_masked(match, ofmatch->dl_src, dl_src_mask);
+        match_set_dl_dst_masked(match, ofmatch->dl_dst, dl_dst_mask);
+        match_set_default_packet_type(match);
+    }
 
     if (!(wc & OFPFW11_DL_VLAN)) {
         if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
@@ -375,11 +392,13 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
                 }
             }
         }
+        match_set_default_packet_type(match);
     }
 
     if (!(wc & OFPFW11_DL_TYPE)) {
         match_set_dl_type(match,
                           ofputil_dl_type_from_openflow(ofmatch->dl_type));
+        match_set_default_packet_type(match);
     }
 
     ipv4 = match->flow.dl_type == htons(ETH_TYPE_IP);
@@ -7688,9 +7707,7 @@ ofputil_normalize_match__(struct match *match, bool may_log)
 
     /* Figure out what fields may be matched. */
     /* 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))) {
+    if (wc.masks.packet_type == 0 || match_has_default_packet_type(match)) {
         may_match = MAY_ETHER;
         dl_type = match->flow.dl_type;
     } else if (wc.masks.packet_type == OVS_BE32_MAX &&
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 149b47cca9a9..d18d58c87c20 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -108,7 +108,7 @@ struct xbridge {
 
     bool has_in_band;             /* Bridge has in band control? */
     bool forward_bpdu;            /* Bridge forwards STP BPDUs? */
-    bool packet_type_aware;       /* Bridge is packet-type-aware? */
+    bool legacy_l3_pipeline;      /* Treat L3 tunnel packets as L2? */
 
     /* Datapath feature support. */
     struct dpif_backer_support support;
@@ -556,7 +556,7 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
                               const struct dpif_ipfix *,
                               const struct netflow *,
                               bool forward_bpdu, bool has_in_band,
-                              bool packet_type_aware,
+                              bool legacy_l3_pipeline,
                               const struct dpif_backer_support *);
 static void xlate_xbundle_set(struct xbundle *xbundle,
                               enum port_vlan_mode vlan_mode,
@@ -817,7 +817,7 @@ xlate_xbridge_set(struct xbridge *xbridge,
                   const struct dpif_ipfix *ipfix,
                   const struct netflow *netflow,
                   bool forward_bpdu, bool has_in_band,
-                  bool packet_type_aware,
+                  bool legacy_l3_pipeline,
                   const struct dpif_backer_support *support)
 {
     if (xbridge->ml != ml) {
@@ -863,7 +863,7 @@ xlate_xbridge_set(struct xbridge *xbridge,
     xbridge->dpif = dpif;
     xbridge->forward_bpdu = forward_bpdu;
     xbridge->has_in_band = has_in_band;
-    xbridge->packet_type_aware = packet_type_aware;
+    xbridge->legacy_l3_pipeline = legacy_l3_pipeline;
     xbridge->support = *support;
 }
 
@@ -954,7 +954,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
                       xbridge->rstp, xbridge->ms, xbridge->mbridge,
                       xbridge->sflow, xbridge->ipfix, xbridge->netflow,
                       xbridge->forward_bpdu, xbridge->has_in_band,
-                      xbridge->packet_type_aware, &xbridge->support);
+                      xbridge->legacy_l3_pipeline, &xbridge->support);
     LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
         xlate_xbundle_copy(new_xbridge, xbundle);
     }
@@ -1106,7 +1106,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
                   const struct dpif_ipfix *ipfix,
                   const struct netflow *netflow,
                   bool forward_bpdu, bool has_in_band,
-                  bool packet_type_aware,
+                  bool legacy_l3_pipeline,
                   const struct dpif_backer_support *support)
 {
     struct xbridge *xbridge;
@@ -1125,7 +1125,8 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
     xbridge->name = xstrdup(name);
 
     xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
-                      netflow, forward_bpdu, has_in_band, packet_type_aware, support);
+                      netflow, forward_bpdu, has_in_band, legacy_l3_pipeline,
+                      support);
 }
 
 static void
@@ -3356,9 +3357,9 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
         return;
     }
 
-    /* In a bridge that is not packet type-aware, convert the packet to what
-     * the output port expects. */
-    if (!ctx->xbridge->packet_type_aware) {
+    /* Honor the legacy_l3_pipeline setting, by treating an L3 packet as
+     * L2.  */
+    if (!ctx->xbridge->legacy_l3_pipeline) {
         if (flow->packet_type == htonl(PT_ETH) && xport->is_layer3 ) {
             /* Ethernet packet to L3 outport -> pop Ethernet header. */
             flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 69eda23c5a37..6b9c6504e894 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -166,7 +166,7 @@ void xlate_ofproto_set(struct ofproto_dpif *, const char *name, struct dpif *,
                        const struct mbridge *, const struct dpif_sflow *,
                        const struct dpif_ipfix *, const struct netflow *,
                        bool forward_bpdu, bool has_in_band,
-                       bool packet_type_aware,
+                       bool legacy_l3_pipeline,
                        const struct dpif_backer_support *support);
 void xlate_remove_ofproto(struct ofproto_dpif *);
 
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 098db18c7ffa..81c8576efce0 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -446,7 +446,7 @@ type_run(const char *type)
                               ofproto->netflow,
                               ofproto->up.forward_bpdu,
                               connmgr_has_in_band(ofproto->up.connmgr),
-                              ofproto->up.packet_type_aware,
+                              ofproto->up.legacy_l3_pipeline,
                               &ofproto->backer->support);
 
             HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
@@ -1793,7 +1793,7 @@ set_tables_version(struct ofproto *ofproto_, ovs_version_t version)
 }
 
 static void
-packet_type_aware_changed(struct ofproto *ofproto_)
+legacy_l3_pipeline_changed(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 
@@ -5652,7 +5652,7 @@ const struct ofproto_class ofproto_dpif_class = {
     flush,
     query_tables,
     set_tables_version,
-    packet_type_aware_changed,
+    legacy_l3_pipeline_changed,
     port_alloc,
     port_construct,
     port_destruct,
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 749f61b34f8b..7ff2d28a5306 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -85,7 +85,7 @@ struct ofproto {
     char *serial_desc;          /* Serial number (NULL for default). */
     char *dp_desc;              /* Datapath description (NULL for default). */
     enum ofputil_frag_handling frag_handling;
-    bool packet_type_aware;     /* Supports packet-type aware pipeline */
+    bool legacy_l3_pipeline;    /* Treat L3 tunnel packets as L2? */
 
     /* Datapath. */
     struct hmap ports;          /* Contains "struct ofport"s. */
@@ -933,8 +933,8 @@ struct ofproto_class {
      * can be triggered. */
     void (*set_tables_version)(struct ofproto *ofproto, ovs_version_t version);
 
-    /* Called when 'ofproto->packet_type_aware' has changed. */
-    void (*packet_type_aware_changed)(struct ofproto *ofproto);
+    /* Called when 'ofproto->legacy_l3_pipeline' has changed. */
+    void (*legacy_l3_pipeline_changed)(struct ofproto *ofproto);
 
 /* ## ---------------- ## */
 /* ## ofport Functions ## */
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 11a26af9f1bb..0f9d99b50cac 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -785,12 +785,12 @@ ofproto_set_dp_desc(struct ofproto *p, const char *dp_desc)
 }
 
 void
-ofproto_set_packet_type_aware(struct ofproto *p, bool pta)
+ofproto_set_legacy_l3_pipeline(struct ofproto *p, bool llp)
 {
-    if (pta != p->packet_type_aware) {
-        p->packet_type_aware = pta;
-        if (p->ofproto_class->packet_type_aware_changed) {
-            p->ofproto_class->packet_type_aware_changed(p);
+    if (llp != p->legacy_l3_pipeline) {
+        p->legacy_l3_pipeline = llp;
+        if (p->ofproto_class->legacy_l3_pipeline_changed) {
+            p->ofproto_class->legacy_l3_pipeline_changed(p);
         }
     }
 }
@@ -3219,8 +3219,8 @@ query_tables(struct ofproto *ofproto,
              struct ofputil_table_stats **statsp)
 {
     struct mf_bitmap rw_fields = oxm_writable_fields();
-    struct mf_bitmap match = oxm_matchable_fields(ofproto->packet_type_aware);
-    struct mf_bitmap mask = oxm_maskable_fields(ofproto->packet_type_aware);
+    struct mf_bitmap match = oxm_matchable_fields();
+    struct mf_bitmap mask = oxm_maskable_fields();
 
     struct ofputil_table_features *features;
     struct ofputil_table_stats *stats;
@@ -4762,19 +4762,6 @@ add_flow_init(struct ofproto *ofproto, struct ofproto_flow_mod *ofm,
         return OFPERR_OFPBRC_EPERM;
     }
 
-    if (ofproto->packet_type_aware) {
-        if (!match.wc.masks.packet_type) {
-            /* Add default match on packet_type Ethernet.*/
-            match.flow.packet_type = htonl(PT_ETH);
-            match.wc.masks.packet_type = OVS_BE32_MAX;
-        }
-    } else {
-        if (match.wc.masks.packet_type) {
-            /* Match on packet_type not allowed. */
-            return OFPERR_OFPBMC_BAD_TYPE;
-        }
-    }
-
     if (!ofm->temp_rule) {
         cls_rule_init(&cr, &match, fm->priority);
 
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 02f81ccf06c6..c93b5103d088 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -325,7 +325,7 @@ void ofproto_set_threads(int n_handlers, int n_revalidators);
 void ofproto_type_set_config(const char *type,
                              const struct smap *other_config);
 void ofproto_set_dp_desc(struct ofproto *, const char *dp_desc);
-void ofproto_set_packet_type_aware(struct ofproto *p, bool pta);
+void ofproto_set_legacy_l3_pipeline(struct ofproto *p, bool llp);
 int ofproto_set_snoops(struct ofproto *, const struct sset *snoops);
 int ofproto_set_netflow(struct ofproto *,
                         const struct netflow_options *nf_options);
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 76a33b6902aa..25aa00aff4a2 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/ovs-ofctl.at b/tests/ovs-ofctl.at
index 52eaf0320cd5..6d2ae1d26bea 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -2107,7 +2107,7 @@ OXM_OF_VLAN_VID_W(0123/1123)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 OXM_OF_VLAN_VID(1123)
 OXM_OF_VLAN_VID(1123)
-<any>
+OXM_OF_PACKET_TYPE(00000000)
 OXM_OF_VLAN_VID_W(1103/1f0f)
 OXM_OF_VLAN_VID_W(1103/1f0f), OXM_OF_VLAN_PCP(01)
 OXM_OF_VLAN_VID_W(1000/1000)
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index ac9550f37fc7..da533ccb5bde 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -254,7 +254,7 @@ static void bridge_configure_ipfix(struct bridge *);
 static void bridge_configure_spanning_tree(struct bridge *);
 static void bridge_configure_tables(struct bridge *);
 static void bridge_configure_dp_desc(struct bridge *);
-static void bridge_configure_packet_type_aware(struct bridge *);
+static void bridge_configure_legacy_l3_pipeline(struct bridge *);
 static void bridge_configure_aa(struct bridge *);
 static void bridge_aa_refresh_queued(struct bridge *);
 static bool bridge_aa_need_refresh(struct bridge *);
@@ -709,7 +709,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
         bridge_configure_tables(br);
         bridge_configure_dp_desc(br);
         bridge_configure_aa(br);
-        bridge_configure_packet_type_aware(br);
+        bridge_configure_legacy_l3_pipeline(br);
     }
     free(managers);
 
@@ -3804,10 +3804,11 @@ bridge_configure_dp_desc(struct bridge *br)
 }
 
 static void
-bridge_configure_packet_type_aware(struct bridge *br)
+bridge_configure_legacy_l3_pipeline(struct bridge *br)
 {
-    ofproto_set_packet_type_aware(br->ofproto,
-            smap_get_bool(&br->cfg->other_config, "packet-type-aware", false));
+    bool legacy_l3_pipeline = smap_get_bool(&br->cfg->other_config,
+                                            "legacy-l3-pipeline", true);
+    ofproto_set_legacy_l3_pipeline(br->ofproto, !legacy_l3_pipeline);
 }
 
 static struct aa_mapping *
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 299c6817c283..dc97d75dc113 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -855,12 +855,22 @@
         ID, the default queue is used instead.
       </column>
 
-      <column name="other_config" key="packet-type-aware"
+      <column name="other_config" key="legacy-l3-tunnel"
               type='{"type": "boolean"}'>
-        If set to <code>true</code>, the bridge supports the packet type
-        awareness feature introduced in OpenFlow version 1.5. The bridge can
-        switch Ethernet and non-Ethernet packets and a controller must consider
-        the type and match on the packet_type match field as appropriate.
+        <p>
+          If set to <code>true</code>, the Open vSwitch pipeline imitates the
+          legacy behavior from Open vSwitch 2.7 and earlier that treats packets
+          received on L3 tunnels as Ethernet packets, by synthesizing an
+          Ethernet header whose Ethertype is the packet's type and all-zero
+          source and destination.  If set to <code>false</code>, the pipeline
+          handles L3 tunnel packets using the OpenFlow 1.5+
+          <code>packet_type</code> field.
+        </p>
+
+        <p>
+          The default in Open vSwitch 2.8 is <code>true</code>.  Later versions
+          of Open vSwitch may default to <code>false</code>.
+        </p>
       </column>
 
       <column name="protocols">
