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