aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorSteffen Klassert <steffen.klassert@secunet.com>2011-10-10 21:43:33 -0400
committerDavid S. Miller <davem@davemloft.net>2011-10-18 23:53:10 -0400
commit299b0767642a65f0c5446ab6d35e6df0daf43d33 (patch)
tree30e41c97fe4cef60540964495c15d0be121e3b3e /net/ipv6
parentc113464d4351591de8791c0cadfc165836e5a725 (diff)
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: Steffen Klassert <steffen.klassert@secunet.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_output.c18
-rw-r--r--net/ipv6/raw.c3
2 files changed, 13 insertions, 8 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 835c04b5239f..1e20b64e646c 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1193,6 +1193,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1193 struct sk_buff *skb; 1193 struct sk_buff *skb;
1194 unsigned int maxfraglen, fragheaderlen; 1194 unsigned int maxfraglen, fragheaderlen;
1195 int exthdrlen; 1195 int exthdrlen;
1196 int dst_exthdrlen;
1196 int hh_len; 1197 int hh_len;
1197 int mtu; 1198 int mtu;
1198 int copy; 1199 int copy;
@@ -1248,7 +1249,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1248 np->cork.hop_limit = hlimit; 1249 np->cork.hop_limit = hlimit;
1249 np->cork.tclass = tclass; 1250 np->cork.tclass = tclass;
1250 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? 1251 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
1251 rt->dst.dev->mtu : dst_mtu(rt->dst.path); 1252 rt->dst.dev->mtu : dst_mtu(&rt->dst);
1252 if (np->frag_size < mtu) { 1253 if (np->frag_size < mtu) {
1253 if (np->frag_size) 1254 if (np->frag_size)
1254 mtu = np->frag_size; 1255 mtu = np->frag_size;
@@ -1259,16 +1260,17 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1259 cork->length = 0; 1260 cork->length = 0;
1260 sk->sk_sndmsg_page = NULL; 1261 sk->sk_sndmsg_page = NULL;
1261 sk->sk_sndmsg_off = 0; 1262 sk->sk_sndmsg_off = 0;
1262 exthdrlen = rt->dst.header_len + (opt ? opt->opt_flen : 0) - 1263 exthdrlen = (opt ? opt->opt_flen : 0) - rt->rt6i_nfheader_len;
1263 rt->rt6i_nfheader_len;
1264 length += exthdrlen; 1264 length += exthdrlen;
1265 transhdrlen += exthdrlen; 1265 transhdrlen += exthdrlen;
1266 dst_exthdrlen = rt->dst.header_len;
1266 } else { 1267 } else {
1267 rt = (struct rt6_info *)cork->dst; 1268 rt = (struct rt6_info *)cork->dst;
1268 fl6 = &inet->cork.fl.u.ip6; 1269 fl6 = &inet->cork.fl.u.ip6;
1269 opt = np->cork.opt; 1270 opt = np->cork.opt;
1270 transhdrlen = 0; 1271 transhdrlen = 0;
1271 exthdrlen = 0; 1272 exthdrlen = 0;
1273 dst_exthdrlen = 0;
1272 mtu = cork->fragsize; 1274 mtu = cork->fragsize;
1273 } 1275 }
1274 1276
@@ -1368,6 +1370,8 @@ alloc_new_skb:
1368 else 1370 else
1369 alloclen = datalen + fragheaderlen; 1371 alloclen = datalen + fragheaderlen;
1370 1372
1373 alloclen += dst_exthdrlen;
1374
1371 /* 1375 /*
1372 * The last fragment gets additional space at tail. 1376 * The last fragment gets additional space at tail.
1373 * Note: we overallocate on fragments with MSG_MODE 1377 * Note: we overallocate on fragments with MSG_MODE
@@ -1419,9 +1423,9 @@ alloc_new_skb:
1419 /* 1423 /*
1420 * Find where to start putting bytes 1424 * Find where to start putting bytes
1421 */ 1425 */
1422 data = skb_put(skb, fraglen); 1426 data = skb_put(skb, fraglen + dst_exthdrlen);
1423 skb_set_network_header(skb, exthdrlen); 1427 skb_set_network_header(skb, exthdrlen + dst_exthdrlen);
1424 data += fragheaderlen; 1428 data += fragheaderlen + dst_exthdrlen;
1425 skb->transport_header = (skb->network_header + 1429 skb->transport_header = (skb->network_header +
1426 fragheaderlen); 1430 fragheaderlen);
1427 if (fraggap) { 1431 if (fraggap) {
@@ -1434,6 +1438,7 @@ alloc_new_skb:
1434 pskb_trim_unique(skb_prev, maxfraglen); 1438 pskb_trim_unique(skb_prev, maxfraglen);
1435 } 1439 }
1436 copy = datalen - transhdrlen - fraggap; 1440 copy = datalen - transhdrlen - fraggap;
1441
1437 if (copy < 0) { 1442 if (copy < 0) {
1438 err = -EINVAL; 1443 err = -EINVAL;
1439 kfree_skb(skb); 1444 kfree_skb(skb);
@@ -1448,6 +1453,7 @@ alloc_new_skb:
1448 length -= datalen - fraggap; 1453 length -= datalen - fraggap;
1449 transhdrlen = 0; 1454 transhdrlen = 0;
1450 exthdrlen = 0; 1455 exthdrlen = 0;
1456 dst_exthdrlen = 0;
1451 csummode = CHECKSUM_NONE; 1457 csummode = CHECKSUM_NONE;
1452 1458
1453 /* 1459 /*
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 3486f62befa3..6f7824e1cea4 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -542,8 +542,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
542 goto out; 542 goto out;
543 543
544 offset = rp->offset; 544 offset = rp->offset;
545 total_len = inet_sk(sk)->cork.base.length - (skb_network_header(skb) - 545 total_len = inet_sk(sk)->cork.base.length;
546 skb->data);
547 if (offset >= total_len - 1) { 546 if (offset >= total_len - 1) {
548 err = -EINVAL; 547 err = -EINVAL;
549 ip6_flush_pending_frames(sk); 548 ip6_flush_pending_frames(sk);