github.com/flowerwrong/netstack@v0.0.0-20191009141956-e5848263af28/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/rand" 19 "testing" 20 21 "github.com/FlowerWrong/netstack/tcpip" 22 ) 23 24 const ( 25 fakeTransNumber tcpip.TransportProtocolNumber = 1 26 fakeNetworkNumber tcpip.NetworkProtocolNumber = 2 27 28 fakeIPAddress = tcpip.Address("\x08\x08\x08\x08") 29 fakeIPAddress1 = tcpip.Address("\x08\x08\x08\x09") 30 ) 31 32 type portReserveTestAction struct { 33 port uint16 34 ip tcpip.Address 35 want *tcpip.Error 36 reuse bool 37 release bool 38 device tcpip.NICID 39 } 40 41 func TestPortReservation(t *testing.T) { 42 for _, test := range []struct { 43 tname string 44 actions []portReserveTestAction 45 }{ 46 { 47 tname: "bind to ip", 48 actions: []portReserveTestAction{ 49 {port: 80, ip: fakeIPAddress, want: nil}, 50 {port: 80, ip: fakeIPAddress1, want: nil}, 51 /* N.B. Order of tests matters! */ 52 {port: 80, ip: anyIPAddress, want: tcpip.ErrPortInUse}, 53 {port: 80, ip: fakeIPAddress, want: tcpip.ErrPortInUse, reuse: true}, 54 }, 55 }, 56 { 57 tname: "bind to inaddr any", 58 actions: []portReserveTestAction{ 59 {port: 22, ip: anyIPAddress, want: nil}, 60 {port: 22, ip: fakeIPAddress, want: tcpip.ErrPortInUse}, 61 /* release fakeIPAddress, but anyIPAddress is still inuse */ 62 {port: 22, ip: fakeIPAddress, release: true}, 63 {port: 22, ip: fakeIPAddress, want: tcpip.ErrPortInUse}, 64 {port: 22, ip: fakeIPAddress, want: tcpip.ErrPortInUse, reuse: true}, 65 /* Release port 22 from any IP address, then try to reserve fake IP address on 22 */ 66 {port: 22, ip: anyIPAddress, want: nil, release: true}, 67 {port: 22, ip: fakeIPAddress, want: nil}, 68 }, 69 }, { 70 tname: "bind to zero port", 71 actions: []portReserveTestAction{ 72 {port: 00, ip: fakeIPAddress, want: nil}, 73 {port: 00, ip: fakeIPAddress, want: nil}, 74 {port: 00, ip: fakeIPAddress, reuse: true, want: nil}, 75 }, 76 }, { 77 tname: "bind to ip with reuseport", 78 actions: []portReserveTestAction{ 79 {port: 25, ip: fakeIPAddress, reuse: true, want: nil}, 80 {port: 25, ip: fakeIPAddress, reuse: true, want: nil}, 81 82 {port: 25, ip: fakeIPAddress, reuse: false, want: tcpip.ErrPortInUse}, 83 {port: 25, ip: anyIPAddress, reuse: false, want: tcpip.ErrPortInUse}, 84 85 {port: 25, ip: anyIPAddress, reuse: true, want: nil}, 86 }, 87 }, { 88 tname: "bind to inaddr any with reuseport", 89 actions: []portReserveTestAction{ 90 {port: 24, ip: anyIPAddress, reuse: true, want: nil}, 91 {port: 24, ip: anyIPAddress, reuse: true, want: nil}, 92 93 {port: 24, ip: anyIPAddress, reuse: false, want: tcpip.ErrPortInUse}, 94 {port: 24, ip: fakeIPAddress, reuse: false, want: tcpip.ErrPortInUse}, 95 96 {port: 24, ip: fakeIPAddress, reuse: true, want: nil}, 97 {port: 24, ip: fakeIPAddress, release: true, want: nil}, 98 99 {port: 24, ip: anyIPAddress, release: true}, 100 {port: 24, ip: anyIPAddress, reuse: false, want: tcpip.ErrPortInUse}, 101 102 {port: 24, ip: anyIPAddress, release: true}, 103 {port: 24, ip: anyIPAddress, reuse: false, want: nil}, 104 }, 105 }, { 106 tname: "bind twice with device fails", 107 actions: []portReserveTestAction{ 108 {port: 24, ip: fakeIPAddress, device: 3, want: nil}, 109 {port: 24, ip: fakeIPAddress, device: 3, want: tcpip.ErrPortInUse}, 110 }, 111 }, { 112 tname: "bind to device", 113 actions: []portReserveTestAction{ 114 {port: 24, ip: fakeIPAddress, device: 1, want: nil}, 115 {port: 24, ip: fakeIPAddress, device: 2, want: nil}, 116 }, 117 }, { 118 tname: "bind to device and then without device", 119 actions: []portReserveTestAction{ 120 {port: 24, ip: fakeIPAddress, device: 123, want: nil}, 121 {port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse}, 122 }, 123 }, { 124 tname: "bind without device", 125 actions: []portReserveTestAction{ 126 {port: 24, ip: fakeIPAddress, want: nil}, 127 {port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse}, 128 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: tcpip.ErrPortInUse}, 129 {port: 24, ip: fakeIPAddress, want: tcpip.ErrPortInUse}, 130 {port: 24, ip: fakeIPAddress, reuse: true, want: tcpip.ErrPortInUse}, 131 }, 132 }, { 133 tname: "bind with device", 134 actions: []portReserveTestAction{ 135 {port: 24, ip: fakeIPAddress, device: 123, want: nil}, 136 {port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse}, 137 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: tcpip.ErrPortInUse}, 138 {port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse}, 139 {port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: tcpip.ErrPortInUse}, 140 {port: 24, ip: fakeIPAddress, device: 456, reuse: true, want: nil}, 141 {port: 24, ip: fakeIPAddress, device: 789, want: nil}, 142 {port: 24, ip: fakeIPAddress, want: tcpip.ErrPortInUse}, 143 {port: 24, ip: fakeIPAddress, reuse: true, want: tcpip.ErrPortInUse}, 144 }, 145 }, { 146 tname: "bind with reuse", 147 actions: []portReserveTestAction{ 148 {port: 24, ip: fakeIPAddress, reuse: true, want: nil}, 149 {port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse}, 150 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil}, 151 {port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse}, 152 {port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil}, 153 }, 154 }, { 155 tname: "binding with reuse and device", 156 actions: []portReserveTestAction{ 157 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil}, 158 {port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse}, 159 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil}, 160 {port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse}, 161 {port: 24, ip: fakeIPAddress, device: 456, reuse: true, want: nil}, 162 {port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil}, 163 {port: 24, ip: fakeIPAddress, device: 789, reuse: true, want: nil}, 164 {port: 24, ip: fakeIPAddress, device: 999, want: tcpip.ErrPortInUse}, 165 }, 166 }, { 167 tname: "mixing reuse and not reuse by binding to device", 168 actions: []portReserveTestAction{ 169 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil}, 170 {port: 24, ip: fakeIPAddress, device: 456, want: nil}, 171 {port: 24, ip: fakeIPAddress, device: 789, reuse: true, want: nil}, 172 {port: 24, ip: fakeIPAddress, device: 999, want: nil}, 173 }, 174 }, { 175 tname: "can't bind to 0 after mixing reuse and not reuse", 176 actions: []portReserveTestAction{ 177 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil}, 178 {port: 24, ip: fakeIPAddress, device: 456, want: nil}, 179 {port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: tcpip.ErrPortInUse}, 180 }, 181 }, { 182 tname: "bind and release", 183 actions: []portReserveTestAction{ 184 {port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil}, 185 {port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil}, 186 {port: 24, ip: fakeIPAddress, device: 345, reuse: false, want: tcpip.ErrPortInUse}, 187 {port: 24, ip: fakeIPAddress, device: 789, reuse: true, want: nil}, 188 189 // Release the bind to device 0 and try again. 190 {port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil, release: true}, 191 {port: 24, ip: fakeIPAddress, device: 345, reuse: false, want: nil}, 192 }, 193 }, { 194 tname: "bind twice with reuse once", 195 actions: []portReserveTestAction{ 196 {port: 24, ip: fakeIPAddress, device: 123, reuse: false, want: nil}, 197 {port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: tcpip.ErrPortInUse}, 198 }, 199 }, { 200 tname: "release an unreserved device", 201 actions: []portReserveTestAction{ 202 {port: 24, ip: fakeIPAddress, device: 123, reuse: false, want: nil}, 203 {port: 24, ip: fakeIPAddress, device: 456, reuse: false, want: nil}, 204 // The below don't exist. 205 {port: 24, ip: fakeIPAddress, device: 345, reuse: false, want: nil, release: true}, 206 {port: 9999, ip: fakeIPAddress, device: 123, reuse: false, want: nil, release: true}, 207 // Release all. 208 {port: 24, ip: fakeIPAddress, device: 123, reuse: false, want: nil, release: true}, 209 {port: 24, ip: fakeIPAddress, device: 456, reuse: false, want: nil, release: true}, 210 }, 211 }, 212 } { 213 t.Run(test.tname, func(t *testing.T) { 214 pm := NewPortManager() 215 net := []tcpip.NetworkProtocolNumber{fakeNetworkNumber} 216 217 for _, test := range test.actions { 218 if test.release { 219 pm.ReleasePort(net, fakeTransNumber, test.ip, test.port, test.device) 220 continue 221 } 222 gotPort, err := pm.ReservePort(net, fakeTransNumber, test.ip, test.port, test.reuse, test.device) 223 if err != test.want { 224 t.Fatalf("ReservePort(.., .., %s, %d, %t, %d) = %v, want %v", test.ip, test.port, test.reuse, test.device, err, test.want) 225 } 226 if test.port == 0 && (gotPort == 0 || gotPort < FirstEphemeral) { 227 t.Fatalf("ReservePort(.., .., .., 0) = %d, want port number >= %d to be picked", gotPort, FirstEphemeral) 228 } 229 } 230 }) 231 232 } 233 } 234 235 func TestPickEphemeralPort(t *testing.T) { 236 customErr := &tcpip.Error{} 237 for _, test := range []struct { 238 name string 239 f func(port uint16) (bool, *tcpip.Error) 240 wantErr *tcpip.Error 241 wantPort uint16 242 }{ 243 { 244 name: "no-port-available", 245 f: func(port uint16) (bool, *tcpip.Error) { 246 return false, nil 247 }, 248 wantErr: tcpip.ErrNoPortAvailable, 249 }, 250 { 251 name: "port-tester-error", 252 f: func(port uint16) (bool, *tcpip.Error) { 253 return false, customErr 254 }, 255 wantErr: customErr, 256 }, 257 { 258 name: "only-port-16042-available", 259 f: func(port uint16) (bool, *tcpip.Error) { 260 if port == FirstEphemeral+42 { 261 return true, nil 262 } 263 return false, nil 264 }, 265 wantPort: FirstEphemeral + 42, 266 }, 267 { 268 name: "only-port-under-16000-available", 269 f: func(port uint16) (bool, *tcpip.Error) { 270 if port < FirstEphemeral { 271 return true, nil 272 } 273 return false, nil 274 }, 275 wantErr: tcpip.ErrNoPortAvailable, 276 }, 277 } { 278 t.Run(test.name, func(t *testing.T) { 279 pm := NewPortManager() 280 if port, err := pm.PickEphemeralPort(test.f); port != test.wantPort || err != test.wantErr { 281 t.Errorf("PickEphemeralPort(..) = (port %d, err %v); want (port %d, err %v)", port, err, test.wantPort, test.wantErr) 282 } 283 }) 284 } 285 } 286 287 func TestPickEphemeralPortStable(t *testing.T) { 288 customErr := &tcpip.Error{} 289 for _, test := range []struct { 290 name string 291 f func(port uint16) (bool, *tcpip.Error) 292 wantErr *tcpip.Error 293 wantPort uint16 294 }{ 295 { 296 name: "no-port-available", 297 f: func(port uint16) (bool, *tcpip.Error) { 298 return false, nil 299 }, 300 wantErr: tcpip.ErrNoPortAvailable, 301 }, 302 { 303 name: "port-tester-error", 304 f: func(port uint16) (bool, *tcpip.Error) { 305 return false, customErr 306 }, 307 wantErr: customErr, 308 }, 309 { 310 name: "only-port-16042-available", 311 f: func(port uint16) (bool, *tcpip.Error) { 312 if port == FirstEphemeral+42 { 313 return true, nil 314 } 315 return false, nil 316 }, 317 wantPort: FirstEphemeral + 42, 318 }, 319 { 320 name: "only-port-under-16000-available", 321 f: func(port uint16) (bool, *tcpip.Error) { 322 if port < FirstEphemeral { 323 return true, nil 324 } 325 return false, nil 326 }, 327 wantErr: tcpip.ErrNoPortAvailable, 328 }, 329 } { 330 t.Run(test.name, func(t *testing.T) { 331 pm := NewPortManager() 332 portOffset := uint32(rand.Int31n(int32(numEphemeralPorts))) 333 if port, err := pm.PickEphemeralPortStable(portOffset, test.f); port != test.wantPort || err != test.wantErr { 334 t.Errorf("PickEphemeralPort(..) = (port %d, err %v); want (port %d, err %v)", port, err, test.wantPort, test.wantErr) 335 } 336 }) 337 } 338 }