Skip to contentSkip to conversation

tcp: Add TCP ROCCET congestion control module.

PATCH Superseded Tim Fuechsel 3 participants 3 messages May 12 – May 21

Subsystems (3)

v2 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 tcp: Add TCP ROCCET congestion control module.

TCP ROCCET is an extension of TCP CUBIC that improves its overall performance in cellular networks. By its mode of function, CUBIC causes bufferbloat while it tries to detect the available throughput of a network path. This is particularly a problem with large buffers in mobile networks. A more detailed description and analysis of this problem caused by TCP CUBIC can be found in [1]. TCP ROCCET addresses the bufferbloat problem by adding two additional metrics to detect bufferbloat. The first metric is the relative increase in RTT from its minimum, the srRTT. Here, bufferbloat can be detected when RTTs increase due to buffer filling. The second metric is the acknowledgment arrival rate sampled over 5 RTT intervals. If CUBIC increases the send rate or congestion window, and the acknowledgment arrival rate stays on the same level, the connection is limited by the bottleneck link's capacity. In such cases, ROCCET reduces the send rate to prevent bufferbloat. ROCCET uses a modified version of slow start rather than HyStart because HyStart is known to enter the congestion avoidance phase too early when used in cellular networks [2]. Therefore, ROCCET uses a combination of srRTT and the acknowledgment arrival rate to determine when to exit slow start. For the congestion avoidance phase, ROCCET relies only on the srRTT.

In real-world mobile 5G NR measurements, TCP ROCCET achieves better performance than CUBIC and BBRv3, by maintaining similar throughput while reducing the latency. In stationary 5G NR scenarios, the performance is similar to that of BBRv3. More information about TCP ROCCET and measurement evaluations can be found here [3].

[1] https://doi.org/10.1109/VTC2023-Fall60731.2023.10333357
[2] https://doi.org/10.1109/WMNC.2016.7543932
[3] https://doi.org/10.23919/WONS68803.2026.11501781

---

Changes since v1:
* Adjust some comments & Rework commit message
* Fix Kconfig format
* Add div-by-0 check variables
* Fix ack summation from +=1 to +=acked (counting now ACKs instead of ACK events)
* Fix wrapping in time comparisons
* Remove most module_params, including 'ignore_loss'
* Remove check that always evaluated to 'true' regarding 'bw_limit_detect'

---
 net/ipv4/Kconfig      |  12 +
 net/ipv4/Makefile     |   1 +
 net/ipv4/tcp_roccet.c | 639 ++++++++++++++++++++++++++++++++++++++++++
 net/ipv4/tcp_roccet.h |  46 +++
 4 files changed, 698 insertions(+)
 create mode 100644 net/ipv4/tcp_roccet.c
 create mode 100644 net/ipv4/tcp_roccet.h
net/ipv4/Kconfig +12 -0
index 301b47660305..23197958324b 100644
@@ -663,6 +663,18 @@ config TCP_CONG_CDG
 	    delay gradients." In Networking 2011. Preprint:
 	    http://caia.swin.edu.au/cv/dahayes/content/networking2011-cdg-preprint.pdf
 
+config TCP_CONG_ROCCET
+	tristate "ROCCET TCP"
+	default n
+	help
+	  TCP ROCCET is a sender-side only modification of the TCP CUBIC
+	  protocol stack/TCP CUBIC congestion control algorithm that
+	  optimizes the performance of TCP congestion control. Especially
+	  for networks with large buffers (wireless, cellular networks),
+	  TCP ROCCET has improved performance by maintaining similar
+	  throughput as CUBIC while reducing the latency.
+	  For more information, see: https://arxiv.org/abs/2510.25281
+
 config TCP_CONG_BBR
 	tristate "BBR TCP"
 	default n
net/ipv4/Makefile +1 -0
index 7964234f0d08..60bf2d2e4ce5 100644
@@ -45,6 +45,7 @@ obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
 obj-$(CONFIG_INET_UDP_DIAG) += udp_diag.o
 obj-$(CONFIG_INET_RAW_DIAG) += raw_diag.o
 obj-$(CONFIG_TCP_CONG_BBR) += tcp_bbr.o
+obj-$(CONFIG_TCP_CONG_ROCCET) += tcp_roccet.o
 obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
 obj-$(CONFIG_TCP_CONG_CDG) += tcp_cdg.o
 obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
net/ipv4/tcp_roccet.c new file +639 -0

639 lines not on this page · raw message

net/ipv4/tcp_roccet.h new file +46 -0

46 lines not on this page · raw message

base-commit: 73d587ae684d176fac9db94173f77d78a794ea4f

Signature · 1 line
2.43.0
maintainer
#1 lore raw

This is an AI-generated review of your patch. The human sending this email has considered the AI review valid, or at least plausible.
---
Note: The format of this report is altered to provide a free-form summary of the findings due to recitation restrictions. The original patch code is not quoted.

