-
Notifications
You must be signed in to change notification settings - Fork 18.3k
Description
Go version
1.24.2
Output of go env
in your module/workspace:
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/yutaro.linux/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/yutaro.linux/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2500888817=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='arm64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/home/yutaro.linux/go/1.24.2/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/yutaro.linux/go/1.24.2'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/yutaro.linux/.goenv/versions/1.24.2'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/yutaro.linux/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/yutaro.linux/.goenv/versions/1.24.2/pkg/tool/linux_arm64'
GOVCS=''
GOVERSION='go1.24.2'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
func TCPMD5SigAvailable() (bool, error) {
listener, err := net.Listen("tcp", "localhost:0")
if err != nil {
return false, fmt.Errorf("listen failed: %w", err)
}
defer listener.Close()
fd, err := listener.(*net.TCPListener).File()
if err != nil {
return false, fmt.Errorf("error retrieving socket's file descriptor: %w", err)
}
defer fd.Close()
err = unix.SetsockoptTCPMD5Sig(int(fd.Fd()), unix.IPPROTO_TCP, unix.TCP_MD5SIG, newTcpMD5Sig("1.2.3.4", "key"))
if err != nil {
if errors.Is(err, syscall.ENOPROTOOPT) {
return false, nil // "protocol not available"
}
return false, fmt.Errorf("other error by setting setting socket option: %w", err)
}
return true, nil
}
func newTcpMD5Sig(address, key string) *unix.TCPMD5Sig {
sig := &unix.TCPMD5Sig{}
addr := net.ParseIP(address)
if addr.To4() != nil {
sig.Addr.Family = unix.AF_INET
copy(sig.Addr.Data[2:], addr.To4())
} else {
sig.Addr.Family = unix.AF_INET6
copy(sig.Addr.Data[6:], addr.To16())
}
sig.Keylen = uint16(len(key))
copy(sig.Key[0:], key)
return sig
}
func main() {
available, err := TCPMD5SigAvailable()
if err != nil {
fmt.Printf("Error checking TCP_MD5SIG availability: %v\n", err)
return
}
if available {
fmt.Println("TCP_MD5SIG is available")
} else {
fmt.Println("TCP_MD5SIG is not available")
}
}
Execute above code with sysctl net.mptcp.enabled=0
(MPTCP disabled) and net.mptcp.enabled=1
(MPTCP enabled) on the Linux with CONFIG_TCP_MD5SIG
enabled. With 1
it shows TCP_MD5SIG is not available
and with 0
, it shows TCP_MD5SIG is available
.
What did you see happen?
Since v1.24, net.Listen
started to enable MPTCP by default for all TCP listeners on Linux.
This makes kernel to call setsockopt of MPTCP (mptcp_setsockopt) instead of TCP.
For some options are compatible with TCP, so they are handled transparently, but in case of MD5, it is not supported in MPTCP, so it returns ENOPROTOOPT
here.
This breaks the existing program that uses net.Listen
with MD5 digest. The concrete example is BGP speaker such as gobgp. The issue was originally reported for Cilium which internally uses GoBGP (ref).
I'm not entirely sure how we should solve this problem. In theory, this problem occurs for any socket option that is supported in TCP, but not supported in the MPTCP. So far, MD5 digest and TCP_REPAIR are not supported. The former is used in BGP and the latter is used in the container migration tool like CRIU (this is not written in Go though).
The workaround we can make is setting multipathtcp
GODEBUG or disable MPTCP with either sysctl or kernel configuration. Both doesn't allow us to adjust the behavior per socket. Since the error happens after socket creation, falling back to the TCP is pretty hard at that point.
(I was overlooking the net.ListenConfig.SetMultipathTCP
)
What did you expect to see?
The existing program works without breakage.