LVS
lvs-devel
Google
 
Web LinuxVirtualServer.org

Re: [PATCH nf] ipvs: skip IPv6 extension headers in SCTP state lookup

To: Yizhou Zhao <zhaoyz24@xxxxxxxxxxxxxxxxxxxxx>
Subject: Re: [PATCH nf] ipvs: skip IPv6 extension headers in SCTP state lookup
Cc: netdev@xxxxxxxxxxxxxxx, Simon Horman <horms@xxxxxxxxxxxx>, Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>, Florian Westphal <fw@xxxxxxxxx>, Phil Sutter <phil@xxxxxx>, "David S. Miller" <davem@xxxxxxxxxxxxx>, Eric Dumazet <edumazet@xxxxxxxxxx>, Jakub Kicinski <kuba@xxxxxxxxxx>, Paolo Abeni <pabeni@xxxxxxxxxx>, lvs-devel@xxxxxxxxxxxxxxx, netfilter-devel@xxxxxxxxxxxxxxx, coreteam@xxxxxxxxxxxxx, linux-kernel@xxxxxxxxxxxxxxx, Yuxiang Yang <yangyx22@xxxxxxxxxxxxxxxxxxxxx>, Ao Wang <wangao@xxxxxxxxxx>, Xuewei Feng <fengxw06@xxxxxxx>, Qi Li <qli01@xxxxxxxxxxxxxxx>, Ke Xu <xuke@xxxxxxxxxxxxxxx>, stable@xxxxxxxxxxxxxxx
From: Julian Anastasov <ja@xxxxxx>
Date: Sun, 5 Jul 2026 20:35:00 +0300 (EEST)
        Hello,

On Sun, 5 Jul 2026, Yizhou Zhao wrote:

> 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 ip_vs_fill_iph_skb() in set_sctp_state() and base the chunk-header
> offset on iph.len, matching sctp_conn_schedule() and the SCTP NAT handlers.
> For IPv4 and IPv6 packets without extension headers this preserves the
> existing offset.
> 
> Fixes: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support")
> Cc: stable@xxxxxxxxxxxxxxx
> Reported-by: Yizhou Zhao <zhaoyz24@xxxxxxxxxxxxxxxxxxxxx>
> Reported-by: Yuxiang Yang <yangyx22@xxxxxxxxxxxxxxxxxxxxx>
> Reported-by: Ao Wang <wangao@xxxxxxxxxx>
> Reported-by: Xuewei Feng <fengxw06@xxxxxxx>
> Reported-by: Qi Li <qli01@xxxxxxxxxxxxxxx>
> Reported-by: Ke Xu <xuke@xxxxxxxxxxxxxxx>
> Assisted-by: Claude-Code:GLM-5.2
> Signed-off-by: Yizhou Zhao <zhaoyz24@xxxxxxxxxxxxxxxxxxxxx>
> ---
>  net/netfilter/ipvs/ip_vs_proto_sctp.c | 12 +++++-------
>  1 file changed, 5 insertions(+), 7 deletions(-)
> 
> diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c 
> b/net/netfilter/ipvs/ip_vs_proto_sctp.c
> index 63c78a1f3918..6e0fc23be305 100644
> --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
> +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
> @@ -375,17 +375,15 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct 
> ip_vs_conn *cp,
>               int direction, const struct sk_buff *skb)
>  {
>       struct sctp_chunkhdr _sctpch, *sch;
>       unsigned char chunk_type;
> +     struct ip_vs_iphdr iph;
>       int event, next_state;
> -     int ihl, cofs;
> +     int cofs;
>  
> -#ifdef CONFIG_IP_VS_IPV6
> -     ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
> -#else
> -     ihl = ip_hdrlen(skb);
> -#endif
> +     if (!ip_vs_fill_iph_skb(cp->af, skb, false, &iph))
> +             return;

        May be it is better starting from ip_vs_set_state()
to provide new arg 'int iph_len/offset' (set to iph.len), down to
state_transition(), sctp_state_transition() and set_sctp_state().
Same for all protos. It should cost less stack and ipv6_find_hdr()
calls and what matters most, correct iph context in case we
have IP+ICMP+TCP (with just two ports or even with TCP flags)
and are scheduling ICMP, i.e. not IP+TCP as usually.

        But what I see is that ip_vs_in_icmp*() are missing
the ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd) call just
after ip_vs_in_stats() and before ip_vs_icmp_xmit() where
we should provide ciph.len. That is why we don't reach the
set_tcp_state() calls to set correct cp->state and timeout
when scheduling related ICMP. So, this should be fixed too.

> -     cofs = ihl + sizeof(struct sctphdr);
> +     cofs = iph.len + sizeof(struct sctphdr);
>       sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch);
>       if (sch == NULL)
>               return;
> -- 
> 2.47.3

Regards

--
Julian Anastasov <ja@xxxxxx>



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