Commit d4bcef3f authored by Toshiaki Makita's avatar Toshiaki Makita Committed by David S. Miller

net: Fix vlan_get_protocol for stacked vlan

vlan_get_protocol() could not get network protocol if a skb has a 802.1ad
vlan tag or multiple vlans, which caused incorrect checksum calculation
in several drivers.

Fix vlan_get_protocol() to retrieve network protocol instead of incorrect
vlan protocol.

As the logic is the same as skb_network_protocol(), create a common helper
function __vlan_get_protocol() and call it from existing functions.
Signed-off-by: default avatarToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cfbf654e
...@@ -472,27 +472,59 @@ static inline int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) ...@@ -472,27 +472,59 @@ static inline int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
/** /**
* vlan_get_protocol - get protocol EtherType. * vlan_get_protocol - get protocol EtherType.
* @skb: skbuff to query * @skb: skbuff to query
* @type: first vlan protocol
* @depth: buffer to store length of eth and vlan tags in bytes
* *
* Returns the EtherType of the packet, regardless of whether it is * Returns the EtherType of the packet, regardless of whether it is
* vlan encapsulated (normal or hardware accelerated) or not. * vlan encapsulated (normal or hardware accelerated) or not.
*/ */
static inline __be16 vlan_get_protocol(const struct sk_buff *skb) static inline __be16 __vlan_get_protocol(struct sk_buff *skb, __be16 type,
int *depth)
{ {
__be16 protocol = 0; unsigned int vlan_depth = skb->mac_len;
if (vlan_tx_tag_present(skb) || /* if type is 802.1Q/AD then the header should already be
skb->protocol != cpu_to_be16(ETH_P_8021Q)) * present at mac_len - VLAN_HLEN (if mac_len > 0), or at
protocol = skb->protocol; * ETH_HLEN otherwise
else { */
__be16 proto, *protop; if (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
protop = skb_header_pointer(skb, offsetof(struct vlan_ethhdr, if (vlan_depth) {
h_vlan_encapsulated_proto), if (WARN_ON(vlan_depth < VLAN_HLEN))
sizeof(proto), &proto); return 0;
if (likely(protop)) vlan_depth -= VLAN_HLEN;
protocol = *protop; } else {
vlan_depth = ETH_HLEN;
}
do {
struct vlan_hdr *vh;
if (unlikely(!pskb_may_pull(skb,
vlan_depth + VLAN_HLEN)))
return 0;
vh = (struct vlan_hdr *)(skb->data + vlan_depth);
type = vh->h_vlan_encapsulated_proto;
vlan_depth += VLAN_HLEN;
} while (type == htons(ETH_P_8021Q) ||
type == htons(ETH_P_8021AD));
} }
return protocol; if (depth)
*depth = vlan_depth;
return type;
}
/**
* vlan_get_protocol - get protocol EtherType.
* @skb: skbuff to query
*
* 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(struct sk_buff *skb)
{
return __vlan_get_protocol(skb, skb->protocol, NULL);
} }
static inline void vlan_set_encap_proto(struct sk_buff *skb, static inline void vlan_set_encap_proto(struct sk_buff *skb,
......
...@@ -2352,7 +2352,6 @@ EXPORT_SYMBOL(skb_checksum_help); ...@@ -2352,7 +2352,6 @@ EXPORT_SYMBOL(skb_checksum_help);
__be16 skb_network_protocol(struct sk_buff *skb, int *depth) __be16 skb_network_protocol(struct sk_buff *skb, int *depth)
{ {
unsigned int vlan_depth = skb->mac_len;
__be16 type = skb->protocol; __be16 type = skb->protocol;
/* Tunnel gso handlers can set protocol to ethernet. */ /* Tunnel gso handlers can set protocol to ethernet. */
...@@ -2366,35 +2365,7 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth) ...@@ -2366,35 +2365,7 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth)
type = eth->h_proto; type = eth->h_proto;
} }
/* if skb->protocol is 802.1Q/AD then the header should already be return __vlan_get_protocol(skb, type, depth);
* present at mac_len - VLAN_HLEN (if mac_len > 0), or at
* ETH_HLEN otherwise
*/
if (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
if (vlan_depth) {
if (WARN_ON(vlan_depth < VLAN_HLEN))
return 0;
vlan_depth -= VLAN_HLEN;
} else {
vlan_depth = ETH_HLEN;
}
do {
struct vlan_hdr *vh;
if (unlikely(!pskb_may_pull(skb,
vlan_depth + VLAN_HLEN)))
return 0;
vh = (struct vlan_hdr *)(skb->data + vlan_depth);
type = vh->h_vlan_encapsulated_proto;
vlan_depth += VLAN_HLEN;
} while (type == htons(ETH_P_8021Q) ||
type == htons(ETH_P_8021AD));
}
*depth = vlan_depth;
return type;
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment