ipv6: no CHECKSUM_PARTIAL on MSG_MORE corked sockets
We cannot reliable calculate packet size on MSG_MORE corked sockets and thus cannot decide if they are going to be fragmented later on, so better not use CHECKSUM_PARTIAL in the first place. The IPv6 code also intended to protect and not use CHECKSUM_PARTIAL in the existence of IPv6 extension headers, but the condition was wrong. Fix it up, too. Also the condition to check whether the packet fits into one fragment was wrong and has been corrected. Fixes: commit32dce968dd
("ipv6: Allow for partial checksums on non-ufo packets") See-also: commit72e843bb09
("ipv6: ip6_fragment() should check CHECKSUM_PARTIAL") Cc: Eric Dumazet <edumazet@google.com> Cc: Vlad Yasevich <vyasevich@gmail.com> Cc: Benjamin Coddington <bcodding@redhat.com> Cc: Tom Herbert <tom@herbertland.com> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
dbd3393c56
Коммит
682b1a9d3f
|
@ -1270,6 +1270,7 @@ static int __ip6_append_data(struct sock *sk,
|
|||
struct rt6_info *rt = (struct rt6_info *)cork->dst;
|
||||
struct ipv6_txoptions *opt = v6_cork->opt;
|
||||
int csummode = CHECKSUM_NONE;
|
||||
unsigned int maxnonfragsize, headersize;
|
||||
|
||||
skb = skb_peek_tail(queue);
|
||||
if (!skb) {
|
||||
|
@ -1287,38 +1288,43 @@ static int __ip6_append_data(struct sock *sk,
|
|||
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
|
||||
sizeof(struct frag_hdr);
|
||||
|
||||
if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
|
||||
unsigned int maxnonfragsize, headersize;
|
||||
headersize = sizeof(struct ipv6hdr) +
|
||||
(opt ? opt->opt_flen + opt->opt_nflen : 0) +
|
||||
(dst_allfrag(&rt->dst) ?
|
||||
sizeof(struct frag_hdr) : 0) +
|
||||
rt->rt6i_nfheader_len;
|
||||
|
||||
headersize = sizeof(struct ipv6hdr) +
|
||||
(opt ? opt->opt_flen + opt->opt_nflen : 0) +
|
||||
(dst_allfrag(&rt->dst) ?
|
||||
sizeof(struct frag_hdr) : 0) +
|
||||
rt->rt6i_nfheader_len;
|
||||
|
||||
if (ip6_sk_ignore_df(sk))
|
||||
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_RAW)) {
|
||||
ipv6_local_rxpmtu(sk, fl6, mtu - headersize +
|
||||
sizeof(struct ipv6hdr));
|
||||
goto emsgsize;
|
||||
}
|
||||
|
||||
if (cork->length + length > maxnonfragsize - headersize) {
|
||||
emsgsize:
|
||||
ipv6_local_error(sk, EMSGSIZE, fl6,
|
||||
mtu - headersize +
|
||||
sizeof(struct ipv6hdr));
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
if (cork->length + length > mtu - headersize && dontfrag &&
|
||||
(sk->sk_protocol == IPPROTO_UDP ||
|
||||
sk->sk_protocol == IPPROTO_RAW)) {
|
||||
ipv6_local_rxpmtu(sk, fl6, mtu - headersize +
|
||||
sizeof(struct ipv6hdr));
|
||||
goto emsgsize;
|
||||
}
|
||||
|
||||
if (ip6_sk_ignore_df(sk))
|
||||
maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
|
||||
else
|
||||
maxnonfragsize = mtu;
|
||||
|
||||
if (cork->length + length > maxnonfragsize - headersize) {
|
||||
emsgsize:
|
||||
ipv6_local_error(sk, EMSGSIZE, fl6,
|
||||
mtu - headersize +
|
||||
sizeof(struct ipv6hdr));
|
||||
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) {
|
||||
sock_tx_timestamp(sk, &tx_flags);
|
||||
if (tx_flags & SKBTX_ANY_SW_TSTAMP &&
|
||||
|
@ -1326,16 +1332,6 @@ emsgsize:
|
|||
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.
|
||||
* Use MTU if total length of the message fits into the MTU.
|
||||
|
|
Загрузка…
Ссылка в новой задаче