mirror of
https://github.com/moby/moby.git
synced 2026-01-11 10:41:43 +00:00
libnet/d/bridge: pass SCTP sock to the proxy
Since commit b3fabedec, the bridge driver maps ports following a 3-step
process: 1. create a socket, and bind it to the host port; 2. create
iptables rules; 3. start the userland proxy (if it's enabled). This
ensures that the port is really free before inserting iptables rules
that could otherwise disrupt host services.
However, this 3-step process wasn't implemented for SCTP, because we had
no way to instiantiate an SCTP listener from an fd. Since
github.com/ishidawataru/sctp@4719921f9, we can.
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -128,18 +127,18 @@ func newProxy(config ProxyConfig) (p Proxy, err error) {
|
||||
p, err = NewUDPProxy(listener, container, ipv)
|
||||
case "sctp":
|
||||
var listener *sctp.SCTPListener
|
||||
if config.ListenSock != nil {
|
||||
// There's no way to construct an SCTPListener from a file descriptor at the moment.
|
||||
// If a socket has been passed in, it's probably from a newer daemon using a version
|
||||
// of the sctp module that does allow it.
|
||||
return nil, errors.New("cannot use supplied SCTP socket, check the latest docker-proxy is in your $PATH")
|
||||
if config.ListenSock == nil {
|
||||
hostAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: config.HostIP}}, Port: config.HostPort}
|
||||
listener, err = sctp.ListenSCTP("sctp"+string(ipv), hostAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to listen on %s: %w", hostAddr, err)
|
||||
}
|
||||
} else {
|
||||
if listener, err = sctp.FileListener(config.ListenSock); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
hostAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: config.HostIP}}, Port: config.HostPort}
|
||||
container := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: config.ContainerIP}}, Port: config.ContainerPort}
|
||||
listener, err = sctp.ListenSCTP("sctp"+string(ipv), hostAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to listen on %s: %w", hostAddr, err)
|
||||
}
|
||||
p, err = NewSCTPProxy(listener, container)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported protocol %s", config.Proto)
|
||||
@@ -179,7 +178,11 @@ func parseFlags() ProxyConfig {
|
||||
}
|
||||
|
||||
if useListenFd {
|
||||
_ = syscall.SetNonblock(int(listenSockFd), true)
|
||||
// Unlike the stdlib, passing a non-blocking socket to `sctp.FileListener`
|
||||
// will result in a non-blocking Accept(). So, do not set this flag for SCTP.
|
||||
if config.Proto != "sctp" {
|
||||
_ = syscall.SetNonblock(int(listenSockFd), true)
|
||||
}
|
||||
config.ListenSock = os.NewFile(listenSockFd, "listen-sock")
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ishidawataru/sctp"
|
||||
"golang.org/x/sys/unix"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
@@ -155,6 +156,65 @@ func udpListener(t *testing.T, nw string, addr *net.UDPAddr) (*os.File, *net.UDP
|
||||
return osFile, l.LocalAddr().(*net.UDPAddr)
|
||||
}
|
||||
|
||||
func sctpListener(t *testing.T, nw string, addr *sctp.SCTPAddr) (*os.File, *sctp.SCTPAddr) {
|
||||
t.Helper()
|
||||
|
||||
var domain int
|
||||
var sa unix.Sockaddr
|
||||
switch nw {
|
||||
case "sctp4":
|
||||
domain = unix.AF_INET
|
||||
sa = &unix.SockaddrInet4{
|
||||
Addr: [4]uint8(addr.IPAddrs[0].IP.To4()),
|
||||
Port: addr.Port,
|
||||
}
|
||||
case "sctp6":
|
||||
domain = unix.AF_INET6
|
||||
sa = &unix.SockaddrInet6{
|
||||
Addr: [16]uint8(addr.IPAddrs[0].IP.To16()),
|
||||
Port: addr.Port,
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unknown SCTP network type: %s", nw)
|
||||
}
|
||||
|
||||
sockfd, err := unix.Socket(domain, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, unix.IPPROTO_SCTP)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = unix.Bind(sockfd, sa)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = unix.Listen(sockfd, -1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
newfd, _, sysErr := unix.Syscall(unix.SYS_FCNTL, uintptr(sockfd), unix.F_DUPFD_CLOEXEC, 0)
|
||||
if sysErr != 0 {
|
||||
t.Fatal(os.NewSyscallError("fcntl", sysErr))
|
||||
}
|
||||
|
||||
err = unix.Close(sockfd)
|
||||
assert.NilError(t, err)
|
||||
|
||||
sockname, err := unix.Getsockname(int(newfd))
|
||||
assert.NilError(t, err)
|
||||
|
||||
var laddr *sctp.SCTPAddr
|
||||
switch sa := sockname.(type) {
|
||||
case *unix.SockaddrInet4:
|
||||
laddr = &sctp.SCTPAddr{
|
||||
IPAddrs: []net.IPAddr{{IP: sa.Addr[:]}},
|
||||
Port: sa.Port,
|
||||
}
|
||||
case *unix.SockaddrInet6:
|
||||
laddr = &sctp.SCTPAddr{
|
||||
IPAddrs: []net.IPAddr{{IP: sa.Addr[:]}},
|
||||
Port: sa.Port,
|
||||
}
|
||||
}
|
||||
|
||||
return os.NewFile(newfd, ""), laddr
|
||||
}
|
||||
|
||||
func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string, halfClose bool) {
|
||||
t.Helper()
|
||||
defer proxy.Close()
|
||||
@@ -414,3 +474,41 @@ func TestSCTP6ProxyNoListener(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
testProxyAt(t, "sctp", proxy, fmt.Sprintf("[%s]:%d", config.HostIP, config.HostPort), false)
|
||||
}
|
||||
|
||||
func TestSCTP4Proxy(t *testing.T) {
|
||||
backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{})
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
listener, frontendAddr := sctpListener(t, "sctp4", &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP("127.0.0.1")}}, Port: 0})
|
||||
backendAddr := backend.LocalAddr().(*sctp.SCTPAddr)
|
||||
config := ProxyConfig{
|
||||
Proto: "sctp",
|
||||
HostIP: frontendAddr.IPAddrs[0].IP,
|
||||
HostPort: frontendAddr.Port,
|
||||
ContainerIP: backendAddr.IPAddrs[0].IP,
|
||||
ContainerPort: backendAddr.Port,
|
||||
ListenSock: listener,
|
||||
}
|
||||
proxy, err := newProxy(config)
|
||||
assert.NilError(t, err)
|
||||
testProxyAt(t, "sctp", proxy, fmt.Sprintf("%s:%d", config.HostIP, config.HostPort), false)
|
||||
}
|
||||
|
||||
func TestSCTP6Proxy(t *testing.T) {
|
||||
backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{})
|
||||
defer backend.Close()
|
||||
backend.Run()
|
||||
listener, frontendAddr := sctpListener(t, "sctp6", &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP("::1")}}, Port: 0})
|
||||
backendAddr := backend.LocalAddr().(*sctp.SCTPAddr)
|
||||
config := ProxyConfig{
|
||||
Proto: "sctp",
|
||||
HostIP: frontendAddr.IPAddrs[0].IP,
|
||||
HostPort: frontendAddr.Port,
|
||||
ContainerIP: backendAddr.IPAddrs[0].IP,
|
||||
ContainerPort: backendAddr.Port,
|
||||
ListenSock: listener,
|
||||
}
|
||||
proxy, err := newProxy(config)
|
||||
assert.NilError(t, err)
|
||||
testProxyAt(t, "sctp", proxy, fmt.Sprintf("[%s]:%d", config.HostIP, config.HostPort), false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user