Support multiple bind addresses + sync upstream (OpenBSD keepalive fix)#22
Merged
Support multiple bind addresses + sync upstream (OpenBSD keepalive fix)#22
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.
Fix TCP keepalive setup on OpenBSD
bind-to now accepts either a single string or an array of addresses: bind-to = "0.0.0.0:443" bind-to = ["127.0.0.1:443", "[::1]:443"] This avoids running multiple instances with separate configs just to listen on different addresses — useful on OpenBSD where rc.d does not expect multiple instances of the same service. A MultiListener fans-in Accept calls from all underlying listeners. When a single address is given, no extra goroutines are spawned. Closes #21
Closed
- Use buffered channel (cap = number of listeners) so all acceptLoop goroutines can exit cleanly on Close instead of blocking forever on the unbuffered send. - Validate duplicate bind-to addresses in Config.Validate(). - Add tests: empty bind array, invalid address, non-string element, duplicate addresses, single-listener MultiListener, concurrent accept under load (3 listeners x 10 connections).
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.
Summary
upstream/master— brings in the OpenBSD keepalive fix from Fix TCP keepalive setup on OpenBSD 9seconds/mtg#459 (build-tag split:SetKeepAliveonly on OpenBSD, fullSetKeepAliveConfigelsewhere)bind-tonow accepts an array of addresses in addition to a single stringA
MultiListenerfans-inAcceptcalls from all underlying listeners. When only one address is given, no extra goroutines are spawned — zero overhead for existing configs.Motivated by #21: on OpenBSD,
rc.dis simple and doesn't expect multiple instances of the same service, so binding to both127.0.0.1and::1previously required running separate processes with separate configs.Changes
internal/config/parse.go—bind-toaccepts bothstringand[]stringin TOML, normalizes to[]stringbefore JSON decodeinternal/config/config.go—BindTois now[]TypeHostPort, addedGetBindAddrs()andGetFirstBindPort()helpersinternal/utils/net_listener.go— newMultiListenertypeinternal/cli/run_proxy.go— creates listeners for each bind addressinternal/cli/access.go,simple_run.go— updated callersexample.config.toml— documents the array syntaxMultiListenerTest plan
go build ./...passesgo vet ./...passesgo test ./...)TestParseMultiBind,TestMultiBindGetAddrs,TestMultiBindGetFirstPort,TestMultiListener(accept, close, addr)bind-to = ["127.0.0.1:3128", "[::1]:3128"], connect to bothCloses #21