Fix TCP keepalive setup on OpenBSD#459
Merged
9seconds merged 1 commit into9seconds:masterfrom Apr 10, 2026
Merged
Conversation
Fixes 9seconds#457. OpenBSD has no user-settable per-socket TCP keepalive options: TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT do not exist on OpenBSD, keepalive timing is controlled system-wide via the sysctls net.inet.tcp.keepidle and net.inet.tcp.keepintvl. Go reflects this in src/net/tcpsockopt_openbsd.go: setKeepAliveIdle / Interval / Count return ENOPROTOOPT for any non-negative value, and only short-circuit to nil for negative values that explicitly mean "leave alone". mtg builds a net.KeepAliveConfig with zero-valued Idle / Interval / Count whenever the user does not override them in the config (which is the default and the documented expectation). It then hands that config to (*TCPConn).SetKeepAliveConfig in two places: - network/sockopts.go: applied to every connection accepted by internal/utils.Listener.Accept and to every server-side dial that goes through the v1 default network. - network/v2/sockopts.go: applied to every connection produced by the v2 network's DialContext. On OpenBSD both calls fail with "set tcp ...: protocol not available". The user-visible effect is that: - `mtg doctor` reports the error for every Telegram DC. - `mtg run` accepts incoming TCP connections at the kernel level but Listener.Accept then closes each one before the proxy server ever sees it, so the client appears to hang on a half-open socket and nothing is logged. - There is no configuration workaround. Setting [network] keep-alive.disabled = true only zeroes Enable; Go still calls setKeepAliveIdle / Interval / Count, which still fail. This change extracts the keepalive setup behind an applyKeepAlive helper that has a per-platform implementation, following the same build-tag pattern already used for sockopts_lowat, sockopts_congestion, sockopts_reuseaddr and sockopts_usertimeout. On every supported platform except OpenBSD it still calls SetKeepAliveConfig and the behaviour is unchanged. On OpenBSD it calls SetKeepAlive(cfg.Enable) instead, which only flips SO_KEEPALIVE on or off and never touches the missing per-socket options. OpenBSD users get the system-wide sysctl-controlled keepalive timing, which is the only thing the kernel exposes anyway. Verified by cross-building (`GOOS=openbsd GOARCH=amd64 go build ./...` and `GOARCH=arm64`) and by running `go test ./network/...` on linux.
Closed
Owner
|
Понял, спасибо большое! |
This was referenced Apr 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #457.
Problem
OpenBSD has no user-settable per-socket TCP keepalive options.
TCP_KEEPIDLE,TCP_KEEPINTVLandTCP_KEEPCNTdo not exist on OpenBSD; keepalive timing is controlled system-wide via the sysctlsnet.inet.tcp.keepidleandnet.inet.tcp.keepintvl. The Go runtime reflects this insrc/net/tcpsockopt_openbsd.go:setKeepAliveIdle/setKeepAliveInterval/setKeepAliveCountreturnENOPROTOOPTfor any non-negative value, and only short-circuit tonilfor negative values (the explicit "leave alone" sentinel).mtg builds a
net.KeepAliveConfigwith zero-valuedIdle/Interval/Countwhenever the user does not override them, which is the default and the documented expectation. It then hands that config to(*TCPConn).SetKeepAliveConfigin two places:network/sockopts.go— applied to every connection accepted byinternal/utils.Listener.Acceptand to every server-side dial that goes through the v1 default network.network/v2/sockopts.go— applied to every connection produced by the v2 network'sDialContext.Both calls fail on OpenBSD with
set tcp ...: protocol not available. The user-visible effects:mtg doctorreports the error for every Telegram DC.mtg runaccepts incoming TCP connections at the kernel level butListener.Acceptcloses each one before the proxy server ever sees it, so the client appears to hang on a half-open socket and nothing is written to the log. This is exactly what @babut85 reports in openbsd #457.[network] keep-alive.disabled = trueonly zeroesEnable; Go'sSetKeepAliveConfigstill calls all four setters, and three of them still fail.Fix
Extract the keepalive setup behind an
applyKeepAlive(conn, cfg)helper that has a per-platform implementation, following the same build-tag pattern already used forsockopts_lowat,sockopts_congestion,sockopts_reuseaddrandsockopts_usertimeout.//go:build !openbsd):applyKeepAlivecallsconn.SetKeepAliveConfig(cfg). Behaviour is unchanged.//go:build openbsd):applyKeepAlivecallsconn.SetKeepAlive(cfg.Enable), which only flipsSO_KEEPALIVEon or off and never touches the missing per-socket options. OpenBSD users get the system-wide sysctl-controlled keepalive timing, which is the only thing the kernel exposes.The same fix is applied symmetrically in
network/(v1) andnetwork/v2/.Verified
GOOS=openbsd GOARCH=amd64 go build ./...— clean.GOOS=openbsd GOARCH=arm64 go build ./...— clean.go vet ./...clean for bothlinuxandopenbsd.go test -count=1 ./network/...passes on linux.Cannot run the test suite under OpenBSD locally; happy to iterate if anything turns up on a real OpenBSD host.