Commit 5b82ac35 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPV4/IPV6]: Fixup checksums properly when fragmenting.

If we end up with a fraggap, when we copy the data
over to the next frag being built we must compute
a checksum for the bit we copy over for the new
fragment and subtract that checksum from the place
we are copying it from.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 58b31cc9
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
#include <net/raw.h> #include <net/raw.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <net/inetpeer.h> #include <net/inetpeer.h>
#include <net/checksum.h>
#include <linux/igmp.h> #include <linux/igmp.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
...@@ -892,14 +893,17 @@ int ip_append_data(struct sock *sk, ...@@ -892,14 +893,17 @@ int ip_append_data(struct sock *sk,
skb->h.raw = data + exthdrlen; skb->h.raw = data + exthdrlen;
if (fraggap) { if (fraggap) {
skb_copy_bits(skb_prev, maxfraglen, skb->csum = skb_copy_and_csum_bits(
data + transhdrlen, fraggap); skb_prev, maxfraglen,
data + transhdrlen, fraggap, 0);
skb_prev->csum = csum_block_sub(
skb_prev->csum, skb->csum, 0);
data += fraggap; data += fraggap;
skb_trim(skb_prev, maxfraglen); skb_trim(skb_prev, maxfraglen);
} }
copy = datalen - transhdrlen - fraggap; copy = datalen - transhdrlen - fraggap;
if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, 0, skb) < 0) { if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {
err = -EFAULT; err = -EFAULT;
kfree_skb(skb); kfree_skb(skb);
goto error; goto error;
...@@ -1087,8 +1091,11 @@ ssize_t ip_append_page(struct sock *sk, struct page *page, ...@@ -1087,8 +1091,11 @@ ssize_t ip_append_page(struct sock *sk, struct page *page,
skb->h.raw = data; skb->h.raw = data;
if (fraggap) { if (fraggap) {
skb_copy_bits(skb_prev, maxfraglen, skb->csum = skb_copy_and_csum_bits(
data, fraggap); skb_prev, maxfraglen,
data, fraggap, 0);
skb_prev->csum = csum_block_sub(
skb_prev->csum, skb->csum, 0);
skb_trim(skb_prev, maxfraglen); skb_trim(skb_prev, maxfraglen);
} }
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include <net/rawv6.h> #include <net/rawv6.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/checksum.h>
static int ip6_fragment(struct sk_buff **pskb, int (*output)(struct sk_buff**)); static int ip6_fragment(struct sk_buff **pskb, int (*output)(struct sk_buff**));
...@@ -981,8 +982,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse ...@@ -981,8 +982,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
skb->h.raw = data + exthdrlen; skb->h.raw = data + exthdrlen;
if (fraggap) { if (fraggap) {
skb_copy_bits(skb_prev, maxfraglen, skb->csum = skb_copy_and_csum_bits(
data + transhdrlen, fraggap); skb_prev, maxfraglen,
data + transhdrlen, fraggap, 0);
skb_prev->csum = csum_block_sub(
skb_prev->csum, skb->csum, 0);
data += fraggap; data += fraggap;
skb_trim(skb_prev, maxfraglen); skb_trim(skb_prev, maxfraglen);
} }
...@@ -991,7 +995,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse ...@@ -991,7 +995,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
err = -EINVAL; err = -EINVAL;
kfree_skb(skb); kfree_skb(skb);
goto error; goto error;
} else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, 0, skb) < 0) { } else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {
err = -EFAULT; err = -EFAULT;
kfree_skb(skb); kfree_skb(skb);
goto error; goto error;
......
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