aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2013-01-25 15:34:37 -0500
committerDavid S. Miller <davem@davemloft.net>2013-01-28 00:27:15 -0500
commitcef401de7be8c4e155c6746bfccf721a4fa5fab9 (patch)
treeaf082329bf0613ed9de5b9575e3f94f3f03b77ec /net/ipv4
parent61550022b9586972082904b80de26a464c558437 (diff)
net: fix possible wrong checksum generation
Pravin Shelar mentioned that GSO could potentially generate wrong TX checksum if skb has fragments that are overwritten by the user between the checksum computation and transmit. He suggested to linearize skbs but this extra copy can be avoided for normal tcp skbs cooked by tcp_sendmsg(). This patch introduces a new SKB_GSO_SHARED_FRAG flag, set in skb_shinfo(skb)->gso_type if at least one frag can be modified by the user. Typical sources of such possible overwrites are {vm}splice(), sendfile(), and macvtap/tun/virtio_net drivers. Tested: $ netperf -H 7.7.8.84 MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3959.52 $ netperf -H 7.7.8.84 -t TCP_SENDFILE TCP SENDFILE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3216.80 Performance of the SENDFILE is impacted by the extra allocation and copy, and because we use order-0 pages, while the TCP_STREAM uses bigger pages. Reported-by: Pravin Shelar <pshelar@nicira.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/af_inet.c1
-rw-r--r--net/ipv4/ip_gre.c4
-rw-r--r--net/ipv4/ipip.c4
-rw-r--r--net/ipv4/tcp.c3
-rw-r--r--net/ipv4/tcp_input.c4
-rw-r--r--net/ipv4/tcp_output.c4
6 files changed, 14 insertions, 6 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 4b7053919976..49ddca31c4da 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1306,6 +1306,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1306 SKB_GSO_UDP | 1306 SKB_GSO_UDP |
1307 SKB_GSO_DODGY | 1307 SKB_GSO_DODGY |
1308 SKB_GSO_TCP_ECN | 1308 SKB_GSO_TCP_ECN |
1309 SKB_GSO_SHARED_FRAG |
1309 0))) 1310 0)))
1310 goto out; 1311 goto out;
1311 1312
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 303012adf9e6..af6be70821c4 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -738,7 +738,7 @@ drop:
738static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) 738static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
739{ 739{
740 struct ip_tunnel *tunnel = netdev_priv(dev); 740 struct ip_tunnel *tunnel = netdev_priv(dev);
741 const struct iphdr *old_iph = ip_hdr(skb); 741 const struct iphdr *old_iph;
742 const struct iphdr *tiph; 742 const struct iphdr *tiph;
743 struct flowi4 fl4; 743 struct flowi4 fl4;
744 u8 tos; 744 u8 tos;
@@ -756,6 +756,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
756 skb_checksum_help(skb)) 756 skb_checksum_help(skb))
757 goto tx_error; 757 goto tx_error;
758 758
759 old_iph = ip_hdr(skb);
760
759 if (dev->type == ARPHRD_ETHER) 761 if (dev->type == ARPHRD_ETHER)
760 IPCB(skb)->flags = 0; 762 IPCB(skb)->flags = 0;
761 763
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 191fc24a745a..8f024d41eefa 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -472,7 +472,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
472 __be16 df = tiph->frag_off; 472 __be16 df = tiph->frag_off;
473 struct rtable *rt; /* Route to the other host */ 473 struct rtable *rt; /* Route to the other host */
474 struct net_device *tdev; /* Device to other host */ 474 struct net_device *tdev; /* Device to other host */
475 const struct iphdr *old_iph = ip_hdr(skb); 475 const struct iphdr *old_iph;
476 struct iphdr *iph; /* Our new IP header */ 476 struct iphdr *iph; /* Our new IP header */
477 unsigned int max_headroom; /* The extra header space needed */ 477 unsigned int max_headroom; /* The extra header space needed */
478 __be32 dst = tiph->daddr; 478 __be32 dst = tiph->daddr;
@@ -486,6 +486,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
486 skb_checksum_help(skb)) 486 skb_checksum_help(skb))
487 goto tx_error; 487 goto tx_error;
488 488
489 old_iph = ip_hdr(skb);
490
489 if (tos & 1) 491 if (tos & 1)
490 tos = old_iph->tos; 492 tos = old_iph->tos;
491 493
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 52271947a471..3ec1f69c5ceb 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -896,6 +896,8 @@ new_segment:
896 skb_fill_page_desc(skb, i, page, offset, copy); 896 skb_fill_page_desc(skb, i, page, offset, copy);
897 } 897 }
898 898
899 skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG;
900
899 skb->len += copy; 901 skb->len += copy;
900 skb->data_len += copy; 902 skb->data_len += copy;
901 skb->truesize += copy; 903 skb->truesize += copy;
@@ -3032,6 +3034,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
3032 SKB_GSO_DODGY | 3034 SKB_GSO_DODGY |
3033 SKB_GSO_TCP_ECN | 3035 SKB_GSO_TCP_ECN |
3034 SKB_GSO_TCPV6 | 3036 SKB_GSO_TCPV6 |
3037 SKB_GSO_SHARED_FRAG |
3035 0) || 3038 0) ||
3036 !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) 3039 !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
3037 goto out; 3040 goto out;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 0905997e5873..492c7cfe1453 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1240,13 +1240,13 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
1240 */ 1240 */
1241 if (!skb_shinfo(prev)->gso_size) { 1241 if (!skb_shinfo(prev)->gso_size) {
1242 skb_shinfo(prev)->gso_size = mss; 1242 skb_shinfo(prev)->gso_size = mss;
1243 skb_shinfo(prev)->gso_type = sk->sk_gso_type; 1243 skb_shinfo(prev)->gso_type |= sk->sk_gso_type;
1244 } 1244 }
1245 1245
1246 /* CHECKME: To clear or not to clear? Mimics normal skb currently */ 1246 /* CHECKME: To clear or not to clear? Mimics normal skb currently */
1247 if (skb_shinfo(skb)->gso_segs <= 1) { 1247 if (skb_shinfo(skb)->gso_segs <= 1) {
1248 skb_shinfo(skb)->gso_size = 0; 1248 skb_shinfo(skb)->gso_size = 0;
1249 skb_shinfo(skb)->gso_type = 0; 1249 skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG;
1250 } 1250 }
1251 1251
1252 /* Difference in this won't matter, both ACKed by the same cumul. ACK */ 1252 /* Difference in this won't matter, both ACKed by the same cumul. ACK */
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 667a6adfccf8..367e2ec01da1 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1133,6 +1133,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
1133static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, 1133static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
1134 unsigned int mss_now) 1134 unsigned int mss_now)
1135{ 1135{
1136 skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG;
1136 if (skb->len <= mss_now || !sk_can_gso(sk) || 1137 if (skb->len <= mss_now || !sk_can_gso(sk) ||
1137 skb->ip_summed == CHECKSUM_NONE) { 1138 skb->ip_summed == CHECKSUM_NONE) {
1138 /* Avoid the costly divide in the normal 1139 /* Avoid the costly divide in the normal
@@ -1140,11 +1141,10 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
1140 */ 1141 */
1141 skb_shinfo(skb)->gso_segs = 1; 1142 skb_shinfo(skb)->gso_segs = 1;
1142 skb_shinfo(skb)->gso_size = 0; 1143 skb_shinfo(skb)->gso_size = 0;
1143 skb_shinfo(skb)->gso_type = 0;
1144 } else { 1144 } else {
1145 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now); 1145 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now);
1146 skb_shinfo(skb)->gso_size = mss_now; 1146 skb_shinfo(skb)->gso_size = mss_now;
1147 skb_shinfo(skb)->gso_type = sk->sk_gso_type; 1147 skb_shinfo(skb)->gso_type |= sk->sk_gso_type;
1148 } 1148 }
1149} 1149}
1150 1150