Skip to content

Sync upstream: keepalive config, TCP BBR, TCP_USER_TIMEOUT, TCP_NOTSENT_LOWAT, sockopts refactor#455

Closed
dolonet wants to merge 36 commits into9seconds:masterfrom
dolonet:sync-upstream-keepalive-tcp-opts
Closed

Sync upstream: keepalive config, TCP BBR, TCP_USER_TIMEOUT, TCP_NOTSENT_LOWAT, sockopts refactor#455
dolonet wants to merge 36 commits into9seconds:masterfrom
dolonet:sync-upstream-keepalive-tcp-opts

Conversation

@dolonet
Copy link
Copy Markdown
Contributor

@dolonet dolonet commented Apr 7, 2026

Syncs 5 substantive commits from upstream 9seconds/mtg (PRs #451#454), skipping merge commits.

Changes

  • Propagate keep alive settings from the config — keepalive interval/count/idle are now read from config and applied to sockets
  • TCP BBR — enables BBR congestion control in best-effort mode on Linux
  • TCP_USER_TIMEOUT — sets TCP user timeout on Linux sockets
  • TCP_NOTSENT_LOWAT — sets TCP_NOTSENT_LOWAT on supported platforms
  • Refactor sockopts — splits sockopts by functionality rather than by build tag (sockopts_congestion, sockopts_reuseaddr, sockopts_usertimeout, sockopts_lowat)

Fork-specific checks

  • "time" import in internal/cli/run_proxy.go preserved (needed for ThrottleCheckInterval)
  • network/v2/sockopts_test.go package declaration unchanged
  • go build ./... passes cleanly

dolonet and others added 30 commits March 29, 2026 16:20
Support multiple named secrets in [secrets] config section. During
FakeTLS handshake each secret is tried until HMAC validates. Matched
secret name is logged and used for per-user statistics tracking.

Built-in HTTP stats API (configurable via api-bind-to) exposes
GET /stats with per-user connection counts, bytes in/out, and
last-seen timestamps.

Backward compatible: single "secret" config key still works.
- Simplify goreleaser to linux/darwin amd64/arm64 only
- Remove Docker CI job (using GitHub Releases instead)
- Add release workflow with goreleaser
- Add monthly upstream sync workflow with conflict detection
- Add mtg-multi binaries to gitignore
# Conflicts:
#	mtglib/proxy.go
# Conflicts:
#	.github/workflows/ci.yaml
Benchmarks added in the per-connection-overhead work triggered errcheck
warnings (37 unchecked Close/Fprintf in tool/bench code) and a data race
on package-level `sink` variable written by concurrent goroutines.

- Exclude benchmarks/ and *_bench_test.go from errcheck/ineffassign
- Replace concurrent `sink = buf[X]` with runtime.KeepAlive(&buf)
issues.exclude-rules is the v1 schema; golangci-lint v2 moved
exclusion rules to linters.exclusions.rules.
Fix CI: benchmark lint exclusions and data race
Upstream changes:
- More idiomatic Golang (b6427ee)
- Change IP address set priority (1fcec38)
- Flaky CI test fixes (eedee63, 73c6a3a, e54d9d6)

Conflicts resolved:
- README.md: keep fork version
- default.pgo: removed (causes SEGV in fork builds)
- proxy.go: keep fork's counting conn, use upstream's tracker
- proxy_opts.go: keep fork's APIBindTo field
go install github.com/dolonet/mtg-multi@latest was broken because go.mod
declared module path as github.com/9seconds/mtg/v2 while the repo lives
at github.com/dolonet/mtg-multi. Users ended up installing upstream
binary without multi-secret support.

Fixes 9seconds#376 (comment)
Sync upstream + fix module path for go install
In TOML, all keys after a [section] header belong to that table.
The examples had api-bind-to after [secrets], causing it to be
parsed as secrets.api-bind-to and triggering "incorrect secret
format" errors.

Fixes #6
…order

# Conflicts:
#	.github/workflows/ci.yaml
#	README.md
#	default.pgo
#	mtglib/internal/tls/fake/client_side.go
#	mtglib/internal/tls/fake/client_side_test.go
#	mtglib/proxy.go
#	mtglib/proxy_opts.go
fix: move [secrets] after global keys in config examples
When total connections exceed a configurable limit, a background
goroutine (every 5s by default) computes per-user caps using a
fair-share algorithm: small users keep their connections, remaining
budget is split equally among heavy users. New connections from
over-cap users are rejected; existing connections are not killed.

Config:
  [throttle]
  max-connections = 5000
  check-interval = "5s"

Stats API response now includes throttle state with active caps.
Upstream replaced our reassembleTLSHandshake with a cleaner
fragmentedHandshakeReader + parseClientHello in utils.go.
Adapted ReadClientHelloMulti to use the new API.
Sync upstream: refactor TLS fragmentation
TCP keepalive was configured (SetKeepAlivePeriod) but never actually
enabled (SO_KEEPALIVE) on accepted client connections. Go 1.26's
SetKeepAlivePeriod only sets TCP_KEEPIDLE — it does not call
setsockopt(SO_KEEPALIVE, 1). Without SO_KEEPALIVE the kernel never
sends probe packets, so dead connections from sleeping mobile clients
linger until the idle timeout fires.

Replace SetKeepAlive + SetKeepAlivePeriod with net.KeepAliveConfig
(available since Go 1.24) for explicit per-socket control:

  Idle:     30s   (time before first probe)
  Interval: 10s   (between probes)
  Count:    3     (failed probes to declare dead)

This detects dead connections in ~60s instead of relying on system
defaults (tcp_keepalive_intvl=75s, probes=9 → up to 11 minutes).

Increase the default idle timeout from 1 minute to 5 minutes.
MTProto clients send ping_delay_disconnect every ~60s, which resets
the idle timer. The previous 1-minute default created a race: if a
ping arrived even 1–2 seconds late the relay was killed. A 5-minute
window also survives typical mobile sleep periods (phone idle 2–5 min)
where the NAT mapping is still alive and the connection can resume
without reconnection.

Cherry-picked from 9seconds/mtg@5f81ae3 with fork-specific fixes:
- keep "time" import in run_proxy.go (used by throttle feature)
- fix network/sockopts_test.go import path to dolonet/mtg-multi
Sync upstream: improve TCP keepalive and idle timeout for mobile clients
Sync upstream history (no-op merge after cherry-pick of 9seconds#441)
…cipher

Merges upstream commits:
- bec321d Fix DPI detection: skip GREASE cipher suite in ClientHello parsing
- eb56493 Add separate handshake timeout
- 39ab557 Small refactoring
- 5bf218f Do not use default TLS cipher

Conflict resolution:
- Kept ReadClientHelloMulti for multi-secret support
- Preserved Secrets/Throttle/APIBindTo features from dolonet fork
- Removed per-connection SetReadDeadline from ReadClientHelloMulti
  (superseded by proxy-level handshake timeout via SetDeadline)
@dolonet
Copy link
Copy Markdown
Contributor Author

dolonet commented Apr 7, 2026

Created by mistake - wrong target repo

@dolonet dolonet closed this Apr 7, 2026
@dolonet dolonet deleted the sync-upstream-keepalive-tcp-opts branch April 7, 2026 14:51
dolonet added a commit to dolonet/mtg-multi that referenced this pull request Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants