github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/network/internal/ip/duplicate_address_detection_test.go (about) 1 // Copyright 2021 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 ip_test 16 17 import ( 18 "bytes" 19 "testing" 20 "time" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/SagerNet/gvisor/pkg/sync" 24 "github.com/SagerNet/gvisor/pkg/tcpip" 25 "github.com/SagerNet/gvisor/pkg/tcpip/faketime" 26 "github.com/SagerNet/gvisor/pkg/tcpip/network/internal/ip" 27 "github.com/SagerNet/gvisor/pkg/tcpip/stack" 28 ) 29 30 type mockDADProtocol struct { 31 t *testing.T 32 33 mu struct { 34 sync.Mutex 35 36 dad ip.DAD 37 sentNonces map[tcpip.Address][][]byte 38 } 39 } 40 41 func (m *mockDADProtocol) init(t *testing.T, c stack.DADConfigurations, opts ip.DADOptions) { 42 m.mu.Lock() 43 defer m.mu.Unlock() 44 45 m.t = t 46 opts.Protocol = m 47 m.mu.dad.Init(&m.mu, c, opts) 48 m.initLocked() 49 } 50 51 func (m *mockDADProtocol) initLocked() { 52 m.mu.sentNonces = make(map[tcpip.Address][][]byte) 53 } 54 55 func (m *mockDADProtocol) SendDADMessage(addr tcpip.Address, nonce []byte) tcpip.Error { 56 m.mu.Lock() 57 defer m.mu.Unlock() 58 m.mu.sentNonces[addr] = append(m.mu.sentNonces[addr], nonce) 59 return nil 60 } 61 62 func (m *mockDADProtocol) check(addrs []tcpip.Address) string { 63 sentNonces := make(map[tcpip.Address][][]byte) 64 for _, a := range addrs { 65 sentNonces[a] = append(sentNonces[a], nil) 66 } 67 68 return m.checkWithNonce(sentNonces) 69 } 70 71 func (m *mockDADProtocol) checkWithNonce(expectedSentNonces map[tcpip.Address][][]byte) string { 72 m.mu.Lock() 73 defer m.mu.Unlock() 74 75 diff := cmp.Diff(expectedSentNonces, m.mu.sentNonces) 76 m.initLocked() 77 return diff 78 } 79 80 func (m *mockDADProtocol) checkDuplicateAddress(addr tcpip.Address, h stack.DADCompletionHandler) stack.DADCheckAddressDisposition { 81 m.mu.Lock() 82 defer m.mu.Unlock() 83 return m.mu.dad.CheckDuplicateAddressLocked(addr, h) 84 } 85 86 func (m *mockDADProtocol) stop(addr tcpip.Address, reason stack.DADResult) { 87 m.mu.Lock() 88 defer m.mu.Unlock() 89 m.mu.dad.StopLocked(addr, reason) 90 } 91 92 func (m *mockDADProtocol) extendIfNonceEqual(addr tcpip.Address, nonce []byte) ip.ExtendIfNonceEqualLockedDisposition { 93 m.mu.Lock() 94 defer m.mu.Unlock() 95 return m.mu.dad.ExtendIfNonceEqualLocked(addr, nonce) 96 } 97 98 func (m *mockDADProtocol) setConfigs(c stack.DADConfigurations) { 99 m.mu.Lock() 100 defer m.mu.Unlock() 101 m.mu.dad.SetConfigsLocked(c) 102 } 103 104 const ( 105 addr1 = tcpip.Address("\x01") 106 addr2 = tcpip.Address("\x02") 107 addr3 = tcpip.Address("\x03") 108 addr4 = tcpip.Address("\x04") 109 ) 110 111 type dadResult struct { 112 Addr tcpip.Address 113 R stack.DADResult 114 } 115 116 func handler(ch chan<- dadResult, a tcpip.Address) func(stack.DADResult) { 117 return func(r stack.DADResult) { 118 ch <- dadResult{Addr: a, R: r} 119 } 120 } 121 122 func TestDADCheckDuplicateAddress(t *testing.T) { 123 var dad mockDADProtocol 124 clock := faketime.NewManualClock() 125 dad.init(t, stack.DADConfigurations{}, ip.DADOptions{ 126 Clock: clock, 127 }) 128 129 ch := make(chan dadResult, 2) 130 131 // DAD should initially be disabled. 132 if res := dad.checkDuplicateAddress(addr1, handler(nil, "")); res != stack.DADDisabled { 133 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr1, res, stack.DADDisabled) 134 } 135 // Wait for any initially fired timers to complete. 136 clock.RunImmediatelyScheduledJobs() 137 if diff := dad.check(nil); diff != "" { 138 t.Errorf("dad check mismatch (-want +got):\n%s", diff) 139 } 140 141 // Enable and request DAD. 142 dadConfigs1 := stack.DADConfigurations{ 143 DupAddrDetectTransmits: 1, 144 RetransmitTimer: time.Second, 145 } 146 dad.setConfigs(dadConfigs1) 147 if res := dad.checkDuplicateAddress(addr1, handler(ch, addr1)); res != stack.DADStarting { 148 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr1, res, stack.DADStarting) 149 } 150 clock.RunImmediatelyScheduledJobs() 151 if diff := dad.check([]tcpip.Address{addr1}); diff != "" { 152 t.Errorf("dad check mismatch (-want +got):\n%s", diff) 153 } 154 // The second request for DAD on the same address should use the original 155 // request since it has not completed yet. 156 if res := dad.checkDuplicateAddress(addr1, handler(ch, addr1)); res != stack.DADAlreadyRunning { 157 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr1, res, stack.DADAlreadyRunning) 158 } 159 clock.RunImmediatelyScheduledJobs() 160 if diff := dad.check(nil); diff != "" { 161 t.Errorf("dad check mismatch (-want +got):\n%s", diff) 162 } 163 164 dadConfigs2 := stack.DADConfigurations{ 165 DupAddrDetectTransmits: 2, 166 RetransmitTimer: time.Second, 167 } 168 dad.setConfigs(dadConfigs2) 169 // A new address should start a new DAD process. 170 if res := dad.checkDuplicateAddress(addr2, handler(ch, addr2)); res != stack.DADStarting { 171 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr2, res, stack.DADStarting) 172 } 173 clock.RunImmediatelyScheduledJobs() 174 if diff := dad.check([]tcpip.Address{addr2}); diff != "" { 175 t.Errorf("dad check mismatch (-want +got):\n%s", diff) 176 } 177 178 // Make sure DAD for addr1 only resolves after the expected timeout. 179 const delta = time.Nanosecond 180 dadConfig1Duration := time.Duration(dadConfigs1.DupAddrDetectTransmits) * dadConfigs1.RetransmitTimer 181 clock.Advance(dadConfig1Duration - delta) 182 select { 183 case r := <-ch: 184 t.Fatalf("unexpectedly got a DAD result before the expected timeout of %s; r = %#v", dadConfig1Duration, r) 185 default: 186 } 187 clock.Advance(delta) 188 for i := 0; i < 2; i++ { 189 if diff := cmp.Diff(dadResult{Addr: addr1, R: &stack.DADSucceeded{}}, <-ch); diff != "" { 190 t.Errorf("(i=%d) dad result mismatch (-want +got):\n%s", i, diff) 191 } 192 } 193 194 // Make sure DAD for addr2 only resolves after the expected timeout. 195 dadConfig2Duration := time.Duration(dadConfigs2.DupAddrDetectTransmits) * dadConfigs2.RetransmitTimer 196 clock.Advance(dadConfig2Duration - dadConfig1Duration - delta) 197 select { 198 case r := <-ch: 199 t.Fatalf("unexpectedly got a DAD result before the expected timeout of %s; r = %#v", dadConfig2Duration, r) 200 default: 201 } 202 clock.Advance(delta) 203 if diff := cmp.Diff(dadResult{Addr: addr2, R: &stack.DADSucceeded{}}, <-ch); diff != "" { 204 t.Errorf("dad result mismatch (-want +got):\n%s", diff) 205 } 206 207 // Should be able to restart DAD for addr2 after it resolved. 208 if res := dad.checkDuplicateAddress(addr2, handler(ch, addr2)); res != stack.DADStarting { 209 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr2, res, stack.DADStarting) 210 } 211 clock.RunImmediatelyScheduledJobs() 212 if diff := dad.check([]tcpip.Address{addr2, addr2}); diff != "" { 213 t.Errorf("dad check mismatch (-want +got):\n%s", diff) 214 } 215 clock.Advance(dadConfig2Duration) 216 if diff := cmp.Diff(dadResult{Addr: addr2, R: &stack.DADSucceeded{}}, <-ch); diff != "" { 217 t.Errorf("dad result mismatch (-want +got):\n%s", diff) 218 } 219 220 // Should not have anymore results. 221 select { 222 case r := <-ch: 223 t.Fatalf("unexpectedly got an extra DAD result; r = %#v", r) 224 default: 225 } 226 } 227 228 func TestDADStop(t *testing.T) { 229 var dad mockDADProtocol 230 clock := faketime.NewManualClock() 231 dadConfigs := stack.DADConfigurations{ 232 DupAddrDetectTransmits: 1, 233 RetransmitTimer: time.Second, 234 } 235 dad.init(t, dadConfigs, ip.DADOptions{ 236 Clock: clock, 237 }) 238 239 ch := make(chan dadResult, 1) 240 241 if res := dad.checkDuplicateAddress(addr1, handler(ch, addr1)); res != stack.DADStarting { 242 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr1, res, stack.DADStarting) 243 } 244 if res := dad.checkDuplicateAddress(addr2, handler(ch, addr2)); res != stack.DADStarting { 245 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr2, res, stack.DADStarting) 246 } 247 if res := dad.checkDuplicateAddress(addr3, handler(ch, addr3)); res != stack.DADStarting { 248 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr2, res, stack.DADStarting) 249 } 250 clock.RunImmediatelyScheduledJobs() 251 if diff := dad.check([]tcpip.Address{addr1, addr2, addr3}); diff != "" { 252 t.Errorf("dad check mismatch (-want +got):\n%s", diff) 253 } 254 255 dad.stop(addr1, &stack.DADAborted{}) 256 if diff := cmp.Diff(dadResult{Addr: addr1, R: &stack.DADAborted{}}, <-ch); diff != "" { 257 t.Errorf("dad result mismatch (-want +got):\n%s", diff) 258 } 259 260 dad.stop(addr2, &stack.DADDupAddrDetected{}) 261 if diff := cmp.Diff(dadResult{Addr: addr2, R: &stack.DADDupAddrDetected{}}, <-ch); diff != "" { 262 t.Errorf("dad result mismatch (-want +got):\n%s", diff) 263 } 264 265 dadResolutionDuration := time.Duration(dadConfigs.DupAddrDetectTransmits) * dadConfigs.RetransmitTimer 266 clock.Advance(dadResolutionDuration) 267 if diff := cmp.Diff(dadResult{Addr: addr3, R: &stack.DADSucceeded{}}, <-ch); diff != "" { 268 t.Errorf("dad result mismatch (-want +got):\n%s", diff) 269 } 270 271 // Should be able to restart DAD for an address we stopped DAD on. 272 if res := dad.checkDuplicateAddress(addr1, handler(ch, addr1)); res != stack.DADStarting { 273 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr1, res, stack.DADStarting) 274 } 275 clock.RunImmediatelyScheduledJobs() 276 if diff := dad.check([]tcpip.Address{addr1}); diff != "" { 277 t.Errorf("dad check mismatch (-want +got):\n%s", diff) 278 } 279 clock.Advance(dadResolutionDuration) 280 if diff := cmp.Diff(dadResult{Addr: addr1, R: &stack.DADSucceeded{}}, <-ch); diff != "" { 281 t.Errorf("dad result mismatch (-want +got):\n%s", diff) 282 } 283 284 // Should not have anymore updates. 285 select { 286 case r := <-ch: 287 t.Fatalf("unexpectedly got an extra DAD result; r = %#v", r) 288 default: 289 } 290 } 291 292 func TestNonce(t *testing.T) { 293 const ( 294 nonceSize = 2 295 296 extendRequestAttempts = 2 297 298 dupAddrDetectTransmits = 2 299 extendTransmits = 5 300 ) 301 302 var secureRNGBytes [nonceSize * (dupAddrDetectTransmits + extendTransmits)]byte 303 for i := range secureRNGBytes { 304 secureRNGBytes[i] = byte(i) 305 } 306 307 tests := []struct { 308 name string 309 mockedReceivedNonce []byte 310 expectedResults [extendRequestAttempts]ip.ExtendIfNonceEqualLockedDisposition 311 expectedTransmits int 312 }{ 313 { 314 name: "not matching", 315 mockedReceivedNonce: []byte{0, 0}, 316 expectedResults: [extendRequestAttempts]ip.ExtendIfNonceEqualLockedDisposition{ip.NonceNotEqual, ip.NonceNotEqual}, 317 expectedTransmits: dupAddrDetectTransmits, 318 }, 319 { 320 name: "matching nonce", 321 mockedReceivedNonce: secureRNGBytes[:nonceSize], 322 expectedResults: [extendRequestAttempts]ip.ExtendIfNonceEqualLockedDisposition{ip.Extended, ip.AlreadyExtended}, 323 expectedTransmits: dupAddrDetectTransmits + extendTransmits, 324 }, 325 } 326 327 for _, test := range tests { 328 t.Run(test.name, func(t *testing.T) { 329 var dad mockDADProtocol 330 clock := faketime.NewManualClock() 331 dadConfigs := stack.DADConfigurations{ 332 DupAddrDetectTransmits: dupAddrDetectTransmits, 333 RetransmitTimer: time.Second, 334 } 335 336 var secureRNG bytes.Reader 337 secureRNG.Reset(secureRNGBytes[:]) 338 dad.init(t, dadConfigs, ip.DADOptions{ 339 Clock: clock, 340 SecureRNG: &secureRNG, 341 NonceSize: nonceSize, 342 ExtendDADTransmits: extendTransmits, 343 }) 344 345 ch := make(chan dadResult, 1) 346 if res := dad.checkDuplicateAddress(addr1, handler(ch, addr1)); res != stack.DADStarting { 347 t.Errorf("got dad.checkDuplicateAddress(%s, _) = %d, want = %d", addr1, res, stack.DADStarting) 348 } 349 350 clock.RunImmediatelyScheduledJobs() 351 for i, want := range test.expectedResults { 352 if got := dad.extendIfNonceEqual(addr1, test.mockedReceivedNonce); got != want { 353 t.Errorf("(i=%d) got dad.extendIfNonceEqual(%s, _) = %d, want = %d", i, addr1, got, want) 354 } 355 } 356 357 for i := 0; i < test.expectedTransmits; i++ { 358 if diff := dad.checkWithNonce(map[tcpip.Address][][]byte{ 359 addr1: { 360 secureRNGBytes[nonceSize*i:][:nonceSize], 361 }, 362 }); diff != "" { 363 t.Errorf("(i=%d) dad check mismatch (-want +got):\n%s", i, diff) 364 } 365 366 clock.Advance(dadConfigs.RetransmitTimer) 367 } 368 369 if diff := cmp.Diff(dadResult{Addr: addr1, R: &stack.DADSucceeded{}}, <-ch); diff != "" { 370 t.Errorf("dad result mismatch (-want +got):\n%s", diff) 371 } 372 373 // Should not have anymore updates. 374 select { 375 case r := <-ch: 376 t.Fatalf("unexpectedly got an extra DAD result; r = %#v", r) 377 default: 378 } 379 }) 380 } 381 }