Skip to content

Commit a09a4c8

Browse files
jessegrossdavem330
authored andcommitted
tunnels: Remove encapsulation offloads on decap.
If a packet is either locally encapsulated or processed through GRO it is marked with the offloads that it requires. However, when it is decapsulated these tunnel offload indications are not removed. This means that if we receive an encapsulated TCP packet, aggregate it with GRO, decapsulate, and retransmit the resulting frame on a NIC that does not support encapsulation, we won't be able to take advantage of hardware offloads even though it is just a simple TCP packet at this point. This fixes the problem by stripping off encapsulation offload indications when packets are decapsulated. The performance impacts of this bug are significant. In a test where a Geneve encapsulated TCP stream is sent to a hypervisor, GRO'ed, decapsulated, and bridged to a VM performance is improved by 60% (5Gbps->8Gbps) as a result of avoiding unnecessary segmentation at the VM tap interface. Reported-by: Ramu Ramamurthy <[email protected]> Fixes: 68c3316 ("v4 GRE: Add TCP segmentation offload for GRE") Signed-off-by: Jesse Gross <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent fac8e0f commit a09a4c8

File tree

4 files changed

+33
-5
lines changed

4 files changed

+33
-5
lines changed

include/net/ip_tunnels.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,22 @@ struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
305305

306306
struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, int gso_type_mask);
307307

308+
static inline int iptunnel_pull_offloads(struct sk_buff *skb)
309+
{
310+
if (skb_is_gso(skb)) {
311+
int err;
312+
313+
err = skb_unclone(skb, GFP_ATOMIC);
314+
if (unlikely(err))
315+
return err;
316+
skb_shinfo(skb)->gso_type &= ~(NETIF_F_GSO_ENCAP_ALL >>
317+
NETIF_F_GSO_SHIFT);
318+
}
319+
320+
skb->encapsulation = 0;
321+
return 0;
322+
}
323+
308324
static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len)
309325
{
310326
if (pkt_len > 0) {

net/ipv4/fou.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static inline struct fou *fou_from_sock(struct sock *sk)
4848
return sk->sk_user_data;
4949
}
5050

51-
static void fou_recv_pull(struct sk_buff *skb, size_t len)
51+
static int fou_recv_pull(struct sk_buff *skb, size_t len)
5252
{
5353
struct iphdr *iph = ip_hdr(skb);
5454

@@ -59,6 +59,7 @@ static void fou_recv_pull(struct sk_buff *skb, size_t len)
5959
__skb_pull(skb, len);
6060
skb_postpull_rcsum(skb, udp_hdr(skb), len);
6161
skb_reset_transport_header(skb);
62+
return iptunnel_pull_offloads(skb);
6263
}
6364

6465
static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
@@ -68,9 +69,14 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
6869
if (!fou)
6970
return 1;
7071

71-
fou_recv_pull(skb, sizeof(struct udphdr));
72+
if (fou_recv_pull(skb, sizeof(struct udphdr)))
73+
goto drop;
7274

7375
return -fou->protocol;
76+
77+
drop:
78+
kfree_skb(skb);
79+
return 0;
7480
}
7581

7682
static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
@@ -170,6 +176,9 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
170176
__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
171177
skb_reset_transport_header(skb);
172178

179+
if (iptunnel_pull_offloads(skb))
180+
goto drop;
181+
173182
return -guehdr->proto_ctype;
174183

175184
drop:

net/ipv4/ip_tunnel_core.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto,
114114
skb->vlan_tci = 0;
115115
skb_set_queue_mapping(skb, 0);
116116
skb_scrub_packet(skb, xnet);
117-
return 0;
117+
118+
return iptunnel_pull_offloads(skb);
118119
}
119120
EXPORT_SYMBOL_GPL(iptunnel_pull_header);
120121

net/ipv6/sit.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,14 +681,16 @@ static int ipip6_rcv(struct sk_buff *skb)
681681
skb->mac_header = skb->network_header;
682682
skb_reset_network_header(skb);
683683
IPCB(skb)->flags = 0;
684-
skb->protocol = htons(ETH_P_IPV6);
684+
skb->dev = tunnel->dev;
685685

686686
if (packet_is_spoofed(skb, iph, tunnel)) {
687687
tunnel->dev->stats.rx_errors++;
688688
goto out;
689689
}
690690

691-
__skb_tunnel_rx(skb, tunnel->dev, tunnel->net);
691+
if (iptunnel_pull_header(skb, 0, htons(ETH_P_IPV6),
692+
!net_eq(tunnel->net, dev_net(tunnel->dev))))
693+
goto out;
692694

693695
err = IP_ECN_decapsulate(iph, skb);
694696
if (unlikely(err)) {

0 commit comments

Comments
 (0)