diff options
Diffstat (limited to 'net/core')
| -rw-r--r-- | net/core/skbuff.c | 36 |
1 files changed, 36 insertions, 0 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); |
