Hi,
Could you please share the pcap files captured at the sender, receiver, and any other points where you might have done packet captures? Reviewing them in Wireshark should help with the analysis.
Thanks,
Taifeng
--
You received this message because you are subscribed to the Google Groups "BBR Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bbr-dev+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/3feb0438-1509-4a4d-84bf-5c28e570f651n%40googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/7c9c2846-853d-4d04-a0c0-a0b8cec032d3n%40googlegroups.com.
Hi Braden,
Thanks for sharing the pcap files and explaining more about the setup.
Is the current topology set up with NGINX and Synproxy deployed on 157.245.93.111, connecting to 207.47.245.215? Is that correct?
Based on both theoretical analysis and packet capture, I
agree with your point: the overly small iRTT in the initial phase leads to its
use as minRTT in the cwnd calculation formula, resulting in lower traffic
during the first 10 seconds of the connection.
From both a theoretical and packet-level perspective, this makes sense.
Figure 1: Theoretical Analysis

Figure 2: BIF on Wireshark Graph

However, since the packet capture tool (tcpdump) is placed
before the synproxy (between the synproxy and the receiver), the current
captures are unable to see that extremely small iRTT or minRTT.
Below is RTT graph:

To see this extremely small iRTT, the setup can be modified
as follows:
Deploy NGINX on 157.245.93.111, and use 207.47.245.215 as the receiver. Place a
separate device in between as the synproxy. In this way, by capturing packets
on all three devices, we can clearly observe the minRTT at any phase.
At present, I don’t have an environment available to conduct this experiment,
but I will try to find the necessary resources. If you are able to deploy this
experiment, that would be ideal.

Gentle note:
Thanks
Taifeng
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/7c9c2846-853d-4d04-a0c0-a0b8cec032d3n%40googlegroups.com.
Hi Neal, thanks for the reply. Would it be fair to say that the problem is in the current SYNPROXY implementation and not necessarily how BBR interacts with RTT?
Is omitting a SYNACK RTT an existing thing or is that a change necessary in the kernel?
It seems that the default syncookie implementation doesn't exhibit this same behaviour in my testing.
I am currently investigating bypassing SYNPROXY completely and relying on the default syncookie implementation to handle the SYN/ACK with the cookie. And thenputting more of the logic on when to generate a SYN cookie in our XDP program.
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/06384010-b07b-490e-9687-bb8ec2080264n%40googlegroups.com.
Hi Braden,
Let me clarify a few points:
You are correct that there is no third connection. In my diagram, Nginx, Synproxy, and tcpdump are all hosted on the same physical device (157.245.93.111). Logically, the packet path follows the order Nginx → Synproxy → tcpdump when analyzing traffic flow.

I agree that the netstat statistics alone suffice to prove the artificially low RTT. While I’m not a Synproxy expert, my intent was to demonstrate this phenomenon visually via Wireshark by capturing the abnormally small min_RTT. To achieve this, placing the three-way handshake proxy device between the TCP data sender (server) and receiver (client) is necessary. Anyway, no worries.
@Neal:
The behavior Braden observed is actually quite common in proxy/load‑balancer/anti‑DDoS deployments.

