On Tue, 2016-12-20 at 22:09 -0800, Cong Wang wrote: > On Tue, Dec 20, 2016 at 2:12 PM, Dave Jones <da...@codemonkey.org.uk> wrote: > > fd = socket(AF_INET6, SOCK_RAW, 7); > > > > setsockopt(fd, SOL_IPV6, IPV6_CHECKSUM, &zero, 4); > > setsockopt(fd, SOL_IPV6, IPV6_DSTOPTS, &buf, LEN); > > > > Interesting, you set the checksum offset to be 0, but the packet size > is actually 49, transport header is located at offset 48, so apparently > the packet doesn't have room for a 16bit checksum after network header. > > Your original patch seems reasonable to me, unless there is some > check in __ip6_append_data() which is supposed to catch this, but > CHECKSUM is specific to raw socket only.
The calculation of total_len is wrong here: total_len = inet_sk(sk)->cork.base.length; if (offset >= total_len - 1) { err = -EINVAL; ip6_flush_pending_frames(sk); goto out; } At least for this bug to fix we need to subtract the extension header length after the fragmentation header, so: --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -536,6 +536,17 @@ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto out; } +static unsigned int raw6_corked_transport_len(const struct sock *sk) +{ + unsigned int len = inet_sk(sk)->cork.base.length; + struct ipv6_txoptions *opt = inet6_sk(sk)->cork.opt; + + if (likely(!opt)) + return len; + + return len - opt->opt_flen; +} + static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct raw6_sock *rp) { @@ -543,7 +554,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, int err = 0; int offset; int len; - int total_len; + int transport_len; __wsum tmp_csum; __sum16 csum; @@ -555,8 +566,8 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, goto out; offset = rp->offset; - total_len = inet_sk(sk)->cork.base.length; - if (offset >= total_len - 1) { + transport_len = raw6_corked_transport_len(sk); + if (offset >= transport_len - 1) { err = -EINVAL; ip6_flush_pending_frames(sk); goto out; @@ -598,7 +609,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, tmp_csum = csum_sub(tmp_csum, csum_unfold(csum)); csum = csum_ipv6_magic(&fl6->saddr, &fl6->daddr, - total_len, fl6->flowi6_proto, tmp_csum); + transport_len, fl6->flowi6_proto, tmp_csum); if (csum == 0 && fl6->flowi6_proto == IPPROTO_UDP) csum = CSUM_MANGLED_0;