* [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers
@ 2026-07-06 10:16 Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 1/3] ipvs: pass parsed transport offset to " Yizhou Zhao
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Yizhou Zhao @ 2026-07-06 10:16 UTC (permalink / raw)
To: netdev
Cc: coreteam, davem, edumazet, fengxw06, fw, horms, ja, kuba,
linux-kernel, lvs-devel, netfilter-devel, pabeni, pablo, phil,
qli01, stable, wangao, xuke, yangyx22, Yizhou Zhao
IPVS parses packets into struct ip_vs_iphdr before scheduling and state
handling. For IPv6, iph.len contains the real transport-header offset
after ipv6_find_hdr() has skipped any extension headers.
TCP and SCTP state handlers still recompute their own transport offsets.
They use sizeof(struct ipv6hdr) for IPv6, so packets with extension
headers make the state machines read the wrong bytes.
Pass the parsed transport offset through the common IPVS state handling
callback, then use it in the TCP and SCTP state lookups.
Changes in v2:
- Pass the parsed transport offset through ip_vs_set_state() and the
protocol callbacks.
- Fix TCP state handling as well as SCTP.
- Avoid reparsing the skb in SCTP state handling.
- Split the common plumbing, TCP fix and SCTP fix into a 3-patch series.
Yizhou Zhao (3):
ipvs: pass parsed transport offset to state handlers
ipvs: use parsed transport offset in TCP state lookup
ipvs: use parsed transport offset in SCTP state lookup
include/net/ip_vs.h | 3 ++-
net/netfilter/ipvs/ip_vs_core.c | 10 +++++-----
net/netfilter/ipvs/ip_vs_proto_sctp.c | 18 +++++++-----------
net/netfilter/ipvs/ip_vs_proto_tcp.c | 11 +++--------
net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++-
5 files changed, 19 insertions(+), 26 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH nf v2 1/3] ipvs: pass parsed transport offset to state handlers
2026-07-06 10:16 [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Yizhou Zhao
@ 2026-07-06 10:16 ` Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 2/3] ipvs: use parsed transport offset in TCP state lookup Yizhou Zhao
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Yizhou Zhao @ 2026-07-06 10:16 UTC (permalink / raw)
To: netdev
Cc: coreteam, davem, edumazet, fengxw06, fw, horms, ja, kuba,
linux-kernel, lvs-devel, netfilter-devel, pabeni, pablo, phil,
qli01, stable, wangao, xuke, yangyx22, Yizhou Zhao
IPVS callers already parse the packet into struct ip_vs_iphdr before
updating connection state. For IPv6 this records the real
transport-header offset after extension headers in iph.len.
Pass this parsed transport offset through ip_vs_set_state() and the
protocol state_transition() callback so protocol handlers can use the
same packet context as scheduling and NAT handling. This patch only
changes the common callback plumbing and adapts the protocol callback
signatures; TCP and SCTP start using the value in follow-up patches.
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
---
include/net/ip_vs.h | 3 ++-
net/netfilter/ipvs/ip_vs_core.c | 10 +++++-----
net/netfilter/ipvs/ip_vs_proto_sctp.c | 3 ++-
net/netfilter/ipvs/ip_vs_proto_tcp.c | 3 ++-
net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++-
5 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 49297fec448a..417ff51f62fc 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -752,7 +752,8 @@ struct ip_vs_protocol {
void (*state_transition)(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd);
+ struct ip_vs_proto_data *pd,
+ unsigned int iph_len);
int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc);
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index d40b404c1bf6..bd90f03fe3a4 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -398,10 +398,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
static inline void
ip_vs_set_state(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd)
+ struct ip_vs_proto_data *pd, unsigned int iph_len)
{
if (likely(pd->pp->state_transition))
- pd->pp->state_transition(cp, direction, skb, pd);
+ pd->pp->state_transition(cp, direction, skb, pd, iph_len);
}
static inline int
@@ -803,7 +803,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
ip_vs_in_stats(cp, skb);
/* set state */
- ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
+ 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);
@@ -1484,7 +1484,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
after_nat:
ip_vs_out_stats(cp, skb);
- ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd);
+ ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len);
skb->ipvs_property = 1;
if (!(cp->flags & IP_VS_CONN_F_NFCT))
ip_vs_notrack(skb);
@@ -2233,7 +2233,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet");
ip_vs_in_stats(cp, skb);
- ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
+ 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);
/* do not touch skb anymore */
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 63c78a1f3918..394367b7b388 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -468,7 +468,8 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
static void
sctp_state_transition(struct ip_vs_conn *cp, int direction,
- const struct sk_buff *skb, struct ip_vs_proto_data *pd)
+ const struct sk_buff *skb, struct ip_vs_proto_data *pd,
+ unsigned int iph_len)
{
spin_lock_bh(&cp->lock);
set_sctp_state(pd, cp, direction, skb);
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index 8cc0a8ce6241..2d3f6aeafe52 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -579,7 +579,8 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
static void
tcp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd)
+ struct ip_vs_proto_data *pd,
+ unsigned int iph_len)
{
struct tcphdr _tcph, *th;
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
index f9de632e38cd..58f9e255927e 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -444,7 +444,8 @@ static const char * udp_state_name(int state)
static void
udp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd)
+ struct ip_vs_proto_data *pd,
+ unsigned int iph_len)
{
if (unlikely(!pd)) {
pr_err("UDP no ns data\n");
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH nf v2 2/3] ipvs: use parsed transport offset in TCP state lookup
2026-07-06 10:16 [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 1/3] ipvs: pass parsed transport offset to " Yizhou Zhao
@ 2026-07-06 10:16 ` Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 3/3] ipvs: use parsed transport offset in SCTP " Yizhou Zhao
2026-07-07 13:51 ` [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Julian Anastasov
3 siblings, 0 replies; 5+ messages in thread
From: Yizhou Zhao @ 2026-07-06 10:16 UTC (permalink / raw)
To: netdev
Cc: coreteam, davem, edumazet, fengxw06, fw, horms, ja, kuba,
linux-kernel, lvs-devel, netfilter-devel, pabeni, pablo, phil,
qli01, stable, wangao, xuke, yangyx22, Yizhou Zhao
TCP state handling reparses the skb to find the TCP header. For IPv6 it
uses sizeof(struct ipv6hdr), while the surrounding IPVS code already
parsed the packet with ip_vs_fill_iph_skb() and has the real
transport-header offset in iph.len.
This makes TCP state handling look at the wrong bytes when an IPv6
packet carries extension headers. Use the parsed transport offset passed
down from ip_vs_set_state() when reading the TCP header.
For IPv4 and for IPv6 packets without extension headers, the passed
offset matches the previous value.
Fixes: 0bbdd42b7efa6 ("IPVS: Extend protocol DNAT/SNAT and state handlers")
Link: https://lore.kernel.org/netdev/20260705125659.37744-1-zhaoyz24@mails.tsinghua.edu.cn/
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reported-by: Ao Wang <wangao@seu.edu.cn>
Reported-by: Xuewei Feng <fengxw06@126.com>
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
Assisted-by: Claude Code:GLM-5.2
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
---
net/netfilter/ipvs/ip_vs_proto_tcp.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index 2d3f6aeafe52..f86b763efcc4 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -584,13 +584,7 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction,
{
struct tcphdr _tcph, *th;
-#ifdef CONFIG_IP_VS_IPV6
- int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
-#else
- int ihl = ip_hdrlen(skb);
-#endif
-
- th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph);
+ th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph);
if (th == NULL)
return;
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH nf v2 3/3] ipvs: use parsed transport offset in SCTP state lookup
2026-07-06 10:16 [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 1/3] ipvs: pass parsed transport offset to " Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 2/3] ipvs: use parsed transport offset in TCP state lookup Yizhou Zhao
@ 2026-07-06 10:16 ` Yizhou Zhao
2026-07-07 13:51 ` [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Julian Anastasov
3 siblings, 0 replies; 5+ messages in thread
From: Yizhou Zhao @ 2026-07-06 10:16 UTC (permalink / raw)
To: netdev
Cc: coreteam, davem, edumazet, fengxw06, fw, horms, ja, kuba,
linux-kernel, lvs-devel, netfilter-devel, pabeni, pablo, phil,
qli01, stable, wangao, xuke, yangyx22, Yizhou Zhao
set_sctp_state() reads the SCTP chunk header again in order to drive the
IPVS SCTP state table. For IPv6 it computes the offset with
sizeof(struct ipv6hdr), while the surrounding IPVS code uses iph.len from
ip_vs_fill_iph_skb(), where ipv6_find_hdr() has already skipped
extension headers and found the real transport header.
This makes the state machine read from the wrong offset for IPv6 SCTP
packets that carry extension headers. For example, an INIT packet with an
8-byte destination options header can be scheduled correctly by
sctp_conn_schedule(), but set_sctp_state() reads the first byte of the
SCTP verification tag as a DATA chunk type. The connection then moves
from NONE to ESTABLISHED instead of INIT1, gets the longer established
timeout, and updates the active/inactive destination counters
incorrectly. This happens even though the SCTP handshake has not
completed.
Use the parsed transport offset passed down from ip_vs_set_state() for
the SCTP chunk-header lookup. For IPv4 and IPv6 packets without
extension headers this preserves the existing offset.
Fixes: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/netdev/20260705123040.35755-1-zhaoyz24@mails.tsinghua.edu.cn/
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reported-by: Ao Wang <wangao@seu.edu.cn>
Reported-by: Xuewei Feng <fengxw06@126.com>
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
Assisted-by: Claude Code:GLM-5.2
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
---
net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 394367b7b388..c67317be17df 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -372,20 +372,15 @@ static const char *sctp_state_name(int state)
static inline void
set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
- int direction, const struct sk_buff *skb)
+ int direction, const struct sk_buff *skb,
+ unsigned int iph_len)
{
struct sctp_chunkhdr _sctpch, *sch;
unsigned char chunk_type;
int event, next_state;
- int ihl, cofs;
-
-#ifdef CONFIG_IP_VS_IPV6
- ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
-#else
- ihl = ip_hdrlen(skb);
-#endif
+ int cofs;
- cofs = ihl + sizeof(struct sctphdr);
+ cofs = iph_len + sizeof(struct sctphdr);
sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch);
if (sch == NULL)
return;
@@ -472,7 +467,7 @@ sctp_state_transition(struct ip_vs_conn *cp, int direction,
unsigned int iph_len)
{
spin_lock_bh(&cp->lock);
- set_sctp_state(pd, cp, direction, skb);
+ set_sctp_state(pd, cp, direction, skb, iph_len);
spin_unlock_bh(&cp->lock);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers
2026-07-06 10:16 [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Yizhou Zhao
` (2 preceding siblings ...)
2026-07-06 10:16 ` [PATCH nf v2 3/3] ipvs: use parsed transport offset in SCTP " Yizhou Zhao
@ 2026-07-07 13:51 ` Julian Anastasov
3 siblings, 0 replies; 5+ messages in thread
From: Julian Anastasov @ 2026-07-07 13:51 UTC (permalink / raw)
To: Yizhou Zhao
Cc: netdev, coreteam, davem, edumazet, fengxw06, fw, horms, kuba,
linux-kernel, lvs-devel, netfilter-devel, pabeni, pablo, phil,
qli01, stable, wangao, xuke, yangyx22
Hello,
On Mon, 6 Jul 2026, Yizhou Zhao wrote:
> IPVS parses packets into struct ip_vs_iphdr before scheduling and state
> handling. For IPv6, iph.len contains the real transport-header offset
> after ipv6_find_hdr() has skipped any extension headers.
>
> TCP and SCTP state handlers still recompute their own transport offsets.
> They use sizeof(struct ipv6hdr) for IPv6, so packets with extension
> headers make the state machines read the wrong bytes.
>
> Pass the parsed transport offset through the common IPVS state handling
> callback, then use it in the TCP and SCTP state lookups.
>
> Changes in v2:
> - Pass the parsed transport offset through ip_vs_set_state() and the
> protocol callbacks.
> - Fix TCP state handling as well as SCTP.
> - Avoid reparsing the skb in SCTP state handling.
> - Split the common plumbing, TCP fix and SCTP fix into a 3-patch series.
>
> Yizhou Zhao (3):
> ipvs: pass parsed transport offset to state handlers
> ipvs: use parsed transport offset in TCP state lookup
> ipvs: use parsed transport offset in SCTP state lookup
>
> include/net/ip_vs.h | 3 ++-
> net/netfilter/ipvs/ip_vs_core.c | 10 +++++-----
> net/netfilter/ipvs/ip_vs_proto_sctp.c | 18 +++++++-----------
> net/netfilter/ipvs/ip_vs_proto_tcp.c | 11 +++--------
> net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++-
> 5 files changed, 19 insertions(+), 26 deletions(-)
The patchset looks good to me, thanks!
Acked-by: Julian Anastasov <ja@ssi.bg>
The Sashiko comments need additional fixes:
https://sashiko.dev/#/patchset/20260706101624.69471-1-zhaoyz24%40mails.tsinghua.edu.cn
Regards
--
Julian Anastasov <ja@ssi.bg>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-07 13:51 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 10:16 [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 1/3] ipvs: pass parsed transport offset to " Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 2/3] ipvs: use parsed transport offset in TCP state lookup Yizhou Zhao
2026-07-06 10:16 ` [PATCH nf v2 3/3] ipvs: use parsed transport offset in SCTP " Yizhou Zhao
2026-07-07 13:51 ` [PATCH nf v2 0/3] ipvs: use parsed transport offsets in state handlers Julian Anastasov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox