2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* xfrm6_output.c - Common IPsec encapsulation code for IPv6.
|
|
|
|
* Copyright (C) 2002 USAGI/WIDE Project
|
|
|
|
* Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
|
|
|
|
*/
|
|
|
|
|
2007-10-09 08:16:30 +08:00
|
|
|
#include <linux/if_ether.h>
|
2007-11-14 13:40:52 +08:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/icmpv6.h>
|
2006-01-07 15:01:48 +08:00
|
|
|
#include <linux/netfilter_ipv6.h>
|
2007-11-14 13:40:52 +08:00
|
|
|
#include <net/dst.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/ipv6.h>
|
2010-12-17 19:42:42 +08:00
|
|
|
#include <net/ip6_route.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/xfrm.h>
|
|
|
|
|
2020-05-04 16:06:06 +08:00
|
|
|
void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
|
2011-10-11 09:44:30 +08:00
|
|
|
{
|
|
|
|
struct flowi6 fl6;
|
|
|
|
struct sock *sk = skb->sk;
|
|
|
|
|
|
|
|
fl6.flowi6_oif = sk->sk_bound_dev_if;
|
2011-11-21 11:39:03 +08:00
|
|
|
fl6.daddr = ipv6_hdr(skb)->daddr;
|
2011-10-11 09:44:30 +08:00
|
|
|
|
|
|
|
ipv6_local_rxpmtu(sk, &fl6, mtu);
|
|
|
|
}
|
|
|
|
|
2013-08-14 19:05:23 +08:00
|
|
|
void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
|
2011-10-11 09:44:30 +08:00
|
|
|
{
|
|
|
|
struct flowi6 fl6;
|
2013-08-18 19:46:57 +08:00
|
|
|
const struct ipv6hdr *hdr;
|
2011-10-11 09:44:30 +08:00
|
|
|
struct sock *sk = skb->sk;
|
|
|
|
|
2013-08-18 19:46:57 +08:00
|
|
|
hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
|
2011-10-11 09:44:30 +08:00
|
|
|
fl6.fl6_dport = inet_sk(sk)->inet_dport;
|
2013-08-18 19:46:57 +08:00
|
|
|
fl6.daddr = hdr->daddr;
|
2011-10-11 09:44:30 +08:00
|
|
|
|
|
|
|
ipv6_local_error(sk, EMSGSIZE, &fl6, mtu);
|
|
|
|
}
|
|
|
|
|
2015-06-13 11:12:04 +08:00
|
|
|
static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
2020-05-04 16:06:09 +08:00
|
|
|
return xfrm_output(sk, skb);
|
2015-06-13 11:12:04 +08:00
|
|
|
}
|
|
|
|
|
xfrm: fix tunnel model fragmentation behavior
in tunnel mode, if outer interface(ipv4) is less, it is easily to let
inner IPV6 mtu be less than 1280. If so, a Packet Too Big ICMPV6 message
is received. When send again, packets are fragmentized with 1280, they
are still rejected with ICMPV6(Packet Too Big) by xfrmi_xmit2().
According to RFC4213 Section3.2.2:
if (IPv4 path MTU - 20) is less than 1280
if packet is larger than 1280 bytes
Send ICMPv6 "packet too big" with MTU=1280
Drop packet
else
Encapsulate but do not set the Don't Fragment
flag in the IPv4 header. The resulting IPv4
packet might be fragmented by the IPv4 layer
on the encapsulator or by some router along
the IPv4 path.
endif
else
if packet is larger than (IPv4 path MTU - 20)
Send ICMPv6 "packet too big" with
MTU = (IPv4 path MTU - 20).
Drop packet.
else
Encapsulate and set the Don't Fragment flag
in the IPv4 header.
endif
endif
Packets should be fragmentized with ipv4 outer interface, so change it.
After it is fragemtized with ipv4, there will be double fragmenation.
No.48 & No.51 are ipv6 fragment packets, No.48 is double fragmentized,
then tunneled with IPv4(No.49& No.50), which obey spec. And received peer
cannot decrypt it rightly.
48 2002::10 2002::11 1296(length) IPv6 fragment (off=0 more=y ident=0xa20da5bc nxt=50)
49 0x0000 (0) 2002::10 2002::11 1304 IPv6 fragment (off=0 more=y ident=0x7448042c nxt=44)
50 0x0000 (0) 2002::10 2002::11 200 ESP (SPI=0x00035000)
51 2002::10 2002::11 180 Echo (ping) request
52 0x56dc 2002::10 2002::11 248 IPv6 fragment (off=1232 more=n ident=0xa20da5bc nxt=50)
xfrm6_noneed_fragment has fixed above issues. Finally, it acted like below:
1 0x6206 192.168.1.138 192.168.1.1 1316 Fragmented IP protocol (proto=Encap Security Payload 50, off=0, ID=6206) [Reassembled in #2]
2 0x6206 2002::10 2002::11 88 IPv6 fragment (off=0 more=y ident=0x1f440778 nxt=50)
3 0x0000 2002::10 2002::11 248 ICMPv6 Echo (ping) request
Signed-off-by: Lina Wang <lina.wang@mediatek.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2022-02-26 15:48:01 +08:00
|
|
|
static int xfrm6_noneed_fragment(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct frag_hdr *fh;
|
|
|
|
u8 prevhdr = ipv6_hdr(skb)->nexthdr;
|
|
|
|
|
|
|
|
if (prevhdr != NEXTHDR_FRAGMENT)
|
|
|
|
return 0;
|
|
|
|
fh = (struct frag_hdr *)(skb->data + sizeof(struct ipv6hdr));
|
|
|
|
if (fh->nexthdr == NEXTHDR_ESP || fh->nexthdr == NEXTHDR_AUTH)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-16 09:04:18 +08:00
|
|
|
static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
2010-12-17 19:42:42 +08:00
|
|
|
{
|
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
|
|
|
struct xfrm_state *x = dst->xfrm;
|
2021-07-02 07:47:00 +08:00
|
|
|
unsigned int mtu;
|
2015-09-04 13:21:06 +08:00
|
|
|
bool toobig;
|
2013-08-13 10:35:58 +08:00
|
|
|
|
2014-04-07 14:08:52 +08:00
|
|
|
#ifdef CONFIG_NETFILTER
|
|
|
|
if (!x) {
|
|
|
|
IP6CB(skb)->flags |= IP6SKB_REROUTED;
|
2015-10-08 05:48:35 +08:00
|
|
|
return dst_output(net, sk, skb);
|
2014-04-07 14:08:52 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-09-04 13:21:06 +08:00
|
|
|
if (x->props.mode != XFRM_MODE_TUNNEL)
|
|
|
|
goto skip_frag;
|
|
|
|
|
2013-08-26 18:31:19 +08:00
|
|
|
if (skb->protocol == htons(ETH_P_IPV6))
|
|
|
|
mtu = ip6_skb_dst_mtu(skb);
|
|
|
|
else
|
|
|
|
mtu = dst_mtu(skb_dst(skb));
|
2011-10-11 09:44:30 +08:00
|
|
|
|
2015-09-04 13:21:06 +08:00
|
|
|
toobig = skb->len > mtu && !skb_is_gso(skb);
|
|
|
|
|
2020-05-04 16:06:07 +08:00
|
|
|
if (toobig && xfrm6_local_dontfrag(skb->sk)) {
|
2011-10-11 09:44:30 +08:00
|
|
|
xfrm6_local_rxpmtu(skb, mtu);
|
2018-08-31 19:38:49 +08:00
|
|
|
kfree_skb(skb);
|
2011-10-11 09:44:30 +08:00
|
|
|
return -EMSGSIZE;
|
xfrm: fix tunnel model fragmentation behavior
in tunnel mode, if outer interface(ipv4) is less, it is easily to let
inner IPV6 mtu be less than 1280. If so, a Packet Too Big ICMPV6 message
is received. When send again, packets are fragmentized with 1280, they
are still rejected with ICMPV6(Packet Too Big) by xfrmi_xmit2().
According to RFC4213 Section3.2.2:
if (IPv4 path MTU - 20) is less than 1280
if packet is larger than 1280 bytes
Send ICMPv6 "packet too big" with MTU=1280
Drop packet
else
Encapsulate but do not set the Don't Fragment
flag in the IPv4 header. The resulting IPv4
packet might be fragmented by the IPv4 layer
on the encapsulator or by some router along
the IPv4 path.
endif
else
if packet is larger than (IPv4 path MTU - 20)
Send ICMPv6 "packet too big" with
MTU = (IPv4 path MTU - 20).
Drop packet.
else
Encapsulate and set the Don't Fragment flag
in the IPv4 header.
endif
endif
Packets should be fragmentized with ipv4 outer interface, so change it.
After it is fragemtized with ipv4, there will be double fragmenation.
No.48 & No.51 are ipv6 fragment packets, No.48 is double fragmentized,
then tunneled with IPv4(No.49& No.50), which obey spec. And received peer
cannot decrypt it rightly.
48 2002::10 2002::11 1296(length) IPv6 fragment (off=0 more=y ident=0xa20da5bc nxt=50)
49 0x0000 (0) 2002::10 2002::11 1304 IPv6 fragment (off=0 more=y ident=0x7448042c nxt=44)
50 0x0000 (0) 2002::10 2002::11 200 ESP (SPI=0x00035000)
51 2002::10 2002::11 180 Echo (ping) request
52 0x56dc 2002::10 2002::11 248 IPv6 fragment (off=1232 more=n ident=0xa20da5bc nxt=50)
xfrm6_noneed_fragment has fixed above issues. Finally, it acted like below:
1 0x6206 192.168.1.138 192.168.1.1 1316 Fragmented IP protocol (proto=Encap Security Payload 50, off=0, ID=6206) [Reassembled in #2]
2 0x6206 2002::10 2002::11 88 IPv6 fragment (off=0 more=y ident=0x1f440778 nxt=50)
3 0x0000 2002::10 2002::11 248 ICMPv6 Echo (ping) request
Signed-off-by: Lina Wang <lina.wang@mediatek.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2022-02-26 15:48:01 +08:00
|
|
|
} else if (toobig && xfrm6_noneed_fragment(skb)) {
|
|
|
|
skb->ignore_df = 1;
|
|
|
|
goto skip_frag;
|
2015-09-04 13:21:06 +08:00
|
|
|
} else if (!skb->ignore_df && toobig && skb->sk) {
|
2013-08-14 19:05:23 +08:00
|
|
|
xfrm_local_error(skb, mtu);
|
2018-08-31 19:38:49 +08:00
|
|
|
kfree_skb(skb);
|
2011-10-11 09:44:30 +08:00
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
2010-12-17 19:42:42 +08:00
|
|
|
|
2023-10-24 22:26:33 +08:00
|
|
|
if (toobig)
|
2015-06-13 11:12:04 +08:00
|
|
|
return ip6_fragment(net, sk, skb,
|
|
|
|
__xfrm6_output_finish);
|
2015-09-04 13:21:06 +08:00
|
|
|
|
|
|
|
skip_frag:
|
2020-05-04 16:06:09 +08:00
|
|
|
return xfrm_output(sk, skb);
|
2010-12-17 19:42:42 +08:00
|
|
|
}
|
|
|
|
|
2015-10-08 05:48:47 +08:00
|
|
|
int xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
2006-01-07 15:01:48 +08:00
|
|
|
{
|
2015-09-16 09:04:16 +08:00
|
|
|
return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
|
2019-11-13 00:14:37 +08:00
|
|
|
net, sk, skb, skb->dev, skb_dst(skb)->dev,
|
2015-09-16 09:04:16 +08:00
|
|
|
__xfrm6_output,
|
2014-04-07 14:08:52 +08:00
|
|
|
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
|
2006-01-07 15:01:48 +08:00
|
|
|
}
|