mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
b4e2f5ed96
This PR moves the userland proxies for TCP and UDP traffic out of the main docker daemon's process ( from goroutines per proxy ) to be a separate reexec of the docker binary. This reduces the cpu and memory needed by the daemon and if the proxy processes crash for some reason the daemon is unaffected. This also displays in the standard process tree so that a user can clearly see if there is a userland proxy that is bound to a certain ip and port. ```bash CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5d349506feb6 busybox:buildroot-2014.02 "sh" 13 minutes ago Up 1 seconds 0.0.0.0:49153->81/tcp, 0.0.0.0:49154->90/tcp hungry_pike root@1cbfdcedc5a7:/go/src/github.com/docker/docker# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 18168 3100 ? Ss 21:09 0:00 bash root 8328 0.7 0.6 329072 13420 ? Sl 22:03 0:00 docker -d -s vfs root 8373 1.0 0.5 196500 10548 ? Sl 22:03 0:00 userland-proxy -proto tcp -host-ip 0.0.0.0 -host-port 49153 -container-ip 10.0.0.2 -container-port 81 root 8382 1.0 0.5 270232 10576 ? Sl 22:03 0:00 userland-proxy -proto tcp -host-ip 0.0.0.0 -host-port 49154 -container-ip 10.0.0.2 -container-port 90 root 8385 1.2 0.0 3168 184 pts/0 Ss+ 22:03 0:00 sh root 8408 0.0 0.1 15568 2112 ? R+ 22:03 0:00 ps aux ``` This also helps us to cleanly cleanup the proxy processes by stopping these commands instead of trying to terminate a goroutine. Signed-off-by: Michael Crosby <michael@docker.com>
152 lines
3.5 KiB
Go
152 lines
3.5 KiB
Go
package portmapper
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/daemon/networkdriver/portallocator"
|
|
"github.com/docker/docker/pkg/iptables"
|
|
)
|
|
|
|
func init() {
|
|
// override this func to mock out the proxy server
|
|
NewProxy = NewMockProxyCommand
|
|
}
|
|
|
|
func reset() {
|
|
chain = nil
|
|
currentMappings = make(map[string]*mapping)
|
|
}
|
|
|
|
func TestSetIptablesChain(t *testing.T) {
|
|
defer reset()
|
|
|
|
c := &iptables.Chain{
|
|
Name: "TEST",
|
|
Bridge: "192.168.1.1",
|
|
}
|
|
|
|
if chain != nil {
|
|
t.Fatal("chain should be nil at init")
|
|
}
|
|
|
|
SetIptablesChain(c)
|
|
if chain == nil {
|
|
t.Fatal("chain should not be nil after set")
|
|
}
|
|
}
|
|
|
|
func TestMapPorts(t *testing.T) {
|
|
dstIp1 := net.ParseIP("192.168.0.1")
|
|
dstIp2 := net.ParseIP("192.168.0.2")
|
|
dstAddr1 := &net.TCPAddr{IP: dstIp1, Port: 80}
|
|
dstAddr2 := &net.TCPAddr{IP: dstIp2, Port: 80}
|
|
|
|
srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
|
|
srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")}
|
|
|
|
addrEqual := func(addr1, addr2 net.Addr) bool {
|
|
return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
|
|
}
|
|
|
|
if host, err := Map(srcAddr1, dstIp1, 80); err != nil {
|
|
t.Fatalf("Failed to allocate port: %s", err)
|
|
} else if !addrEqual(dstAddr1, host) {
|
|
t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s",
|
|
dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network())
|
|
}
|
|
|
|
if _, err := Map(srcAddr1, dstIp1, 80); err == nil {
|
|
t.Fatalf("Port is in use - mapping should have failed")
|
|
}
|
|
|
|
if _, err := Map(srcAddr2, dstIp1, 80); err == nil {
|
|
t.Fatalf("Port is in use - mapping should have failed")
|
|
}
|
|
|
|
if _, err := Map(srcAddr2, dstIp2, 80); err != nil {
|
|
t.Fatalf("Failed to allocate port: %s", err)
|
|
}
|
|
|
|
if Unmap(dstAddr1) != nil {
|
|
t.Fatalf("Failed to release port")
|
|
}
|
|
|
|
if Unmap(dstAddr2) != nil {
|
|
t.Fatalf("Failed to release port")
|
|
}
|
|
|
|
if Unmap(dstAddr2) == nil {
|
|
t.Fatalf("Port already released, but no error reported")
|
|
}
|
|
}
|
|
|
|
func TestGetUDPKey(t *testing.T) {
|
|
addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
|
|
|
key := getKey(addr)
|
|
|
|
if expected := "192.168.1.5:53/udp"; key != expected {
|
|
t.Fatalf("expected key %s got %s", expected, key)
|
|
}
|
|
}
|
|
|
|
func TestGetTCPKey(t *testing.T) {
|
|
addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80}
|
|
|
|
key := getKey(addr)
|
|
|
|
if expected := "192.168.1.5:80/tcp"; key != expected {
|
|
t.Fatalf("expected key %s got %s", expected, key)
|
|
}
|
|
}
|
|
|
|
func TestGetUDPIPAndPort(t *testing.T) {
|
|
addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
|
|
|
ip, port := getIPAndPort(addr)
|
|
if expected := "192.168.1.5"; ip.String() != expected {
|
|
t.Fatalf("expected ip %s got %s", expected, ip)
|
|
}
|
|
|
|
if ep := 53; port != ep {
|
|
t.Fatalf("expected port %d got %d", ep, port)
|
|
}
|
|
}
|
|
|
|
func TestMapAllPortsSingleInterface(t *testing.T) {
|
|
dstIp1 := net.ParseIP("0.0.0.0")
|
|
srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
|
|
|
|
hosts := []net.Addr{}
|
|
var host net.Addr
|
|
var err error
|
|
|
|
defer func() {
|
|
for _, val := range hosts {
|
|
Unmap(val)
|
|
}
|
|
}()
|
|
|
|
for i := 0; i < 10; i++ {
|
|
for i := portallocator.BeginPortRange; i < portallocator.EndPortRange; i++ {
|
|
if host, err = Map(srcAddr1, dstIp1, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
hosts = append(hosts, host)
|
|
}
|
|
|
|
if _, err := Map(srcAddr1, dstIp1, portallocator.BeginPortRange); err == nil {
|
|
t.Fatalf("Port %d should be bound but is not", portallocator.BeginPortRange)
|
|
}
|
|
|
|
for _, val := range hosts {
|
|
if err := Unmap(val); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
hosts = []net.Addr{}
|
|
}
|
|
}
|