Commit 7e3a42a12c4b ("xfrm6: handling fragment") stopped decoding IPv6 selector fields after a Fragment header. That is correct for real fragments because later fragments do not carry those fields, yet an atomic fragment is different because it contains the complete datagram, which allows XFRM to read its ports, ICMPv6 type and code, and GRE key. Leaving those fields zero lets a plaintext atomic fragment miss a policy that matches on one of them. Leave ports, ICMPv6 type and code, and GRE key unset for real fragments. For atomic fragments, treat the packet as a complete datagram during IPv6 session decoding and recover the ports at the transport offset found by the flow dissector. Fixes: 7e3a42a12c4b ("xfrm6: handling fragment") Cc: stable@vger.kernel.org Signed-off-by: Jérémy Jean --- net/xfrm/xfrm_policy.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index bda5100c9fdbf..24470dbd82639 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3526,9 +3526,25 @@ int __xfrm_decode_session(struct net *net, struct sk_buff *skb, struct flowi *fl decode_session4(&flkeys, fl, reverse); break; #if IS_ENABLED(CONFIG_IPV6) - case AF_INET6: + case AF_INET6: { + struct frag_hdr _fh, *fh; + unsigned int offset = 0; + + if ((flkeys.control.flags & FLOW_DIS_IS_FRAGMENT) && + ipv6_find_hdr(skb, &offset, NEXTHDR_FRAGMENT, + NULL, NULL) == NEXTHDR_FRAGMENT) { + fh = skb_header_pointer(skb, offset, sizeof(_fh), &_fh); + if (fh && !(fh->frag_off & htons(IP6_OFFSET | IP6_MF))) { + flkeys.control.flags &= ~FLOW_DIS_IS_FRAGMENT; + flkeys.ports.ports = skb_flow_get_ports(skb, + flkeys.control.thoff, + flkeys.basic.ip_proto, + NULL, 0); + } + } decode_session6(&flkeys, fl, reverse); break; + } #endif default: return -EAFNOSUPPORT; -- 2.47.3