commit 838c20dc5dd6210ad01e69f78cc66400822cd8c7
Author: Tim Fuechsel <[email protected]>

tcp: Add TCP ROCCET congestion control module.

TCP ROCCET is an extension of TCP CUBIC that improves its overall performance in cellular networks by using additional metrics to detect bufferbloat. It introduces a modified slow start and uses srRTT and the acknowledgment arrival rate to determine when to exit slow start.

Can the srRTT calculation cause a catastrophic integer underflow? If a socket has not yet received a valid RTT sample, curr_rtt remains 0. When roccettcp_cong_avoid() executes, it calls update_min_rtt(), which computes curr_min_rtt as max(0, 1), resulting in 1. Then update_srrtt() calculates curr_rtt - curr_min_rtt (0 - 1). Since both are unsigned 32-bit integers, does this underflow to 0xFFFFFFFF? Multiplying this by 100 yields 0xFFFFFF9C, which gets integrated into the curr_srrtt EWMA. Would this resulting massive value instantly exceed the threshold and falsely trigger a severe bufferbloat response that permanently cripples the congestion window?

Are the congestion control state variables curr_rtt and curr_srrtt properly initialized? In roccettcp_reset(), memset zeroes the state struct only up to the curr_rtt field, leaving curr_rtt and curr_srrtt uninitialized. If a user dynamically switches an active socket's congestion control to ROCCET via setsockopt, could the icsk_ca_priv area contain leftover memory from the previous algorithm? Might these garbage values be fed into the core RTT logic, causing unpredictable bufferbloat detection and spurious throughput drops?

Is it safe to expose internal TCP congestion control callbacks as BPF kfuncs? The module natively hooks into the TCP stack, but it annotates its static internal functions with __bpf_kfunc and exports them via register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS). Does this allow arbitrary BPF struct_ops programs to call these native state-modifying functions directly? Could a BPF program invoke these functions on a socket using a different congestion control algorithm, blindly casting sk->icsk_ca_priv to struct roccettcp and corrupting the socket's internal state?

Could the 16-bit ACK rate counter wrap around on high-speed links? The ack_rate.cnt field is defined as a u16, accumulating the number of acknowledged segments over a 100ms interval. On modern fast links, more than 83,000 segments can be acknowledged in a single 100ms window. Will this silently overflow multiple times per interval, recording an artificially low, randomized rate and breaking the slow start exit conditions?

Does the interval boundary logic discard acknowledged packets? In update_ack_rate(), when the 100ms interval rolls over, the interval state rotates and ack_rate.cnt is reset to 0. This happens inside an if/else block where the else branch contains the counter increment. Because the else branch is bypassed during a rollover, are any segments acknowledged by the specific ACK packet that triggered the boundary cross permanently discarded, causing a persistent undercounting of the throughput?

Can a timestamp wraparound freeze ACK rate tracking for idle connections? The update_ack_rate() function evaluates elapsed time via an explicit signed cast of a 32-bit microsecond timestamp delta. Since this timestamp wraps around roughly every 71 minutes, if a socket remains idle for more than about 35 minutes, the unsigned delta exceeds 0x7FFFFFFF. Would the signed cast convert this into a large negative number, rendering the interval condition false, freezing the interval logic, and causing the counter to accumulate indefinitely without rotation?

Are ECN marks being ignored and cached, leading to spurious later drops? In roccet_in_ack_event(), receiving an ECE flag sets ece_received to true. However, in roccettcp_cong_avoid(), this flag is only evaluated and cleared if curr_srrtt exceeds roccet_xj. If an ECN mark arrives while the RTT is low, is it ignored, leaving ece_received stuck at true? Could this stale flag falsely trigger a severe congestion window reduction hours later if the RTT naturally spikes above the threshold?

Is there a division by zero vulnerability during module initialization? The variables beta and bic_scale are configurable module parameters. In roccettcp_register(), bic_scale is used directly in a do_div() call, and beta is used in a denominator. If the module is loaded with bic_scale set to 0 or beta set to 1024, will this instantly cause a division-by-zero kernel panic?

Does the implementation match its stated ACK rate bufferbloat detection mechanism? The commit message states that the rate is sampled over 5 RTT intervals, and slow start exits if the rate stays on the same level. However, the code uses a hardcoded 100ms interval, and the exit condition only triggers if the new rate drops by 10 or more segments. If the rate genuinely stays on the same level, the difference is 0. Does this completely fail the check?

Signature · 1 line
pw-bot: cr
#2 lore raw

Thanks for the review.

Quoted 11 lines · Jakub Kicinski

