From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=217.70.190.124 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794532521; b=AF3QKT9KNkK8ciwqV7RTkP8ruc8/rVt0INW3zGDnELAS2WL1k+89orJYkyprXJeRG0DF YI5nbb56TFCOjS7ORQF4W4LznJLAKhrR6NsO+XDerKDWhxdfNbJuRaEV6wGojQERFgpcO PXySHFyQVecQnEmQco4lBvOMuyDosvD+WJ9Oeg4nJLZjX1SfurCk52z33nlmGBDULRmVm QzXeq4su59hIBRjSo6UH6uvBtGwlD9DLpiuqSOD8dPfM62w6ybMMLj867DyYEAMoaGeZ8 zL+KdwriiyG+atbjvLn41MOdIYrE976QRCcrlgczOSNW/FmkwQrc3TXni+H3usF5SYA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794532521; h=DMARC-Filter:DKIM-Signature:Received:From:To:Cc:Subject:Date: Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding:X-Spam-Status:X-Spam-Checker-Version; bh=OOfLGHv9MHQwrFtifX4dpFuCyuxbPUVu6wmCjsZ3K4I=; b=Nn/me0+GhuLwcGbsTE8MFtZdyYNrnlNEPk8VxW+PoJO55i/LCm0gcZ1iT46lbYjBeFIK Jdnz+xmJbOwiDmo1SfHGIFs0kpiY9wNenrL+KHr7pjpvIiYWshqgruF4HmFRGAc6aa37D mfg2vWGfJRllewkwG00p2DSjZ7nrp09wtqZR5D/kasNAHCKo7tw9HmLu8QipTvzSEB26R by/GwfJO6Cn2KT16atzB3MKJYwixSlgAU5Q+BXwbWARMf4OLnGIjqvmt6+xupZa1PJJmO mea88DtCGts2BETmH4v1AS5KzmXOudWCfnDMz5AGbO/3kKqO48WKFe09SEp4LrrgSHQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none header.from=netfilter.org; spf=pass smtp.mailfrom=netfilter.org; dkim=pass header.d=netfilter.org header.i=@netfilter.org header.a=rsa-sha256 header.s=2025 header.b=vgu6eGPs; arc=none smtp.remote-ip=217.70.190.124 Received: from mail.netfilter.org (mail.netfilter.org [217.70.190.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8ABFE60A3822; Fri, 13 Nov 2026 01:15:20 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 8ABFE60A3822 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=netfilter.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=netfilter.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=netfilter.org header.i=@netfilter.org header.a=rsa-sha256 header.s=2025 header.b=vgu6eGPs DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=netfilter.org; s=2025; t=1794532517; bh=OOfLGHv9MHQwrFtifX4dpFuCyuxbPUVu6wmCjsZ3K4I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vgu6eGPs95l+g7rZlbCLOrxSFe69gYYyRDsZTEvn+nKW91Gal6ZBUSZbebI6+jShQ oThljHEVetV81+zNjQc6VIQN3F03LiY7owxrHR2UesPJH1WK67Wed885omcnVXy7pd PGjnJWCUa7Z48hLZbwWWHxoabUiGts/jV3JPtuh/gSKwWhvUDgC3IXv75BM3c9JYYV fZfKHl8EswiibhMR8ygBhlCo+cjG9CJVsj3gpDvSkPd+QDu0Pm4CHM+r77OzavfeCm LdhILOY9NS49UeaHKzycqHj9aQ7anAOKam4SmrvCYpelPrWtaHCeWSPqVF/QGvW7Pq GpH+md9M2WceQ== Received: from localhost.localdomain (mail-agni [217.70.190.124]) by mail.netfilter.org (Postfix) with ESMTPSA id 339686005C; Fri, 13 Nov 2026 02:15:17 +0100 (CET) From: Pablo Neira Ayuso To: netfilter-devel@vger.kernel.org Cc: davem@davemloft.net, netdev@vger.kernel.org, kuba@kernel.org, pabeni@redhat.com, edumazet@kernel.org, horms@kernel.org, fw@strlen.de, ja@ssi.bg Subject: [PATCH net 15/15] ipvs: pass correct sk to update_pmtu and on tunneling Date: Fri, 13 Nov 2026 02:14:47 +0100 Message-ID: <20261113011447.2357709-16-pablo@netfilter.org> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261113011447.2357709-1-pablo@netfilter.org> References: <20261113011447.2357709-1-pablo@netfilter.org> X-Mailing-List: netdev@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Julian Anastasov This commit fixes two cases of providing sk on tunneling, one to the update_pmtu handler and the second case is to the ip_local_out() call. For local clients, we can see at LOCAL_OUT a netfilter hook's state->sk that is different from the protocol's skb->sk. This can be the case for UDP_TUNNEL where different sk is provided to ip_local_out. If such packet is then forwarded via IPVS tunnel we may need to provide state->sk to the update_pmtu handler, not skb->sk. Fix that by passing the netfilter's hook state via the packet_xmit and conn_schedule handlers to call the update_pmtu handler with state->sk. While here, change maybe_update_pmtu() to be family independent by using dst and do not request neigh confirm via the last update_pmtu arg because we do not generate the event based on remote signal. Sashiko has some warnings for the update_pmtu handlers because we provide only sk, not skb: - we do not provide skb because the handlers assume they are called from tunnel transmitters where skb->dev is present but for IPVS skb->dev is not set yet - without skb, the zero destination address for unconnected sockets can be blindly used for FIB nexthop or route exceptions. So, we now restrict the sk to be for TCP_ESTABLISHED state where the sk is connected to destination address. - as we provide the original sk (from state->sk) for the original IP header, make sure we do not provide sk from one family to the update_pmtu handler for different family. The second problem is that on tunnel forwarding we call ip_local_out() with skb->sk. As we push our own outer IP header in ip_vs_tunnel_xmit*(), the right thing is to provide NULL instead. For consistency, even if we do not walk the netfilter hooks and just pass the packet with NF_ACCEPT, provide ip_vs_null_xmit_v6 handler to properly indicate that we are sending IPv6 packet. Before now we used ip_vs_null_xmit for both families. Link: https://sashiko.dev/#/patchset/20261103152550.60449-1-ja%40ssi.bg Fixes: 79b16aadea32 ("udp_tunnel: Pass UDP socket down through udp_tunnel{, 6}_xmit_skb().") Fixes: 7026b1ddb6b8 ("netfilter: Pass socket pointer down through okfn().") Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 45 +++++++--- net/netfilter/ipvs/ip_vs_conn.c | 2 +- net/netfilter/ipvs/ip_vs_core.c | 41 +++++---- net/netfilter/ipvs/ip_vs_proto_ah_esp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_sctp.c | 5 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 5 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 5 +- net/netfilter/ipvs/ip_vs_xmit.c | 114 ++++++++++++++++-------- 8 files changed, 141 insertions(+), 78 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 8dc80d5d42dc..fdb3a9063e60 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -731,7 +731,8 @@ struct ip_vs_protocol { int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp, - struct ip_vs_iphdr *iph); + struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); struct ip_vs_conn * (*conn_in_get)(struct netns_ipvs *ipvs, @@ -872,7 +873,8 @@ struct ip_vs_conn { * NF_ACCEPT can be returned when destination is local. */ int (*packet_xmit)(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); struct rcu_head rcu_head; }; @@ -1872,7 +1874,8 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *ignored, struct ip_vs_iphdr *iph); int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, - struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph); + struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg); @@ -1987,32 +1990,46 @@ static inline int ip_vs_est_max_threads(struct netns_ipvs *ipvs) /* Various IPVS packet transmitters (from ip_vs_xmit.c) */ int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int hooknum, struct ip_vs_iphdr *ciph); + const struct nf_hook_state *state, + struct ip_vs_iphdr *ciph); void ip_vs_dest_dst_rcu_free(struct rcu_head *head); #ifdef CONFIG_IP_VS_IPV6 +int ip_vs_null_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state); int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int hooknum, struct ip_vs_iphdr *ciph); + const struct nf_hook_state *state, + struct ip_vs_iphdr *ciph); #endif #ifdef CONFIG_SYSCTL diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 83d8a9153a34..7cb8de634f5f 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1057,7 +1057,7 @@ static inline void ip_vs_bind_xmit(struct ip_vs_conn *cp) break; case IP_VS_CONN_F_LOCALNODE: - cp->packet_xmit = ip_vs_null_xmit; + cp->packet_xmit = ip_vs_null_xmit_v6; break; case IP_VS_CONN_F_BYPASS: diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 15617bd19e96..85ea82367cfe 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -760,7 +760,8 @@ static inline int ip_vs_addr_is_unicast(struct net *net, int af, * no destination is available for a new connection. */ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, - struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph) + struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph, + const struct nf_hook_state *state) { __be16 _ports[2], *pptr, dport; struct netns_ipvs *ipvs = svc->ipvs; @@ -805,7 +806,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len); /* transmit the first SYN packet */ - ret = cp->packet_xmit(skb, cp, pd->pp, iph); + ret = cp->packet_xmit(skb, cp, pd->pp, iph, state); /* do not touch skb anymore */ if ((cp->flags & IP_VS_CONN_F_ONE_PACKET) && cp->control) @@ -1655,7 +1656,8 @@ static unsigned int ip_vs_try_to_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp, - struct ip_vs_iphdr *iph) + struct ip_vs_iphdr *iph, + const struct nf_hook_state *state) { struct ip_vs_protocol *pp = pd->pp; @@ -1665,7 +1667,8 @@ ip_vs_try_to_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, */ /* Schedule and create new connection entry into cpp */ - if (!pp->conn_schedule(ipvs, af, skb, pd, verdict, cpp, iph)) + if (!pp->conn_schedule(ipvs, af, skb, pd, verdict, cpp, iph, + state)) return 0; } @@ -1765,7 +1768,7 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, */ static int ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, - unsigned int hooknum, struct ip_vs_iphdr *iph) + const struct nf_hook_state *state, struct ip_vs_iphdr *iph) { struct icmphdr _icmph, *ic; struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ @@ -1784,7 +1787,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* reassemble IP fragments */ if (ip_is_fragment(ip_hdr(skb))) { - if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + if (ip_vs_gather_frags(ipvs, skb, + ip_vs_defrag_user(state->hook))) return NF_STOLEN; if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) return NF_ACCEPT; @@ -1901,7 +1905,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (tunnel || !sysctl_schedule_icmp(ipvs)) return NF_ACCEPT; - if (!ip_vs_try_to_schedule(ipvs, AF_INET, skb, pd, &v, &cp, &ciph)) + if (!ip_vs_try_to_schedule(ipvs, AF_INET, skb, pd, &v, &cp, + &ciph, state)) return v; new_cp = true; } @@ -1989,7 +1994,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); - verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); + verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, state, &ciph); out: if (likely(!new_cp)) @@ -2002,7 +2007,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, #ifdef CONFIG_IP_VS_IPV6 static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, - int *related, unsigned int hooknum, + int *related, const struct nf_hook_state *state, struct ip_vs_iphdr *iph) { struct icmp6hdr _icmph, *ic; @@ -2075,7 +2080,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, if (!sysctl_schedule_icmp(ipvs)) return NF_ACCEPT; - if (!ip_vs_try_to_schedule(ipvs, AF_INET6, skb, pd, &v, &cp, &ciph)) + if (!ip_vs_try_to_schedule(ipvs, AF_INET6, skb, pd, &v, &cp, + &ciph, state)) return v; new_cp = true; @@ -2096,7 +2102,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); - verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); + verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, state, &ciph); out: if (likely(!new_cp)) @@ -2165,7 +2171,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { int related; int verdict = ip_vs_in_icmp_v6(ipvs, skb, &related, - hooknum, &iph); + state, &iph); if (related) return verdict; @@ -2175,7 +2181,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state if (unlikely(iph.protocol == IPPROTO_ICMP)) { int related; int verdict = ip_vs_in_icmp(ipvs, skb, &related, - hooknum, &iph); + state, &iph); if (related) return verdict; @@ -2263,7 +2269,8 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state if (unlikely(!cp)) { int v; - if (!ip_vs_try_to_schedule(ipvs, af, skb, pd, &v, &cp, &iph)) + if (!ip_vs_try_to_schedule(ipvs, af, skb, pd, &v, &cp, &iph, + state)) return v; } @@ -2272,7 +2279,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state ip_vs_in_stats(cp, skb); ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len); if (cp->packet_xmit) - ret = cp->packet_xmit(skb, cp, pp, &iph); + ret = cp->packet_xmit(skb, cp, pp, &iph, state); /* do not touch skb anymore */ else { IP_VS_DBG_RL("warning: packet_xmit is null"); @@ -2335,11 +2342,11 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, if (iphdr.protocol != IPPROTO_ICMPV6) return NF_ACCEPT; - return ip_vs_in_icmp_v6(ipvs, skb, &r, state->hook, &iphdr); + return ip_vs_in_icmp_v6(ipvs, skb, &r, state, &iphdr); #endif } - return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); + return ip_vs_in_icmp(ipvs, skb, &r, state, &iphdr); } static const struct nf_hook_ops ip_vs_ops4[] = { diff --git a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c index 44e14acc187e..c7f90b29be51 100644 --- a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c +++ b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c @@ -102,7 +102,7 @@ static int ah_esp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp, - struct ip_vs_iphdr *iph) + struct ip_vs_iphdr *iph, const struct nf_hook_state *state) { /* * AH/ESP is only related traffic. Pass the packet to IP stack. diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index cb34bcceb4f6..3d773c881f75 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -17,7 +17,7 @@ static int sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp, - struct ip_vs_iphdr *iph) + struct ip_vs_iphdr *iph, const struct nf_hook_state *state) { struct ip_vs_service *svc; struct sctp_chunkhdr _schunkh, *sch; @@ -78,7 +78,8 @@ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph); if (!*cpp && ignored <= 0) { if (!ignored) - *verdict = ip_vs_leave(svc, skb, pd, iph); + *verdict = ip_vs_leave(svc, skb, pd, iph, + state); else *verdict = NF_DROP; return 0; diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 25d4576d0e12..13ac917a0ac8 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -35,7 +35,7 @@ static int tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp, - struct ip_vs_iphdr *iph) + struct ip_vs_iphdr *iph, const struct nf_hook_state *state) { struct ip_vs_service *svc; struct tcphdr _tcph, *th; @@ -97,7 +97,8 @@ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph); if (!*cpp && ignored <= 0) { if (!ignored) - *verdict = ip_vs_leave(svc, skb, pd, iph); + *verdict = ip_vs_leave(svc, skb, pd, iph, + state); else *verdict = NF_DROP; return 0; diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index ab3ab9524168..760b019e6a06 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -31,7 +31,7 @@ static int udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp, - struct ip_vs_iphdr *iph) + struct ip_vs_iphdr *iph, const struct nf_hook_state *state) { struct ip_vs_service *svc; struct udphdr _udph, *uh; @@ -84,7 +84,8 @@ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph); if (!*cpp && ignored <= 0) { if (!ignored) - *verdict = ip_vs_leave(svc, skb, pd, iph); + *verdict = ip_vs_leave(svc, skb, pd, iph, + state); else *verdict = NF_DROP; return 0; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index ebc6b697ae51..c777493eee74 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -205,13 +205,24 @@ static inline bool crosses_local_route_boundary(int skb_af, struct sk_buff *skb, return false; } -static inline void maybe_update_pmtu(int skb_af, struct sk_buff *skb, int mtu) +static inline void maybe_update_pmtu(int skb_af, struct sk_buff *skb, int mtu, + const struct nf_hook_state *state) { - struct sock *sk = skb->sk; - struct rtable *ort = skb_rtable(skb); - - if (!skb->dev && sk && sk_fullsock(sk)) - ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu, true); + struct dst_entry *dst = skb_dst(skb); + struct sock *sk = state->sk; + + /* The update_pmtu handlers are missing some checks for: + * - skb->dev: present for tunnels, not for IPVS, so we do not + * provide skb, only sk + * - present daddr: so, we require TCP_ESTABLISHED (connected socket) + * + * In some states the senders ignore the PMTU events: + * - sk_fullsock(): so use the TCP_ESTABLISHED check + */ + if (sk && sk->sk_state == TCP_ESTABLISHED && + state->hook == NF_INET_LOCAL_OUT && + sk->sk_family == dst->ops->family) + dst->ops->update_pmtu(dst, sk, NULL, mtu, false); } static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af, @@ -316,7 +327,7 @@ static int __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, __be32 daddr, int rt_mode, __be32 *ret_saddr, - struct ip_vs_iphdr *ipvsh) + struct ip_vs_iphdr *ipvsh, const struct nf_hook_state *state) { struct net *net = ipvs->net; struct ip_vs_dest_dst *dest_dst; @@ -417,7 +428,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__); goto err_put; } - maybe_update_pmtu(skb_af, skb, mtu); + maybe_update_pmtu(skb_af, skb, mtu, state); } if (!ensure_mtu_is_adequate(ipvs, skb_af, rt_mode, ipvsh, skb, mtu)) @@ -489,7 +500,8 @@ static int __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, struct in6_addr *daddr, struct in6_addr *ret_saddr, - struct ip_vs_iphdr *ipvsh, int do_xfrm, int rt_mode) + struct ip_vs_iphdr *ipvsh, int do_xfrm, int rt_mode, + const struct nf_hook_state *state) { struct net *net = ipvs->net; struct ip_vs_dest_dst *dest_dst; @@ -596,7 +608,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, IPV6_MIN_MTU); goto err_put; } - maybe_update_pmtu(skb_af, skb, mtu); + maybe_update_pmtu(skb_af, skb, mtu, state); } if (!ensure_mtu_is_adequate(ipvs, skb_af, rt_mode, ipvsh, skb, mtu)) @@ -720,12 +732,26 @@ static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb, */ int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { /* we do not touch skb and do not need pskb ptr */ return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1); } +#ifdef CONFIG_IP_VS_IPV6 +/* + * NULL transmitter (do nothing except return NF_ACCEPT) + */ +int +ip_vs_null_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) +{ + /* we do not touch skb and do not need pskb ptr */ + return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1); +} +#endif /* * Bypass transmitter @@ -734,10 +760,11 @@ ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, */ int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, ip_hdr(skb)->daddr, - IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0) + IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh, state) < 0) goto tx_error; ip_send_check(ip_hdr(skb)); @@ -757,13 +784,15 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { struct ipv6hdr *iph = ipv6_hdr(skb); if (__ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, NULL, &iph->daddr, NULL, - ipvsh, 0, IP_VS_RT_MODE_NON_LOCAL) < 0) + ipvsh, 0, IP_VS_RT_MODE_NON_LOCAL, + state) < 0) goto tx_error; /* Another hack: avoid icmp_send in ip_fragment */ @@ -785,7 +814,8 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, */ int ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { struct rtable *rt; /* Route to the other host */ int local, rc, was_input; @@ -805,7 +835,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | - IP_VS_RT_MODE_RDR, NULL, ipvsh); + IP_VS_RT_MODE_RDR, NULL, ipvsh, state); if (local < 0) goto tx_error; rt = skb_rtable(skb); @@ -869,7 +899,8 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { struct rt6_info *rt; /* Route to the other host */ int local, rc; @@ -889,7 +920,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, NULL, ipvsh, 0, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | - IP_VS_RT_MODE_RDR); + IP_VS_RT_MODE_RDR, state); if (local < 0) goto tx_error; rt = dst_rt6_info(skb_dst(skb)); @@ -1145,7 +1176,8 @@ ipvs_gre_encap(struct net *net, struct sk_buff *skb, */ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { struct netns_ipvs *ipvs = cp->ipvs; struct net *net = ipvs->net; @@ -1167,7 +1199,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_CONNECT | - IP_VS_RT_MODE_TUNNEL, &saddr, ipvsh); + IP_VS_RT_MODE_TUNNEL, &saddr, ipvsh, state); if (local < 0) goto tx_error; if (local) @@ -1276,7 +1308,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) - ip_local_out(net, skb->sk, skb); + ip_local_out(net, NULL, skb); else if (ret == NF_DROP) kfree_skb(skb); @@ -1290,7 +1322,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { struct netns_ipvs *ipvs = cp->ipvs; struct net *net = ipvs->net; @@ -1312,7 +1345,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, &saddr, ipvsh, 1, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | - IP_VS_RT_MODE_TUNNEL); + IP_VS_RT_MODE_TUNNEL, state); if (local < 0) goto tx_error; if (local) @@ -1418,7 +1451,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) - ip6_local_out(net, skb->sk, skb); + ip6_local_out(net, NULL, skb); else if (ret == NF_DROP) kfree_skb(skb); @@ -1437,14 +1470,15 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, */ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { int local; local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | - IP_VS_RT_MODE_KNOWN_NH, NULL, ipvsh); + IP_VS_RT_MODE_KNOWN_NH, NULL, ipvsh, state); if (local < 0) goto tx_error; if (local) @@ -1467,7 +1501,8 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh, + const struct nf_hook_state *state) { int local; @@ -1476,7 +1511,7 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, NULL, ipvsh, 0, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | - IP_VS_RT_MODE_KNOWN_NH); + IP_VS_RT_MODE_KNOWN_NH, state); if (local < 0) goto tx_error; if (local) @@ -1503,7 +1538,7 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int hooknum, struct ip_vs_iphdr *ciph) + const struct nf_hook_state *state, struct ip_vs_iphdr *ciph) { struct rtable *rt; /* Route to the other host */ int rc; @@ -1517,7 +1552,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, translate address/port back */ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (cp->packet_xmit) - rc = cp->packet_xmit(skb, cp, pp, ciph); + rc = cp->packet_xmit(skb, cp, pp, ciph, state); else rc = NF_ACCEPT; /* do not touch skb anymore */ @@ -1531,11 +1566,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, was_input = rt_is_input_route(skb_rtable(skb)); /* LOCALNODE from FORWARD hook is not supported */ - rt_mode = (hooknum != NF_INET_FORWARD) ? + rt_mode = (state->hook != NF_INET_FORWARD) ? IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; - local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, - NULL, ciph); + local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, + cp->daddr.ip, rt_mode, NULL, ciph, state); if (local < 0) goto tx_error; rt = skb_rtable(skb); @@ -1597,8 +1632,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, unsigned int toff, - unsigned int hooknum, struct ip_vs_iphdr *ciph) + struct ip_vs_protocol *pp, unsigned int toff, + const struct nf_hook_state *state, struct ip_vs_iphdr *ciph) { bool has_ports = false; struct rt6_info *rt; /* Route to the other host */ @@ -1612,7 +1647,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, translate address/port back */ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (cp->packet_xmit) - rc = cp->packet_xmit(skb, cp, pp, ciph); + rc = cp->packet_xmit(skb, cp, pp, ciph, state); else rc = NF_ACCEPT; /* do not touch skb anymore */ @@ -1625,11 +1660,12 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, */ /* LOCALNODE from FORWARD hook is not supported */ - rt_mode = (hooknum != NF_INET_FORWARD) ? + rt_mode = (state->hook != NF_INET_FORWARD) ? IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, - &cp->daddr.in6, NULL, ciph, 0, rt_mode); + &cp->daddr.in6, NULL, ciph, 0, rt_mode, + state); if (local < 0) goto tx_error; rt = dst_rt6_info(skb_dst(skb)); -- 2.47.3