gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/ports/ports_test.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 16 17 import ( 18 "math" 19 "testing" 20 21 "github.com/google/go-cmp/cmp" 22 cryptorand "gvisor.dev/gvisor/pkg/rand" 23 "gvisor.dev/gvisor/pkg/tcpip" 24 "gvisor.dev/gvisor/pkg/tcpip/testutil" 25 ) 26 27 const ( 28 fakeTransNumber tcpip.TransportProtocolNumber = 1 29 fakeNetworkNumber tcpip.NetworkProtocolNumber = 2 30 ) 31 32 var ( 33 fakeIPAddress = testutil.MustParse4("8.8.8.8") 34 fakeIPAddress1 = testutil.MustParse4("8.8.8.9") 35 ) 36 37 type portReserveTestAction struct { 38 port uint16 39 ip tcpip.Address 40 want tcpip.Error 41 flags Flags 42 release bool 43 device tcpip.NICID 44 dest tcpip.FullAddress 45 } 46 47 func TestPortReservation(t *testing.T) { 48 for _, test := range []struct { 49 tname string 50 actions []portReserveTestAction 51 }{ 52 { 53 tname: "bind to ip", 54 actions: []portReserveTestAction{ 55 {port: 80, ip: fakeIPAddress, want: nil}, 56 {port: 80, ip: fakeIPAddress1, want: nil}, 57 /* N.B. Order of tests matters! */ 58 {port: 80, ip: anyIPAddress, want: &tcpip.ErrPortInUse{}}, 59 {port: 80, ip: fakeIPAddress, want: &tcpip.ErrPortInUse{}, flags: Flags{LoadBalanced: true}}, 60 }, 61 }, 62 { 63 tname: "bind to inaddr any", 64 actions: []portReserveTestAction{ 65 {port: 22, ip: anyIPAddress, want: nil}, 66 {port: 22, ip: fakeIPAddress, want: &tcpip.ErrPortInUse{}}, 67 /* release fakeIPAddress, but anyIPAddress is still inuse */ 68 {port: 22, ip: fakeIPAddress, release: true}, 69 {port: 22, ip: fakeIPAddress, want: &tcpip.ErrPortInUse{}}, 70 {port: 22, ip: fakeIPAddress, want: &tcpip.ErrPortInUse{}, flags: Flags{LoadBalanced: true}}, 71 /* Release port 22 from any IP address, then try to reserve fake IP address on 22 */ 72 {port: 22, ip: anyIPAddress, want: nil, release: true}, 73 {port: 22, ip: fakeIPAddress, want: nil}, 74 }, 75 }, { 76 tname: "bind to zero port", 77 actions: []portReserveTestAction{ 78 {port: 00, ip: fakeIPAddress, want: nil}, 79 {port: 00, ip: fakeIPAddress, want: nil}, 80 {port: 00, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 81 }, 82 }, { 83 tname: "bind to ip with reuseport", 84 actions: []portReserveTestAction{ 85 {port: 25, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 86 {port: 25, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 87 88 {port: 25, ip: fakeIPAddress, flags: Flags{}, want: &tcpip.ErrPortInUse{}}, 89 {port: 25, ip: anyIPAddress, flags: Flags{}, want: &tcpip.ErrPortInUse{}}, 90 91 {port: 25, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 92 }, 93 }, { 94 tname: "bind to inaddr any with reuseport", 95 actions: []portReserveTestAction{ 96 {port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 97 {port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 98 99 {port: 24, ip: anyIPAddress, flags: Flags{}, want: &tcpip.ErrPortInUse{}}, 100 {port: 24, ip: fakeIPAddress, flags: Flags{}, want: &tcpip.ErrPortInUse{}}, 101 102 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 103 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, release: true, want: nil}, 104 105 {port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, release: true}, 106 {port: 24, ip: anyIPAddress, flags: Flags{}, want: &tcpip.ErrPortInUse{}}, 107 108 {port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, release: true}, 109 {port: 24, ip: anyIPAddress, flags: Flags{}, want: nil}, 110 }, 111 }, { 112 tname: "bind twice with device fails", 113 actions: []portReserveTestAction{ 114 {port: 24, ip: fakeIPAddress, device: 3, want: nil}, 115 {port: 24, ip: fakeIPAddress, device: 3, want: &tcpip.ErrPortInUse{}}, 116 }, 117 }, { 118 tname: "bind to device", 119 actions: []portReserveTestAction{ 120 {port: 24, ip: fakeIPAddress, device: 1, want: nil}, 121 {port: 24, ip: fakeIPAddress, device: 2, want: nil}, 122 }, 123 }, { 124 tname: "bind to device and then without device", 125 actions: []portReserveTestAction{ 126 {port: 24, ip: fakeIPAddress, device: 123, want: nil}, 127 {port: 24, ip: fakeIPAddress, device: 0, want: &tcpip.ErrPortInUse{}}, 128 }, 129 }, { 130 tname: "bind without device", 131 actions: []portReserveTestAction{ 132 {port: 24, ip: fakeIPAddress, want: nil}, 133 {port: 24, ip: fakeIPAddress, device: 123, want: &tcpip.ErrPortInUse{}}, 134 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 135 {port: 24, ip: fakeIPAddress, want: &tcpip.ErrPortInUse{}}, 136 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 137 }, 138 }, { 139 tname: "bind with device", 140 actions: []portReserveTestAction{ 141 {port: 24, ip: fakeIPAddress, device: 123, want: nil}, 142 {port: 24, ip: fakeIPAddress, device: 123, want: &tcpip.ErrPortInUse{}}, 143 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 144 {port: 24, ip: fakeIPAddress, device: 0, want: &tcpip.ErrPortInUse{}}, 145 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 146 {port: 24, ip: fakeIPAddress, device: 456, flags: Flags{LoadBalanced: true}, want: nil}, 147 {port: 24, ip: fakeIPAddress, device: 789, want: nil}, 148 {port: 24, ip: fakeIPAddress, want: &tcpip.ErrPortInUse{}}, 149 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 150 }, 151 }, { 152 tname: "bind with reuseport", 153 actions: []portReserveTestAction{ 154 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 155 {port: 24, ip: fakeIPAddress, device: 123, want: &tcpip.ErrPortInUse{}}, 156 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil}, 157 {port: 24, ip: fakeIPAddress, device: 0, want: &tcpip.ErrPortInUse{}}, 158 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil}, 159 }, 160 }, { 161 tname: "binding with reuseport and device", 162 actions: []portReserveTestAction{ 163 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil}, 164 {port: 24, ip: fakeIPAddress, device: 123, want: &tcpip.ErrPortInUse{}}, 165 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil}, 166 {port: 24, ip: fakeIPAddress, device: 0, want: &tcpip.ErrPortInUse{}}, 167 {port: 24, ip: fakeIPAddress, device: 456, flags: Flags{LoadBalanced: true}, want: nil}, 168 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil}, 169 {port: 24, ip: fakeIPAddress, device: 789, flags: Flags{LoadBalanced: true}, want: nil}, 170 {port: 24, ip: fakeIPAddress, device: 999, want: &tcpip.ErrPortInUse{}}, 171 }, 172 }, { 173 tname: "mixing reuseport and not reuseport by binding to device", 174 actions: []portReserveTestAction{ 175 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil}, 176 {port: 24, ip: fakeIPAddress, device: 456, want: nil}, 177 {port: 24, ip: fakeIPAddress, device: 789, flags: Flags{LoadBalanced: true}, want: nil}, 178 {port: 24, ip: fakeIPAddress, device: 999, want: nil}, 179 }, 180 }, { 181 tname: "can't bind to 0 after mixing reuseport and not reuseport", 182 actions: []portReserveTestAction{ 183 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil}, 184 {port: 24, ip: fakeIPAddress, device: 456, want: nil}, 185 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 186 }, 187 }, { 188 tname: "bind and release", 189 actions: []portReserveTestAction{ 190 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil}, 191 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil}, 192 {port: 24, ip: fakeIPAddress, device: 345, flags: Flags{}, want: &tcpip.ErrPortInUse{}}, 193 {port: 24, ip: fakeIPAddress, device: 789, flags: Flags{LoadBalanced: true}, want: nil}, 194 195 // Release the bind to device 0 and try again. 196 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil, release: true}, 197 {port: 24, ip: fakeIPAddress, device: 345, flags: Flags{}, want: nil}, 198 }, 199 }, { 200 tname: "bind twice with reuseport once", 201 actions: []portReserveTestAction{ 202 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil}, 203 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 204 }, 205 }, { 206 tname: "release an unreserved device", 207 actions: []portReserveTestAction{ 208 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil}, 209 {port: 24, ip: fakeIPAddress, device: 456, flags: Flags{}, want: nil}, 210 // The below don't exist. 211 {port: 24, ip: fakeIPAddress, device: 345, flags: Flags{}, want: nil, release: true}, 212 {port: 9999, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil, release: true}, 213 // Release all. 214 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil, release: true}, 215 {port: 24, ip: fakeIPAddress, device: 456, flags: Flags{}, want: nil, release: true}, 216 }, 217 }, { 218 tname: "bind with reuseaddr", 219 actions: []portReserveTestAction{ 220 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil}, 221 {port: 24, ip: fakeIPAddress, device: 123, want: &tcpip.ErrPortInUse{}}, 222 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{MostRecent: true}, want: nil}, 223 {port: 24, ip: fakeIPAddress, device: 0, want: &tcpip.ErrPortInUse{}}, 224 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{MostRecent: true}, want: nil}, 225 }, 226 }, { 227 tname: "bind twice with reuseaddr once", 228 actions: []portReserveTestAction{ 229 {port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil}, 230 {port: 24, ip: fakeIPAddress, device: 0, flags: Flags{MostRecent: true}, want: &tcpip.ErrPortInUse{}}, 231 }, 232 }, { 233 tname: "bind with reuseaddr and reuseport", 234 actions: []portReserveTestAction{ 235 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 236 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 237 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 238 }, 239 }, { 240 tname: "bind with reuseaddr and reuseport, and then reuseaddr", 241 actions: []portReserveTestAction{ 242 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 243 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil}, 244 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 245 }, 246 }, { 247 tname: "bind with reuseaddr and reuseport, and then reuseport", 248 actions: []portReserveTestAction{ 249 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 250 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 251 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: &tcpip.ErrPortInUse{}}, 252 }, 253 }, { 254 tname: "bind with reuseaddr and reuseport twice, and then reuseaddr", 255 actions: []portReserveTestAction{ 256 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 257 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 258 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil}, 259 }, 260 }, { 261 tname: "bind with reuseaddr and reuseport twice, and then reuseport", 262 actions: []portReserveTestAction{ 263 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 264 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 265 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 266 }, 267 }, { 268 tname: "bind with reuseaddr, and then reuseaddr and reuseport", 269 actions: []portReserveTestAction{ 270 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil}, 271 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 272 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: &tcpip.ErrPortInUse{}}, 273 }, 274 }, { 275 tname: "bind with reuseport, and then reuseaddr and reuseport", 276 actions: []portReserveTestAction{ 277 {port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil}, 278 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil}, 279 {port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: &tcpip.ErrPortInUse{}}, 280 }, 281 }, { 282 tname: "bind tuple with reuseaddr, and then wildcard with reuseaddr", 283 actions: []portReserveTestAction{ 284 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 24}, want: nil}, 285 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{}, want: nil}, 286 }, 287 }, { 288 tname: "bind tuple with reuseaddr, and then wildcard", 289 actions: []portReserveTestAction{ 290 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 24}, want: nil}, 291 {port: 24, ip: fakeIPAddress, want: &tcpip.ErrPortInUse{}}, 292 }, 293 }, { 294 tname: "bind wildcard with reuseaddr, and then tuple with reuseaddr", 295 actions: []portReserveTestAction{ 296 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{}, want: nil}, 297 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 24}, want: nil}, 298 }, 299 }, { 300 tname: "bind tuple with reuseaddr, and then wildcard", 301 actions: []portReserveTestAction{ 302 {port: 24, ip: fakeIPAddress, want: nil}, 303 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 24}, want: &tcpip.ErrPortInUse{}}, 304 }, 305 }, { 306 tname: "bind two tuples with reuseaddr", 307 actions: []portReserveTestAction{ 308 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 24}, want: nil}, 309 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 25}, want: nil}, 310 }, 311 }, { 312 tname: "bind two tuples", 313 actions: []portReserveTestAction{ 314 {port: 24, ip: fakeIPAddress, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 24}, want: nil}, 315 {port: 24, ip: fakeIPAddress, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 25}, want: nil}, 316 }, 317 }, { 318 tname: "bind wildcard, and then tuple with reuseaddr", 319 actions: []portReserveTestAction{ 320 {port: 24, ip: fakeIPAddress, dest: tcpip.FullAddress{}, want: nil}, 321 {port: 24, ip: fakeIPAddress, flags: Flags{TupleOnly: true}, dest: tcpip.FullAddress{Addr: fakeIPAddress, Port: 24}, want: &tcpip.ErrPortInUse{}}, 322 }, 323 }, { 324 tname: "bind wildcard twice with reuseaddr", 325 actions: []portReserveTestAction{ 326 {port: 24, ip: anyIPAddress, flags: Flags{TupleOnly: true}, want: nil}, 327 {port: 24, ip: anyIPAddress, flags: Flags{TupleOnly: true}, want: nil}, 328 }, 329 }, 330 } { 331 t.Run(test.tname, func(t *testing.T) { 332 pm := NewPortManager() 333 net := []tcpip.NetworkProtocolNumber{fakeNetworkNumber} 334 rng := cryptorand.RNGFrom(cryptorand.Reader) 335 336 for _, test := range test.actions { 337 first, _ := pm.PortRange() 338 if test.release { 339 portRes := Reservation{ 340 Networks: net, 341 Transport: fakeTransNumber, 342 Addr: test.ip, 343 Port: test.port, 344 Flags: test.flags, 345 BindToDevice: test.device, 346 Dest: test.dest, 347 } 348 pm.ReleasePort(portRes) 349 continue 350 } 351 portRes := Reservation{ 352 Networks: net, 353 Transport: fakeTransNumber, 354 Addr: test.ip, 355 Port: test.port, 356 Flags: test.flags, 357 BindToDevice: test.device, 358 Dest: test.dest, 359 } 360 gotPort, err := pm.ReservePort(rng, portRes, nil /* testPort */) 361 if diff := cmp.Diff(test.want, err); diff != "" { 362 t.Fatalf("unexpected error from ReservePort(%+v, _), (-want, +got):\n%s", portRes, diff) 363 } 364 if test.port == 0 && (gotPort == 0 || gotPort < first) { 365 t.Fatalf("ReservePort(%+v, _) = %d, want port number >= %d to be picked", portRes, gotPort, first) 366 } 367 } 368 }) 369 } 370 } 371 372 func TestPickEphemeralPort(t *testing.T) { 373 const ( 374 firstEphemeral = 32000 375 numEphemeralPorts = 1000 376 ) 377 378 for _, test := range []struct { 379 name string 380 f func(port uint16) (bool, tcpip.Error) 381 wantErr tcpip.Error 382 wantPort uint16 383 }{ 384 { 385 name: "no-port-available", 386 f: func(port uint16) (bool, tcpip.Error) { 387 return false, nil 388 }, 389 wantErr: &tcpip.ErrNoPortAvailable{}, 390 }, 391 { 392 name: "port-tester-error", 393 f: func(port uint16) (bool, tcpip.Error) { 394 return false, &tcpip.ErrBadBuffer{} 395 }, 396 wantErr: &tcpip.ErrBadBuffer{}, 397 }, 398 { 399 name: "only-port-16042-available", 400 f: func(port uint16) (bool, tcpip.Error) { 401 if port == firstEphemeral+42 { 402 return true, nil 403 } 404 return false, nil 405 }, 406 wantPort: firstEphemeral + 42, 407 }, 408 { 409 name: "only-port-under-16000-available", 410 f: func(port uint16) (bool, tcpip.Error) { 411 if port < firstEphemeral { 412 return true, nil 413 } 414 return false, nil 415 }, 416 wantErr: &tcpip.ErrNoPortAvailable{}, 417 }, 418 } { 419 t.Run(test.name, func(t *testing.T) { 420 pm := NewPortManager() 421 rng := cryptorand.RNGFrom(cryptorand.Reader) 422 if err := pm.SetPortRange(firstEphemeral, firstEphemeral+numEphemeralPorts); err != nil { 423 t.Fatalf("failed to set ephemeral port range: %s", err) 424 } 425 port, err := pm.PickEphemeralPort(rng, test.f) 426 if diff := cmp.Diff(test.wantErr, err); diff != "" { 427 t.Fatalf("unexpected error from PickEphemeralPort(..), (-want, +got):\n%s", diff) 428 } 429 if port != test.wantPort { 430 t.Errorf("got PickEphemeralPort(..) = (%d, nil); want (%d, nil)", port, test.wantPort) 431 } 432 }) 433 } 434 } 435 436 // TestOverflow addresses b/183593432, wherein an overflowing uint16 causes a 437 // port allocation failure. 438 func TestOverflow(t *testing.T) { 439 // Use a small range and start at offsets that will cause an overflow. 440 count := uint16(50) 441 for offset := uint32(math.MaxUint16 - count); offset < math.MaxUint16; offset++ { 442 reservedPorts := make(map[uint16]struct{}) 443 // Ensure we can reserve everything in the allowed range. 444 for i := uint16(0); i < count; i++ { 445 port, err := pickEphemeralPort(offset, firstEphemeral, count, func(port uint16) (bool, tcpip.Error) { 446 if _, ok := reservedPorts[port]; !ok { 447 reservedPorts[port] = struct{}{} 448 return true, nil 449 } 450 return false, nil 451 }) 452 if err != nil { 453 t.Fatalf("port picking failed at iteration %d, for offset %d, len(reserved): %+v", i, offset, len(reservedPorts)) 454 } 455 if port < firstEphemeral || port > firstEphemeral+count { 456 t.Fatalf("reserved port %d, which is not in range [%d, %d]", port, firstEphemeral, firstEphemeral+count-1) 457 } 458 } 459 } 460 }