ipvs: skip ipv6 extension headers for csum checks

Protocol checksum validation fails for IPv6 if there are extension
headers before the protocol header. iph->len already contains its
offset, so use it to fix the problem.

Fixes: 2906f66a56 ("ipvs: SCTP Trasport Loadbalancing Support")
Fixes: 0bbdd42b7e ("IPVS: Extend protocol DNAT/SNAT and state handlers")
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Florian Westphal <fw@strlen.de>
This commit is contained in:
Julian Anastasov 2026-02-14 16:58:49 +02:00 committed by Florian Westphal
parent 4edd4ba71c
commit 05cfe9863e
3 changed files with 20 additions and 39 deletions

View File

@ -10,7 +10,8 @@
#include <net/ip_vs.h>
static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int sctphoff);
static int
sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@ -108,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!sctp_csum_check(cp->af, skb, pp))
if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
return 0;
/* Call application helper if needed */
@ -156,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!sctp_csum_check(cp->af, skb, pp))
if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
return 0;
/* Call application helper if needed */
@ -185,19 +186,12 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
}
static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int sctphoff)
{
unsigned int sctphoff;
struct sctphdr *sh;
__le32 cmp, val;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
sctphoff = sizeof(struct ipv6hdr);
else
#endif
sctphoff = ip_hdrlen(skb);
sh = (struct sctphdr *)(skb->data + sctphoff);
cmp = sh->checksum;
val = sctp_compute_cksum(skb, sctphoff);

View File

@ -28,7 +28,8 @@
#include <net/ip_vs.h>
static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int tcphoff);
static int
tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@ -165,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!tcp_csum_check(cp->af, skb, pp))
if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
return 0;
/* Call application helper if needed */
@ -243,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!tcp_csum_check(cp->af, skb, pp))
if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
return 0;
/*
@ -300,17 +301,9 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int tcphoff)
{
unsigned int tcphoff;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
tcphoff = sizeof(struct ipv6hdr);
else
#endif
tcphoff = ip_hdrlen(skb);
switch (skb->ip_summed) {
case CHECKSUM_NONE:
skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
@ -321,7 +314,7 @@ tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
skb->len - tcphoff,
ipv6_hdr(skb)->nexthdr,
IPPROTO_TCP,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
"Failed checksum for");

View File

@ -24,7 +24,8 @@
#include <net/ip6_checksum.h>
static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int udphoff);
static int
udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@ -154,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!udp_csum_check(cp->af, skb, pp))
if (!udp_csum_check(cp->af, skb, pp, udphoff))
return 0;
/*
@ -237,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!udp_csum_check(cp->af, skb, pp))
if (!udp_csum_check(cp->af, skb, pp, udphoff))
return 0;
/*
@ -296,17 +297,10 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int udphoff)
{
struct udphdr _udph, *uh;
unsigned int udphoff;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
udphoff = sizeof(struct ipv6hdr);
else
#endif
udphoff = ip_hdrlen(skb);
uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
if (uh == NULL)
@ -324,7 +318,7 @@ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
skb->len - udphoff,
ipv6_hdr(skb)->nexthdr,
IPPROTO_UDP,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
"Failed checksum for");