Skip to content

Commit 0c18337

Browse files
Gao fengdavem330
Gao feng
authored andcommitted
ipv6: fix incorrect ipsec fragment
Since commit ad0081e "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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 91657ea commit 0c18337

File tree

3 files changed

+54
-18
lines changed

3 files changed

+54
-18
lines changed

include/net/dst.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ struct dst_entry {
6060
#define DST_NOCOUNT 0x0020
6161
#define DST_NOPEER 0x0040
6262
#define DST_FAKE_RTABLE 0x0080
63+
#define DST_XFRM_TUNNEL 0x0100
6364

6465
short error;
6566
short obsolete;

net/ipv6/ip6_output.c

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,29 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
11871187
return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
11881188
}
11891189

1190+
static 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+
11901213
int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
11911214
int offset, int len, int odd, struct sk_buff *skb),
11921215
void *from, int length, int transhdrlen,
@@ -1196,7 +1219,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
11961219
struct inet_sock *inet = inet_sk(sk);
11971220
struct ipv6_pinfo *np = inet6_sk(sk);
11981221
struct inet_cork *cork;
1199-
struct sk_buff *skb;
1222+
struct sk_buff *skb, *skb_prev = NULL;
12001223
unsigned int maxfraglen, fragheaderlen;
12011224
int exthdrlen;
12021225
int dst_exthdrlen;
@@ -1253,8 +1276,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
12531276
inet->cork.fl.u.ip6 = *fl6;
12541277
np->cork.hop_limit = hlimit;
12551278
np->cork.tclass = tclass;
1256-
mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
1257-
rt->dst.dev->mtu : dst_mtu(&rt->dst);
1279+
if (rt->dst.flags & DST_XFRM_TUNNEL)
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);
12581285
if (np->frag_size < mtu) {
12591286
if (np->frag_size)
12601287
mtu = np->frag_size;
@@ -1350,25 +1377,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
13501377
unsigned int fraglen;
13511378
unsigned int fraggap;
13521379
unsigned int alloclen;
1353-
struct sk_buff *skb_prev;
13541380
alloc_new_skb:
1355-
skb_prev = skb;
1356-
13571381
/* There's no room in the current skb */
1358-
if (skb_prev)
1359-
fraggap = skb_prev->len - maxfraglen;
1382+
if (skb)
1383+
fraggap = skb->len - maxfraglen;
13601384
else
13611385
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;
13621392

13631393
/*
13641394
* If remaining data exceeds the mtu,
13651395
* we know we need more fragment(s).
13661396
*/
13671397
datalen = length + fraggap;
1368-
if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
1369-
datalen = maxfraglen - fragheaderlen;
13701398

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;
13721401
if ((flags & MSG_MORE) &&
13731402
!(rt->dst.dev->features&NETIF_F_SG))
13741403
alloclen = mtu;
@@ -1377,13 +1406,16 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
13771406

13781407
alloclen += dst_exthdrlen;
13791408

1380-
/*
1381-
* The last fragment gets additional space at tail.
1382-
* Note: we overallocate on fragments with MSG_MODE
1383-
* because we have no idea if we're the last one.
1384-
*/
1385-
if (datalen == length + fraggap)
1386-
alloclen += rt->dst.trailer_len;
1409+
if (datalen != length + fraggap) {
1410+
/*
1411+
* this is not the last fragment, the trailer
1412+
* space is regarded as data space.
1413+
*/
1414+
datalen += rt->dst.trailer_len;
1415+
}
1416+
1417+
alloclen += rt->dst.trailer_len;
1418+
fraglen = datalen + fragheaderlen;
13871419

13881420
/*
13891421
* We just reserve space for fragment header.

net/xfrm/xfrm_policy.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
19211921
}
19221922
ok:
19231923
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;
19241927
return dst;
19251928

19261929
nopol:

0 commit comments

Comments
 (0)