aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorGao feng <gaofeng@cn.fujitsu.com>2012-05-25 21:30:53 -0400
committerLuis Henriques <luis.henriques@canonical.com>2012-07-03 11:29:03 -0400
commitc62c612446a42a5de3559a1de7623ce86cc51636 (patch)
treed8f89ca60d16754fe49f7b9eaebc7ac526e2c65f /net
parentb457b5b19c6484670ea162666f1b3a4699cc6de2 (diff)
ipv6: fix incorrect ipsec fragment
BugLink: http://bugs.launchpad.net/bugs/1013748 [ Upstream commit 0c1833797a5a6ec23ea9261d979aa18078720b74 ] Since commit ad0081e43a "ipv6: Fragment locally generated tunnel-mode IPSec6 packets as needed" the fragment of packets is incorrect. because tunnel mode needs IPsec headers and trailer for all fragments, while on transport mode it is sufficient to add the headers to the first fragment and the trailer to the last. so modify mtu and maxfraglen base on ipsec mode and if fragment is first or last. with my test,it work well(every fragment's size is the mtu) and does not trigger slow fragment path. Changes from v1: though optimization, mtu_prev and maxfraglen_prev can be delete. replace xfrm mode codes with dst_entry's new frag DST_XFRM_TUNNEL. add fuction ip6_append_data_mtu to make codes clearer. Signed-off-by: Gao feng <gaofeng@cn.fujitsu.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Herton Ronaldo Krzesinski <herton.krzesinski@canonical.com>
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/ip6_output.c68
-rw-r--r--net/xfrm/xfrm_policy.c3
2 files changed, 53 insertions, 18 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 9cbf17686a1..ae9f6d43617 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1194,6 +1194,29 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
1194 return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL; 1194 return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
1195} 1195}
1196 1196
1197static void ip6_append_data_mtu(int *mtu,
1198 int *maxfraglen,
1199 unsigned int fragheaderlen,
1200 struct sk_buff *skb,
1201 struct rt6_info *rt)
1202{
1203 if (!(rt->dst.flags & DST_XFRM_TUNNEL)) {
1204 if (skb == NULL) {
1205 /* first fragment, reserve header_len */
1206 *mtu = *mtu - rt->dst.header_len;
1207
1208 } else {
1209 /*
1210 * this fragment is not first, the headers
1211 * space is regarded as data space.
1212 */
1213 *mtu = dst_mtu(rt->dst.path);
1214 }
1215 *maxfraglen = ((*mtu - fragheaderlen) & ~7)
1216 + fragheaderlen - sizeof(struct frag_hdr);
1217 }
1218}
1219
1197int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, 1220int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1198 int offset, int len, int odd, struct sk_buff *skb), 1221 int offset, int len, int odd, struct sk_buff *skb),
1199 void *from, int length, int transhdrlen, 1222 void *from, int length, int transhdrlen,
@@ -1203,7 +1226,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1203 struct inet_sock *inet = inet_sk(sk); 1226 struct inet_sock *inet = inet_sk(sk);
1204 struct ipv6_pinfo *np = inet6_sk(sk); 1227 struct ipv6_pinfo *np = inet6_sk(sk);
1205 struct inet_cork *cork; 1228 struct inet_cork *cork;
1206 struct sk_buff *skb; 1229 struct sk_buff *skb, *skb_prev = NULL;
1207 unsigned int maxfraglen, fragheaderlen; 1230 unsigned int maxfraglen, fragheaderlen;
1208 int exthdrlen; 1231 int exthdrlen;
1209 int hh_len; 1232 int hh_len;
@@ -1260,8 +1283,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1260 inet->cork.fl.u.ip6 = *fl6; 1283 inet->cork.fl.u.ip6 = *fl6;
1261 np->cork.hop_limit = hlimit; 1284 np->cork.hop_limit = hlimit;
1262 np->cork.tclass = tclass; 1285 np->cork.tclass = tclass;
1263 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? 1286 if (rt->dst.flags & DST_XFRM_TUNNEL)
1264 rt->dst.dev->mtu : dst_mtu(rt->dst.path); 1287 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
1288 rt->dst.dev->mtu : dst_mtu(&rt->dst);
1289 else
1290 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
1291 rt->dst.dev->mtu : dst_mtu(rt->dst.path);
1265 if (np->frag_size < mtu) { 1292 if (np->frag_size < mtu) {
1266 if (np->frag_size) 1293 if (np->frag_size)
1267 mtu = np->frag_size; 1294 mtu = np->frag_size;
@@ -1356,38 +1383,43 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1356 unsigned int fraglen; 1383 unsigned int fraglen;
1357 unsigned int fraggap; 1384 unsigned int fraggap;
1358 unsigned int alloclen; 1385 unsigned int alloclen;
1359 struct sk_buff *skb_prev;
1360alloc_new_skb: 1386alloc_new_skb:
1361 skb_prev = skb;
1362
1363 /* There's no room in the current skb */ 1387 /* There's no room in the current skb */
1364 if (skb_prev) 1388 if (skb)
1365 fraggap = skb_prev->len - maxfraglen; 1389 fraggap = skb->len - maxfraglen;
1366 else 1390 else
1367 fraggap = 0; 1391 fraggap = 0;
1392 /* update mtu and maxfraglen if necessary */
1393 if (skb == NULL || skb_prev == NULL)
1394 ip6_append_data_mtu(&mtu, &maxfraglen,
1395 fragheaderlen, skb, rt);
1396
1397 skb_prev = skb;
1368 1398
1369 /* 1399 /*
1370 * If remaining data exceeds the mtu, 1400 * If remaining data exceeds the mtu,
1371 * we know we need more fragment(s). 1401 * we know we need more fragment(s).
1372 */ 1402 */
1373 datalen = length + fraggap; 1403 datalen = length + fraggap;
1374 if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
1375 datalen = maxfraglen - fragheaderlen;
1376 1404
1377 fraglen = datalen + fragheaderlen; 1405 if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
1406 datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len;
1378 if ((flags & MSG_MORE) && 1407 if ((flags & MSG_MORE) &&
1379 !(rt->dst.dev->features&NETIF_F_SG)) 1408 !(rt->dst.dev->features&NETIF_F_SG))
1380 alloclen = mtu; 1409 alloclen = mtu;
1381 else 1410 else
1382 alloclen = datalen + fragheaderlen; 1411 alloclen = datalen + fragheaderlen;
1383 1412
1384 /* 1413 if (datalen != length + fraggap) {
1385 * The last fragment gets additional space at tail. 1414 /*
1386 * Note: we overallocate on fragments with MSG_MODE 1415 * this is not the last fragment, the trailer
1387 * because we have no idea if we're the last one. 1416 * space is regarded as data space.
1388 */ 1417 */
1389 if (datalen == length + fraggap) 1418 datalen += rt->dst.trailer_len;
1390 alloclen += rt->dst.trailer_len; 1419 }
1420
1421 alloclen += rt->dst.trailer_len;
1422 fraglen = datalen + fragheaderlen;
1391 1423
1392 /* 1424 /*
1393 * We just reserve space for fragment header. 1425 * We just reserve space for fragment header.
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 7803eb6af41..0c0e40e9cfc 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1917,6 +1917,9 @@ no_transform:
1917 } 1917 }
1918ok: 1918ok:
1919 xfrm_pols_put(pols, drop_pols); 1919 xfrm_pols_put(pols, drop_pols);
1920 if (dst && dst->xfrm &&
1921 dst->xfrm->props.mode == XFRM_MODE_TUNNEL)
1922 dst->flags |= DST_XFRM_TUNNEL;
1920 return dst; 1923 return dst;
1921 1924
1922nopol: 1925nopol: