Skip to content

Commit

Permalink
ipv6: Fix IPsec slowpath fragmentation problem
Browse files Browse the repository at this point in the history
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: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Steffen Klassert authored and David S. Miller committed Oct 19, 2011
1 parent c113464 commit 299b076
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 8 deletions.
18 changes: 12 additions & 6 deletions net/ipv6/ip6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
struct sk_buff *skb;
unsigned int maxfraglen, fragheaderlen;
int exthdrlen;
int dst_exthdrlen;
int hh_len;
int mtu;
int copy;
Expand Down Expand Up @@ -1248,7 +1249,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
np->cork.hop_limit = hlimit;
np->cork.tclass = tclass;
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 = np->frag_size;
Expand All @@ -1259,16 +1260,17 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
cork->length = 0;
sk->sk_sndmsg_page = NULL;
sk->sk_sndmsg_off = 0;
exthdrlen = rt->dst.header_len + (opt ? opt->opt_flen : 0) -
rt->rt6i_nfheader_len;
exthdrlen = (opt ? opt->opt_flen : 0) - rt->rt6i_nfheader_len;
length += exthdrlen;
transhdrlen += exthdrlen;
dst_exthdrlen = rt->dst.header_len;
} else {
rt = (struct rt6_info *)cork->dst;
fl6 = &inet->cork.fl.u.ip6;
opt = np->cork.opt;
transhdrlen = 0;
exthdrlen = 0;
dst_exthdrlen = 0;
mtu = cork->fragsize;
}

Expand Down Expand Up @@ -1368,6 +1370,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
else
alloclen = datalen + fragheaderlen;

alloclen += dst_exthdrlen;

/*
* The last fragment gets additional space at tail.
* Note: we overallocate on fragments with MSG_MODE
Expand Down Expand Up @@ -1419,9 +1423,9 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
/*
* Find where to start putting bytes
*/
data = skb_put(skb, fraglen);
skb_set_network_header(skb, exthdrlen);
data += fragheaderlen;
data = skb_put(skb, fraglen + dst_exthdrlen);
skb_set_network_header(skb, exthdrlen + dst_exthdrlen);
data += fragheaderlen + dst_exthdrlen;
skb->transport_header = (skb->network_header +
fragheaderlen);
if (fraggap) {
Expand All @@ -1434,6 +1438,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
pskb_trim_unique(skb_prev, maxfraglen);
}
copy = datalen - transhdrlen - fraggap;

if (copy < 0) {
err = -EINVAL;
kfree_skb(skb);
Expand All @@ -1448,6 +1453,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
length -= datalen - fraggap;
transhdrlen = 0;
exthdrlen = 0;
dst_exthdrlen = 0;
csummode = CHECKSUM_NONE;

/*
Expand Down
3 changes: 1 addition & 2 deletions net/ipv6/raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,8 +542,7 @@ 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 - (skb_network_header(skb) -
skb->data);
total_len = inet_sk(sk)->cork.base.length;
if (offset >= total_len - 1) {
err = -EINVAL;
ip6_flush_pending_frames(sk);
Expand Down

0 comments on commit 299b076

Please sign in to comment.