mirror of
https://github.com/moby/moby.git
synced 2026-07-24 16:26:51 +00:00
In preparation for the daemon passing a listen fd, add command line option -use-listen-fd to indicate that the fd is present (as fd 4). If the new flag isn't given, open the listener as normal. Refactor the TCP and UDP proxies to be constructed with an existing TCPListener or UDPConn, respectively. Lift the responsibilty of opening the listener to the entrypoint. Per the Single Responsibility Principle, this structure affords changing how the listener is created without having to touch the proxy implementations. Co-authored-by: Cory Snider <csnider@mirantis.com> Signed-off-by: Rob Murray <rob.murray@docker.com>
170 lines
5.0 KiB
Go
170 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/docker/docker/dockerversion"
|
|
"github.com/ishidawataru/sctp"
|
|
)
|
|
|
|
// The caller is expected to pass-in open file descriptors ...
|
|
const (
|
|
// Pipe for reporting status, as a string. "0\n" if the proxy
|
|
// started normally. "1\n<error message>" otherwise.
|
|
parentPipeFd uintptr = 3 + iota
|
|
// If -use-listen-fd=true, a listening socket ready to accept TCP
|
|
// connections or receive UDP. (Without that option on the command
|
|
// line, the listener needs to be opened by docker-proxy, for
|
|
// compatibility with older docker daemons. In this case fd 4
|
|
// may belong to the Go runtime.)
|
|
listenSockFd
|
|
)
|
|
|
|
func main() {
|
|
config := parseFlags()
|
|
p, err := newProxy(config)
|
|
if config.ListenSock != nil {
|
|
config.ListenSock.Close()
|
|
}
|
|
|
|
f := os.NewFile(parentPipeFd, "signal-parent")
|
|
if err != nil {
|
|
fmt.Fprintf(f, "1\n%s", err)
|
|
f.Close()
|
|
os.Exit(1)
|
|
}
|
|
go handleStopSignals(p)
|
|
fmt.Fprint(f, "0\n")
|
|
f.Close()
|
|
|
|
// Run will block until the proxy stops
|
|
p.Run()
|
|
}
|
|
|
|
func newProxy(config ProxyConfig) (p Proxy, err error) {
|
|
ipv := ipv4
|
|
if config.HostIP.To4() == nil {
|
|
ipv = ipv6
|
|
}
|
|
|
|
switch config.Proto {
|
|
case "tcp":
|
|
var listener *net.TCPListener
|
|
if config.ListenSock == nil {
|
|
// Fall back to HostIP:HostPort if no socket on fd 4, for compatibility with older daemons.
|
|
hostAddr := &net.TCPAddr{IP: config.HostIP, Port: config.HostPort}
|
|
listener, err = net.ListenTCP("tcp"+string(ipv), hostAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to listen on %s: %w", hostAddr, err)
|
|
}
|
|
} else {
|
|
l, err := net.FileListener(config.ListenSock)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var ok bool
|
|
listener, ok = l.(*net.TCPListener)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected socket type for listener fd: %s", l.Addr().Network())
|
|
}
|
|
}
|
|
container := &net.TCPAddr{IP: config.ContainerIP, Port: config.ContainerPort}
|
|
p, err = NewTCPProxy(listener, container)
|
|
case "udp":
|
|
var listener *net.UDPConn
|
|
if config.ListenSock == nil {
|
|
// Fall back to HostIP:HostPort if no socket on fd 4, for compatibility with older daemons.
|
|
hostAddr := &net.UDPAddr{IP: config.HostIP, Port: config.HostPort}
|
|
listener, err = net.ListenUDP("udp"+string(ipv), hostAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to listen on %s: %w", hostAddr, err)
|
|
}
|
|
} else {
|
|
l, err := net.FilePacketConn(config.ListenSock)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var ok bool
|
|
listener, ok = l.(*net.UDPConn)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected socket type for listener fd: %s", l.LocalAddr().Network())
|
|
}
|
|
}
|
|
container := &net.UDPAddr{IP: config.ContainerIP, Port: config.ContainerPort}
|
|
p, err = NewUDPProxy(listener, container)
|
|
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")
|
|
}
|
|
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)
|
|
}
|
|
|
|
return p, err
|
|
}
|
|
|
|
type ProxyConfig struct {
|
|
Proto string
|
|
HostIP, ContainerIP net.IP
|
|
HostPort, ContainerPort int
|
|
ListenSock *os.File
|
|
}
|
|
|
|
// parseFlags parses the flags passed on reexec to create the TCP/UDP/SCTP
|
|
// net.Addrs to map the host and container ports.
|
|
func parseFlags() ProxyConfig {
|
|
var (
|
|
config ProxyConfig
|
|
useListenFd bool
|
|
printVer bool
|
|
)
|
|
flag.StringVar(&config.Proto, "proto", "tcp", "proxy protocol")
|
|
flag.TextVar(&config.HostIP, "host-ip", net.IPv4zero, "host ip")
|
|
flag.IntVar(&config.HostPort, "host-port", -1, "host port")
|
|
flag.TextVar(&config.ContainerIP, "container-ip", net.IPv4zero, "container ip")
|
|
flag.IntVar(&config.ContainerPort, "container-port", -1, "container port")
|
|
flag.BoolVar(&useListenFd, "use-listen-fd", false, "use a supplied listen fd")
|
|
flag.BoolVar(&printVer, "v", false, "print version information and quit")
|
|
flag.BoolVar(&printVer, "version", false, "print version information and quit")
|
|
flag.Parse()
|
|
|
|
if printVer {
|
|
fmt.Printf("docker-proxy (commit %s) version %s\n", dockerversion.GitCommit, dockerversion.Version)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if useListenFd {
|
|
config.ListenSock = os.NewFile(listenSockFd, "listen-sock")
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
func handleStopSignals(p Proxy) {
|
|
s := make(chan os.Signal, 10)
|
|
signal.Notify(s, os.Interrupt, syscall.SIGTERM)
|
|
|
|
for range s {
|
|
p.Close()
|
|
|
|
os.Exit(0)
|
|
}
|
|
}
|