github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/portmapper/mapper.go (about) 1 package portmapper 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 8 "github.com/docker/libnetwork/portallocator" 9 "github.com/ishidawataru/sctp" 10 "github.com/sirupsen/logrus" 11 ) 12 13 type mapping struct { 14 proto string 15 userlandProxy userlandProxy 16 host net.Addr 17 container net.Addr 18 } 19 20 var newProxy = newProxyCommand 21 22 var ( 23 // ErrUnknownBackendAddressType refers to an unknown container or unsupported address type 24 ErrUnknownBackendAddressType = errors.New("unknown container address type not supported") 25 // ErrPortMappedForIP refers to a port already mapped to an ip address 26 ErrPortMappedForIP = errors.New("port is already mapped to ip") 27 // ErrPortNotMapped refers to an unmapped port 28 ErrPortNotMapped = errors.New("port is not mapped") 29 // ErrSCTPAddrNoIP refers to a SCTP address without IP address. 30 ErrSCTPAddrNoIP = errors.New("sctp address does not contain any IP address") 31 ) 32 33 // New returns a new instance of PortMapper 34 func New(proxyPath string) *PortMapper { 35 return NewWithPortAllocator(portallocator.Get(), proxyPath) 36 } 37 38 // NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator 39 func NewWithPortAllocator(allocator *portallocator.PortAllocator, proxyPath string) *PortMapper { 40 return &PortMapper{ 41 currentMappings: make(map[string]*mapping), 42 Allocator: allocator, 43 proxyPath: proxyPath, 44 } 45 } 46 47 // Map maps the specified container transport address to the host's network address and transport port 48 func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) { 49 return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy) 50 } 51 52 // MapRange maps the specified container transport address to the host's network address and transport port range 53 func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) { 54 pm.lock.Lock() 55 defer pm.lock.Unlock() 56 57 var ( 58 m *mapping 59 proto string 60 allocatedHostPort int 61 ) 62 63 switch container.(type) { 64 case *net.TCPAddr: 65 proto = "tcp" 66 if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { 67 return nil, err 68 } 69 70 m = &mapping{ 71 proto: proto, 72 host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort}, 73 container: container, 74 } 75 76 if useProxy { 77 m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port, pm.proxyPath) 78 if err != nil { 79 return nil, err 80 } 81 } else { 82 m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) 83 if err != nil { 84 return nil, err 85 } 86 } 87 case *net.UDPAddr: 88 proto = "udp" 89 if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { 90 return nil, err 91 } 92 93 m = &mapping{ 94 proto: proto, 95 host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort}, 96 container: container, 97 } 98 99 if useProxy { 100 m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port, pm.proxyPath) 101 if err != nil { 102 return nil, err 103 } 104 } else { 105 m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) 106 if err != nil { 107 return nil, err 108 } 109 } 110 case *sctp.SCTPAddr: 111 proto = "sctp" 112 if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { 113 return nil, err 114 } 115 116 m = &mapping{ 117 proto: proto, 118 host: &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: allocatedHostPort}, 119 container: container, 120 } 121 122 if useProxy { 123 sctpAddr := container.(*sctp.SCTPAddr) 124 if len(sctpAddr.IPAddrs) == 0 { 125 return nil, ErrSCTPAddrNoIP 126 } 127 m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IPAddrs[0].IP, sctpAddr.Port, pm.proxyPath) 128 if err != nil { 129 return nil, err 130 } 131 } else { 132 m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) 133 if err != nil { 134 return nil, err 135 } 136 } 137 default: 138 return nil, ErrUnknownBackendAddressType 139 } 140 141 // release the allocated port on any further error during return. 142 defer func() { 143 if err != nil { 144 pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort) 145 } 146 }() 147 148 key := getKey(m.host) 149 if _, exists := pm.currentMappings[key]; exists { 150 return nil, ErrPortMappedForIP 151 } 152 153 containerIP, containerPort := getIPAndPort(m.container) 154 if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { 155 return nil, err 156 } 157 158 cleanup := func() error { 159 // need to undo the iptables rules before we return 160 m.userlandProxy.Stop() 161 pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) 162 if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { 163 return err 164 } 165 166 return nil 167 } 168 169 if err := m.userlandProxy.Start(); err != nil { 170 if err := cleanup(); err != nil { 171 return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) 172 } 173 return nil, err 174 } 175 176 pm.currentMappings[key] = m 177 return m.host, nil 178 } 179 180 // Unmap removes stored mapping for the specified host transport address 181 func (pm *PortMapper) Unmap(host net.Addr) error { 182 pm.lock.Lock() 183 defer pm.lock.Unlock() 184 185 key := getKey(host) 186 data, exists := pm.currentMappings[key] 187 if !exists { 188 return ErrPortNotMapped 189 } 190 191 if data.userlandProxy != nil { 192 data.userlandProxy.Stop() 193 } 194 195 delete(pm.currentMappings, key) 196 197 containerIP, containerPort := getIPAndPort(data.container) 198 hostIP, hostPort := getIPAndPort(data.host) 199 if err := pm.DeleteForwardingTableEntry(data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { 200 logrus.Errorf("Error on iptables delete: %s", err) 201 } 202 203 switch a := host.(type) { 204 case *net.TCPAddr: 205 return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port) 206 case *net.UDPAddr: 207 return pm.Allocator.ReleasePort(a.IP, "udp", a.Port) 208 case *sctp.SCTPAddr: 209 if len(a.IPAddrs) == 0 { 210 return ErrSCTPAddrNoIP 211 } 212 return pm.Allocator.ReleasePort(a.IPAddrs[0].IP, "sctp", a.Port) 213 } 214 return ErrUnknownBackendAddressType 215 } 216 217 // ReMapAll will re-apply all port mappings 218 func (pm *PortMapper) ReMapAll() { 219 pm.lock.Lock() 220 defer pm.lock.Unlock() 221 logrus.Debugln("Re-applying all port mappings.") 222 for _, data := range pm.currentMappings { 223 containerIP, containerPort := getIPAndPort(data.container) 224 hostIP, hostPort := getIPAndPort(data.host) 225 if err := pm.AppendForwardingTableEntry(data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { 226 logrus.Errorf("Error on iptables add: %s", err) 227 } 228 } 229 } 230 231 func getKey(a net.Addr) string { 232 switch t := a.(type) { 233 case *net.TCPAddr: 234 return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp") 235 case *net.UDPAddr: 236 return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp") 237 case *sctp.SCTPAddr: 238 if len(t.IPAddrs) == 0 { 239 logrus.Error(ErrSCTPAddrNoIP) 240 return "" 241 } 242 return fmt.Sprintf("%s:%d/%s", t.IPAddrs[0].IP.String(), t.Port, "sctp") 243 } 244 return "" 245 } 246 247 func getIPAndPort(a net.Addr) (net.IP, int) { 248 switch t := a.(type) { 249 case *net.TCPAddr: 250 return t.IP, t.Port 251 case *net.UDPAddr: 252 return t.IP, t.Port 253 case *sctp.SCTPAddr: 254 if len(t.IPAddrs) == 0 { 255 logrus.Error(ErrSCTPAddrNoIP) 256 return nil, 0 257 } 258 return t.IPAddrs[0].IP, t.Port 259 } 260 return nil, 0 261 }