Commit 299b0767 authored by Steffen Klassert's avatar Steffen Klassert Committed by David S. Miller

ipv6: Fix IPsec slowpath fragmentation problem

ip6_append_data() builds packets based on the mtu from dst_mtu(rt->dst.path).
On IPsec the effective mtu is lower because we need to add the protocol
headers and trailers later when we do the IPsec transformations. So after
the IPsec transformations the packet might be too big, which leads to a
slowpath fragmentation then. This patch fixes this by building the packets
based on the lower IPsec mtu from dst_mtu(&rt->dst) and adapts the exthdr
handling to this.
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c113464d
...@@ -1193,6 +1193,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1193,6 +1193,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
struct sk_buff *skb; struct sk_buff *skb;
unsigned int maxfraglen, fragheaderlen; unsigned int maxfraglen, fragheaderlen;
int exthdrlen; int exthdrlen;
int dst_exthdrlen;
int hh_len; int hh_len;
int mtu; int mtu;
int copy; int copy;
...@@ -1248,7 +1249,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1248,7 +1249,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
np->cork.hop_limit = hlimit; np->cork.hop_limit = hlimit;
np->cork.tclass = tclass; np->cork.tclass = tclass;
mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
rt->dst.dev->mtu : dst_mtu(rt->dst.path); rt->dst.dev->mtu : dst_mtu(&rt->dst);
if (np->frag_size < mtu) { if (np->frag_size < mtu) {
if (np->frag_size) if (np->frag_size)
mtu = np->frag_size; mtu = np->frag_size;
...@@ -1259,16 +1260,17 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1259,16 +1260,17 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
cork->length = 0; 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->dst.header_len + (opt ? opt->opt_flen : 0) - exthdrlen = (opt ? opt->opt_flen : 0) - rt->rt6i_nfheader_len;
rt->rt6i_nfheader_len;
length += exthdrlen; length += exthdrlen;
transhdrlen += exthdrlen; transhdrlen += exthdrlen;
dst_exthdrlen = rt->dst.header_len;
} else { } else {
rt = (struct rt6_info *)cork->dst; rt = (struct rt6_info *)cork->dst;
fl6 = &inet->cork.fl.u.ip6; fl6 = &inet->cork.fl.u.ip6;
opt = np->cork.opt; opt = np->cork.opt;
transhdrlen = 0; transhdrlen = 0;
exthdrlen = 0; exthdrlen = 0;
dst_exthdrlen = 0;
mtu = cork->fragsize; mtu = cork->fragsize;
} }
...@@ -1368,6 +1370,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1368,6 +1370,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
else else
alloclen = datalen + fragheaderlen; alloclen = datalen + fragheaderlen;
alloclen += dst_exthdrlen;
/* /*
* The last fragment gets additional space at tail. * The last fragment gets additional space at tail.
* Note: we overallocate on fragments with MSG_MODE * Note: we overallocate on fragments with MSG_MODE
...@@ -1419,9 +1423,9 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1419,9 +1423,9 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
/* /*
* Find where to start putting bytes * Find where to start putting bytes
*/ */
data = skb_put(skb, fraglen); data = skb_put(skb, fraglen + dst_exthdrlen);
skb_set_network_header(skb, exthdrlen); skb_set_network_header(skb, exthdrlen + dst_exthdrlen);
data += fragheaderlen; data += fragheaderlen + dst_exthdrlen;
skb->transport_header = (skb->network_header + skb->transport_header = (skb->network_header +
fragheaderlen); fragheaderlen);
if (fraggap) { if (fraggap) {
...@@ -1434,6 +1438,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1434,6 +1438,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
pskb_trim_unique(skb_prev, maxfraglen); pskb_trim_unique(skb_prev, maxfraglen);
} }
copy = datalen - transhdrlen - fraggap; copy = datalen - transhdrlen - fraggap;
if (copy < 0) { if (copy < 0) {
err = -EINVAL; err = -EINVAL;
kfree_skb(skb); kfree_skb(skb);
...@@ -1448,6 +1453,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, ...@@ -1448,6 +1453,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
length -= datalen - fraggap; length -= datalen - fraggap;
transhdrlen = 0; transhdrlen = 0;
exthdrlen = 0; exthdrlen = 0;
dst_exthdrlen = 0;
csummode = CHECKSUM_NONE; csummode = CHECKSUM_NONE;
/* /*
......
...@@ -542,8 +542,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, ...@@ -542,8 +542,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
goto out; goto out;
offset = rp->offset; offset = rp->offset;
total_len = inet_sk(sk)->cork.base.length - (skb_network_header(skb) - total_len = inet_sk(sk)->cork.base.length;
skb->data);
if (offset >= total_len - 1) { if (offset >= total_len - 1) {
err = -EINVAL; err = -EINVAL;
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
......
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