[PATCH net-next v3] tcp: honor BPF_SOCK_OPS_RWND_INIT on the active connect path

Tejas Birajdar <[email protected]>
Newsgroups org.kernel.vger.bpf,org.kernel.vger.linux-kernel,org.kernel.vger.netdev
Message-ID <[email protected]>
BPF_SOCK_OPS_RWND_INIT lets a sockops BPF program pick the initial TCP
receive window, e.g. to advertise a larger window up front in environments
where that is known to be safe. Today it is only effective for the passive
(listener) side; on the active (connect) side the value is computed and
then silently discarded.

On the passive path tcp_openreq_init_rwin() inflates full_space when the
program returns a non-zero window, so tcp_select_initial_window() can offer
it:

	else if (full_space < (u64)rcv_wnd * mss)
		full_space = min_t(u64, (u64)rcv_wnd * mss, INT_MAX);

tcp_select_initial_window() only clamps the requested window *down* to the
available space, so without inflating the space first the BPF reply can
never raise the offered window above tcp_full_space(sk).

tcp_connect_init() calls tcp_rwnd_init_bpf() but never inflates full_space,
so on connect() the requested window is clamped back to tcp_full_space(sk)
(~64KB at the default rcvbuf) and the program's value is ignored.

Inflate full_space in tcp_connect_init() as well; tp->advmss is the mss the
listener path uses (both are tcp_mss_clamp(tp, dst_metric_advmss(dst))).
Read full_space after tcp_rwnd_init_bpf() so a program that also adjusts
SO_RCVBUF is still reflected. Compute the inflated value in u64 and clamp
to INT_MAX to avoid overflow (full_space is int, rcv_wnd is u32), and
apply the same overflow fix to the existing listener-side computation.

tcp_select_initial_window() itself also computes init_rcv_wnd * mss in
32-bit when clamping the offered window down to the requested value. A
large requested window (init_rcv_wnd greater than ~2.9M segments at
1460 mss) wraps this multiply and collapses the offered window to a tiny
value, so compute it in u64 as well.

Fixes: 13d3b1ebe287 ("bpf: Support for setting initial receive window")
Suggested-by: Eric Dumazet <[email protected]>
Suggested-by: Paolo Abeni <[email protected]>
Suggested-by: Sashiko <[email protected]>
Signed-off-by: Tejas Birajdar <[email protected]>
---
v3:
  - Also compute init_rcv_wnd * mss in u64 in tcp_select_initial_window();
    a large requested window otherwise overflows the 32-bit multiply and
    collapses the offered window.
v2: https://lore.kernel.org/netdev/[email protected]/
  - Compute the inflated full_space in u64 and clamp to INT_MAX in both the
    connect and listener paths; read full_space after tcp_rwnd_init_bpf().
v1: https://lore.kernel.org/netdev/[email protected]/

Verified on the connect path with packetdrill: the offered initial window
now tracks the BPF-requested value, and a large request that previously
overflowed no longer collapses it. No new failures in the in-tree
packetdrill selftests.

 net/ipv4/tcp_minisocks.c | 4 ++--
 net/ipv4/tcp_output.c    | 8 ++++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ddc4b17a826b..f8c1123aba43 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -453,8 +453,8 @@ void tcp_openreq_init_rwin(struct request_sock *req,
 	rcv_wnd = tcp_rwnd_init_bpf((struct sock *)req);
 	if (rcv_wnd == 0)
 		rcv_wnd = dst_metric(dst, RTAX_INITRWND);
-	else if (full_space < rcv_wnd * mss)
-		full_space = rcv_wnd * mss;
+	else if (full_space < (u64)rcv_wnd * mss)
+		full_space = min_t(u64, (u64)rcv_wnd * mss, INT_MAX);
 
 	/* tcp_full_space because it is guaranteed to be the first packet */
 	tcp_select_initial_window(sk_listener, full_space,
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index d7c1444b5e30..fcaa04e65189 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -251,7 +251,7 @@ void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss,
 		(*rcv_wnd) = space;
 
 	if (init_rcv_wnd)
-		*rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
+		*rcv_wnd = min_t(u64, *rcv_wnd, (u64)init_rcv_wnd * mss);
 
 	*rcv_wscale = 0;
 	if (wscale_ok) {
@@ -4103,6 +4103,7 @@ static void tcp_connect_init(struct sock *sk)
 	const struct dst_entry *dst = __sk_dst_get(sk);
 	struct tcp_sock *tp = tcp_sk(sk);
 	__u8 rcv_wscale;
+	int full_space;
 	u16 user_mss;
 	u32 rcv_wnd;
 
@@ -4137,10 +4138,13 @@ static void tcp_connect_init(struct sock *sk)
 		WRITE_ONCE(tp->window_clamp, tcp_full_space(sk));
 
 	rcv_wnd = tcp_rwnd_init_bpf(sk);
+	full_space = tcp_full_space(sk);
 	if (rcv_wnd == 0)
 		rcv_wnd = dst_metric(dst, RTAX_INITRWND);
+	else if (full_space < (u64)rcv_wnd * tp->advmss)
+		full_space = min_t(u64, (u64)rcv_wnd * tp->advmss, INT_MAX);
 
-	tcp_select_initial_window(sk, tcp_full_space(sk),
+	tcp_select_initial_window(sk, full_space,
 				  tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0),
 				  &tp->rcv_wnd,
 				  &tp->window_clamp,
-- 
2.53.0-Meta
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.