Here’s what happens:
If BBR uses the handshake RTT as its min_rtt, it may underestimate the real RTT. Although I haven’t built a testbed to prove this yet, the theory aligns exactly with Braden’s scenario.
Please consider excluding the initial handshake RTT (iRTT) from RTT calculations (min_rtt, RTO, etc.).
Site note: Different proxy/LB/anti‑DDoS devices handle traffic in various ways, but whenever SYN proxy (three‑way handshake proxying) is enabled and backend keep‑alives are disabled, you’ll see exactly this pattern. It’s a common configuration, not a rare edge case. These devices are ubiquitous, and despite their differing purposes, their SYN proxy implementations behave similarly—so the potential impact is broad.
Thanks,
Taifeng
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/CADVnQyn6qd5BJKBrQPihoO5S91G9rEzOfGFhA18H9X-h3dM1Mg%40mail.gmail.com.
Hi Braden,
Let me clarify a few points:
You are correct that there is no third connection. In my diagram, Nginx, Synproxy, and tcpdump are all hosted on the same physical device (157.245.93.111). Logically, the packet path follows the order Nginx → Synproxy → tcpdump when analyzing traffic flow.
I agree that the netstat statistics alone suffice to prove the artificially low RTT. While I’m not a Synproxy expert, my intent was to demonstrate this phenomenon visually via Wireshark by capturing the abnormally small min_RTT. To achieve this, placing the three-way handshake proxy device between the TCP data sender (server) and receiver (client) is necessary. Anyway, no worries.
@Neal:
The behavior Braden observed is actually quite common in proxy/load‑balancer/anti‑DDoS deployments.
Here’s what happens:
- Client ↔ Proxy RTT
The RTT between the TCP client (data receiver) and the proxy/LB/anti‑DDoS device is relatively large and closely matches the true end‑to‑end RTT.- Proxy ↔ Backend RTT
Once the proxy terminates the client’s handshake (SYN proxy) and establishes its own handshake with the backend server (the TCP sender), that RTT is typically very small.- Accurate RTT measurement
After data transfer begins, the RTT₃ (time from sending a segment to receiving its ACK) reflects the true end‑to‑end RTT.If BBR uses the handshake RTT as its min_rtt, it may underestimate the real RTT. Although I haven’t built a testbed to prove this yet, the theory aligns exactly with Braden’s scenario.
Please consider excluding the initial handshake RTT (iRTT) from RTT calculations (min_rtt, RTO, etc.).
Hi Neal,
Would it be reasonable to set different minRTO values for different network scenarios, similar to how Windows configures it?
For example, Windows assigns different minRTO values based on the network environment:
| Parameter | InternetCustom | DatacenterCustom | Compat | Datacenter | Internet |
|---|---|---|---|---|---|
| MinRTO (ms) | 300 | 20 | 300 | 20 | 300 |
After obtaining the initial RTT (iRTT), would it be reasonable to categorize it into predefined ranges and assign different minRTO values accordingly?
And other mechanisms, such as TLP, would also help mitigate the impact during loss recovery. And, is it possible to use history connection RTT (if any) to set a more reasonable initial RTO?
Thanks,
Taifeng
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/e2a04f83-6af4-41da-b243-91d1f2b6169dn%40googlegroups.com.
I did some searching. It looks like the RTO clamping mechanism is a universal feature of the TCP protocol stack and is present across all standard TCP congestion control algorithms. BBR's RTO calculation also follows this.
If the RTT is very small (such as in the synproxy scenario here), and the system's TCP_RTO_MIN is set to 200ms, then the final RTO will be clamped to the minRTO value, such as 200ms. Is that right?
Hi Neal,
Would it be reasonable to set different
minRTOvalues for different network scenarios, similar to how Windows configures it?For example, Windows assigns different
minRTOvalues based on the network environment:
Parameter InternetCustom DatacenterCustom Compat Datacenter Internet MinRTO (ms) 300 20 300 20 300 After obtaining the initial RTT (
iRTT), would it be reasonable to categorize it into predefined ranges and assign differentminRTOvalues accordingly?
And other mechanisms, such as TLP, would also help mitigate the impact during loss recovery.
And, is it possible to use history connection RTT (if any) to set a more reasonable initial RTO?
I did some searching. It looks like the RTO clamping mechanism is a universal feature of the TCP protocol stack and is present across all standard TCP congestion control algorithms. BBR's RTO calculation also follows this.
If the RTT is very small (such as in the synproxy scenario here), and the system's
TCP_RTO_MINis set to 200ms, then the final RTO will be clamped to the minRTO value, such as 200ms. Is that right?
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/CANsso2hmRJt%2Bm6pbK%3D5a7ESqrbsVyvBj0Fe%3DVQ04NX8r%2BmZE1g%40mail.gmail.com.
Hi Neal,
Regarding setting different minRTO values for different network scenarios (e.g., using a relatively smaller minRTO than 1s, like 20ms or 300ms), would that be better than using a small RTO calculated from the iRTT (just my guess)?
In the current RTO calculation during the first 10 seconds, it relies on the iRTT, right? Under what circumstances would it fall back to using 1 second as the RTO?
As for calculating the minRTT during the first 10 seconds, is it reasonable to use the RTT measured during data transmission—such as RTT3 in my diagram—instead of relying on the iRTT?
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/CANsso2iFLr_vEBHxgSumFAjoCJYBKWHcz6NrjzXzCVWKLm71Lg%40mail.gmail.com.
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/CANsso2gYzJJzbxmJXh3F5JLTPf77KVVniw1B3%3Dx2xKgmMzGLVg%40mail.gmail.com.
(1) the TCP stack could have a static branch that controls whether the TCP stack uses the SYNACK RTT sample for connections for which TCP has seen a SYN and SYNACK; by default this is enabled, to use such RTT samples(2) If/when the SYNPROXY iptables module is enabled, it would set the static branch to disable use of the SYNACK RTT sample(3) If/when the SYNPROXY iptables module is unloaded, it would set the static branch to re-enable use of the SYNACK RTT sampleHopefully that would allow SYNPROXY connections to not be fooled by the bogus SYNACK RTT sample, but also have a minimal performance impact for the case of machines not using the SYNPROXY feature.
Hi Braden, Neal, and all,
I built a small 4-namespace testbed to reproduce and isolate this issue. It looks like the issue has been resolved on Ubuntu 26.04 LTS or earlier.
I wanted to share the data point in case it is useful.
Topology (one host, veth pairs):
server (NS1) -- SYNPROXY (NS2) -- netem WAN (NS3) -- client (NS4)
netem adds 10 ms one-way delay on each WAN egress (~20 ms RTT), with no rate shaping. The client runs a single 20~25s bulk download (iperf3 -R).
I compare four cases:
Results:
The BBRv3 used here is the out-of-tree hrimfaxi/tcp_bbr_modules DKMS module (registered name "bbr3").
Graphs are as blow:
Ubuntu 22 BBRv1
Ubuntu 22 BBRv3
Ubuntu 26 BBRv1
Ubuntu 26 BBRv3
So it looks like the issue has been resolved on Ubuntu 26.04 LTS, (I have not tested every Ubuntu version). I did some digging but could not determine whether this was fixed in the TCP stack/kernel or in BBR itself; if anyone has information or an update on where the fix landed, it would be much appreciated.
This would also remove a significant real-world risk: SYNPROXY-style split handshakes are extremely common in proxies, WAFs, CDNs and anti-DDoS deployments. If this issue is genuinely fixed, those deployments immediately benefit.
The setup/test/teardown scripts are available here:
https://drive.google.com/drive/folders/1PDwuSxoz97asvGW0d2soUPS3w_zeFFCm?usp=sharing
The Pcap files are here: https://drive.google.com/drive/folders/1FNvHJvKTbn6J42ul1Ega9wUi0HaEf5xs?usp=sharing
Thanks,
Taifeng
Hi Braden, Neal, and all,
I built a small 4-namespace testbed to reproduce and isolate this issue. It looks like the issue has been resolved on Ubuntu 26.04 LTS or earlier.
I wanted to share the data point in case it is useful.
Topology (one host, veth pairs):
server (NS1) -- SYNPROXY (NS2) -- netem WAN (NS3) -- client (NS4)
netem adds 10 ms one-way delay on each WAN egress (~20 ms RTT), with no rate shaping. The client runs a single 20~25s bulk download (iperf3 -R).
I compare four cases:
- A. SYNPROXY split handshake (the reported scenario)
- B. plain end-to-end TCP (SYNPROXY rules removed)
- C. SYNPROXY + CUBIC (control)
- D. a userspace terminating proxy on NS2, i.e. two fully independent TCP sessions, client↔proxy and proxy↔server.
Results:
- On Ubuntu 22.04.5 LTS (5.15 GA and 6.8 HWE), both BBRv1 and BBRv3 reproduce the problem in case A only: the sender's initial RTT is the sub-millisecond server↔SYNPROXY RTT, throughput stays very low for ~10 s, and once the 10 s min_rtt filter expires it jumps to much higher rate. Cases B, C and D are fine.
- On Ubuntu 26.04 LTS (7.0.0-x-generic), I do NOT observe the symptom with either BBRv1 or BBRv3 in case A.
The BBRv3 used here is the out-of-tree hrimfaxi/tcp_bbr_modules DKMS module (registered name "bbr3").
Graphs are as blow:
Ubuntu 22 BBRv1
Ubuntu 22 BBRv3
Ubuntu 26 BBRv1
Ubuntu 26 BBRv3
So it looks like the issue has been resolved on Ubuntu 26.04 LTS, (I have not tested every Ubuntu version). I did some digging but could not determine whether this was fixed in the TCP stack/kernel or in BBR itself; if anyone has information or an update on where the fix landed, it would be much appreciated.
This would also remove a significant real-world risk: SYNPROXY-style split handshakes are extremely common in proxies, WAFs, CDNs and anti-DDoS deployments. If this issue is genuinely fixed, those deployments immediately benefit.
The setup/test/teardown scripts are available here:
https://drive.google.com/drive/folders/1PDwuSxoz97asvGW0d2soUPS3w_zeFFCm?usp=sharing
The Pcap files are here: https://drive.google.com/drive/folders/1FNvHJvKTbn6J42ul1Ega9wUi0HaEf5xs?usp=sharing
Thanks,
Taifeng
在2025年4月16日星期三 UTC+8 08:19:18<danie...@gmail.com> 写道:On Wednesday, April 9, 2025 at 8:56:52 a.m. UTC-4 Neal Cardwell wrote:(1) the TCP stack could have a static branch that controls whether the TCP stack uses the SYNACK RTT sample for connections for which TCP has seen a SYN and SYNACK; by default this is enabled, to use such RTT samples(2) If/when the SYNPROXY iptables module is enabled, it would set the static branch to disable use of the SYNACK RTT sample(3) If/when the SYNPROXY iptables module is unloaded, it would set the static branch to re-enable use of the SYNACK RTT sampleHopefully that would allow SYNPROXY connections to not be fooled by the bogus SYNACK RTT sample, but also have a minimal performance impact for the case of machines not using the SYNPROXY feature.Similarly to the rest of the netfilter functionality, synproxy is designed to work the same way whether it's on the same machine or a separate machine. A host-based firewall using synproxy on the same machine is a special case. In general, netfilter doesn't handle the host-based firewall case in a special way. It could theoretically create the TCP session instead of sending a spoofed TCP handshake via loopback for the host-based firewall case but it wouldn't be a general solution. The host-based firewall case is mainly for small deployments using it to defend the host-based firewall from resource exhaustion (conntrack table exhaustion). At scale, it will be handled on edge nodes acting as a reverse proxy and load balancer. It can be at an enormous scale such as a DDoS protection provider with a huge anycast network using it to handle TCP handshakes as part of their service along with potentially implemented established connection limits which can't be exhausted with spoofed SYN packets.
Synproxy can be set up so that it only activates for packets above a certain rate limit threshold under SYN flood conditions. That's a nice way of doing it and means it's not as simple as it either being enabled or not enabled. SYN packets going over the rate limit would get handled with it and those under the rate limit without it. Setting things up to handle things as if there's an ongoing SYN flood attack and synproxy is being used for the general case where it's not being used would be far from ideal.Synproxy is crucial when using a stateful firewall since otherwise it's trivial to exhaust the resources of the stateful firewall via a SYN flood filling the conntrack table. Many don't have it which is a disaster. It's a core part of what DDoS protection services for TCP services need to provide whether or not they implement an application layer reverse proxy or just filter the connections.It would be nice if synproxy special cased the host-based firewall case where it could inform the TCP stack of what's happening but I don't think that will help with most real world usage.
--
You received this message because you are subscribed to the Google Groups "BBR Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bbr-dev+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bbr-dev/4e943d6d-a52b-420a-8a30-ee04dea34ef5n%40googlegroups.com.