Skip to content

Commit

Permalink
net: Account for all vlan headers in skb_mac_gso_segment
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Vlad Yasevich authored and davem330 committed Mar 28, 2014
1 parent 898602a commit 53d6471
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 6 deletions.
2 changes: 1 addition & 1 deletion include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -3014,7 +3014,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features)
{
return __skb_gso_segment(skb, features, true);
}
__be16 skb_network_protocol(struct sk_buff *skb);
__be16 skb_network_protocol(struct sk_buff *skb, int *depth);

static inline bool can_checksum_protocol(netdev_features_t features,
__be16 protocol)
Expand Down
13 changes: 9 additions & 4 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2286,7 +2286,7 @@ int skb_checksum_help(struct sk_buff *skb)
}
EXPORT_SYMBOL(skb_checksum_help);

__be16 skb_network_protocol(struct sk_buff *skb)
__be16 skb_network_protocol(struct sk_buff *skb, int *depth)
{
__be16 type = skb->protocol;
int vlan_depth = ETH_HLEN;
Expand All @@ -2313,6 +2313,8 @@ __be16 skb_network_protocol(struct sk_buff *skb)
vlan_depth += VLAN_HLEN;
}

*depth = vlan_depth;

return type;
}

Expand All @@ -2326,12 +2328,13 @@ struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
{
struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
struct packet_offload *ptype;
__be16 type = skb_network_protocol(skb);
int vlan_depth = skb->mac_len;
__be16 type = skb_network_protocol(skb, &vlan_depth);

if (unlikely(!type))
return ERR_PTR(-EINVAL);

__skb_pull(skb, skb->mac_len);
__skb_pull(skb, vlan_depth);

rcu_read_lock();
list_for_each_entry_rcu(ptype, &offload_base, list) {
Expand Down Expand Up @@ -2498,8 +2501,10 @@ static netdev_features_t harmonize_features(struct sk_buff *skb,
const struct net_device *dev,
netdev_features_t features)
{
int tmp;

if (skb->ip_summed != CHECKSUM_NONE &&
!can_checksum_protocol(features, skb_network_protocol(skb))) {
!can_checksum_protocol(features, skb_network_protocol(skb, &tmp))) {
features &= ~NETIF_F_ALL_CSUM;
} else if (illegal_highdma(dev, skb)) {
features &= ~NETIF_F_SG;
Expand Down
3 changes: 2 additions & 1 deletion net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -2879,8 +2879,9 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
int err = -ENOMEM;
int i = 0;
int pos;
int dummy;

proto = skb_network_protocol(head_skb);
proto = skb_network_protocol(head_skb, &dummy);
if (unlikely(!proto))
return ERR_PTR(-EINVAL);

Expand Down

0 comments on commit 53d6471

Please sign in to comment.