* [PATCH net] tcp: secure_seq: add back ports to TS offset
@ 2026-03-02 20:55 Eric Dumazet
2026-03-02 21:47 ` Kuniyuki Iwashima
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Eric Dumazet @ 2026-03-02 20:55 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Neal Cardwell, Kuniyuki Iwashima, Willy Tarreau,
netdev, eric.dumazet, Eric Dumazet, Zhouyan Deng,
Florian Westphal
This reverts 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
tcp_tw_recycle went away in 2017.
Zhouyan Deng reported off-path TCP source port leakage via
SYN cookie side-channel that can be fixed in multiple ways.
One of them is to bring back TCP ports in TS offset randomization.
As a bonus, we perform a single siphash() computation
to provide both an ISN and a TS offset.
Fixes: 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
Reported-by: Zhouyan Deng <dengzhouyan_nwpu@163•com>
Signed-off-by: Eric Dumazet <edumazet@google•com>
Cc: Florian Westphal <fw@strlen•de>
---
include/net/secure_seq.h | 45 ++++++++++++++++++----
include/net/tcp.h | 6 ++-
net/core/secure_seq.c | 80 +++++++++++++++-------------------------
net/ipv4/syncookies.c | 11 ++++--
net/ipv4/tcp_input.c | 8 +++-
net/ipv4/tcp_ipv4.c | 37 +++++++++----------
net/ipv6/syncookies.c | 11 ++++--
net/ipv6/tcp_ipv6.c | 37 +++++++++----------
8 files changed, 127 insertions(+), 108 deletions(-)
diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h
index cddebafb9f779ebd5d9c02e8ff26c13b5697c7d1..6f996229167b3c3f7861b2d5693ef81b5eed0d74 100644
--- a/include/net/secure_seq.h
+++ b/include/net/secure_seq.h
@@ -5,16 +5,47 @@
#include <linux/types.h>
struct net;
+extern struct net init_net;
+
+union tcp_seq_and_ts_off {
+ struct {
+ u32 seq;
+ u32 ts_off;
+ };
+ u64 hash64;
+};
u64 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
u64 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
__be16 dport);
-u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport);
-u32 secure_tcp_ts_off(const struct net *net, __be32 saddr, __be32 daddr);
-u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
- __be16 sport, __be16 dport);
-u32 secure_tcpv6_ts_off(const struct net *net,
- const __be32 *saddr, const __be32 *daddr);
+union tcp_seq_and_ts_off
+secure_tcp_seq_and_ts_off(const struct net *net, __be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport);
+
+static inline u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport)
+{
+ union tcp_seq_and_ts_off ts;
+
+ ts = secure_tcp_seq_and_ts_off(&init_net, saddr, daddr,
+ sport, dport);
+
+ return ts.seq;
+}
+
+union tcp_seq_and_ts_off
+secure_tcpv6_seq_and_ts_off(const struct net *net, const __be32 *saddr,
+ const __be32 *daddr,
+ __be16 sport, __be16 dport);
+
+static inline u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
+ __be16 sport, __be16 dport)
+{
+ union tcp_seq_and_ts_off ts;
+
+ ts = secure_tcpv6_seq_and_ts_off(&init_net, saddr, daddr,
+ sport, dport);
+ return ts.seq;
+}
#endif /* _NET_SECURE_SEQ */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index eb8bf63fdafc3243469f293fd06aef0ce086c5a4..978eea2d5df04f378dceb251025bee3101120f69 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -43,6 +43,7 @@
#include <net/dst.h>
#include <net/mptcp.h>
#include <net/xfrm.h>
+#include <net/secure_seq.h>
#include <linux/seq_file.h>
#include <linux/memcontrol.h>
@@ -2464,8 +2465,9 @@ struct tcp_request_sock_ops {
struct flowi *fl,
struct request_sock *req,
u32 tw_isn);
- u32 (*init_seq)(const struct sk_buff *skb);
- u32 (*init_ts_off)(const struct net *net, const struct sk_buff *skb);
+ union tcp_seq_and_ts_off (*init_seq_and_ts_off)(
+ const struct net *net,
+ const struct sk_buff *skb);
int (*send_synack)(const struct sock *sk, struct dst_entry *dst,
struct flowi *fl, struct request_sock *req,
struct tcp_fastopen_cookie *foc,
diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c
index 9a39656804513dcef0888d280d8289913ef27eea..6a6f2cda5aaef82074718439920c75a75592e967 100644
--- a/net/core/secure_seq.c
+++ b/net/core/secure_seq.c
@@ -20,7 +20,6 @@
#include <net/tcp.h>
static siphash_aligned_key_t net_secret;
-static siphash_aligned_key_t ts_secret;
#define EPHEMERAL_PORT_SHUFFLE_PERIOD (10 * HZ)
@@ -28,11 +27,6 @@ static __always_inline void net_secret_init(void)
{
net_get_random_once(&net_secret, sizeof(net_secret));
}
-
-static __always_inline void ts_secret_init(void)
-{
- net_get_random_once(&ts_secret, sizeof(ts_secret));
-}
#endif
#ifdef CONFIG_INET
@@ -53,28 +47,9 @@ static u32 seq_scale(u32 seq)
#endif
#if IS_ENABLED(CONFIG_IPV6)
-u32 secure_tcpv6_ts_off(const struct net *net,
- const __be32 *saddr, const __be32 *daddr)
-{
- const struct {
- struct in6_addr saddr;
- struct in6_addr daddr;
- } __aligned(SIPHASH_ALIGNMENT) combined = {
- .saddr = *(struct in6_addr *)saddr,
- .daddr = *(struct in6_addr *)daddr,
- };
-
- if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
- return 0;
-
- ts_secret_init();
- return siphash(&combined, offsetofend(typeof(combined), daddr),
- &ts_secret);
-}
-EXPORT_IPV6_MOD(secure_tcpv6_ts_off);
-
-u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
- __be16 sport, __be16 dport)
+union tcp_seq_and_ts_off
+secure_tcpv6_seq_and_ts_off(const struct net *net, const __be32 *saddr,
+ const __be32 *daddr, __be16 sport, __be16 dport)
{
const struct {
struct in6_addr saddr;
@@ -87,14 +62,20 @@ u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
.sport = sport,
.dport = dport
};
- u32 hash;
+ union tcp_seq_and_ts_off st;
net_secret_init();
- hash = siphash(&combined, offsetofend(typeof(combined), dport),
- &net_secret);
- return seq_scale(hash);
+
+ st.hash64 = siphash(&combined, offsetofend(typeof(combined), dport),
+ &net_secret);
+
+ if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
+ st.ts_off = 0;
+
+ st.seq = seq_scale(st.seq);
+ return st;
}
-EXPORT_SYMBOL(secure_tcpv6_seq);
+EXPORT_SYMBOL(secure_tcpv6_seq_and_ts_off);
u64 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
__be16 dport)
@@ -118,33 +99,30 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
#endif
#ifdef CONFIG_INET
-u32 secure_tcp_ts_off(const struct net *net, __be32 saddr, __be32 daddr)
-{
- if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
- return 0;
-
- ts_secret_init();
- return siphash_2u32((__force u32)saddr, (__force u32)daddr,
- &ts_secret);
-}
-
/* secure_tcp_seq_and_tsoff(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d),
* but fortunately, `sport' cannot be 0 in any circumstances. If this changes,
* it would be easy enough to have the former function use siphash_4u32, passing
* the arguments as separate u32.
*/
-u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
- __be16 sport, __be16 dport)
+union tcp_seq_and_ts_off
+secure_tcp_seq_and_ts_off(const struct net *net, __be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport)
{
- u32 hash;
+ u32 ports = (__force u32)sport << 16 | (__force u32)dport;
+ union tcp_seq_and_ts_off st;
net_secret_init();
- hash = siphash_3u32((__force u32)saddr, (__force u32)daddr,
- (__force u32)sport << 16 | (__force u32)dport,
- &net_secret);
- return seq_scale(hash);
+
+ st.hash64 = siphash_3u32((__force u32)saddr, (__force u32)daddr,
+ ports, &net_secret);
+
+ if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
+ st.ts_off = 0;
+
+ st.seq = seq_scale(st.seq);
+ return st;
}
-EXPORT_SYMBOL_GPL(secure_tcp_seq);
+EXPORT_SYMBOL_GPL(secure_tcp_seq_and_ts_off);
u64 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
{
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 061751aabc8e16c5d536a19f7b920d1bca2b0f4f..fc3affd9c8014b1d4e9f161421a7753717cdcd73 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -378,9 +378,14 @@ static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk,
tcp_parse_options(net, skb, &tcp_opt, 0, NULL);
if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
- tsoff = secure_tcp_ts_off(net,
- ip_hdr(skb)->daddr,
- ip_hdr(skb)->saddr);
+ union tcp_seq_and_ts_off st;
+
+ st = secure_tcp_seq_and_ts_off(net,
+ ip_hdr(skb)->daddr,
+ ip_hdr(skb)->saddr,
+ tcp_hdr(skb)->dest,
+ tcp_hdr(skb)->source);
+ tsoff = st.ts_off;
tcp_opt.rcv_tsecr -= tsoff;
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 7b03f2460751f366dd6cf15505e49ae26cd6466e..cba89733d1216bc2663758b4bda21984835e6055 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -7646,6 +7646,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
const struct tcp_sock *tp = tcp_sk(sk);
struct net *net = sock_net(sk);
struct sock *fastopen_sk = NULL;
+ union tcp_seq_and_ts_off st;
struct request_sock *req;
bool want_cookie = false;
struct dst_entry *dst;
@@ -7715,9 +7716,12 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
if (!dst)
goto drop_and_free;
+ if (tmp_opt.tstamp_ok || (!want_cookie && !isn))
+ st = af_ops->init_seq_and_ts_off(net, skb);
+
if (tmp_opt.tstamp_ok) {
tcp_rsk(req)->req_usec_ts = dst_tcp_usec_ts(dst);
- tcp_rsk(req)->ts_off = af_ops->init_ts_off(net, skb);
+ tcp_rsk(req)->ts_off = st.ts_off;
}
if (!want_cookie && !isn) {
int max_syn_backlog = READ_ONCE(net->ipv4.sysctl_max_syn_backlog);
@@ -7739,7 +7743,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
goto drop_and_release;
}
- isn = af_ops->init_seq(skb);
+ isn = st.seq;
}
tcp_ecn_create_request(req, skb, sk, dst);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index d53d39be291a5750af3ab2a160b35f0f8a28ff9d..56c0db955177edd3fdd04d26d6cd07b5e379e7bc 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -104,17 +104,14 @@ static DEFINE_PER_CPU(struct sock_bh_locked, ipv4_tcp_sk) = {
static DEFINE_MUTEX(tcp_exit_batch_mutex);
-static u32 tcp_v4_init_seq(const struct sk_buff *skb)
+static union tcp_seq_and_ts_off
+tcp_v4_init_seq_and_ts_off(const struct net *net, const struct sk_buff *skb)
{
- return secure_tcp_seq(ip_hdr(skb)->daddr,
- ip_hdr(skb)->saddr,
- tcp_hdr(skb)->dest,
- tcp_hdr(skb)->source);
-}
-
-static u32 tcp_v4_init_ts_off(const struct net *net, const struct sk_buff *skb)
-{
- return secure_tcp_ts_off(net, ip_hdr(skb)->daddr, ip_hdr(skb)->saddr);
+ return secure_tcp_seq_and_ts_off(net,
+ ip_hdr(skb)->daddr,
+ ip_hdr(skb)->saddr,
+ tcp_hdr(skb)->dest,
+ tcp_hdr(skb)->source);
}
int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
@@ -326,15 +323,16 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len
rt = NULL;
if (likely(!tp->repair)) {
+ union tcp_seq_and_ts_off st;
+
+ st = secure_tcp_seq_and_ts_off(net,
+ inet->inet_saddr,
+ inet->inet_daddr,
+ inet->inet_sport,
+ usin->sin_port);
if (!tp->write_seq)
- WRITE_ONCE(tp->write_seq,
- secure_tcp_seq(inet->inet_saddr,
- inet->inet_daddr,
- inet->inet_sport,
- usin->sin_port));
- WRITE_ONCE(tp->tsoffset,
- secure_tcp_ts_off(net, inet->inet_saddr,
- inet->inet_daddr));
+ WRITE_ONCE(tp->write_seq, st.seq);
+ WRITE_ONCE(tp->tsoffset, st.ts_off);
}
atomic_set(&inet->inet_id, get_random_u16());
@@ -1676,8 +1674,7 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
.cookie_init_seq = cookie_v4_init_sequence,
#endif
.route_req = tcp_v4_route_req,
- .init_seq = tcp_v4_init_seq,
- .init_ts_off = tcp_v4_init_ts_off,
+ .init_seq_and_ts_off = tcp_v4_init_seq_and_ts_off,
.send_synack = tcp_v4_send_synack,
};
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 7e007f013ec827c99bcab4ceb85eb35e9242b439..4f6f0d751d6c533231ca0397319935dc90ba4dba 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -151,9 +151,14 @@ static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk,
tcp_parse_options(net, skb, &tcp_opt, 0, NULL);
if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
- tsoff = secure_tcpv6_ts_off(net,
- ipv6_hdr(skb)->daddr.s6_addr32,
- ipv6_hdr(skb)->saddr.s6_addr32);
+ union tcp_seq_and_ts_off st;
+
+ st = secure_tcpv6_seq_and_ts_off(net,
+ ipv6_hdr(skb)->daddr.s6_addr32,
+ ipv6_hdr(skb)->saddr.s6_addr32,
+ tcp_hdr(skb)->dest,
+ tcp_hdr(skb)->source);
+ tsoff = st.ts_off;
tcp_opt.rcv_tsecr -= tsoff;
}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index e46a0efae01235ae7430ed268b92cb47309b8d28..5d95f9ab86973777c29399348adcc4b07e98d2b4 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -104,18 +104,14 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
}
}
-static u32 tcp_v6_init_seq(const struct sk_buff *skb)
+static union tcp_seq_and_ts_off
+tcp_v6_init_seq_and_ts_off(const struct net *net, const struct sk_buff *skb)
{
- return secure_tcpv6_seq(ipv6_hdr(skb)->daddr.s6_addr32,
- ipv6_hdr(skb)->saddr.s6_addr32,
- tcp_hdr(skb)->dest,
- tcp_hdr(skb)->source);
-}
-
-static u32 tcp_v6_init_ts_off(const struct net *net, const struct sk_buff *skb)
-{
- return secure_tcpv6_ts_off(net, ipv6_hdr(skb)->daddr.s6_addr32,
- ipv6_hdr(skb)->saddr.s6_addr32);
+ return secure_tcpv6_seq_and_ts_off(net,
+ ipv6_hdr(skb)->daddr.s6_addr32,
+ ipv6_hdr(skb)->saddr.s6_addr32,
+ tcp_hdr(skb)->dest,
+ tcp_hdr(skb)->source);
}
static int tcp_v6_pre_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
@@ -319,14 +315,16 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
sk_set_txhash(sk);
if (likely(!tp->repair)) {
+ union tcp_seq_and_ts_off st;
+
+ st = secure_tcpv6_seq_and_ts_off(net,
+ np->saddr.s6_addr32,
+ sk->sk_v6_daddr.s6_addr32,
+ inet->inet_sport,
+ inet->inet_dport);
if (!tp->write_seq)
- WRITE_ONCE(tp->write_seq,
- secure_tcpv6_seq(np->saddr.s6_addr32,
- sk->sk_v6_daddr.s6_addr32,
- inet->inet_sport,
- inet->inet_dport));
- tp->tsoffset = secure_tcpv6_ts_off(net, np->saddr.s6_addr32,
- sk->sk_v6_daddr.s6_addr32);
+ WRITE_ONCE(tp->write_seq, st.seq);
+ tp->tsoffset = st.ts_off;
}
if (tcp_fastopen_defer_connect(sk, &err))
@@ -816,8 +814,7 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
.cookie_init_seq = cookie_v6_init_sequence,
#endif
.route_req = tcp_v6_route_req,
- .init_seq = tcp_v6_init_seq,
- .init_ts_off = tcp_v6_init_ts_off,
+ .init_seq_and_ts_off = tcp_v6_init_seq_and_ts_off,
.send_synack = tcp_v6_send_synack,
};
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net] tcp: secure_seq: add back ports to TS offset
2026-03-02 20:55 [PATCH net] tcp: secure_seq: add back ports to TS offset Eric Dumazet
@ 2026-03-02 21:47 ` Kuniyuki Iwashima
2026-03-03 1:41 ` Florian Westphal
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Kuniyuki Iwashima @ 2026-03-02 21:47 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Neal Cardwell, Willy Tarreau, netdev, eric.dumazet, Zhouyan Deng,
Florian Westphal
On Mon, Mar 2, 2026 at 12:55 PM Eric Dumazet <edumazet@google•com> wrote:
>
> This reverts 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
>
> tcp_tw_recycle went away in 2017.
>
> Zhouyan Deng reported off-path TCP source port leakage via
> SYN cookie side-channel that can be fixed in multiple ways.
>
> One of them is to bring back TCP ports in TS offset randomization.
>
> As a bonus, we perform a single siphash() computation
> to provide both an ISN and a TS offset.
>
> Fixes: 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
> Reported-by: Zhouyan Deng <dengzhouyan_nwpu@163•com>
> Signed-off-by: Eric Dumazet <edumazet@google•com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google•com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net] tcp: secure_seq: add back ports to TS offset
2026-03-02 20:55 [PATCH net] tcp: secure_seq: add back ports to TS offset Eric Dumazet
2026-03-02 21:47 ` Kuniyuki Iwashima
@ 2026-03-03 1:41 ` Florian Westphal
2026-03-03 7:39 ` Jörg Sommer
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Florian Westphal @ 2026-03-03 1:41 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Neal Cardwell, Kuniyuki Iwashima, Willy Tarreau, netdev,
eric.dumazet, Zhouyan Deng
Eric Dumazet <edumazet@google•com> wrote:
> This reverts 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
>
> tcp_tw_recycle went away in 2017.
Indeed. Thanks Eric. The original situation that prompted per-host
offsets might not be applicable anymore.
I think its worth a try.
Acked-by: Florian Westphal <fw@strlen•de>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net] tcp: secure_seq: add back ports to TS offset
2026-03-02 20:55 [PATCH net] tcp: secure_seq: add back ports to TS offset Eric Dumazet
2026-03-02 21:47 ` Kuniyuki Iwashima
2026-03-03 1:41 ` Florian Westphal
@ 2026-03-03 7:39 ` Jörg Sommer
2026-03-05 2:00 ` patchwork-bot+netdevbpf
2026-06-06 11:04 ` xietangxin
4 siblings, 0 replies; 6+ messages in thread
From: Jörg Sommer @ 2026-03-03 7:39 UTC (permalink / raw)
To: Eric Dumazet, Kuniyuki Iwashima
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Neal Cardwell, Willy Tarreau, netdev, eric.dumazet, Zhouyan Deng,
Florian Westphal
[-- Attachment #1: Type: text/plain, Size: 2496 bytes --]
Eric Dumazet schrieb am Mo 02. Mär, 20:55 (+0000):
> This reverts 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
>
> tcp_tw_recycle went away in 2017.
>
> Zhouyan Deng reported off-path TCP source port leakage via
> SYN cookie side-channel that can be fixed in multiple ways.
>
> One of them is to bring back TCP ports in TS offset randomization.
>
> As a bonus, we perform a single siphash() computation
> to provide both an ISN and a TS offset.
This sounds great! I was questioning myself if the grace period for
tcp_tw_recycle isn't over.
> @@ -118,33 +99,30 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
> #endif
>
> #ifdef CONFIG_INET
> -u32 secure_tcp_ts_off(const struct net *net, __be32 saddr, __be32 daddr)
> -{
> - if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
> - return 0;
> -
> - ts_secret_init();
> - return siphash_2u32((__force u32)saddr, (__force u32)daddr,
> - &ts_secret);
> -}
> -
> /* secure_tcp_seq_and_tsoff(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d),
> * but fortunately, `sport' cannot be 0 in any circumstances. If this changes,
> * it would be easy enough to have the former function use siphash_4u32, passing
> * the arguments as separate u32.
> */
> -u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
> - __be16 sport, __be16 dport)
> +union tcp_seq_and_ts_off
> +secure_tcp_seq_and_ts_off(const struct net *net, __be32 saddr, __be32 daddr,
> + __be16 sport, __be16 dport)
> {
> - u32 hash;
> + u32 ports = (__force u32)sport << 16 | (__force u32)dport;
> + union tcp_seq_and_ts_off st;
>
> net_secret_init();
> - hash = siphash_3u32((__force u32)saddr, (__force u32)daddr,
> - (__force u32)sport << 16 | (__force u32)dport,
> - &net_secret);
> - return seq_scale(hash);
> +
> + st.hash64 = siphash_3u32((__force u32)saddr, (__force u32)daddr,
> + ports, &net_secret);
Sorry, if this is a dump question, but does this make the ts_off unique per
connection or only per quadruple (saddr, sport, daddr, dport), i.e. the same
remote port gets the same ts_off. The documentation says ‘per connection’
and it might be helpful to say it gets the same ts_off if the addresses and
ports are the same.
Kind regards, Jörg
--
“Computer games don't affect kids. If Pacman would have affected us as
children, we would now run around in darkened rooms, munching yellow
pills and listening to repetetive music.”
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net] tcp: secure_seq: add back ports to TS offset
2026-03-02 20:55 [PATCH net] tcp: secure_seq: add back ports to TS offset Eric Dumazet
` (2 preceding siblings ...)
2026-03-03 7:39 ` Jörg Sommer
@ 2026-03-05 2:00 ` patchwork-bot+netdevbpf
2026-06-06 11:04 ` xietangxin
4 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-03-05 2:00 UTC (permalink / raw)
To: Eric Dumazet
Cc: davem, kuba, pabeni, horms, ncardwell, kuniyu, w, netdev,
eric.dumazet, dengzhouyan_nwpu, fw
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel•org>:
On Mon, 2 Mar 2026 20:55:27 +0000 you wrote:
> This reverts 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
>
> tcp_tw_recycle went away in 2017.
>
> Zhouyan Deng reported off-path TCP source port leakage via
> SYN cookie side-channel that can be fixed in multiple ways.
>
> [...]
Here is the summary with links:
- [net] tcp: secure_seq: add back ports to TS offset
https://git.kernel.org/netdev/net/c/165573e41f2f
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net] tcp: secure_seq: add back ports to TS offset
2026-03-02 20:55 [PATCH net] tcp: secure_seq: add back ports to TS offset Eric Dumazet
` (3 preceding siblings ...)
2026-03-05 2:00 ` patchwork-bot+netdevbpf
@ 2026-06-06 11:04 ` xietangxin
4 siblings, 0 replies; 6+ messages in thread
From: xietangxin @ 2026-06-06 11:04 UTC (permalink / raw)
To: Eric Dumazet, David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Neal Cardwell, Kuniyuki Iwashima, Willy Tarreau,
netdev, eric.dumazet, Zhouyan Deng, Florian Westphal
On 3/3/2026 4:55 AM, Eric Dumazet wrote:
> This reverts 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
>
> tcp_tw_recycle went away in 2017.
>
> Zhouyan Deng reported off-path TCP source port leakage via
> SYN cookie side-channel that can be fixed in multiple ways.
>
> One of them is to bring back TCP ports in TS offset randomization.
>
> As a bonus, we perform a single siphash() computation
> to provide both an ISN and a TS offset.
>
> Fixes: 28ee1b746f49 ("secure_seq: downgrade to per-host timestamp offsets")
> Reported-by: Zhouyan Deng <dengzhouyan_nwpu@163•com>
> Signed-off-by: Eric Dumazet <edumazet@google•com>
> Cc: Florian Westphal <fw@strlen•de>
> ---
> include/net/secure_seq.h | 45 ++++++++++++++++++----
> include/net/tcp.h | 6 ++-
> net/core/secure_seq.c | 80 +++++++++++++++-------------------------
> net/ipv4/syncookies.c | 11 ++++--
> net/ipv4/tcp_input.c | 8 +++-
> net/ipv4/tcp_ipv4.c | 37 +++++++++----------
> net/ipv6/syncookies.c | 11 ++++--
> net/ipv6/tcp_ipv6.c | 37 +++++++++----------
> 8 files changed, 127 insertions(+), 108 deletions(-)
>
> diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h
> index cddebafb9f779ebd5d9c02e8ff26c13b5697c7d1..6f996229167b3c3f7861b2d5693ef81b5eed0d74 100644
> --- a/include/net/secure_seq.h
> +++ b/include/net/secure_seq.h
> @@ -5,16 +5,47 @@
> #include <linux/types.h>
>
> struct net;
> +extern struct net init_net;
> +
> +union tcp_seq_and_ts_off {
> + struct {
> + u32 seq;
> + u32 ts_off;
> + };
> + u64 hash64;
> +};
>
> u64 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
> u64 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
> __be16 dport);
> -u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
> - __be16 sport, __be16 dport);
> -u32 secure_tcp_ts_off(const struct net *net, __be32 saddr, __be32 daddr);
> -u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
> - __be16 sport, __be16 dport);
> -u32 secure_tcpv6_ts_off(const struct net *net,
> - const __be32 *saddr, const __be32 *daddr);
> +union tcp_seq_and_ts_off
> +secure_tcp_seq_and_ts_off(const struct net *net, __be32 saddr, __be32 daddr,
> + __be16 sport, __be16 dport);
> +
> +static inline u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
> + __be16 sport, __be16 dport)
> +{
> + union tcp_seq_and_ts_off ts;
> +
> + ts = secure_tcp_seq_and_ts_off(&init_net, saddr, daddr,
> + sport, dport);
> +
> + return ts.seq;
> +}
> +
> +union tcp_seq_and_ts_off
> +secure_tcpv6_seq_and_ts_off(const struct net *net, const __be32 *saddr,
> + const __be32 *daddr,
> + __be16 sport, __be16 dport);
> +
> +static inline u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
> + __be16 sport, __be16 dport)
> +{
> + union tcp_seq_and_ts_off ts;
> +
> + ts = secure_tcpv6_seq_and_ts_off(&init_net, saddr, daddr,
> + sport, dport);
>
> + return ts.seq;
> +}
> #endif /* _NET_SECURE_SEQ */
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index eb8bf63fdafc3243469f293fd06aef0ce086c5a4..978eea2d5df04f378dceb251025bee3101120f69 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -43,6 +43,7 @@
> #include <net/dst.h>
> #include <net/mptcp.h>
> #include <net/xfrm.h>
> +#include <net/secure_seq.h>
>
> #include <linux/seq_file.h>
> #include <linux/memcontrol.h>
> @@ -2464,8 +2465,9 @@ struct tcp_request_sock_ops {
> struct flowi *fl,
> struct request_sock *req,
> u32 tw_isn);
> - u32 (*init_seq)(const struct sk_buff *skb);
> - u32 (*init_ts_off)(const struct net *net, const struct sk_buff *skb);
> + union tcp_seq_and_ts_off (*init_seq_and_ts_off)(
> + const struct net *net,
> + const struct sk_buff *skb);
> int (*send_synack)(const struct sock *sk, struct dst_entry *dst,
> struct flowi *fl, struct request_sock *req,
> struct tcp_fastopen_cookie *foc,
> diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c
> index 9a39656804513dcef0888d280d8289913ef27eea..6a6f2cda5aaef82074718439920c75a75592e967 100644
> --- a/net/core/secure_seq.c
> +++ b/net/core/secure_seq.c
> @@ -20,7 +20,6 @@
> #include <net/tcp.h>
>
> static siphash_aligned_key_t net_secret;
> -static siphash_aligned_key_t ts_secret;
>
> #define EPHEMERAL_PORT_SHUFFLE_PERIOD (10 * HZ)
>
> @@ -28,11 +27,6 @@ static __always_inline void net_secret_init(void)
> {
> net_get_random_once(&net_secret, sizeof(net_secret));
> }
> -
> -static __always_inline void ts_secret_init(void)
> -{
> - net_get_random_once(&ts_secret, sizeof(ts_secret));
> -}
> #endif
>
> #ifdef CONFIG_INET
> @@ -53,28 +47,9 @@ static u32 seq_scale(u32 seq)
> #endif
>
> #if IS_ENABLED(CONFIG_IPV6)
> -u32 secure_tcpv6_ts_off(const struct net *net,
> - const __be32 *saddr, const __be32 *daddr)
> -{
> - const struct {
> - struct in6_addr saddr;
> - struct in6_addr daddr;
> - } __aligned(SIPHASH_ALIGNMENT) combined = {
> - .saddr = *(struct in6_addr *)saddr,
> - .daddr = *(struct in6_addr *)daddr,
> - };
> -
> - if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
> - return 0;
> -
> - ts_secret_init();
> - return siphash(&combined, offsetofend(typeof(combined), daddr),
> - &ts_secret);
> -}
> -EXPORT_IPV6_MOD(secure_tcpv6_ts_off);
> -
> -u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
> - __be16 sport, __be16 dport)
> +union tcp_seq_and_ts_off
> +secure_tcpv6_seq_and_ts_off(const struct net *net, const __be32 *saddr,
> + const __be32 *daddr, __be16 sport, __be16 dport)
> {
> const struct {
> struct in6_addr saddr;
> @@ -87,14 +62,20 @@ u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
> .sport = sport,
> .dport = dport
> };
> - u32 hash;
> + union tcp_seq_and_ts_off st;
>
> net_secret_init();
> - hash = siphash(&combined, offsetofend(typeof(combined), dport),
> - &net_secret);
> - return seq_scale(hash);
> +
> + st.hash64 = siphash(&combined, offsetofend(typeof(combined), dport),
> + &net_secret);
> +
> + if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
> + st.ts_off = 0;
> +
> + st.seq = seq_scale(st.seq);
> + return st;
> }
> -EXPORT_SYMBOL(secure_tcpv6_seq);
> +EXPORT_SYMBOL(secure_tcpv6_seq_and_ts_off);
>
> u64 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
> __be16 dport)
> @@ -118,33 +99,30 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
> #endif
>
> #ifdef CONFIG_INET
> -u32 secure_tcp_ts_off(const struct net *net, __be32 saddr, __be32 daddr)
> -{
> - if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
> - return 0;
> -
> - ts_secret_init();
> - return siphash_2u32((__force u32)saddr, (__force u32)daddr,
> - &ts_secret);
> -}
> -
> /* secure_tcp_seq_and_tsoff(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d),
> * but fortunately, `sport' cannot be 0 in any circumstances. If this changes,
> * it would be easy enough to have the former function use siphash_4u32, passing
> * the arguments as separate u32.
> */
> -u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
> - __be16 sport, __be16 dport)
> +union tcp_seq_and_ts_off
> +secure_tcp_seq_and_ts_off(const struct net *net, __be32 saddr, __be32 daddr,
> + __be16 sport, __be16 dport)
> {
> - u32 hash;
> + u32 ports = (__force u32)sport << 16 | (__force u32)dport;
> + union tcp_seq_and_ts_off st;
>
> net_secret_init();
> - hash = siphash_3u32((__force u32)saddr, (__force u32)daddr,
> - (__force u32)sport << 16 | (__force u32)dport,
> - &net_secret);
> - return seq_scale(hash);
> +
> + st.hash64 = siphash_3u32((__force u32)saddr, (__force u32)daddr,
> + ports, &net_secret);
> +
> + if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
> + st.ts_off = 0;
> +
> + st.seq = seq_scale(st.seq);
> + return st;
> }
> -EXPORT_SYMBOL_GPL(secure_tcp_seq);
> +EXPORT_SYMBOL_GPL(secure_tcp_seq_and_ts_off);
>
> u64 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
> {
> diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
> index 061751aabc8e16c5d536a19f7b920d1bca2b0f4f..fc3affd9c8014b1d4e9f161421a7753717cdcd73 100644
> --- a/net/ipv4/syncookies.c
> +++ b/net/ipv4/syncookies.c
> @@ -378,9 +378,14 @@ static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk,
> tcp_parse_options(net, skb, &tcp_opt, 0, NULL);
>
> if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
> - tsoff = secure_tcp_ts_off(net,
> - ip_hdr(skb)->daddr,
> - ip_hdr(skb)->saddr);
> + union tcp_seq_and_ts_off st;
> +
> + st = secure_tcp_seq_and_ts_off(net,
> + ip_hdr(skb)->daddr,
> + ip_hdr(skb)->saddr,
> + tcp_hdr(skb)->dest,
> + tcp_hdr(skb)->source);
> + tsoff = st.ts_off;
> tcp_opt.rcv_tsecr -= tsoff;
> }
>
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index 7b03f2460751f366dd6cf15505e49ae26cd6466e..cba89733d1216bc2663758b4bda21984835e6055 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -7646,6 +7646,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
> const struct tcp_sock *tp = tcp_sk(sk);
> struct net *net = sock_net(sk);
> struct sock *fastopen_sk = NULL;
> + union tcp_seq_and_ts_off st;
> struct request_sock *req;
> bool want_cookie = false;
> struct dst_entry *dst;
> @@ -7715,9 +7716,12 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
> if (!dst)
> goto drop_and_free;
>
> + if (tmp_opt.tstamp_ok || (!want_cookie && !isn))
> + st = af_ops->init_seq_and_ts_off(net, skb);
> +
> if (tmp_opt.tstamp_ok) {
> tcp_rsk(req)->req_usec_ts = dst_tcp_usec_ts(dst);
> - tcp_rsk(req)->ts_off = af_ops->init_ts_off(net, skb);
> + tcp_rsk(req)->ts_off = st.ts_off;
> }
> if (!want_cookie && !isn) {
> int max_syn_backlog = READ_ONCE(net->ipv4.sysctl_max_syn_backlog);
> @@ -7739,7 +7743,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
> goto drop_and_release;
> }
>
> - isn = af_ops->init_seq(skb);
> + isn = st.seq;
> }
>
> tcp_ecn_create_request(req, skb, sk, dst);
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index d53d39be291a5750af3ab2a160b35f0f8a28ff9d..56c0db955177edd3fdd04d26d6cd07b5e379e7bc 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -104,17 +104,14 @@ static DEFINE_PER_CPU(struct sock_bh_locked, ipv4_tcp_sk) = {
>
> static DEFINE_MUTEX(tcp_exit_batch_mutex);
>
> -static u32 tcp_v4_init_seq(const struct sk_buff *skb)
> +static union tcp_seq_and_ts_off
> +tcp_v4_init_seq_and_ts_off(const struct net *net, const struct sk_buff *skb)
> {
> - return secure_tcp_seq(ip_hdr(skb)->daddr,
> - ip_hdr(skb)->saddr,
> - tcp_hdr(skb)->dest,
> - tcp_hdr(skb)->source);
> -}
> -
> -static u32 tcp_v4_init_ts_off(const struct net *net, const struct sk_buff *skb)
> -{
> - return secure_tcp_ts_off(net, ip_hdr(skb)->daddr, ip_hdr(skb)->saddr);
> + return secure_tcp_seq_and_ts_off(net,
> + ip_hdr(skb)->daddr,
> + ip_hdr(skb)->saddr,
> + tcp_hdr(skb)->dest,
> + tcp_hdr(skb)->source);
> }
>
> int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
> @@ -326,15 +323,16 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len
> rt = NULL;
>
> if (likely(!tp->repair)) {
> + union tcp_seq_and_ts_off st;
> +
> + st = secure_tcp_seq_and_ts_off(net,
> + inet->inet_saddr,
> + inet->inet_daddr,
> + inet->inet_sport,
> + usin->sin_port);
> if (!tp->write_seq)
> - WRITE_ONCE(tp->write_seq,
> - secure_tcp_seq(inet->inet_saddr,
> - inet->inet_daddr,
> - inet->inet_sport,
> - usin->sin_port));
> - WRITE_ONCE(tp->tsoffset,
> - secure_tcp_ts_off(net, inet->inet_saddr,
> - inet->inet_daddr));
> + WRITE_ONCE(tp->write_seq, st.seq);
> + WRITE_ONCE(tp->tsoffset, st.ts_off);
> }
>
> atomic_set(&inet->inet_id, get_random_u16());
> @@ -1676,8 +1674,7 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
> .cookie_init_seq = cookie_v4_init_sequence,
> #endif
> .route_req = tcp_v4_route_req,
> - .init_seq = tcp_v4_init_seq,
> - .init_ts_off = tcp_v4_init_ts_off,
> + .init_seq_and_ts_off = tcp_v4_init_seq_and_ts_off,
> .send_synack = tcp_v4_send_synack,
> };
>
> diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
> index 7e007f013ec827c99bcab4ceb85eb35e9242b439..4f6f0d751d6c533231ca0397319935dc90ba4dba 100644
> --- a/net/ipv6/syncookies.c
> +++ b/net/ipv6/syncookies.c
> @@ -151,9 +151,14 @@ static struct request_sock *cookie_tcp_check(struct net *net, struct sock *sk,
> tcp_parse_options(net, skb, &tcp_opt, 0, NULL);
>
> if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
> - tsoff = secure_tcpv6_ts_off(net,
> - ipv6_hdr(skb)->daddr.s6_addr32,
> - ipv6_hdr(skb)->saddr.s6_addr32);
> + union tcp_seq_and_ts_off st;
> +
> + st = secure_tcpv6_seq_and_ts_off(net,
> + ipv6_hdr(skb)->daddr.s6_addr32,
> + ipv6_hdr(skb)->saddr.s6_addr32,
> + tcp_hdr(skb)->dest,
> + tcp_hdr(skb)->source);
> + tsoff = st.ts_off;
> tcp_opt.rcv_tsecr -= tsoff;
> }
>
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index e46a0efae01235ae7430ed268b92cb47309b8d28..5d95f9ab86973777c29399348adcc4b07e98d2b4 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -104,18 +104,14 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
> }
> }
>
> -static u32 tcp_v6_init_seq(const struct sk_buff *skb)
> +static union tcp_seq_and_ts_off
> +tcp_v6_init_seq_and_ts_off(const struct net *net, const struct sk_buff *skb)
> {
> - return secure_tcpv6_seq(ipv6_hdr(skb)->daddr.s6_addr32,
> - ipv6_hdr(skb)->saddr.s6_addr32,
> - tcp_hdr(skb)->dest,
> - tcp_hdr(skb)->source);
> -}
> -
> -static u32 tcp_v6_init_ts_off(const struct net *net, const struct sk_buff *skb)
> -{
> - return secure_tcpv6_ts_off(net, ipv6_hdr(skb)->daddr.s6_addr32,
> - ipv6_hdr(skb)->saddr.s6_addr32);
> + return secure_tcpv6_seq_and_ts_off(net,
> + ipv6_hdr(skb)->daddr.s6_addr32,
> + ipv6_hdr(skb)->saddr.s6_addr32,
> + tcp_hdr(skb)->dest,
> + tcp_hdr(skb)->source);
> }
>
> static int tcp_v6_pre_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
> @@ -319,14 +315,16 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
> sk_set_txhash(sk);
>
> if (likely(!tp->repair)) {
> + union tcp_seq_and_ts_off st;
> +
> + st = secure_tcpv6_seq_and_ts_off(net,
> + np->saddr.s6_addr32,
> + sk->sk_v6_daddr.s6_addr32,
> + inet->inet_sport,
> + inet->inet_dport);
> if (!tp->write_seq)
> - WRITE_ONCE(tp->write_seq,
> - secure_tcpv6_seq(np->saddr.s6_addr32,
> - sk->sk_v6_daddr.s6_addr32,
> - inet->inet_sport,
> - inet->inet_dport));
> - tp->tsoffset = secure_tcpv6_ts_off(net, np->saddr.s6_addr32,
> - sk->sk_v6_daddr.s6_addr32);
> + WRITE_ONCE(tp->write_seq, st.seq);
> + tp->tsoffset = st.ts_off;
> }
>
> if (tcp_fastopen_defer_connect(sk, &err))
> @@ -816,8 +814,7 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
> .cookie_init_seq = cookie_v6_init_sequence,
> #endif
> .route_req = tcp_v6_route_req,
> - .init_seq = tcp_v6_init_seq,
> - .init_ts_off = tcp_v6_init_ts_off,
> + .init_seq_and_ts_off = tcp_v6_init_seq_and_ts_off,
> .send_synack = tcp_v6_send_synack,
> };
>
Hi Eric and netdev,
I noticed a significant TCP performance regression (QPS drop) when using
iptables MASQUERADE with the `--random-fully` option, and I have bisected
it down to commit 165573e41f2f66ef98940cf65f838b2cb575d9d1
(tcp: secure_seq: add back ports to TS offset).
Here is the benchmark environment and test results.
Environment:
- Client & Server: 2 VMs
- Server: Nginx listening on port 80 (HTTP), and ip 10.0.0.1
- Benchmark tool: wrk (short-lived connections with "Connection: close")
Test Commands
1. With random-fully:
# iptables -t nat -A POSTROUTING -d 10.0.0.1 -p tcp --dport 80 -j MASQUERADE --random-fully
# wrk -t8 -c200 -H "Connection: close" -d10s --latency http://10.0.0.1:80
2. Without random-fully:
# iptables -t nat -A POSTROUTING -d 10.0.0.1 -p tcp --dport 80 -j MASQUERADE
# wrk -t8 -c200 -H "Connection: close" -d10s --latency http://10.0.0.1:80
Test Results (QPS):
1. Parent Commit (7f083faf59d14c04e01ec05a7507f036c965acf8):
- with random-fully: 18145.74, 15006.39, 15716.67
- without random-fully: 18556.36, 16339.22, 21506.02
2. Bad Commit (165573e41f2f66ef98940cf65f838b2cb575d9d1):
- with random-fully: 11074.76, 10383.20, 10164.81 <-- (~35% drop)
- without random-fully: 17310.75, 20279.85, 18399.48
Is this performance degradation an expected side-effect of the security fix,
or is there any sysctl param we should tune when `--random-fully` is
required for high-concurrency short connections?
Looking forward to your insights.
--
Best regards,
Tangxin Xie
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-06 11:09 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-02 20:55 [PATCH net] tcp: secure_seq: add back ports to TS offset Eric Dumazet
2026-03-02 21:47 ` Kuniyuki Iwashima
2026-03-03 1:41 ` Florian Westphal
2026-03-03 7:39 ` Jörg Sommer
2026-03-05 2:00 ` patchwork-bot+netdevbpf
2026-06-06 11:04 ` xietangxin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox