LVS
lvs-devel
Google
 
Web LinuxVirtualServer.org

[PATCH nf 1/2] ipvs: skip ipv6 extension headers for csum checks

To: Simon Horman <horms@xxxxxxxxxxxx>
Subject: [PATCH nf 1/2] ipvs: skip ipv6 extension headers for csum checks
Cc: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>, lvs-devel@xxxxxxxxxxxxxxx, netfilter-devel@xxxxxxxxxxxxxxx, Dust Li <dust.li@xxxxxxxxxxxxxxxxx>, Jiejian Wu <jiejian@xxxxxxxxxxxxxxxxx>
From: Julian Anastasov <ja@xxxxxx>
Date: Sat, 14 Feb 2026 16:58:49 +0200
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: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support")
Fixes: 0bbdd42b7efa ("IPVS: Extend protocol DNAT/SNAT and state handlers")
Signed-off-by: Julian Anastasov <ja@xxxxxx>
---
 net/netfilter/ipvs/ip_vs_proto_sctp.c | 18 ++++++------------
 net/netfilter/ipvs/ip_vs_proto_tcp.c  | 21 +++++++--------------
 net/netfilter/ipvs/ip_vs_proto_udp.c  | 20 +++++++-------------
 3 files changed, 20 insertions(+), 39 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c 
b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 83e452916403..63c78a1f3918 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -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);
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c 
b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index f68a1533ee45..8cc0a8ce6241 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -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");
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c 
b/net/netfilter/ipvs/ip_vs_proto_udp.c
index 0f0107c80dd2..f9de632e38cd 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -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");
-- 
2.53.0




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