mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #27519 from justincormack/proxy-build
Build docker-proxy from git checkout like other external binaries
This commit is contained in:
commit
36f47aa252
20 changed files with 35 additions and 428 deletions
|
@ -222,10 +222,10 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
|||
hello-world:latest@sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
|
||||
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
# Install tomlv, runc, containerd and grimes
|
||||
# Install tomlv, runc, containerd, grimes, docker-proxy
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes proxy
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
|
|
@ -165,10 +165,10 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
|||
aarch64/hello-world:latest@sha256:65a4a158587b307bb02db4de41b836addb0c35175bdc801367b1ac1ddeb9afda
|
||||
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
# Install tomlv, runc, containerd and grimes
|
||||
# Install tomlv, runc, containerd, grimes, docker-proxy
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes proxy
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
|
|
@ -164,10 +164,10 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
|||
armhf/hello-world:latest@sha256:161dcecea0225975b2ad5f768058212c1e0d39e8211098666ffa1ac74cfb7791
|
||||
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
# Install tomlv, runc, containerd and grimes
|
||||
# Install tomlv, runc, containerd, grimes, docker-proxy
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes proxy
|
||||
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
|
|
|
@ -183,10 +183,10 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
|||
ppc64le/hello-world:latest@sha256:186a40a9a02ca26df0b6c8acdfb8ac2f3ae6678996a838f977e57fac9d963974
|
||||
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
# Install tomlv, runc, containerd and grimes
|
||||
# Install tomlv, runc, containerd, grimes, docker-proxy
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes proxy
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
|
|
@ -175,10 +175,10 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
|||
s390x/hello-world:latest@sha256:780d80b3a7677c3788c0d5cd9168281320c8d4a6d9183892d8ee5cdd610f5699
|
||||
# See also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
# Install tomlv, runc, containerd and grimes
|
||||
# Install tomlv, runc, containerd, grimes, docker-proxy
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes
|
||||
RUN /tmp/install-binaries.sh tomlv runc containerd grimes proxy
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
|
|
@ -56,10 +56,10 @@ ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
|||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
ENV CGO_LDFLAGS -L/lib
|
||||
|
||||
# Install runc, containerd and grimes
|
||||
# Install runc, containerd, grimes and docker-proxy
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh runc containerd grimes
|
||||
RUN /tmp/install-binaries.sh runc containerd grimes proxy
|
||||
|
||||
ENV AUTO_GOPATH 1
|
||||
WORKDIR /usr/src/docker
|
||||
|
|
|
@ -124,9 +124,6 @@ clean() {
|
|||
findArgs+=( -path "vendor/src/$import" )
|
||||
done
|
||||
|
||||
# The docker proxy command is built from libnetwork
|
||||
findArgs+=( -or -path vendor/src/github.com/docker/libnetwork/cmd/proxy )
|
||||
|
||||
local IFS=$'\n'
|
||||
local prune=( $($find vendor -depth -type d -not '(' "${findArgs[@]}" ')') )
|
||||
unset IFS
|
||||
|
|
|
@ -6,6 +6,7 @@ TOMLV_COMMIT=9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
|
|||
RUNC_COMMIT=02f8fa7863dd3f82909a73e2061897828460d52f
|
||||
CONTAINERD_COMMIT=52ef1ceb4b660c42cf4ea9013180a5663968d4c7
|
||||
GRIMES_COMMIT=74341e923bdf06cfb6b70cf54089c4d3ac87ec2d
|
||||
LIBNETWORK_COMMIT=0f534354b813003a754606689722fe253101bc4e
|
||||
|
||||
export GOPATH="$(mktemp -d)"
|
||||
|
||||
|
@ -66,8 +67,16 @@ do
|
|||
cp init /usr/local/bin/docker-init
|
||||
;;
|
||||
|
||||
proxy)
|
||||
echo "Install docker-proxy version $LIBNETWORK_COMMIT"
|
||||
git clone https://github.com/docker/libnetwork.git "$GOPATH/src/github.com/docker/libnetwork"
|
||||
cd "$GOPATH/src/github.com/docker/libnetwork"
|
||||
git checkout -q "$LIBNETWORK_COMMIT"
|
||||
CGO_ENABLED=0 go build -v -o /usr/local/bin/docker-proxy github.com/docker/libnetwork/cmd/proxy
|
||||
;;
|
||||
|
||||
*)
|
||||
echo echo "Usage: $0 [tomlv|runc|containerd|grimes]"
|
||||
echo echo "Usage: $0 [tomlv|runc|containerd|grimes|proxy]"
|
||||
exit 1
|
||||
|
||||
esac
|
||||
|
|
|
@ -257,7 +257,7 @@ copy_binaries() {
|
|||
if [ "$(go env GOOS)/$(go env GOARCH)" == "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" ]; then
|
||||
if [ -x /usr/local/bin/docker-runc ]; then
|
||||
echo "Copying nested executables into $dir"
|
||||
for file in containerd containerd-shim containerd-ctr runc init; do
|
||||
for file in containerd containerd-shim containerd-ctr runc init proxy; do
|
||||
cp `which "docker-$file"` "$dir/"
|
||||
if [ "$2" == "hash" ]; then
|
||||
hash_files "$dir/docker-$file"
|
||||
|
|
|
@ -24,11 +24,12 @@ override_dh_auto_install:
|
|||
mkdir -p debian/docker-engine/usr/bin
|
||||
cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-client/docker)" debian/docker-engine/usr/bin/docker
|
||||
cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-daemon/dockerd)" debian/docker-engine/usr/bin/dockerd
|
||||
cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-daemon/docker-proxy)" debian/docker-engine/usr/bin/docker-proxy
|
||||
cp -aT /usr/local/bin/docker-proxy debian/docker-engine/usr/bin/docker-proxy
|
||||
cp -aT /usr/local/bin/docker-containerd debian/docker-engine/usr/bin/docker-containerd
|
||||
cp -aT /usr/local/bin/docker-containerd-shim debian/docker-engine/usr/bin/docker-containerd-shim
|
||||
cp -aT /usr/local/bin/docker-containerd-ctr debian/docker-engine/usr/bin/docker-containerd-ctr
|
||||
cp -aT /usr/local/bin/docker-runc debian/docker-engine/usr/bin/docker-runc
|
||||
cp -aT /usr/local/bin/docker-init debian/docker-engine/usr/bin/docker-init
|
||||
mkdir -p debian/docker-engine/usr/lib/docker
|
||||
|
||||
override_dh_installinit:
|
||||
|
|
|
@ -126,7 +126,9 @@ export DOCKER_GITCOMMIT=%{_gitcommit}
|
|||
install -d $RPM_BUILD_ROOT/%{_bindir}
|
||||
install -p -m 755 bundles/%{_origversion}/dynbinary-client/docker-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/docker
|
||||
install -p -m 755 bundles/%{_origversion}/dynbinary-daemon/dockerd-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/dockerd
|
||||
install -p -m 755 bundles/%{_origversion}/dynbinary-daemon/docker-proxy-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/docker-proxy
|
||||
|
||||
# install proxy
|
||||
install -p -m 755 /usr/local/bin/docker-proxy $RPM_BUILD_ROOT/%{_bindir}/docker-proxy
|
||||
|
||||
# install containerd
|
||||
install -p -m 755 /usr/local/bin/docker-containerd $RPM_BUILD_ROOT/%{_bindir}/docker-containerd
|
||||
|
@ -136,6 +138,9 @@ install -p -m 755 /usr/local/bin/docker-containerd-ctr $RPM_BUILD_ROOT/%{_bindir
|
|||
# install runc
|
||||
install -p -m 755 /usr/local/bin/docker-runc $RPM_BUILD_ROOT/%{_bindir}/docker-runc
|
||||
|
||||
# install grimes
|
||||
install -p -m 755 /usr/local/bin/docker-init $RPM_BUILD_ROOT/%{_bindir}/docker-init
|
||||
|
||||
# install udev rules
|
||||
install -d $RPM_BUILD_ROOT/%{_sysconfdir}/udev/rules.d
|
||||
install -p -m 644 contrib/udev/80-docker.rules $RPM_BUILD_ROOT/%{_sysconfdir}/udev/rules.d/80-docker.rules
|
||||
|
|
|
@ -9,8 +9,5 @@ set -e
|
|||
export BINARY_SHORT_NAME="$DOCKER_DAEMON_BINARY_NAME"
|
||||
export SOURCE_PATH='./cmd/dockerd'
|
||||
source "${MAKEDIR}/.binary"
|
||||
export BINARY_SHORT_NAME="$DOCKER_PROXY_BINARY_NAME"
|
||||
export SOURCE_PATH='./vendor/src/github.com/docker/libnetwork/cmd/proxy'
|
||||
source "${MAKEDIR}/.binary"
|
||||
copy_binaries "$DEST" 'hash'
|
||||
)
|
||||
|
|
|
@ -73,8 +73,8 @@ set -e
|
|||
EOF
|
||||
|
||||
cat >> "$DEST/$version/Dockerfile.build" <<-EOF
|
||||
# Install runc and containerd
|
||||
RUN ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic
|
||||
# Install runc, containerd, proxy and grimes
|
||||
RUN ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic proxy grimes
|
||||
EOF
|
||||
cat >> "$DEST/$version/Dockerfile.build" <<-EOF
|
||||
RUN cp -aL hack/make/.build-deb debian
|
||||
|
|
|
@ -93,8 +93,8 @@ set -e
|
|||
EOF
|
||||
|
||||
cat >> "$DEST/$version/Dockerfile.build" <<-EOF
|
||||
# Install runc and containerd
|
||||
RUN ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic
|
||||
# Install runc, containerd, proxy and grimes
|
||||
RUN ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic proxy grimes
|
||||
EOF
|
||||
if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then
|
||||
echo 'ENV DOCKER_EXPERIMENTAL 1' >> "$DEST/$version/Dockerfile.build"
|
||||
|
|
|
@ -9,8 +9,4 @@ set -e
|
|||
export BUILDFLAGS=( "${BUILDFLAGS[@]/netgo /}" ) # disable netgo, since we don't need it for a dynamic binary
|
||||
export BUILDFLAGS=( "${BUILDFLAGS[@]/static_build /}" ) # we're not building a "static" binary here
|
||||
source "${MAKEDIR}/.binary"
|
||||
export BINARY_SHORT_NAME='docker-proxy'
|
||||
export SOURCE_PATH='./vendor/src/github.com/docker/libnetwork/cmd/proxy'
|
||||
export LDFLAGS_STATIC_DOCKER='-linkmode=external'
|
||||
source "${MAKEDIR}/.binary"
|
||||
)
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f := os.NewFile(3, "signal-parent")
|
||||
host, container := parseHostContainerAddrs()
|
||||
|
||||
p, err := NewProxy(host, container)
|
||||
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()
|
||||
}
|
||||
|
||||
// parseHostContainerAddrs parses the flags passed on reexec to create the TCP or UDP
|
||||
// net.Addrs to map the host and container ports
|
||||
func parseHostContainerAddrs() (host net.Addr, container net.Addr) {
|
||||
var (
|
||||
proto = flag.String("proto", "tcp", "proxy protocol")
|
||||
hostIP = flag.String("host-ip", "", "host ip")
|
||||
hostPort = flag.Int("host-port", -1, "host port")
|
||||
containerIP = flag.String("container-ip", "", "container ip")
|
||||
containerPort = flag.Int("container-port", -1, "container port")
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
|
||||
switch *proto {
|
||||
case "tcp":
|
||||
host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||
container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||
case "udp":
|
||||
host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
|
||||
container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
|
||||
default:
|
||||
log.Fatalf("unsupported protocol %s", *proto)
|
||||
}
|
||||
|
||||
return host, container
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
// docker-proxy provides a network Proxy interface and implementations for TCP
|
||||
// and UDP.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
|
||||
// between two endpoints : the frontend and the backend.
|
||||
// It can be used to do software port-mapping between two addresses.
|
||||
// e.g. forward all traffic between the frontend (host) 127.0.0.1:3000
|
||||
// to the backend (container) at 172.17.42.108:4000.
|
||||
type Proxy interface {
|
||||
// Run starts forwarding traffic back and forth between the front
|
||||
// and back-end addresses.
|
||||
Run()
|
||||
// Close stops forwarding traffic and close both ends of the Proxy.
|
||||
Close()
|
||||
// FrontendAddr returns the address on which the proxy is listening.
|
||||
FrontendAddr() net.Addr
|
||||
// BackendAddr returns the proxied address.
|
||||
BackendAddr() net.Addr
|
||||
}
|
||||
|
||||
// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr.
|
||||
func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
|
||||
switch frontendAddr.(type) {
|
||||
case *net.UDPAddr:
|
||||
return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
|
||||
case *net.TCPAddr:
|
||||
return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
|
||||
default:
|
||||
panic(fmt.Errorf("Unsupported protocol"))
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// StubProxy is a proxy that is a stub (does nothing).
|
||||
type StubProxy struct {
|
||||
frontendAddr net.Addr
|
||||
backendAddr net.Addr
|
||||
}
|
||||
|
||||
// Run does nothing.
|
||||
func (p *StubProxy) Run() {}
|
||||
|
||||
// Close does nothing.
|
||||
func (p *StubProxy) Close() {}
|
||||
|
||||
// FrontendAddr returns the frontend address.
|
||||
func (p *StubProxy) FrontendAddr() net.Addr { return p.frontendAddr }
|
||||
|
||||
// BackendAddr returns the backend address.
|
||||
func (p *StubProxy) BackendAddr() net.Addr { return p.backendAddr }
|
||||
|
||||
// NewStubProxy creates a new StubProxy
|
||||
func NewStubProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
|
||||
return &StubProxy{
|
||||
frontendAddr: frontendAddr,
|
||||
backendAddr: backendAddr,
|
||||
}, nil
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to
|
||||
// handle TCP traffic forwarding between the frontend and backend addresses.
|
||||
type TCPProxy struct {
|
||||
listener *net.TCPListener
|
||||
frontendAddr *net.TCPAddr
|
||||
backendAddr *net.TCPAddr
|
||||
}
|
||||
|
||||
// NewTCPProxy creates a new TCPProxy.
|
||||
func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
|
||||
listener, err := net.ListenTCP("tcp", frontendAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If the port in frontendAddr was 0 then ListenTCP will have a picked
|
||||
// a port to listen on, hence the call to Addr to get that actual port:
|
||||
return &TCPProxy{
|
||||
listener: listener,
|
||||
frontendAddr: listener.Addr().(*net.TCPAddr),
|
||||
backendAddr: backendAddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
|
||||
backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
|
||||
if err != nil {
|
||||
log.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err)
|
||||
client.Close()
|
||||
return
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var broker = func(to, from *net.TCPConn) {
|
||||
if _, err := io.Copy(to, from); err != nil {
|
||||
// If the socket we are writing to is shutdown with
|
||||
// SHUT_WR, forward it to the other end of the pipe:
|
||||
if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE {
|
||||
from.CloseWrite()
|
||||
}
|
||||
}
|
||||
to.CloseRead()
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
wg.Add(2)
|
||||
go broker(client, backend)
|
||||
go broker(backend, client)
|
||||
|
||||
finish := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(finish)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-quit:
|
||||
case <-finish:
|
||||
}
|
||||
client.Close()
|
||||
backend.Close()
|
||||
<-finish
|
||||
}
|
||||
|
||||
// Run starts forwarding the traffic using TCP.
|
||||
func (proxy *TCPProxy) Run() {
|
||||
quit := make(chan bool)
|
||||
defer close(quit)
|
||||
for {
|
||||
client, err := proxy.listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
|
||||
return
|
||||
}
|
||||
go proxy.clientLoop(client.(*net.TCPConn), quit)
|
||||
}
|
||||
}
|
||||
|
||||
// Close stops forwarding the traffic.
|
||||
func (proxy *TCPProxy) Close() { proxy.listener.Close() }
|
||||
|
||||
// FrontendAddr returns the TCP address on which the proxy is listening.
|
||||
func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
|
||||
|
||||
// BackendAddr returns the TCP proxied address.
|
||||
func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
|
|
@ -1,168 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// UDPConnTrackTimeout is the timeout used for UDP connection tracking
|
||||
UDPConnTrackTimeout = 90 * time.Second
|
||||
// UDPBufSize is the buffer size for the UDP proxy
|
||||
UDPBufSize = 65507
|
||||
)
|
||||
|
||||
// A net.Addr where the IP is split into two fields so you can use it as a key
|
||||
// in a map:
|
||||
type connTrackKey struct {
|
||||
IPHigh uint64
|
||||
IPLow uint64
|
||||
Port int
|
||||
}
|
||||
|
||||
func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
|
||||
if len(addr.IP) == net.IPv4len {
|
||||
return &connTrackKey{
|
||||
IPHigh: 0,
|
||||
IPLow: uint64(binary.BigEndian.Uint32(addr.IP)),
|
||||
Port: addr.Port,
|
||||
}
|
||||
}
|
||||
return &connTrackKey{
|
||||
IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
|
||||
IPLow: binary.BigEndian.Uint64(addr.IP[8:]),
|
||||
Port: addr.Port,
|
||||
}
|
||||
}
|
||||
|
||||
type connTrackMap map[connTrackKey]*net.UDPConn
|
||||
|
||||
// UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy
|
||||
// interface to handle UDP traffic forwarding between the frontend and backend
|
||||
// addresses.
|
||||
type UDPProxy struct {
|
||||
listener *net.UDPConn
|
||||
frontendAddr *net.UDPAddr
|
||||
backendAddr *net.UDPAddr
|
||||
connTrackTable connTrackMap
|
||||
connTrackLock sync.Mutex
|
||||
}
|
||||
|
||||
// NewUDPProxy creates a new UDPProxy.
|
||||
func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
|
||||
listener, err := net.ListenUDP("udp", frontendAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UDPProxy{
|
||||
listener: listener,
|
||||
frontendAddr: listener.LocalAddr().(*net.UDPAddr),
|
||||
backendAddr: backendAddr,
|
||||
connTrackTable: make(connTrackMap),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
|
||||
defer func() {
|
||||
proxy.connTrackLock.Lock()
|
||||
delete(proxy.connTrackTable, *clientKey)
|
||||
proxy.connTrackLock.Unlock()
|
||||
proxyConn.Close()
|
||||
}()
|
||||
|
||||
readBuf := make([]byte, UDPBufSize)
|
||||
for {
|
||||
proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
|
||||
again:
|
||||
read, err := proxyConn.Read(readBuf)
|
||||
if err != nil {
|
||||
if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
|
||||
// This will happen if the last write failed
|
||||
// (e.g: nothing is actually listening on the
|
||||
// proxied port on the container), ignore it
|
||||
// and continue until UDPConnTrackTimeout
|
||||
// expires:
|
||||
goto again
|
||||
}
|
||||
return
|
||||
}
|
||||
for i := 0; i != read; {
|
||||
written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i += written
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run starts forwarding the traffic using UDP.
|
||||
func (proxy *UDPProxy) Run() {
|
||||
readBuf := make([]byte, UDPBufSize)
|
||||
for {
|
||||
read, from, err := proxy.listener.ReadFromUDP(readBuf)
|
||||
if err != nil {
|
||||
// NOTE: Apparently ReadFrom doesn't return
|
||||
// ECONNREFUSED like Read do (see comment in
|
||||
// UDPProxy.replyLoop)
|
||||
if !isClosedError(err) {
|
||||
log.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
fromKey := newConnTrackKey(from)
|
||||
proxy.connTrackLock.Lock()
|
||||
proxyConn, hit := proxy.connTrackTable[*fromKey]
|
||||
if !hit {
|
||||
proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
|
||||
if err != nil {
|
||||
log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
|
||||
proxy.connTrackLock.Unlock()
|
||||
continue
|
||||
}
|
||||
proxy.connTrackTable[*fromKey] = proxyConn
|
||||
go proxy.replyLoop(proxyConn, from, fromKey)
|
||||
}
|
||||
proxy.connTrackLock.Unlock()
|
||||
for i := 0; i != read; {
|
||||
written, err := proxyConn.Write(readBuf[i:read])
|
||||
if err != nil {
|
||||
log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
|
||||
break
|
||||
}
|
||||
i += written
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close stops forwarding the traffic.
|
||||
func (proxy *UDPProxy) Close() {
|
||||
proxy.listener.Close()
|
||||
proxy.connTrackLock.Lock()
|
||||
defer proxy.connTrackLock.Unlock()
|
||||
for _, conn := range proxy.connTrackTable {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// FrontendAddr returns the UDP address on which the proxy is listening.
|
||||
func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
|
||||
|
||||
// BackendAddr returns the proxied UDP address.
|
||||
func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
|
||||
|
||||
func isClosedError(err error) bool {
|
||||
/* This comparison is ugly, but unfortunately, net.go doesn't export errClosing.
|
||||
* See:
|
||||
* http://golang.org/src/pkg/net/net.go
|
||||
* https://code.google.com/p/go/issues/detail?id=4337
|
||||
* https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ
|
||||
*/
|
||||
return strings.HasSuffix(err.Error(), "use of closed network connection")
|
||||
}
|
Loading…
Reference in a new issue