ipvs: return the csum validation for forward hook

Sashiko notes that playing games with the skb dst and rt
flags instead of providing hooknum is not a good idea
when validating the checksums.

Also, skipping checksum validation for FORWARD packets
risk silent data corruption, even if the only user is
the FTP-CMD packets coming from the real server.

Sashiko also noticed that by using common checksum
helper in the previous commit we actually fixed old bug
where the TCP/UDP checksum for IPv6 on CHECKSUM_COMPLETE
was not validated correctly.

Fixes: e876b75b90 ("ipvs: fix the checksum validations")
Link: https://sashiko.dev/#/patchset/20260722211420.153933-1-pablo%40netfilter.org
Link: https://sashiko.dev/#/patchset/20260727185024.67534-1-ja%40ssi.bg
Link: https://sashiko.dev/#/patchset/20260728202520.59179-1-ja%40ssi.bg
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Julian Anastasov 2026-07-30 21:35:06 +03:00 committed by Pablo Neira Ayuso
parent 646922a037
commit 99609cb0aa
2 changed files with 6 additions and 15 deletions

View File

@ -25,9 +25,7 @@
#include <linux/netfilter.h> /* for union nf_inet_addr */
#include <linux/ip.h>
#include <linux/ipv6.h> /* for struct ipv6hdr */
#include <net/route.h>
#include <net/ipv6.h>
#include <net/ip6_fib.h>
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#endif
@ -2095,30 +2093,23 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
return csum_partial(diff, sizeof(diff), oldsum);
}
static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
static inline bool ip_vs_checksum_needed(struct sk_buff *skb)
{
/* Checksum unnecessary or already validated? */
if (skb_csum_unnecessary(skb))
return false;
/* LOCAL_OUT ? */
if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
/* Locally generated ? */
if (!skb->dev)
return false;
/* !LOCAL_IN (FORWARD) ? */
if (af == AF_INET6) {
if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
return false;
} else {
if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
return false;
}
return true;
}
static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
int offset, int proto, int af)
{
if (!ip_vs_checksum_needed(skb, af))
if (!ip_vs_checksum_needed(skb))
return true;
/* Validate csum even for FORWARD */
return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
}

View File

@ -193,7 +193,7 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
struct sctphdr *sh;
__le32 cmp, val;
if (!ip_vs_checksum_needed(skb, af))
if (!ip_vs_checksum_needed(skb))
return 1;
sh = (struct sctphdr *)(skb->data + sctphoff);
cmp = sh->checksum;