Commit 7a7ffbab authored by Wei-Chun Chao's avatar Wei-Chun Chao Committed by David S. Miller

ipv4: fix tunneled VM traffic over hw VXLAN/GRE GSO NIC

VM to VM GSO traffic is broken if it goes through VXLAN or GRE
tunnel and the physical NIC on the host supports hardware VXLAN/GRE
GSO offload (e.g. bnx2x and next-gen mlx4).

Two issues -
(VXLAN) VM traffic has SKB_GSO_DODGY and SKB_GSO_UDP_TUNNEL with
SKB_GSO_TCP/UDP set depending on the inner protocol. GSO header
integrity check fails in udp4_ufo_fragment if inner protocol is
TCP. Also gso_segs is calculated incorrectly using skb->len that
includes tunnel header. Fix: robust check should only be applied
to the inner packet.

(VXLAN & GRE) Once GSO header integrity check passes, NULL segs
is returned and the original skb is sent to hardware. However the
tunnel header is already pulled. Fix: tunnel header needs to be
restored so that hardware can perform GSO properly on the original
packet.
Signed-off-by: default avatarWei-Chun Chao <weichunc@plumgrid.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 619a60ee
...@@ -3017,6 +3017,19 @@ static inline void netif_set_gso_max_size(struct net_device *dev, ...@@ -3017,6 +3017,19 @@ static inline void netif_set_gso_max_size(struct net_device *dev,
dev->gso_max_size = size; dev->gso_max_size = size;
} }
static inline void skb_gso_error_unwind(struct sk_buff *skb, __be16 protocol,
int pulled_hlen, u16 mac_offset,
int mac_len)
{
skb->protocol = protocol;
skb->encapsulation = 1;
skb_push(skb, pulled_hlen);
skb_reset_transport_header(skb);
skb->mac_header = mac_offset;
skb->network_header = skb->mac_header + mac_len;
skb->mac_len = mac_len;
}
static inline bool netif_is_macvlan(struct net_device *dev) static inline bool netif_is_macvlan(struct net_device *dev)
{ {
return dev->priv_flags & IFF_MACVLAN; return dev->priv_flags & IFF_MACVLAN;
......
...@@ -28,6 +28,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, ...@@ -28,6 +28,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
netdev_features_t enc_features; netdev_features_t enc_features;
int ghl = GRE_HEADER_SECTION; int ghl = GRE_HEADER_SECTION;
struct gre_base_hdr *greh; struct gre_base_hdr *greh;
u16 mac_offset = skb->mac_header;
int mac_len = skb->mac_len; int mac_len = skb->mac_len;
__be16 protocol = skb->protocol; __be16 protocol = skb->protocol;
int tnl_hlen; int tnl_hlen;
...@@ -58,13 +59,13 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, ...@@ -58,13 +59,13 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
} else } else
csum = false; csum = false;
if (unlikely(!pskb_may_pull(skb, ghl)))
goto out;
/* setup inner skb. */ /* setup inner skb. */
skb->protocol = greh->protocol; skb->protocol = greh->protocol;
skb->encapsulation = 0; skb->encapsulation = 0;
if (unlikely(!pskb_may_pull(skb, ghl)))
goto out;
__skb_pull(skb, ghl); __skb_pull(skb, ghl);
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
skb_set_network_header(skb, skb_inner_network_offset(skb)); skb_set_network_header(skb, skb_inner_network_offset(skb));
...@@ -73,8 +74,10 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, ...@@ -73,8 +74,10 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
/* segment inner packet. */ /* segment inner packet. */
enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
segs = skb_mac_gso_segment(skb, enc_features); segs = skb_mac_gso_segment(skb, enc_features);
if (!segs || IS_ERR(segs)) if (!segs || IS_ERR(segs)) {
skb_gso_error_unwind(skb, protocol, ghl, mac_offset, mac_len);
goto out; goto out;
}
skb = segs; skb = segs;
tnl_hlen = skb_tnl_header_len(skb); tnl_hlen = skb_tnl_header_len(skb);
......
...@@ -2478,6 +2478,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, ...@@ -2478,6 +2478,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features) netdev_features_t features)
{ {
struct sk_buff *segs = ERR_PTR(-EINVAL); struct sk_buff *segs = ERR_PTR(-EINVAL);
u16 mac_offset = skb->mac_header;
int mac_len = skb->mac_len; int mac_len = skb->mac_len;
int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
__be16 protocol = skb->protocol; __be16 protocol = skb->protocol;
...@@ -2497,8 +2498,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, ...@@ -2497,8 +2498,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
/* segment inner packet. */ /* segment inner packet. */
enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
segs = skb_mac_gso_segment(skb, enc_features); segs = skb_mac_gso_segment(skb, enc_features);
if (!segs || IS_ERR(segs)) if (!segs || IS_ERR(segs)) {
skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset,
mac_len);
goto out; goto out;
}
outer_hlen = skb_tnl_header_len(skb); outer_hlen = skb_tnl_header_len(skb);
skb = segs; skb = segs;
......
...@@ -41,6 +41,14 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, ...@@ -41,6 +41,14 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
{ {
struct sk_buff *segs = ERR_PTR(-EINVAL); struct sk_buff *segs = ERR_PTR(-EINVAL);
unsigned int mss; unsigned int mss;
int offset;
__wsum csum;
if (skb->encapsulation &&
skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) {
segs = skb_udp_tunnel_segment(skb, features);
goto out;
}
mss = skb_shinfo(skb)->gso_size; mss = skb_shinfo(skb)->gso_size;
if (unlikely(skb->len <= mss)) if (unlikely(skb->len <= mss))
...@@ -63,15 +71,6 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, ...@@ -63,15 +71,6 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
goto out; goto out;
} }
/* Fragment the skb. IP headers of the fragments are updated in
* inet_gso_segment()
*/
if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
segs = skb_udp_tunnel_segment(skb, features);
else {
int offset;
__wsum csum;
/* Do software UFO. Complete and fill in the UDP checksum as /* Do software UFO. Complete and fill in the UDP checksum as
* HW cannot do checksum of UDP packets sent as multiple * HW cannot do checksum of UDP packets sent as multiple
* IP fragments. * IP fragments.
...@@ -82,8 +81,10 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, ...@@ -82,8 +81,10 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
*(__sum16 *)(skb->data + offset) = csum_fold(csum); *(__sum16 *)(skb->data + offset) = csum_fold(csum);
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
/* Fragment the skb. IP headers of the fragments are updated in
* inet_gso_segment()
*/
segs = skb_segment(skb, features); segs = skb_segment(skb, features);
}
out: out:
return segs; return segs;
} }
......
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