summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/core/skbuff.c36
-rw-r--r--net/packet/af_packet.c5
2 files changed, 39 insertions, 2 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 648c20e19038..45809986dfe1 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -7444,3 +7444,39 @@ void __put_netmem(netmem_ref netmem)
net_devmem_put_net_iov(netmem_to_net_iov(netmem));
}
EXPORT_SYMBOL(__put_netmem);
+
+struct vlan_type_depth __vlan_get_protocol_offset(const struct sk_buff *skb,
+ __be16 type,
+ int mac_offset)
+{
+ unsigned int vlan_depth = skb->mac_len, parse_depth = VLAN_MAX_DEPTH;
+
+ /* if type is 802.1Q/AD then the header should already be
+ * present at mac_len - VLAN_HLEN (if mac_len > 0), or at
+ * ETH_HLEN otherwise
+ */
+ if (vlan_depth) {
+ if (WARN_ON_ONCE(vlan_depth < VLAN_HLEN))
+ return (struct vlan_type_depth) { 0 };
+ vlan_depth -= VLAN_HLEN;
+ } else {
+ vlan_depth = ETH_HLEN;
+ }
+ do {
+ struct vlan_hdr vhdr, *vh;
+
+ vh = skb_header_pointer(skb, mac_offset + vlan_depth,
+ sizeof(vhdr), &vhdr);
+ if (unlikely(!vh || !--parse_depth))
+ return (struct vlan_type_depth) { 0 };
+
+ type = vh->h_vlan_encapsulated_proto;
+ vlan_depth += VLAN_HLEN;
+ } while (eth_type_vlan(type));
+
+ return (struct vlan_type_depth) {
+ .type = type,
+ .depth = vlan_depth
+ };
+}
+EXPORT_SYMBOL(__vlan_get_protocol_offset);
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 494d628d10a5..a1005359085a 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -572,8 +572,9 @@ static __be16 vlan_get_protocol_dgram(const struct sk_buff *skb)
__be16 proto = skb->protocol;
if (unlikely(eth_type_vlan(proto)))
- proto = __vlan_get_protocol_offset(skb, proto,
- skb_mac_offset(skb), NULL);
+ proto = vlan_get_protocol_offset_inline(skb, proto,
+ skb_mac_offset(skb),
+ NULL);
return proto;
}