Can the srRTT calculation cause a catastrophic integer underflow? If a socket has not yet received a valid RTT sample, curr_rtt remains 0. When roccettcp_cong_avoid() executes, it calls update_min_rtt(), which computes curr_min_rtt as max(0, 1), resulting in 1. Then update_srrtt()calculates curr_rtt - curr_min_rtt (0 - 1). Since both are unsigned 32-bit integers, does this underflow to 0xFFFFFFFF? Multiplying this by 100yields 0xFFFFFF9C, which gets integrated into the curr_srrtt EWMA. Would this resulting massive value instantly exceed the threshold and falselytrigger a severe bufferbloat response that permanently cripples the congestion window?

Yes, this is a problem. We will add a check if curr_rtt < min_rtt

Quoted 9 lines

Are the congestion control state variables curr_rtt and curr_srrtt properly initialized? In roccettcp_reset(), memset zeroes the statestruct only up to the curr_rtt field, leaving curr_rtt and curr_srrtt uninitialized. If a user dynamically switches an active socket's congestion control to ROCCET via setsockopt, could the icsk_ca_priv area contain leftover memory from the previous algorithm? Might these garbage values be fed into the core RTT logic, causing unpredictable bufferbloatdetection and spurious throughput drops?

We will initially initialize the whole struct to 0 using sizeof instead of offsetof.

Quoted 9 lines

Is it safe to expose internal TCP congestion control callbacks as BPF kfuncs? The module natively hooks into the TCP stack, but it annotates its static internal functions with __bpf_kfunc and exports them via register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS). Does this allow arbitrary BPF struct_ops programs to call these native state-modifyingfunctions directly? Could a BPF program invoke these functions on a socket using a different congestion control algorithm, blindly casting sk->icsk_ca_priv to struct roccettcp and corrupting the socket's internal state?

Not sure which functions are meant.
In my understanding, all exposed functions are the ones that algorithms like CUBIC, BBR, or dctcp also expose...
The same problem (reinterpreting the memory layout of the private sk data as a roccet struct applies to the bbr struct in tcp_bbr.c. So I am unsure if we should just not expose the callbacks as kfuncs or leave them in)

Quoted 7 lines

Could the 16-bit ACK rate counter wrap around on high-speed links? The ack_rate.cnt field is defined as a u16, accumulating the number of acknowledged segments over a 100ms interval. On modern fast links, more than 83,000 segments can be acknowledged in a single 100ms window. Will this silently overflow multiple times per interval, recording an artificially low, randomized rate and breaking the slow start exit conditions?

That could well happen here. We will change the type to u32.

Quoted 8 lines

Does the interval boundary logic discard acknowledged packets? In update_ack_rate(), when the 100ms interval rolls over, the interval state rotates and ack_rate.cnt is reset to 0. This happens inside an if/else block where the else branch contains the counter increment. Because the else branch is bypassed during a rollover, are any segments acknowledged by the specific ACK packet that triggered the boundary cross permanently discarded, causing a persistent undercounting of the throughput?

Yes, should probably reset the counter to acked instead of back to 0.

Quoted 9 lines

Can a timestamp wraparound freeze ACK rate tracking for idle connections? The update_ack_rate() function evaluates elapsed time via an explicit signed cast of a 32-bit microsecond timestamp delta. Since this timestamp wraps around roughly every 71 minutes, if a socketremains idle for more than about 35 minutes, the unsigned delta exceeds 0x7FFFFFFF. Would the signed cast convert this into a large negative number, rendering the interval condition false, freezing the interval logic, and causing the counter to accumulate indefinitely without rotation?

Yes, but usage of the after() function should resolve this. We will add this.

Quoted 8 lines

Are ECN marks being ignored and cached, leading to spurious later drops? In roccet_in_ack_event(), receiving an ECE flag sets ece_received to true. However, in roccettcp_cong_avoid(), this flag is only evaluated and cleared if curr_srrtt exceeds roccet_xj. If an ECN mark arrives while the RTT is low, is it ignored, leaving ece_received stuck at true? Could this stale flag falsely trigger a severe congestion window reduction hours later if the RTT naturally spikes above the threshold?

We will change this. ECN should always be handled.

Quoted 6 lines

Is there a division by zero vulnerability during module initialization? The variables beta and bic_scale are configurable module parameters. In roccettcp_register(), bic_scale is used directly in a do_div() call, and beta is used in a denominator. If the module isloaded with bic_scale set to 0 or beta set to 1024, will this instantly cause a division-by-zero kernel panic?

This is CUBIC's code. Should we add a check there?

Quoted 7 lines

Does the implementation match its stated ACK rate bufferbloat detection mechanism? The commit message states that the rate is sampled over 5 RTT intervals, and slow start exits if the rate stays on the same level. However, the code uses a hardcoded 100ms interval, and the exit condition only triggers if the new rate drops by 10 or more segments. If the rate genuinely stays on the same level, the difference is 0. Does this completely fail the check?

The evaluation of the ack rate diff is faulty. In fact, it would evaluate false if it were true. We will change this.