Commit 51382d09 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPV4/IPV6]: Fix IPsec calculation in ip_append_data/ip6_append_data

This patch fixes the IPsec overhead handling in ip_append_data and
ip6_append_data.  As it is they assume that the IPsec overhead is
constant.  This is not true as with ESP the IPsec overhead will vary
as the MTU varies.

The result is that they may produce packets that will exceed the MTU
when ESP is used.  Had it taken the trailer_len into account, it would
have produced packets less than the real MTU.

By switching to dst_mtu we get the optimal result.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8e5c060b
...@@ -719,7 +719,6 @@ int ip_append_data(struct sock *sk, ...@@ -719,7 +719,6 @@ int ip_append_data(struct sock *sk,
struct ip_options *opt = NULL; struct ip_options *opt = NULL;
int hh_len; int hh_len;
int exthdrlen;
int mtu; int mtu;
int copy; int copy;
int err; int err;
...@@ -746,22 +745,17 @@ int ip_append_data(struct sock *sk, ...@@ -746,22 +745,17 @@ int ip_append_data(struct sock *sk,
inet->cork.addr = ipc->addr; inet->cork.addr = ipc->addr;
} }
dst_hold(&rt->u.dst); dst_hold(&rt->u.dst);
inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst); inet->cork.fragsize = mtu = dst_mtu(&rt->u.dst);
inet->cork.rt = rt; inet->cork.rt = rt;
inet->cork.length = 0; inet->cork.length = 0;
sk->sk_sndmsg_page = NULL; sk->sk_sndmsg_page = NULL;
sk->sk_sndmsg_off = 0; sk->sk_sndmsg_off = 0;
if ((exthdrlen = rt->u.dst.header_len) != 0) {
length += exthdrlen;
transhdrlen += exthdrlen;
}
} else { } else {
rt = inet->cork.rt; rt = inet->cork.rt;
if (inet->cork.flags & IPCORK_OPT) if (inet->cork.flags & IPCORK_OPT)
opt = inet->cork.opt; opt = inet->cork.opt;
transhdrlen = 0; transhdrlen = 0;
exthdrlen = 0;
mtu = inet->cork.fragsize; mtu = inet->cork.fragsize;
} }
hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);
...@@ -770,7 +764,7 @@ int ip_append_data(struct sock *sk, ...@@ -770,7 +764,7 @@ int ip_append_data(struct sock *sk,
maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
if (inet->cork.length + length > 0xFFFF - fragheaderlen) { if (inet->cork.length + length > 0xFFFF - fragheaderlen) {
ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen); ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu);
return -EMSGSIZE; return -EMSGSIZE;
} }
...@@ -781,7 +775,7 @@ int ip_append_data(struct sock *sk, ...@@ -781,7 +775,7 @@ int ip_append_data(struct sock *sk,
if (transhdrlen && if (transhdrlen &&
length + fragheaderlen <= mtu && length + fragheaderlen <= mtu &&
rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) && rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) &&
!exthdrlen) !rt->u.dst.header_len)
csummode = CHECKSUM_HW; csummode = CHECKSUM_HW;
inet->cork.length += length; inet->cork.length += length;
...@@ -866,9 +860,9 @@ int ip_append_data(struct sock *sk, ...@@ -866,9 +860,9 @@ int ip_append_data(struct sock *sk,
* Find where to start putting bytes. * Find where to start putting bytes.
*/ */
data = skb_put(skb, fraglen); data = skb_put(skb, fraglen);
skb->nh.raw = data + exthdrlen; skb->nh.raw = data;
data += fragheaderlen; data += fragheaderlen;
skb->h.raw = data + exthdrlen; skb->h.raw = data;
if (fraggap) { if (fraggap) {
skb->csum = skb_copy_and_csum_bits( skb->csum = skb_copy_and_csum_bits(
...@@ -890,7 +884,6 @@ int ip_append_data(struct sock *sk, ...@@ -890,7 +884,6 @@ int ip_append_data(struct sock *sk,
offset += copy; offset += copy;
length -= datalen - fraggap; length -= datalen - fraggap;
transhdrlen = 0; transhdrlen = 0;
exthdrlen = 0;
csummode = CHECKSUM_NONE; csummode = CHECKSUM_NONE;
/* /*
......
...@@ -850,13 +850,13 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse ...@@ -850,13 +850,13 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
np->cork.rt = rt; np->cork.rt = rt;
inet->cork.fl = *fl; inet->cork.fl = *fl;
np->cork.hop_limit = hlimit; np->cork.hop_limit = hlimit;
inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst); inet->cork.fragsize = mtu = dst_mtu(&rt->u.dst);
if (dst_allfrag(&rt->u.dst)) if (dst_allfrag(&rt->u.dst))
inet->cork.flags |= IPCORK_ALLFRAG; inet->cork.flags |= IPCORK_ALLFRAG;
inet->cork.length = 0; inet->cork.length = 0;
sk->sk_sndmsg_page = NULL; sk->sk_sndmsg_page = NULL;
sk->sk_sndmsg_off = 0; sk->sk_sndmsg_off = 0;
exthdrlen = rt->u.dst.header_len + (opt ? opt->opt_flen : 0); exthdrlen = opt ? opt->opt_flen : 0;
length += exthdrlen; length += exthdrlen;
transhdrlen += exthdrlen; transhdrlen += exthdrlen;
} else { } else {
......
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