net: Account for all vlan headers in skb_mac_gso_segment

skb_network_protocol() already accounts for multiple vlan
headers that may be present in the skb. However, skb_mac_gso_segment()
doesn't know anything about it and assumes that skb->mac_len
is set correctly to skip all mac headers. That may not
always be the case. If we are simply forwarding the packet (via
bridge or macvtap), all vlan headers may not be accounted for.

A simple solution is to allow skb_network_protocol to return
the vlan depth it has calculated. This way skb_mac_gso_segment
will correctly skip all mac headers.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Vlad Yasevich and committed by David S. Miller 53d6471c 898602a0

Changed files
+12 -6
include
linux
net
+1 -1
include/linux/netdevice.h
··· 3014 3014 { 3015 3015 return __skb_gso_segment(skb, features, true); 3016 3016 } 3017 - __be16 skb_network_protocol(struct sk_buff *skb); 3017 + __be16 skb_network_protocol(struct sk_buff *skb, int *depth); 3018 3018 3019 3019 static inline bool can_checksum_protocol(netdev_features_t features, 3020 3020 __be16 protocol)
+9 -4
net/core/dev.c
··· 2286 2286 } 2287 2287 EXPORT_SYMBOL(skb_checksum_help); 2288 2288 2289 - __be16 skb_network_protocol(struct sk_buff *skb) 2289 + __be16 skb_network_protocol(struct sk_buff *skb, int *depth) 2290 2290 { 2291 2291 __be16 type = skb->protocol; 2292 2292 int vlan_depth = ETH_HLEN; ··· 2313 2313 vlan_depth += VLAN_HLEN; 2314 2314 } 2315 2315 2316 + *depth = vlan_depth; 2317 + 2316 2318 return type; 2317 2319 } 2318 2320 ··· 2328 2326 { 2329 2327 struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); 2330 2328 struct packet_offload *ptype; 2331 - __be16 type = skb_network_protocol(skb); 2329 + int vlan_depth = skb->mac_len; 2330 + __be16 type = skb_network_protocol(skb, &vlan_depth); 2332 2331 2333 2332 if (unlikely(!type)) 2334 2333 return ERR_PTR(-EINVAL); 2335 2334 2336 - __skb_pull(skb, skb->mac_len); 2335 + __skb_pull(skb, vlan_depth); 2337 2336 2338 2337 rcu_read_lock(); 2339 2338 list_for_each_entry_rcu(ptype, &offload_base, list) { ··· 2501 2498 const struct net_device *dev, 2502 2499 netdev_features_t features) 2503 2500 { 2501 + int tmp; 2502 + 2504 2503 if (skb->ip_summed != CHECKSUM_NONE && 2505 - !can_checksum_protocol(features, skb_network_protocol(skb))) { 2504 + !can_checksum_protocol(features, skb_network_protocol(skb, &tmp))) { 2506 2505 features &= ~NETIF_F_ALL_CSUM; 2507 2506 } else if (illegal_highdma(dev, skb)) { 2508 2507 features &= ~NETIF_F_SG;
+2 -1
net/core/skbuff.c
··· 2879 2879 int err = -ENOMEM; 2880 2880 int i = 0; 2881 2881 int pos; 2882 + int dummy; 2882 2883 2883 - proto = skb_network_protocol(head_skb); 2884 + proto = skb_network_protocol(head_skb, &dummy); 2884 2885 if (unlikely(!proto)) 2885 2886 return ERR_PTR(-EINVAL); 2886 2887