aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorGao feng <gaofeng@cn.fujitsu.com>2012-05-25 21:30:53 -0400
committerDavid S. Miller <davem@davemloft.net>2012-05-27 01:11:22 -0400
commit0c1833797a5a6ec23ea9261d979aa18078720b74 (patch)
tree1ea0d0ee1fb3915b30f7b40a91c0d047db5aeac3 /net
parent91657eafb64b4cb53ec3a2fbc4afc3497f735788 (diff)
ipv6: fix incorrect ipsec fragment
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>
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 d99fdc699625..17b8c67998bb 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1187,6 +1187,29 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
1187 return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL; 1187 return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
1188} 1188}
1189 1189
1190static void ip6_append_data_mtu(int *mtu,
1191 int *maxfraglen,
1192 unsigned int fragheaderlen,
1193 struct sk_buff *skb,
1194 struct rt6_info *rt)
1195{
1196 if (!(rt->dst.flags & DST_XFRM_TUNNEL)) {
1197 if (skb == NULL) {
1198 /* first fragment, reserve header_len */
1199 *mtu = *mtu - rt->dst.header_len;
1200
1201 } else {
1202 /*
1203 * this fragment is not first, the headers
1204 * space is regarded as data space.
1205 */
1206 *mtu = dst_mtu(rt->dst.path);
1207 }
1208 *maxfraglen = ((*mtu - fragheaderlen) & ~7)
1209 + fragheaderlen - sizeof(struct frag_hdr);
1210 }
1211}
1212
1190int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, 1213int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1191 int offset, int len, int odd, struct sk_buff *skb), 1214 int offset, int len, int odd, struct sk_buff *skb),
1192 void *from, int length, int transhdrlen, 1215 void *from, int length, int transhdrlen,
@@ -1196,7 +1219,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1196 struct inet_sock *inet = inet_sk(sk); 1219 struct inet_sock *inet = inet_sk(sk);
1197 struct ipv6_pinfo *np = inet6_sk(sk); 1220 struct ipv6_pinfo *np = inet6_sk(sk);
1198 struct inet_cork *cork; 1221 struct inet_cork *cork;
1199 struct sk_buff *skb; 1222 struct sk_buff *skb, *skb_prev = NULL;
1200 unsigned int maxfraglen, fragheaderlen; 1223 unsigned int maxfraglen, fragheaderlen;
1201 int exthdrlen; 1224 int exthdrlen;
1202 int dst_exthdrlen; 1225 int dst_exthdrlen;
@@ -1253,8 +1276,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1253 inet->cork.fl.u.ip6 = *fl6; 1276 inet->cork.fl.u.ip6 = *fl6;
1254 np->cork.hop_limit = hlimit; 1277 np->cork.hop_limit = hlimit;
1255 np->cork.tclass = tclass; 1278 np->cork.tclass = tclass;
1256 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? 1279 if (rt->dst.flags & DST_XFRM_TUNNEL)
1257 rt->dst.dev->mtu : dst_mtu(&rt->dst); 1280 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
1281 rt->dst.dev->mtu : dst_mtu(&rt->dst);
1282 else
1283 mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
1284 rt->dst.dev->mtu : dst_mtu(rt->dst.path);
1258 if (np->frag_size < mtu) { 1285 if (np->frag_size < mtu) {
1259 if (np->frag_size) 1286 if (np->frag_size)
1260 mtu = np->frag_size; 1287 mtu = np->frag_size;
@@ -1350,25 +1377,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
1350 unsigned int fraglen; 1377 unsigned int fraglen;
1351 unsigned int fraggap; 1378 unsigned int fraggap;
1352 unsigned int alloclen; 1379 unsigned int alloclen;
1353 struct sk_buff *skb_prev;
1354alloc_new_skb: 1380alloc_new_skb:
1355 skb_prev = skb;
1356
1357 /* There's no room in the current skb */ 1381 /* There's no room in the current skb */
1358 if (skb_prev) 1382 if (skb)
1359 fraggap = skb_prev->len - maxfraglen; 1383 fraggap = skb->len - maxfraglen;
1360 else 1384 else
1361 fraggap = 0; 1385 fraggap = 0;
1386 /* update mtu and maxfraglen if necessary */
1387 if (skb == NULL || skb_prev == NULL)
1388 ip6_append_data_mtu(&mtu, &maxfraglen,
1389 fragheaderlen, skb, rt);
1390
1391 skb_prev = skb;
1362 1392
1363 /* 1393 /*
1364 * If remaining data exceeds the mtu, 1394 * If remaining data exceeds the mtu,
1365 * we know we need more fragment(s). 1395 * we know we need more fragment(s).
1366 */ 1396 */
1367 datalen = length + fraggap; 1397 datalen = length + fraggap;
1368 if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
1369 datalen = maxfraglen - fragheaderlen;
1370 1398
1371 fraglen = datalen + fragheaderlen; 1399 if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
1400 datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len;
1372 if ((flags & MSG_MORE) && 1401 if ((flags & MSG_MORE) &&
1373 !(rt->dst.dev->features&NETIF_F_SG)) 1402 !(rt->dst.dev->features&NETIF_F_SG))
1374 alloclen = mtu; 1403 alloclen = mtu;
@@ -1377,13 +1406,16 @@ alloc_new_skb:
1377 1406
1378 alloclen += dst_exthdrlen; 1407 alloclen += dst_exthdrlen;
1379 1408
1380 /* 1409 if (datalen != length + fraggap) {
1381 * The last fragment gets additional space at tail. 1410 /*
1382 * Note: we overallocate on fragments with MSG_MODE 1411 * this is not the last fragment, the trailer
1383 * because we have no idea if we're the last one. 1412 * space is regarded as data space.
1384 */ 1413 */
1385 if (datalen == length + fraggap) 1414 datalen += rt->dst.trailer_len;
1386 alloclen += rt->dst.trailer_len; 1415 }
1416
1417 alloclen += rt->dst.trailer_len;
1418 fraglen = datalen + fragheaderlen;
1387 1419
1388 /* 1420 /*
1389 * We just reserve space for fragment header. 1421 * We just reserve space for fragment header.
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index c53e8f42aa75..ccfbd328a69d 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1921,6 +1921,9 @@ no_transform:
1921 } 1921 }
1922ok: 1922ok:
1923 xfrm_pols_put(pols, drop_pols); 1923 xfrm_pols_put(pols, drop_pols);
1924 if (dst && dst->xfrm &&
1925 dst->xfrm->props.mode == XFRM_MODE_TUNNEL)
1926 dst->flags |= DST_XFRM_TUNNEL;
1924 return dst; 1927 return dst;
1925 1928
1926nopol: 1929nopol: