LVS
lvs-devel
Google
 
Web LinuxVirtualServer.org

[PATCH nf] ipvs: ensure inner headers in ICMP errors are in headroom

To: Simon Horman <horms@xxxxxxxxxxxx>
Subject: [PATCH nf] ipvs: ensure inner headers in ICMP errors are in headroom
Cc: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>, Florian Westphal <fw@xxxxxxxxx>, lvs-devel@xxxxxxxxxxxxxxx, netfilter-devel@xxxxxxxxxxxxxxx
From: Julian Anastasov <ja@xxxxxx>
Date: Mon, 6 Jul 2026 18:10:41 +0300
Sashiko points out that after stripping the outer headers
with pskb_pull() we should ensure the inner IP headers
in ICMP errors from tunnels are present in the skb headroom
for functions like ipv4_update_pmtu(), icmp_send() and
IP_VS_DBG().

Also, add more checks for the length of the inner headers.

Fixes: f2edb9f7706d ("ipvs: implement passive PMTUD for IPIP packets")
Link: 
https://sashiko.dev/#/patchset/20260702073430.67680-1-zhaoyz24%40mails.tsinghua.edu.cn
Signed-off-by: Julian Anastasov <ja@xxxxxx>
---
 net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 906f2c361676..f332ba422a65 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff 
*skb, int *related,
        bool tunnel, new_cp = false;
        union nf_inet_addr *raddr;
        char *outer_proto = "IPIP";
+       unsigned int hlen_ipip = 0;
        int ulen = 0;
 
        *related = 1;
@@ -1822,9 +1823,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff 
*skb, int *related,
                /* Only for known tunnel */
                if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
                        return NF_ACCEPT;
-               offset += cih->ihl * 4;
+               hlen_ipip = cih->ihl * 4;
+               offset += hlen_ipip;
                cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
-               if (cih == NULL)
+               if (!(cih && cih->version == 4 && cih->ihl >= 5))
                        return NF_ACCEPT; /* The packet looks wrong, ignore */
                tunnel = true;
        } else if ((cih->protocol == IPPROTO_UDP ||     /* Can be UDP encap */
@@ -1836,7 +1838,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff 
*skb, int *related,
                /* Non-first fragment has no UDP/GRE header */
                if (unlikely(cih->frag_off & htons(IP_OFFSET)))
                        return NF_ACCEPT;
-               offset2 = offset + cih->ihl * 4;
+               hlen_ipip = cih->ihl * 4;
+               offset2 = offset + hlen_ipip;
                if (cih->protocol == IPPROTO_UDP) {
                        ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET,
                                              raddr, &iproto);
@@ -1905,6 +1908,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff 
*skb, int *related,
        }
 
        if (tunnel) {
+               unsigned int hlen_orig = cih->ihl * 4;
                __be32 info = ic->un.gateway;
                __u8 type = ic->type;
                __u8 code = ic->code;
@@ -1921,6 +1925,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff 
*skb, int *related,
                                goto ignore_tunnel;
                        offset2 -= ihl + sizeof(_icmph);
                        skb_reset_network_header(skb);
+                       /* Ensure the IP header is present in headroom */
+                       if (!pskb_may_pull(skb, hlen_ipip))
+                               goto ignore_tunnel;
                        IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n",
                                  outer_proto, &ip_hdr(skb)->saddr,
                                  &ip_hdr(skb)->daddr, mtu);
@@ -1936,8 +1943,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff 
*skb, int *related,
                                if (dest_dst)
                                        mtu = dst_mtu(dest_dst->dst_cache);
                        }
-                       if (mtu > 68 + sizeof(struct iphdr) + ulen)
-                               mtu -= sizeof(struct iphdr) + ulen;
+                       if (mtu > 68 + hlen_ipip + ulen)
+                               mtu -= hlen_ipip + ulen;
                        info = htonl(mtu);
                }
                /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of
@@ -1946,6 +1953,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff 
*skb, int *related,
                if (pskb_pull(skb, offset2) == NULL)
                        goto ignore_tunnel;
                skb_reset_network_header(skb);
+               /* Ensure the IP header is present in headroom */
+               if (!pskb_may_pull(skb, hlen_orig))
+                       goto ignore_tunnel;
                IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n",
                        &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
                        type, code, ntohl(info));
-- 
2.55.0




<Prev in Thread] Current Thread [Next in Thread>