ipv6_find_hdr() does not set the header offset for non-first fragments, so an ICMPv6 error embedding such an ESP fragment leaves ip_vs_fill_iph_skb_icmp() with len == off. The NAT paths size skb_ensure_writable() from len, and ip_vs_nat_icmp_v6() then writes the embedded addresses after the validated area. Include the embedded IP header in ciph.len at both ICMPv6 call sites when parsing stops at a non-first fragment. ESP lookup does not require a transport header. Non-first TCP, UDP and SCTP fragments bypass IPVS ICMP handling before connection lookup. This fix does not depend on patch 2. Stable kernels without commit 342e24a339b9 ("ipvs: do not mangle ICMP replies for non-first fragments") also need that commit to handle incoming ICMPv6 errors quoting non-first ESP fragments. Fixes: 63dca2c0b0e7 ("ipvs: Fix faulty IPv6 extension header handling in IPVS") Cc: stable@vger.kernel.org Suggested-by: Julian Anastasov Signed-off-by: Axel Mierczuk --- net/netfilter/ipvs/ip_vs_core.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index fd503f0efb57..cfd193196a5b 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1206,6 +1206,13 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, true, &ciph)) return NF_ACCEPT; /* The packet looks wrong, ignore */ + /* ipv6_find_hdr() does not include the embedded header for + * non-first fragments, add it so that ESP can pass and the + * NAT writable checks cover the rewritten addresses + */ + if (ciph.len == ciph.off) + ciph.len += sizeof(struct ipv6hdr); + pp = ip_vs_proto_get(ciph.protocol); if (!pp) return NF_ACCEPT; @@ -2036,6 +2043,13 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, if (!ip_vs_fill_iph_skb_icmp(AF_INET6, skb, offset, true, &ciph)) return NF_ACCEPT; + /* ipv6_find_hdr() does not include the embedded header for + * non-first fragments, add it so that ESP can pass and the + * NAT writable checks cover the rewritten addresses + */ + if (ciph.len == ciph.off) + ciph.len += sizeof(struct ipv6hdr); + pd = ip_vs_proto_data_get(ipvs, ciph.protocol); if (!pd) return NF_ACCEPT; -- 2.43.0