* vlan_get_protocol - get protocol EtherType.
  * @skb: skbuff to query
  * @type: first vlan protocol
+ * @mac_offset: MAC offset
  * @depth: buffer to store length of eth and vlan tags in bytes
  *
  * Returns the EtherType of the packet, regardless of whether it is
  * vlan encapsulated (normal or hardware accelerated) or not.
  */
-static inline __be16 __vlan_get_protocol(const struct sk_buff *skb, __be16 type,
-                                        int *depth)
+static inline __be16 __vlan_get_protocol_offset(const struct sk_buff *skb,
+                                               __be16 type,
+                                               int mac_offset,
+                                               int *depth)
 {
        unsigned int vlan_depth = skb->mac_len, parse_depth = VLAN_MAX_DEPTH;
 
                do {
                        struct vlan_hdr vhdr, *vh;
 
-                       vh = skb_header_pointer(skb, vlan_depth, sizeof(vhdr), &vhdr);
+                       vh = skb_header_pointer(skb, mac_offset + vlan_depth,
+                                               sizeof(vhdr), &vhdr);
                        if (unlikely(!vh || !--parse_depth))
                                return 0;
 
        return type;
 }
 
+static inline __be16 __vlan_get_protocol(const struct sk_buff *skb, __be16 type,
+                                        int *depth)
+{
+       return __vlan_get_protocol_offset(skb, type, 0, depth);
+}
+
 /**
  * vlan_get_protocol - get protocol EtherType.
  * @skb: skbuff to query
 
        return ntohs(vh->h_vlan_TCI);
 }
 
-static __be16 vlan_get_protocol_dgram(struct sk_buff *skb)
+static __be16 vlan_get_protocol_dgram(const struct sk_buff *skb)
 {
        __be16 proto = skb->protocol;
 
-       if (unlikely(eth_type_vlan(proto))) {
-               u8 *skb_orig_data = skb->data;
-               int skb_orig_len = skb->len;
-
-               skb_push(skb, skb->data - skb_mac_header(skb));
-               proto = __vlan_get_protocol(skb, proto, NULL);
-               if (skb_orig_data != skb->data) {
-                       skb->data = skb_orig_data;
-                       skb->len = skb_orig_len;
-               }
-       }
+       if (unlikely(eth_type_vlan(proto)))
+               proto = __vlan_get_protocol_offset(skb, proto,
+                                                  skb_mac_offset(skb), NULL);
 
        return proto;
 }