| From 4b52fcd74c50a3bfc086ebe2fce4e9eb38742931 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 16 Apr 2021 11:27:59 +0200 |
| Subject: xfrm: xfrm_state_mtu should return at least 1280 for ipv6 |
| |
| From: Sabrina Dubroca <sd@queasysnail.net> |
| |
| [ Upstream commit b515d2637276a3810d6595e10ab02c13bfd0b63a ] |
| |
| Jianwen reported that IPv6 Interoperability tests are failing in an |
| IPsec case where one of the links between the IPsec peers has an MTU |
| of 1280. The peer generates a packet larger than this MTU, the router |
| replies with a "Packet too big" message indicating an MTU of 1280. |
| When the peer tries to send another large packet, xfrm_state_mtu |
| returns 1280 - ipsec_overhead, which causes ip6_setup_cork to fail |
| with EINVAL. |
| |
| We can fix this by forcing xfrm_state_mtu to return IPV6_MIN_MTU when |
| IPv6 is used. After going through IPsec, the packet will then be |
| fragmented to obey the actual network's PMTU, just before leaving the |
| host. |
| |
| Currently, TFC padding is capped to PMTU - overhead to avoid |
| fragementation: after padding and encapsulation, we still fit within |
| the PMTU. That behavior is preserved in this patch. |
| |
| Fixes: 91657eafb64b ("xfrm: take net hdr len into account for esp payload size calculation") |
| Reported-by: Jianwen Ji <jiji@redhat.com> |
| Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> |
| Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/net/xfrm.h | 1 + |
| net/ipv4/esp4.c | 2 +- |
| net/ipv6/esp6.c | 2 +- |
| net/xfrm/xfrm_state.c | 14 ++++++++++++-- |
| 4 files changed, 15 insertions(+), 4 deletions(-) |
| |
| diff --git a/include/net/xfrm.h b/include/net/xfrm.h |
| index c58a6d4eb610..6232a5f048bd 100644 |
| --- a/include/net/xfrm.h |
| +++ b/include/net/xfrm.h |
| @@ -1546,6 +1546,7 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); |
| void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); |
| u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); |
| int xfrm_init_replay(struct xfrm_state *x); |
| +u32 __xfrm_state_mtu(struct xfrm_state *x, int mtu); |
| u32 xfrm_state_mtu(struct xfrm_state *x, int mtu); |
| int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload); |
| int xfrm_init_state(struct xfrm_state *x); |
| diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c |
| index 4b834bbf95e0..ed9857b2875d 100644 |
| --- a/net/ipv4/esp4.c |
| +++ b/net/ipv4/esp4.c |
| @@ -673,7 +673,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) |
| struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); |
| u32 padto; |
| |
| - padto = min(x->tfcpad, xfrm_state_mtu(x, dst->child_mtu_cached)); |
| + padto = min(x->tfcpad, __xfrm_state_mtu(x, dst->child_mtu_cached)); |
| if (skb->len < padto) |
| esp.tfclen = padto - skb->len; |
| } |
| diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c |
| index 4071cb7c7a15..8d001f665fb1 100644 |
| --- a/net/ipv6/esp6.c |
| +++ b/net/ipv6/esp6.c |
| @@ -708,7 +708,7 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) |
| struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); |
| u32 padto; |
| |
| - padto = min(x->tfcpad, xfrm_state_mtu(x, dst->child_mtu_cached)); |
| + padto = min(x->tfcpad, __xfrm_state_mtu(x, dst->child_mtu_cached)); |
| if (skb->len < padto) |
| esp.tfclen = padto - skb->len; |
| } |
| diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c |
| index 77499abd9f99..c158e70e8ae1 100644 |
| --- a/net/xfrm/xfrm_state.c |
| +++ b/net/xfrm/xfrm_state.c |
| @@ -2516,7 +2516,7 @@ void xfrm_state_delete_tunnel(struct xfrm_state *x) |
| } |
| EXPORT_SYMBOL(xfrm_state_delete_tunnel); |
| |
| -u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) |
| +u32 __xfrm_state_mtu(struct xfrm_state *x, int mtu) |
| { |
| const struct xfrm_type *type = READ_ONCE(x->type); |
| struct crypto_aead *aead; |
| @@ -2547,7 +2547,17 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) |
| return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - |
| net_adj) & ~(blksize - 1)) + net_adj - 2; |
| } |
| -EXPORT_SYMBOL_GPL(xfrm_state_mtu); |
| +EXPORT_SYMBOL_GPL(__xfrm_state_mtu); |
| + |
| +u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) |
| +{ |
| + mtu = __xfrm_state_mtu(x, mtu); |
| + |
| + if (x->props.family == AF_INET6 && mtu < IPV6_MIN_MTU) |
| + return IPV6_MIN_MTU; |
| + |
| + return mtu; |
| +} |
| |
| int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) |
| { |
| -- |
| 2.30.2 |
| |