github.com/erriapo/docker@v1.6.0-rc2/daemon/networkdriver/portmapper/mapper.go (about) 1 package portmapper 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "sync" 8 9 log "github.com/Sirupsen/logrus" 10 "github.com/docker/docker/daemon/networkdriver/portallocator" 11 "github.com/docker/docker/pkg/iptables" 12 ) 13 14 type mapping struct { 15 proto string 16 userlandProxy UserlandProxy 17 host net.Addr 18 container net.Addr 19 } 20 21 var ( 22 chain *iptables.Chain 23 lock sync.Mutex 24 25 // udp:ip:port 26 currentMappings = make(map[string]*mapping) 27 28 NewProxy = NewProxyCommand 29 ) 30 31 var ( 32 ErrUnknownBackendAddressType = errors.New("unknown container address type not supported") 33 ErrPortMappedForIP = errors.New("port is already mapped to ip") 34 ErrPortNotMapped = errors.New("port is not mapped") 35 ) 36 37 func SetIptablesChain(c *iptables.Chain) { 38 chain = c 39 } 40 41 func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) { 42 lock.Lock() 43 defer lock.Unlock() 44 45 var ( 46 m *mapping 47 proto string 48 allocatedHostPort int 49 proxy UserlandProxy 50 ) 51 52 switch container.(type) { 53 case *net.TCPAddr: 54 proto = "tcp" 55 if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil { 56 return nil, err 57 } 58 59 m = &mapping{ 60 proto: proto, 61 host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort}, 62 container: container, 63 } 64 65 proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port) 66 case *net.UDPAddr: 67 proto = "udp" 68 if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil { 69 return nil, err 70 } 71 72 m = &mapping{ 73 proto: proto, 74 host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort}, 75 container: container, 76 } 77 78 proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port) 79 default: 80 return nil, ErrUnknownBackendAddressType 81 } 82 83 // release the allocated port on any further error during return. 84 defer func() { 85 if err != nil { 86 portallocator.ReleasePort(hostIP, proto, allocatedHostPort) 87 } 88 }() 89 90 key := getKey(m.host) 91 if _, exists := currentMappings[key]; exists { 92 return nil, ErrPortMappedForIP 93 } 94 95 containerIP, containerPort := getIPAndPort(m.container) 96 if err := forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { 97 return nil, err 98 } 99 100 cleanup := func() error { 101 // need to undo the iptables rules before we return 102 proxy.Stop() 103 forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) 104 if err := portallocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { 105 return err 106 } 107 108 return nil 109 } 110 111 if err := proxy.Start(); err != nil { 112 if err := cleanup(); err != nil { 113 return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) 114 } 115 return nil, err 116 } 117 m.userlandProxy = proxy 118 currentMappings[key] = m 119 return m.host, nil 120 } 121 122 func Unmap(host net.Addr) error { 123 lock.Lock() 124 defer lock.Unlock() 125 126 key := getKey(host) 127 data, exists := currentMappings[key] 128 if !exists { 129 return ErrPortNotMapped 130 } 131 132 data.userlandProxy.Stop() 133 134 delete(currentMappings, key) 135 136 containerIP, containerPort := getIPAndPort(data.container) 137 hostIP, hostPort := getIPAndPort(data.host) 138 if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { 139 log.Errorf("Error on iptables delete: %s", err) 140 } 141 142 switch a := host.(type) { 143 case *net.TCPAddr: 144 return portallocator.ReleasePort(a.IP, "tcp", a.Port) 145 case *net.UDPAddr: 146 return portallocator.ReleasePort(a.IP, "udp", a.Port) 147 } 148 return nil 149 } 150 151 func getKey(a net.Addr) string { 152 switch t := a.(type) { 153 case *net.TCPAddr: 154 return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp") 155 case *net.UDPAddr: 156 return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp") 157 } 158 return "" 159 } 160 161 func getIPAndPort(a net.Addr) (net.IP, int) { 162 switch t := a.(type) { 163 case *net.TCPAddr: 164 return t.IP, t.Port 165 case *net.UDPAddr: 166 return t.IP, t.Port 167 } 168 return nil, 0 169 } 170 171 func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { 172 if chain == nil { 173 return nil 174 } 175 return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort) 176 }