Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c183379 authored by Gao feng's avatar Gao feng Committed by David S. Miller
Browse files

ipv6: fix incorrect ipsec fragment



Since commit ad0081e4
"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: default avatarGao feng <gaofeng@cn.fujitsu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 91657eaf
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -60,6 +60,7 @@ struct dst_entry {
#define DST_NOCOUNT		0x0020
#define DST_NOCOUNT		0x0020
#define DST_NOPEER		0x0040
#define DST_NOPEER		0x0040
#define DST_FAKE_RTABLE		0x0080
#define DST_FAKE_RTABLE		0x0080
#define DST_XFRM_TUNNEL		0x0100


	short			error;
	short			error;
	short			obsolete;
	short			obsolete;
+50 −18
Original line number Original line Diff line number Diff line
@@ -1187,6 +1187,29 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
	return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
	return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
}
}


static void ip6_append_data_mtu(int *mtu,
				int *maxfraglen,
				unsigned int fragheaderlen,
				struct sk_buff *skb,
				struct rt6_info *rt)
{
	if (!(rt->dst.flags & DST_XFRM_TUNNEL)) {
		if (skb == NULL) {
			/* first fragment, reserve header_len */
			*mtu = *mtu - rt->dst.header_len;

		} else {
			/*
			 * this fragment is not first, the headers
			 * space is regarded as data space.
			 */
			*mtu = dst_mtu(rt->dst.path);
		}
		*maxfraglen = ((*mtu - fragheaderlen) & ~7)
			      + fragheaderlen - sizeof(struct frag_hdr);
	}
}

int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
	int offset, int len, int odd, struct sk_buff *skb),
	int offset, int len, int odd, struct sk_buff *skb),
	void *from, int length, int transhdrlen,
	void *from, int length, int transhdrlen,
@@ -1196,7 +1219,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
	struct inet_sock *inet = inet_sk(sk);
	struct inet_sock *inet = inet_sk(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct inet_cork *cork;
	struct inet_cork *cork;
	struct sk_buff *skb;
	struct sk_buff *skb, *skb_prev = NULL;
	unsigned int maxfraglen, fragheaderlen;
	unsigned int maxfraglen, fragheaderlen;
	int exthdrlen;
	int exthdrlen;
	int dst_exthdrlen;
	int dst_exthdrlen;
@@ -1253,8 +1276,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
		inet->cork.fl.u.ip6 = *fl6;
		inet->cork.fl.u.ip6 = *fl6;
		np->cork.hop_limit = hlimit;
		np->cork.hop_limit = hlimit;
		np->cork.tclass = tclass;
		np->cork.tclass = tclass;
		if (rt->dst.flags & DST_XFRM_TUNNEL)
			mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
			mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
			      rt->dst.dev->mtu : dst_mtu(&rt->dst);
			      rt->dst.dev->mtu : dst_mtu(&rt->dst);
		else
			mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
			      rt->dst.dev->mtu : dst_mtu(rt->dst.path);
		if (np->frag_size < mtu) {
		if (np->frag_size < mtu) {
			if (np->frag_size)
			if (np->frag_size)
				mtu = np->frag_size;
				mtu = np->frag_size;
@@ -1350,25 +1377,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
			unsigned int fraglen;
			unsigned int fraglen;
			unsigned int fraggap;
			unsigned int fraggap;
			unsigned int alloclen;
			unsigned int alloclen;
			struct sk_buff *skb_prev;
alloc_new_skb:
alloc_new_skb:
			skb_prev = skb;

			/* There's no room in the current skb */
			/* There's no room in the current skb */
			if (skb_prev)
			if (skb)
				fraggap = skb_prev->len - maxfraglen;
				fraggap = skb->len - maxfraglen;
			else
			else
				fraggap = 0;
				fraggap = 0;
			/* update mtu and maxfraglen if necessary */
			if (skb == NULL || skb_prev == NULL)
				ip6_append_data_mtu(&mtu, &maxfraglen,
						    fragheaderlen, skb, rt);

			skb_prev = skb;


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


			fraglen = datalen + fragheaderlen;
			if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
				datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len;
			if ((flags & MSG_MORE) &&
			if ((flags & MSG_MORE) &&
			    !(rt->dst.dev->features&NETIF_F_SG))
			    !(rt->dst.dev->features&NETIF_F_SG))
				alloclen = mtu;
				alloclen = mtu;
@@ -1377,13 +1406,16 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,


			alloclen += dst_exthdrlen;
			alloclen += dst_exthdrlen;


			if (datalen != length + fraggap) {
				/*
				/*
			 * The last fragment gets additional space at tail.
				 * this is not the last fragment, the trailer
			 * Note: we overallocate on fragments with MSG_MODE
				 * space is regarded as data space.
			 * because we have no idea if we're the last one.
				 */
				 */
			if (datalen == length + fraggap)
				datalen += rt->dst.trailer_len;
			}

			alloclen += rt->dst.trailer_len;
			alloclen += rt->dst.trailer_len;
			fraglen = datalen + fragheaderlen;


			/*
			/*
			 * We just reserve space for fragment header.
			 * We just reserve space for fragment header.
+3 −0
Original line number Original line Diff line number Diff line
@@ -1921,6 +1921,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
	}
	}
ok:
ok:
	xfrm_pols_put(pols, drop_pols);
	xfrm_pols_put(pols, drop_pols);
	if (dst && dst->xfrm &&
	    dst->xfrm->props.mode == XFRM_MODE_TUNNEL)
		dst->flags |= DST_XFRM_TUNNEL;
	return dst;
	return dst;


nopol:
nopol: