github.com/polevpn/netstack@v1.10.9/tcpip/ports/ports.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package ports provides PortManager that manages allocating, reserving and releasing ports. 16 package ports 17 18 import ( 19 "math" 20 "math/rand" 21 "sync" 22 "sync/atomic" 23 24 "github.com/polevpn/netstack/tcpip" 25 ) 26 27 const ( 28 // FirstEphemeral is the first ephemeral port. 29 FirstEphemeral = 16000 30 31 // numEphemeralPorts it the mnumber of available ephemeral ports to 32 // Netstack. 33 numEphemeralPorts = math.MaxUint16 - FirstEphemeral + 1 34 35 anyIPAddress tcpip.Address = "" 36 ) 37 38 type portDescriptor struct { 39 network tcpip.NetworkProtocolNumber 40 transport tcpip.TransportProtocolNumber 41 port uint16 42 } 43 44 // PortManager manages allocating, reserving and releasing ports. 45 type PortManager struct { 46 mu sync.RWMutex 47 allocatedPorts map[portDescriptor]bindAddresses 48 49 // hint is used to pick ports ephemeral ports in a stable order for 50 // a given port offset. 51 // 52 // hint must be accessed using the portHint/incPortHint helpers. 53 // TODO(gvisor.dev/issue/940): S/R this field. 54 hint uint32 55 } 56 57 type portNode struct { 58 reuse bool 59 refs int 60 } 61 62 // deviceNode is never empty. When it has no elements, it is removed from the 63 // map that references it. 64 type deviceNode map[tcpip.NICID]portNode 65 66 // isAvailable checks whether binding is possible by device. If not binding to a 67 // device, check against all portNodes. If binding to a specific device, check 68 // against the unspecified device and the provided device. 69 func (d deviceNode) isAvailable(reuse bool, bindToDevice tcpip.NICID) bool { 70 if bindToDevice == 0 { 71 // Trying to binding all devices. 72 if !reuse { 73 // Can't bind because the (addr,port) is already bound. 74 return false 75 } 76 for _, p := range d { 77 if !p.reuse { 78 // Can't bind because the (addr,port) was previously bound without reuse. 79 return false 80 } 81 } 82 return true 83 } 84 85 if p, ok := d[0]; ok { 86 if !reuse || !p.reuse { 87 return false 88 } 89 } 90 91 if p, ok := d[bindToDevice]; ok { 92 if !reuse || !p.reuse { 93 return false 94 } 95 } 96 97 return true 98 } 99 100 // bindAddresses is a set of IP addresses. 101 type bindAddresses map[tcpip.Address]deviceNode 102 103 // isAvailable checks whether an IP address is available to bind to. If the 104 // address is the "any" address, check all other addresses. Otherwise, just 105 // check against the "any" address and the provided address. 106 func (b bindAddresses) isAvailable(addr tcpip.Address, reuse bool, bindToDevice tcpip.NICID) bool { 107 if addr == anyIPAddress { 108 // If binding to the "any" address then check that there are no conflicts 109 // with all addresses. 110 for _, d := range b { 111 if !d.isAvailable(reuse, bindToDevice) { 112 return false 113 } 114 } 115 return true 116 } 117 118 // Check that there is no conflict with the "any" address. 119 if d, ok := b[anyIPAddress]; ok { 120 if !d.isAvailable(reuse, bindToDevice) { 121 return false 122 } 123 } 124 125 // Check that this is no conflict with the provided address. 126 if d, ok := b[addr]; ok { 127 if !d.isAvailable(reuse, bindToDevice) { 128 return false 129 } 130 } 131 132 return true 133 } 134 135 // NewPortManager creates new PortManager. 136 func NewPortManager() *PortManager { 137 return &PortManager{allocatedPorts: make(map[portDescriptor]bindAddresses)} 138 } 139 140 // PickEphemeralPort randomly chooses a starting point and iterates over all 141 // possible ephemeral ports, allowing the caller to decide whether a given port 142 // is suitable for its needs, and stopping when a port is found or an error 143 // occurs. 144 func (s *PortManager) PickEphemeralPort(testPort func(p uint16) (bool, *tcpip.Error)) (port uint16, err *tcpip.Error) { 145 offset := uint32(rand.Int31n(numEphemeralPorts)) 146 return s.pickEphemeralPort(offset, numEphemeralPorts, testPort) 147 } 148 149 // portHint atomically reads and returns the s.hint value. 150 func (s *PortManager) portHint() uint32 { 151 return atomic.LoadUint32(&s.hint) 152 } 153 154 // incPortHint atomically increments s.hint by 1. 155 func (s *PortManager) incPortHint() { 156 atomic.AddUint32(&s.hint, 1) 157 } 158 159 // PickEphemeralPortStable starts at the specified offset + s.portHint and 160 // iterates over all ephemeral ports, allowing the caller to decide whether a 161 // given port is suitable for its needs and stopping when a port is found or an 162 // error occurs. 163 func (s *PortManager) PickEphemeralPortStable(offset uint32, testPort func(p uint16) (bool, *tcpip.Error)) (port uint16, err *tcpip.Error) { 164 p, err := s.pickEphemeralPort(s.portHint()+offset, numEphemeralPorts, testPort) 165 if err == nil { 166 s.incPortHint() 167 } 168 return p, err 169 170 } 171 172 // pickEphemeralPort starts at the offset specified from the FirstEphemeral port 173 // and iterates over the number of ports specified by count and allows the 174 // caller to decide whether a given port is suitable for its needs, and stopping 175 // when a port is found or an error occurs. 176 func (s *PortManager) pickEphemeralPort(offset, count uint32, testPort func(p uint16) (bool, *tcpip.Error)) (port uint16, err *tcpip.Error) { 177 for i := uint32(0); i < count; i++ { 178 port = uint16(FirstEphemeral + (offset+i)%count) 179 ok, err := testPort(port) 180 if err != nil { 181 return 0, err 182 } 183 184 if ok { 185 return port, nil 186 } 187 } 188 189 return 0, tcpip.ErrNoPortAvailable 190 } 191 192 // IsPortAvailable tests if the given port is available on all given protocols. 193 func (s *PortManager) IsPortAvailable(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) bool { 194 s.mu.Lock() 195 defer s.mu.Unlock() 196 return s.isPortAvailableLocked(networks, transport, addr, port, reuse, bindToDevice) 197 } 198 199 func (s *PortManager) isPortAvailableLocked(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) bool { 200 for _, network := range networks { 201 desc := portDescriptor{network, transport, port} 202 if addrs, ok := s.allocatedPorts[desc]; ok { 203 if !addrs.isAvailable(addr, reuse, bindToDevice) { 204 return false 205 } 206 } 207 } 208 return true 209 } 210 211 // ReservePort marks a port/IP combination as reserved so that it cannot be 212 // reserved by another endpoint. If port is zero, ReservePort will search for 213 // an unreserved ephemeral port and reserve it, returning its value in the 214 // "port" return value. 215 func (s *PortManager) ReservePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) (reservedPort uint16, err *tcpip.Error) { 216 s.mu.Lock() 217 defer s.mu.Unlock() 218 219 // If a port is specified, just try to reserve it for all network 220 // protocols. 221 if port != 0 { 222 if !s.reserveSpecificPort(networks, transport, addr, port, reuse, bindToDevice) { 223 return 0, tcpip.ErrPortInUse 224 } 225 return port, nil 226 } 227 228 // A port wasn't specified, so try to find one. 229 return s.PickEphemeralPort(func(p uint16) (bool, *tcpip.Error) { 230 return s.reserveSpecificPort(networks, transport, addr, p, reuse, bindToDevice), nil 231 }) 232 } 233 234 // reserveSpecificPort tries to reserve the given port on all given protocols. 235 func (s *PortManager) reserveSpecificPort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) bool { 236 if !s.isPortAvailableLocked(networks, transport, addr, port, reuse, bindToDevice) { 237 return false 238 } 239 240 // Reserve port on all network protocols. 241 for _, network := range networks { 242 desc := portDescriptor{network, transport, port} 243 m, ok := s.allocatedPorts[desc] 244 if !ok { 245 m = make(bindAddresses) 246 s.allocatedPorts[desc] = m 247 } 248 d, ok := m[addr] 249 if !ok { 250 d = make(deviceNode) 251 m[addr] = d 252 } 253 if n, ok := d[bindToDevice]; ok { 254 n.refs++ 255 d[bindToDevice] = n 256 } else { 257 d[bindToDevice] = portNode{reuse: reuse, refs: 1} 258 } 259 } 260 261 return true 262 } 263 264 // ReleasePort releases the reservation on a port/IP combination so that it can 265 // be reserved by other endpoints. 266 func (s *PortManager) ReleasePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, bindToDevice tcpip.NICID) { 267 s.mu.Lock() 268 defer s.mu.Unlock() 269 270 for _, network := range networks { 271 desc := portDescriptor{network, transport, port} 272 if m, ok := s.allocatedPorts[desc]; ok { 273 d, ok := m[addr] 274 if !ok { 275 continue 276 } 277 n, ok := d[bindToDevice] 278 if !ok { 279 continue 280 } 281 n.refs-- 282 d[bindToDevice] = n 283 if n.refs == 0 { 284 delete(d, bindToDevice) 285 } 286 if len(d) == 0 { 287 delete(m, addr) 288 } 289 if len(m) == 0 { 290 delete(s.allocatedPorts, desc) 291 } 292 } 293 } 294 }