OpenSnell
Alpha Branch

TCP Brutal Congestion Control

A per-connection, rate-pinned kernel congestion control algorithm. Useful on high-loss long-fat paths where cubic/bbr collapse — and a sharp footgun on snell, which has no mux.

apernet/tcp-brutal is a Linux kernel module that exposes a brutal TCP congestion-control algorithm. Userspace sets a fixed per-socket sending rate; the kernel paces the socket to that rate without listening to loss feedback. Useful on high-loss long-fat paths where standard cubic / bbr back off aggressively and collapse.

OpenSnell's alpha branch wires brutal into both the server-side TCP listener and the client-side outbound dial, behind explicit opt-in config keys.

Read The multiplex trap before turning this on. Brutal sets a per-connection rate. Snell has no mux. The two together can hand you N × brutal-mbps if you have N parallel connections.

What brutal actually does

Standard TCP congestion control reacts to loss: when ACKs go missing, the sender assumes the path is congested and shrinks its window. On a clean fiber path that works well. On a long-fat path with non-trivial loss — transcontinental links, mobile carriers in interference, ISPs with rate-limit-induced drops — it collapses to a fraction of the available bandwidth.

Brutal flips the model: you tell the kernel what rate you want, and the kernel sends at that rate regardless of what the receiver's ACK pattern implies. There is no congestion control; there is a configured rate limit. If the path can carry it, great. If it can't, you'll see overhead in retransmits but the sender will not slow down.

This is exactly what you want for single, sustained, bulk transfers through a known-good proxy where you've already characterized the path. It is not what you want for many concurrent connections, because each one of them will independently try to push the same rate, and collectively saturate the receiver.

How OpenSnell wires it up

Server side

When brutal = true is set in [snell-server], the server calls setsockopt(TCP_CONGESTION, "brutal") on every accepted TCP connection from a snell client, then writes brutal-mbps (converted to bytes/sec in a brutal-specific sockopt struct) and an optional brutal-cwnd-gain multiplier.

If the brutal kernel module isn't loaded, setsockopt returns ENOENT; OpenSnell logs a warning at startup and falls back to the kernel default CC — it does not refuse to start. This is intentional: if the module gets unloaded out from under a running server, behavior degrades gracefully rather than dropping all traffic.

Client side

When brutal = true is set in [snell-client], the same setsockopt sequence runs on the client → server dial. This controls only the upload direction (client → server). For download-side acceleration, the server must have brutal enabled too.

Either side can enable brutal independently:

  • Server-only enable → server → client (downloads — the typical proxy traffic).
  • Client-only enable → client → server (uploads).

They don't need to match, and using only one side is the common case.

Installing the kernel module

On the server host (assuming a stock Debian / Ubuntu kernel):

apt install linux-headers-$(uname -r) make gcc
git clone https://github.com/apernet/tcp-brutal /tmp/tcp-brutal
cd /tmp/tcp-brutal && make && insmod brutal.ko

Verify it loaded:

cat /proc/sys/net/ipv4/tcp_available_congestion_control
# should now include "brutal"

Make it persistent across reboots by either:

  • Adding brutal to /etc/modules-load.d/brutal.conf (and copying the built .ko to /lib/modules/$(uname -r)/extra/, then running depmod), or
  • Building it once per kernel upgrade — there are DKMS-style scripts in the upstream repo if you prefer.

Then set the config keys in snell-server.conf (and/or snell-client.conf):

brutal = true
brutal-mbps = 100        ; required when brutal = true
brutal-cwnd-gain = 15    ; optional; tenths (15 = 1.5x, 20 = 2.0x)

Restart and the relevant direction will be rate-pinned per connection.

Verified end-to-end

On a v6-capable Linux server, with brutal-mbps = 50 and a 100 MB download via cachefly.cachefly.net/100mb.test:

Server CCTimeSpeed
vanilla (cubic)1.89 s444 Mbps
brutal (50)16.97 s49.4 Mbps (< 2 % off the configured 50)

The brutal-paced transfer hit the configured 50 Mbps to within 1.2 %. On a path with poor loss characteristics (which the cachefly path is not, since it's clean), brutal would win against vanilla cubic by not collapsing. On this clean path, it's strictly slower — which is the point: it's a rate limit, not an accelerator.

The multiplex trap — read this before turning brutal on

Quoting the upstream maintainers verbatim:

"Brutal's rate setting applies to each individual connection. This makes it suitable only for protocols that support multiplexing (mux). For protocols that require a separate connection for each proxy connection, using TCP Brutal will overwhelm the receiver if multiple connections are active at the same time."

Snell does not support mux. Reuse-mode (CommandConnectV2) is serial — one CONNECT per TCP at a time — and the client may still hold multiple pooled TCPs concurrently. If your workload opens N parallel snell connections, your server tries to push N × brutal-mbps total and the receiver may collapse with packet loss.

Concretely: with brutal-mbps = 100 and a browser holding 6 parallel connections to load a page, the server is now pushing up to 600 Mbps toward the client regardless of whether the client's downlink can absorb it. The downlink saturates, packets drop, every connection retransmits (brutal doesn't back off, remember), and you get worse performance than you would have with vanilla cubic.

Use brutal only when:

  • You have a sustained single-stream workload (large file download, video stream, sftp pull, etc.), or
  • You're testing.

If your workload is web browsing or anything else that opens many short-lived parallel connections, brutal is the wrong tool. Leave it off, or use reuse = true to keep concurrency low and pair brutal with that — but even then, the reuse pool caps at 2 per origin, so brutal can still double-up.

Tuning notes

  • brutal-mbps is the target rate per connection. Set it to your bottleneck link's per-flow share, not the total link capacity.
  • brutal-cwnd-gain is in tenths (15 = 1.5×). The default of 15 is almost always fine. Bumping it to 20 helps on extremely high-BDP paths (intercontinental + GbE-class throughput) where the default cwnd ceiling otherwise becomes the bottleneck.
  • The kernel module reads its rate on every setsockopt call, so there's no "set and forget" caching pitfall — restart OpenSnell to change rates and the new value applies to new connections immediately.

When to not use brutal

If your link is clean (sub-1 % loss, < 50 ms RTT) and you're not hitting a CC-induced ceiling, vanilla cubic or bbr will beat brutal because they actually use the available bandwidth dynamically. Brutal exists to win on bad paths, not good ones.

On this page