Commit 24c39de0 authored by David S. Miller's avatar David S. Miller

Merge branch 'csum_partial_frags'

Hannes Frederic Sowa says:

====================
net: clean up interactions of CHECKSUM_PARTIAL and fragmentation

This series fixes wrong checksums on the wire for IPv4 and IPv6. Large
send buffers and especially NFS lead to wrong checksums in both IPv4
and IPv6.

CHECKSUM_PARTIAL skbs should not receive the respective fragmentations
functions, so we add WARN_ON_ONCE to those functions to fix up those as
soon as they get reported.

Changelog:
v2: added v4 checks
v3: removed WARN_ON_ONCES (advice by Tom Herbert)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b75ec3af 405c92f7
...@@ -533,6 +533,11 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, ...@@ -533,6 +533,11 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
dev = rt->dst.dev; dev = rt->dst.dev;
/* for offloaded checksums cleanup checksum before fragmentation */
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(err = skb_checksum_help(skb)))
goto fail;
/* /*
* Point into the IP datagram header. * Point into the IP datagram header.
*/ */
...@@ -657,9 +662,6 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, ...@@ -657,9 +662,6 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
} }
slow_path: slow_path:
/* for offloaded checksums cleanup checksum before fragmentation */
if ((skb->ip_summed == CHECKSUM_PARTIAL) && skb_checksum_help(skb))
goto fail;
iph = ip_hdr(skb); iph = ip_hdr(skb);
left = skb->len - hlen; /* Space per frame */ left = skb->len - hlen; /* Space per frame */
...@@ -911,6 +913,7 @@ static int __ip_append_data(struct sock *sk, ...@@ -911,6 +913,7 @@ static int __ip_append_data(struct sock *sk,
if (transhdrlen && if (transhdrlen &&
length + fragheaderlen <= mtu && length + fragheaderlen <= mtu &&
rt->dst.dev->features & NETIF_F_V4_CSUM && rt->dst.dev->features & NETIF_F_V4_CSUM &&
!(flags & MSG_MORE) &&
!exthdrlen) !exthdrlen)
csummode = CHECKSUM_PARTIAL; csummode = CHECKSUM_PARTIAL;
......
...@@ -603,6 +603,10 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, ...@@ -603,6 +603,10 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr, frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr,
&ipv6_hdr(skb)->saddr); &ipv6_hdr(skb)->saddr);
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(err = skb_checksum_help(skb)))
goto fail;
hroom = LL_RESERVED_SPACE(rt->dst.dev); hroom = LL_RESERVED_SPACE(rt->dst.dev);
if (skb_has_frag_list(skb)) { if (skb_has_frag_list(skb)) {
int first_len = skb_pagelen(skb); int first_len = skb_pagelen(skb);
...@@ -731,10 +735,6 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, ...@@ -731,10 +735,6 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
} }
slow_path: slow_path:
if ((skb->ip_summed == CHECKSUM_PARTIAL) &&
skb_checksum_help(skb))
goto fail;
left = skb->len - hlen; /* Space per frame */ left = skb->len - hlen; /* Space per frame */
ptr = hlen; /* Where to start from */ ptr = hlen; /* Where to start from */
...@@ -1270,6 +1270,7 @@ static int __ip6_append_data(struct sock *sk, ...@@ -1270,6 +1270,7 @@ static int __ip6_append_data(struct sock *sk,
struct rt6_info *rt = (struct rt6_info *)cork->dst; struct rt6_info *rt = (struct rt6_info *)cork->dst;
struct ipv6_txoptions *opt = v6_cork->opt; struct ipv6_txoptions *opt = v6_cork->opt;
int csummode = CHECKSUM_NONE; int csummode = CHECKSUM_NONE;
unsigned int maxnonfragsize, headersize;
skb = skb_peek_tail(queue); skb = skb_peek_tail(queue);
if (!skb) { if (!skb) {
...@@ -1287,22 +1288,13 @@ static int __ip6_append_data(struct sock *sk, ...@@ -1287,22 +1288,13 @@ static int __ip6_append_data(struct sock *sk,
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
sizeof(struct frag_hdr); sizeof(struct frag_hdr);
if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
unsigned int maxnonfragsize, headersize;
headersize = sizeof(struct ipv6hdr) + headersize = sizeof(struct ipv6hdr) +
(opt ? opt->opt_flen + opt->opt_nflen : 0) + (opt ? opt->opt_flen + opt->opt_nflen : 0) +
(dst_allfrag(&rt->dst) ? (dst_allfrag(&rt->dst) ?
sizeof(struct frag_hdr) : 0) + sizeof(struct frag_hdr) : 0) +
rt->rt6i_nfheader_len; rt->rt6i_nfheader_len;
if (ip6_sk_ignore_df(sk)) if (cork->length + length > mtu - headersize && dontfrag &&
maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
else
maxnonfragsize = mtu;
/* dontfrag active */
if ((cork->length + length > mtu - headersize) && dontfrag &&
(sk->sk_protocol == IPPROTO_UDP || (sk->sk_protocol == IPPROTO_UDP ||
sk->sk_protocol == IPPROTO_RAW)) { sk->sk_protocol == IPPROTO_RAW)) {
ipv6_local_rxpmtu(sk, fl6, mtu - headersize + ipv6_local_rxpmtu(sk, fl6, mtu - headersize +
...@@ -1310,6 +1302,11 @@ static int __ip6_append_data(struct sock *sk, ...@@ -1310,6 +1302,11 @@ static int __ip6_append_data(struct sock *sk,
goto emsgsize; goto emsgsize;
} }
if (ip6_sk_ignore_df(sk))
maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
else
maxnonfragsize = mtu;
if (cork->length + length > maxnonfragsize - headersize) { if (cork->length + length > maxnonfragsize - headersize) {
emsgsize: emsgsize:
ipv6_local_error(sk, EMSGSIZE, fl6, ipv6_local_error(sk, EMSGSIZE, fl6,
...@@ -1317,7 +1314,16 @@ static int __ip6_append_data(struct sock *sk, ...@@ -1317,7 +1314,16 @@ static int __ip6_append_data(struct sock *sk,
sizeof(struct ipv6hdr)); sizeof(struct ipv6hdr));
return -EMSGSIZE; return -EMSGSIZE;
} }
}
/* CHECKSUM_PARTIAL only with no extension headers and when
* we are not going to fragment
*/
if (transhdrlen && sk->sk_protocol == IPPROTO_UDP &&
headersize == sizeof(struct ipv6hdr) &&
length < mtu - headersize &&
!(flags & MSG_MORE) &&
rt->dst.dev->features & NETIF_F_V6_CSUM)
csummode = CHECKSUM_PARTIAL;
if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) { if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) {
sock_tx_timestamp(sk, &tx_flags); sock_tx_timestamp(sk, &tx_flags);
...@@ -1326,16 +1332,6 @@ static int __ip6_append_data(struct sock *sk, ...@@ -1326,16 +1332,6 @@ static int __ip6_append_data(struct sock *sk,
tskey = sk->sk_tskey++; tskey = sk->sk_tskey++;
} }
/* If this is the first and only packet and device
* supports checksum offloading, let's use it.
* Use transhdrlen, same as IPv4, because partial
* sums only work when transhdrlen is set.
*/
if (transhdrlen && sk->sk_protocol == IPPROTO_UDP &&
length + fragheaderlen < mtu &&
rt->dst.dev->features & NETIF_F_V6_CSUM &&
!exthdrlen)
csummode = CHECKSUM_PARTIAL;
/* /*
* Let's try using as much space as possible. * Let's try using as much space as possible.
* Use MTU if total length of the message fits into the MTU. * Use MTU if total length of the message fits into the MTU.
......
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