static void invalidate_flow_key(struct sw_flow_key *key)
 {
-       key->eth.type = htons(0);
+       key->mac_proto |= SW_FLOW_KEY_INVALID;
 }
 
 static bool is_flow_key_valid(const struct sw_flow_key *key)
 {
-       return !!key->eth.type;
+       return !(key->mac_proto & SW_FLOW_KEY_INVALID);
 }
 
 static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr,
                        ovs_vport_send(vport, skb);
                } else if (mru <= vport->dev->mtu) {
                        struct net *net = read_pnet(&dp->net);
-                       __be16 ethertype = key->eth.type;
 
-                       if (!is_flow_key_valid(key)) {
-                               if (eth_p_mpls(skb->protocol))
-                                       ethertype = skb->inner_protocol;
-                               else
-                                       ethertype = vlan_get_protocol(skb);
-                       }
-
-                       ovs_fragment(net, vport, skb, mru, ethertype);
+                       ovs_fragment(net, vport, skb, mru, key->eth.type);
                } else {
                        kfree_skb(skb);
                }
 
        key->phy.skb_mark = skb->mark;
        ovs_ct_fill_key(skb, key);
        key->ovs_flow_hash = 0;
+       key->mac_proto = MAC_PROTO_ETHERNET;
        key->recirc_id = 0;
 
        return key_extract(skb, key);
 
 
 struct sk_buff;
 
+enum sw_flow_mac_proto {
+       MAC_PROTO_NONE = 0,
+       MAC_PROTO_ETHERNET,
+};
+#define SW_FLOW_KEY_INVALID    0x80
+
 /* Store options at the end of the array if they are less than the
  * maximum size. This allows us to get the benefits of variable length
  * matching for small options.
                u32     skb_mark;       /* SKB mark. */
                u16     in_port;        /* Input switch port (or DP_MAX_PORTS). */
        } __packed phy; /* Safe when right after 'tun_key'. */
+       u8 mac_proto;                   /* MAC layer protocol (e.g. Ethernet). */
        u8 tun_proto;                   /* Protocol of encapsulating tunnel. */
        u32 ovs_flow_hash;              /* Datapath computed hash value.  */
        u32 recirc_id;                  /* Recirculation ID.  */
        unsigned char       ar_tip[4];          /* target IP address        */
 } __packed;
 
+static inline u8 ovs_key_mac_proto(const struct sw_flow_key *key)
+{
+       return key->mac_proto & ~SW_FLOW_KEY_INVALID;
+}
+
+static inline u16 __ovs_mac_header_len(u8 mac_proto)
+{
+       return mac_proto == MAC_PROTO_ETHERNET ? ETH_HLEN : 0;
+}
+
+static inline u16 ovs_mac_header_len(const struct sw_flow_key *key)
+{
+       return __ovs_mac_header_len(ovs_key_mac_proto(key));
+}
+
 static inline bool ovs_identifier_is_ufid(const struct sw_flow_id *sfid)
 {
        return sfid->ufid_len;
 
                                   sizeof(*cl), is_mask);
                *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABELS);
        }
+
+       /* Always exact match mac_proto */
+       SW_FLOW_KEY_PUT(match, mac_proto, is_mask ? 0xff : MAC_PROTO_ETHERNET,
+                       is_mask);
+
        return 0;
 }