github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/network/ipv6/ndp_test.go (about) 1 // Copyright 2019 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 ipv6 16 17 import ( 18 "bytes" 19 "math/rand" 20 "strings" 21 "testing" 22 "time" 23 24 "github.com/google/go-cmp/cmp" 25 "github.com/SagerNet/gvisor/pkg/tcpip" 26 "github.com/SagerNet/gvisor/pkg/tcpip/buffer" 27 "github.com/SagerNet/gvisor/pkg/tcpip/checker" 28 "github.com/SagerNet/gvisor/pkg/tcpip/faketime" 29 "github.com/SagerNet/gvisor/pkg/tcpip/header" 30 "github.com/SagerNet/gvisor/pkg/tcpip/link/channel" 31 "github.com/SagerNet/gvisor/pkg/tcpip/stack" 32 "github.com/SagerNet/gvisor/pkg/tcpip/transport/icmp" 33 ) 34 35 var _ NDPDispatcher = (*testNDPDispatcher)(nil) 36 37 // testNDPDispatcher is an NDPDispatcher only allows default router discovery. 38 type testNDPDispatcher struct { 39 addr tcpip.Address 40 } 41 42 func (*testNDPDispatcher) OnDuplicateAddressDetectionResult(tcpip.NICID, tcpip.Address, stack.DADResult) { 43 } 44 45 func (t *testNDPDispatcher) OnOffLinkRouteUpdated(_ tcpip.NICID, _ tcpip.Subnet, addr tcpip.Address, _ header.NDPRoutePreference) { 46 t.addr = addr 47 } 48 49 func (t *testNDPDispatcher) OnOffLinkRouteInvalidated(_ tcpip.NICID, _ tcpip.Subnet, addr tcpip.Address) { 50 t.addr = addr 51 } 52 53 func (*testNDPDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) { 54 } 55 56 func (*testNDPDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) { 57 } 58 59 func (*testNDPDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) { 60 } 61 62 func (*testNDPDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) { 63 } 64 65 func (*testNDPDispatcher) OnAutoGenAddressInvalidated(tcpip.NICID, tcpip.AddressWithPrefix) { 66 } 67 68 func (*testNDPDispatcher) OnRecursiveDNSServerOption(tcpip.NICID, []tcpip.Address, time.Duration) { 69 } 70 71 func (*testNDPDispatcher) OnDNSSearchListOption(tcpip.NICID, []string, time.Duration) { 72 } 73 74 func (*testNDPDispatcher) OnDHCPv6Configuration(tcpip.NICID, DHCPv6ConfigurationFromNDPRA) { 75 } 76 77 func TestStackNDPEndpointInvalidateDefaultRouter(t *testing.T) { 78 var ndpDisp testNDPDispatcher 79 s := stack.New(stack.Options{ 80 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocolWithOptions(Options{ 81 NDPDisp: &ndpDisp, 82 })}, 83 }) 84 85 if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil { 86 t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err) 87 } 88 89 ep, err := s.GetNetworkEndpoint(nicID, ProtocolNumber) 90 if err != nil { 91 t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID, ProtocolNumber, err) 92 } 93 94 ipv6EP := ep.(*endpoint) 95 ipv6EP.mu.Lock() 96 ipv6EP.mu.ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: header.IPv6EmptySubnet, router: lladdr1}, time.Hour, header.MediumRoutePreference) 97 ipv6EP.mu.Unlock() 98 99 if ndpDisp.addr != lladdr1 { 100 t.Fatalf("got ndpDisp.addr = %s, want = %s", ndpDisp.addr, lladdr1) 101 } 102 103 ndpDisp.addr = "" 104 ndpEP := ep.(stack.NDPEndpoint) 105 ndpEP.InvalidateDefaultRouter(lladdr1) 106 if ndpDisp.addr != lladdr1 { 107 t.Fatalf("got ndpDisp.addr = %s, want = %s", ndpDisp.addr, lladdr1) 108 } 109 } 110 111 // TestNeighborSolicitationWithSourceLinkLayerOption tests that receiving a 112 // valid NDP NS message with the Source Link Layer Address option results in a 113 // new entry in the link address cache for the sender of the message. 114 func TestNeighborSolicitationWithSourceLinkLayerOption(t *testing.T) { 115 const nicID = 1 116 117 tests := []struct { 118 name string 119 optsBuf []byte 120 expectedLinkAddr tcpip.LinkAddress 121 }{ 122 { 123 name: "Valid", 124 optsBuf: []byte{1, 1, 2, 3, 4, 5, 6, 7}, 125 expectedLinkAddr: "\x02\x03\x04\x05\x06\x07", 126 }, 127 { 128 name: "Too Small", 129 optsBuf: []byte{1, 1, 2, 3, 4, 5, 6}, 130 }, 131 { 132 name: "Invalid Length", 133 optsBuf: []byte{1, 2, 2, 3, 4, 5, 6, 7}, 134 }, 135 } 136 137 for _, test := range tests { 138 t.Run(test.name, func(t *testing.T) { 139 s := stack.New(stack.Options{ 140 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol}, 141 }) 142 e := channel.New(0, 1280, linkAddr0) 143 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 144 if err := s.CreateNIC(nicID, e); err != nil { 145 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 146 } 147 if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil { 148 t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err) 149 } 150 151 ndpNSSize := header.ICMPv6NeighborSolicitMinimumSize + len(test.optsBuf) 152 hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNSSize) 153 pkt := header.ICMPv6(hdr.Prepend(ndpNSSize)) 154 pkt.SetType(header.ICMPv6NeighborSolicit) 155 ns := header.NDPNeighborSolicit(pkt.MessageBody()) 156 ns.SetTargetAddress(lladdr0) 157 opts := ns.Options() 158 copy(opts, test.optsBuf) 159 pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 160 Header: pkt, 161 Src: lladdr1, 162 Dst: lladdr0, 163 })) 164 payloadLength := hdr.UsedLength() 165 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 166 ip.Encode(&header.IPv6Fields{ 167 PayloadLength: uint16(payloadLength), 168 TransportProtocol: header.ICMPv6ProtocolNumber, 169 HopLimit: 255, 170 SrcAddr: lladdr1, 171 DstAddr: lladdr0, 172 }) 173 174 invalid := s.Stats().ICMP.V6.PacketsReceived.Invalid 175 176 // Invalid count should initially be 0. 177 if got := invalid.Value(); got != 0 { 178 t.Fatalf("got invalid = %d, want = 0", got) 179 } 180 181 e.InjectInbound(ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ 182 Data: hdr.View().ToVectorisedView(), 183 })) 184 185 neighbors, err := s.Neighbors(nicID, ProtocolNumber) 186 if err != nil { 187 t.Fatalf("s.Neighbors(%d, %d): %s", nicID, ProtocolNumber, err) 188 } 189 190 neighborByAddr := make(map[tcpip.Address]stack.NeighborEntry) 191 for _, n := range neighbors { 192 if existing, ok := neighborByAddr[n.Addr]; ok { 193 if diff := cmp.Diff(existing, n); diff != "" { 194 t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry (-existing +got):\n%s", nicID, ProtocolNumber, diff) 195 } 196 t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry: %#v", nicID, ProtocolNumber, existing) 197 } 198 neighborByAddr[n.Addr] = n 199 } 200 201 if neigh, ok := neighborByAddr[lladdr1]; len(test.expectedLinkAddr) != 0 { 202 // Invalid count should not have increased. 203 if got := invalid.Value(); got != 0 { 204 t.Errorf("got invalid = %d, want = 0", got) 205 } 206 207 if !ok { 208 t.Fatalf("expected a neighbor entry for %q", lladdr1) 209 } 210 if neigh.LinkAddr != test.expectedLinkAddr { 211 t.Errorf("got link address = %s, want = %s", neigh.LinkAddr, test.expectedLinkAddr) 212 } 213 if neigh.State != stack.Stale { 214 t.Errorf("got NUD state = %s, want = %s", neigh.State, stack.Stale) 215 } 216 } else { 217 // Invalid count should have increased. 218 if got := invalid.Value(); got != 1 { 219 t.Errorf("got invalid = %d, want = 1", got) 220 } 221 222 if ok { 223 t.Fatalf("unexpectedly got neighbor entry: %#v", neigh) 224 } 225 } 226 }) 227 } 228 } 229 230 func TestNeighborSolicitationResponse(t *testing.T) { 231 const nicID = 1 232 nicAddr := lladdr0 233 remoteAddr := lladdr1 234 nicAddrSNMC := header.SolicitedNodeAddr(nicAddr) 235 nicLinkAddr := linkAddr0 236 remoteLinkAddr0 := linkAddr1 237 remoteLinkAddr1 := linkAddr2 238 239 tests := []struct { 240 name string 241 nsOpts header.NDPOptionsSerializer 242 nsSrcLinkAddr tcpip.LinkAddress 243 nsSrc tcpip.Address 244 nsDst tcpip.Address 245 nsInvalid bool 246 naDstLinkAddr tcpip.LinkAddress 247 naSolicited bool 248 naSrc tcpip.Address 249 naDst tcpip.Address 250 performsLinkResolution bool 251 }{ 252 { 253 name: "Unspecified source to solicited-node multicast destination", 254 nsOpts: nil, 255 nsSrcLinkAddr: remoteLinkAddr0, 256 nsSrc: header.IPv6Any, 257 nsDst: nicAddrSNMC, 258 nsInvalid: false, 259 naDstLinkAddr: header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllNodesMulticastAddress), 260 naSolicited: false, 261 naSrc: nicAddr, 262 naDst: header.IPv6AllNodesMulticastAddress, 263 }, 264 { 265 name: "Unspecified source with source ll option to multicast destination", 266 nsOpts: header.NDPOptionsSerializer{ 267 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]), 268 }, 269 nsSrcLinkAddr: remoteLinkAddr0, 270 nsSrc: header.IPv6Any, 271 nsDst: nicAddrSNMC, 272 nsInvalid: true, 273 }, 274 { 275 name: "Unspecified source to unicast destination", 276 nsOpts: nil, 277 nsSrcLinkAddr: remoteLinkAddr0, 278 nsSrc: header.IPv6Any, 279 nsDst: nicAddr, 280 nsInvalid: true, 281 }, 282 { 283 name: "Unspecified source with source ll option to unicast destination", 284 nsOpts: header.NDPOptionsSerializer{ 285 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]), 286 }, 287 nsSrcLinkAddr: remoteLinkAddr0, 288 nsSrc: header.IPv6Any, 289 nsDst: nicAddr, 290 nsInvalid: true, 291 }, 292 { 293 name: "Specified source with 1 source ll to multicast destination", 294 nsOpts: header.NDPOptionsSerializer{ 295 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]), 296 }, 297 nsSrcLinkAddr: remoteLinkAddr0, 298 nsSrc: remoteAddr, 299 nsDst: nicAddrSNMC, 300 nsInvalid: false, 301 naDstLinkAddr: remoteLinkAddr0, 302 naSolicited: true, 303 naSrc: nicAddr, 304 naDst: remoteAddr, 305 }, 306 { 307 name: "Specified source with 1 source ll different from route to multicast destination", 308 nsOpts: header.NDPOptionsSerializer{ 309 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]), 310 }, 311 nsSrcLinkAddr: remoteLinkAddr0, 312 nsSrc: remoteAddr, 313 nsDst: nicAddrSNMC, 314 nsInvalid: false, 315 naDstLinkAddr: remoteLinkAddr1, 316 naSolicited: true, 317 naSrc: nicAddr, 318 naDst: remoteAddr, 319 }, 320 { 321 name: "Specified source to multicast destination", 322 nsOpts: nil, 323 nsSrcLinkAddr: remoteLinkAddr0, 324 nsSrc: remoteAddr, 325 nsDst: nicAddrSNMC, 326 nsInvalid: true, 327 }, 328 { 329 name: "Specified source with 2 source ll to multicast destination", 330 nsOpts: header.NDPOptionsSerializer{ 331 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]), 332 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]), 333 }, 334 nsSrcLinkAddr: remoteLinkAddr0, 335 nsSrc: remoteAddr, 336 nsDst: nicAddrSNMC, 337 nsInvalid: true, 338 }, 339 340 { 341 name: "Specified source to unicast destination", 342 nsOpts: nil, 343 nsSrcLinkAddr: remoteLinkAddr0, 344 nsSrc: remoteAddr, 345 nsDst: nicAddr, 346 nsInvalid: false, 347 naDstLinkAddr: remoteLinkAddr0, 348 naSolicited: true, 349 naSrc: nicAddr, 350 naDst: remoteAddr, 351 // Since we send a unicast solicitations to a node without an entry for 352 // the remote, the node needs to perform neighbor discovery to get the 353 // remote's link address to send the advertisement response. 354 performsLinkResolution: true, 355 }, 356 { 357 name: "Specified source with 1 source ll to unicast destination", 358 nsOpts: header.NDPOptionsSerializer{ 359 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]), 360 }, 361 nsSrcLinkAddr: remoteLinkAddr0, 362 nsSrc: remoteAddr, 363 nsDst: nicAddr, 364 nsInvalid: false, 365 naDstLinkAddr: remoteLinkAddr0, 366 naSolicited: true, 367 naSrc: nicAddr, 368 naDst: remoteAddr, 369 }, 370 { 371 name: "Specified source with 1 source ll different from route to unicast destination", 372 nsOpts: header.NDPOptionsSerializer{ 373 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]), 374 }, 375 nsSrcLinkAddr: remoteLinkAddr0, 376 nsSrc: remoteAddr, 377 nsDst: nicAddr, 378 nsInvalid: false, 379 naDstLinkAddr: remoteLinkAddr1, 380 naSolicited: true, 381 naSrc: nicAddr, 382 naDst: remoteAddr, 383 }, 384 { 385 name: "Specified source with 2 source ll to unicast destination", 386 nsOpts: header.NDPOptionsSerializer{ 387 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]), 388 header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]), 389 }, 390 nsSrcLinkAddr: remoteLinkAddr0, 391 nsSrc: remoteAddr, 392 nsDst: nicAddr, 393 nsInvalid: true, 394 }, 395 } 396 397 for _, test := range tests { 398 t.Run(test.name, func(t *testing.T) { 399 clock := faketime.NewManualClock() 400 s := stack.New(stack.Options{ 401 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol}, 402 Clock: clock, 403 }) 404 e := channel.New(1, 1280, nicLinkAddr) 405 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 406 if err := s.CreateNIC(nicID, e); err != nil { 407 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 408 } 409 if err := s.AddAddress(nicID, ProtocolNumber, nicAddr); err != nil { 410 t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, nicAddr, err) 411 } 412 413 s.SetRouteTable([]tcpip.Route{ 414 { 415 Destination: header.IPv6EmptySubnet, 416 NIC: 1, 417 }, 418 }) 419 420 ndpNSSize := header.ICMPv6NeighborSolicitMinimumSize + test.nsOpts.Length() 421 hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNSSize) 422 pkt := header.ICMPv6(hdr.Prepend(ndpNSSize)) 423 pkt.SetType(header.ICMPv6NeighborSolicit) 424 ns := header.NDPNeighborSolicit(pkt.MessageBody()) 425 ns.SetTargetAddress(nicAddr) 426 opts := ns.Options() 427 opts.Serialize(test.nsOpts) 428 pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 429 Header: pkt, 430 Src: test.nsSrc, 431 Dst: test.nsDst, 432 })) 433 payloadLength := hdr.UsedLength() 434 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 435 ip.Encode(&header.IPv6Fields{ 436 PayloadLength: uint16(payloadLength), 437 TransportProtocol: header.ICMPv6ProtocolNumber, 438 HopLimit: 255, 439 SrcAddr: test.nsSrc, 440 DstAddr: test.nsDst, 441 }) 442 443 invalid := s.Stats().ICMP.V6.PacketsReceived.Invalid 444 445 // Invalid count should initially be 0. 446 if got := invalid.Value(); got != 0 { 447 t.Fatalf("got invalid = %d, want = 0", got) 448 } 449 450 e.InjectLinkAddr(ProtocolNumber, test.nsSrcLinkAddr, stack.NewPacketBuffer(stack.PacketBufferOptions{ 451 Data: hdr.View().ToVectorisedView(), 452 })) 453 454 if test.nsInvalid { 455 if got := invalid.Value(); got != 1 { 456 t.Fatalf("got invalid = %d, want = 1", got) 457 } 458 459 if p, got := e.Read(); got { 460 t.Fatalf("unexpected response to an invalid NS = %+v", p.Pkt) 461 } 462 463 // If we expected the NS to be invalid, we have nothing else to check. 464 return 465 } 466 467 if got := invalid.Value(); got != 0 { 468 t.Fatalf("got invalid = %d, want = 0", got) 469 } 470 471 if test.performsLinkResolution { 472 clock.RunImmediatelyScheduledJobs() 473 p, got := e.Read() 474 if !got { 475 t.Fatal("expected an NDP NS response") 476 } 477 478 respNSDst := header.SolicitedNodeAddr(test.nsSrc) 479 var want stack.RouteInfo 480 want.NetProto = ProtocolNumber 481 want.RemoteLinkAddress = header.EthernetAddressFromMulticastIPv6Address(respNSDst) 482 if diff := cmp.Diff(want, p.Route, cmp.AllowUnexported(want)); diff != "" { 483 t.Errorf("route info mismatch (-want +got):\n%s", diff) 484 } 485 486 checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()), 487 checker.SrcAddr(nicAddr), 488 checker.DstAddr(respNSDst), 489 checker.TTL(header.NDPHopLimit), 490 checker.NDPNS( 491 checker.NDPNSTargetAddress(test.nsSrc), 492 checker.NDPNSOptions([]header.NDPOption{ 493 header.NDPSourceLinkLayerAddressOption(nicLinkAddr), 494 }), 495 )) 496 497 ser := header.NDPOptionsSerializer{ 498 header.NDPTargetLinkLayerAddressOption(linkAddr1), 499 } 500 ndpNASize := header.ICMPv6NeighborAdvertMinimumSize + ser.Length() 501 hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNASize) 502 pkt := header.ICMPv6(hdr.Prepend(ndpNASize)) 503 pkt.SetType(header.ICMPv6NeighborAdvert) 504 na := header.NDPNeighborAdvert(pkt.MessageBody()) 505 na.SetSolicitedFlag(true) 506 na.SetOverrideFlag(true) 507 na.SetTargetAddress(test.nsSrc) 508 na.Options().Serialize(ser) 509 pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 510 Header: pkt, 511 Src: test.nsSrc, 512 Dst: nicAddr, 513 })) 514 payloadLength := hdr.UsedLength() 515 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 516 ip.Encode(&header.IPv6Fields{ 517 PayloadLength: uint16(payloadLength), 518 TransportProtocol: header.ICMPv6ProtocolNumber, 519 HopLimit: header.NDPHopLimit, 520 SrcAddr: test.nsSrc, 521 DstAddr: nicAddr, 522 }) 523 e.InjectLinkAddr(ProtocolNumber, "", stack.NewPacketBuffer(stack.PacketBufferOptions{ 524 Data: hdr.View().ToVectorisedView(), 525 })) 526 } 527 528 clock.RunImmediatelyScheduledJobs() 529 p, got := e.Read() 530 if !got { 531 t.Fatal("expected an NDP NA response") 532 } 533 534 if p.Route.LocalAddress != test.naSrc { 535 t.Errorf("got p.Route.LocalAddress = %s, want = %s", p.Route.LocalAddress, test.naSrc) 536 } 537 if p.Route.LocalLinkAddress != nicLinkAddr { 538 t.Errorf("p.Route.LocalLinkAddress = %s, want = %s", p.Route.LocalLinkAddress, nicLinkAddr) 539 } 540 if p.Route.RemoteAddress != test.naDst { 541 t.Errorf("got p.Route.RemoteAddress = %s, want = %s", p.Route.RemoteAddress, test.naDst) 542 } 543 if p.Route.RemoteLinkAddress != test.naDstLinkAddr { 544 t.Errorf("got p.Route.RemoteLinkAddress = %s, want = %s", p.Route.RemoteLinkAddress, test.naDstLinkAddr) 545 } 546 547 checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()), 548 checker.SrcAddr(test.naSrc), 549 checker.DstAddr(test.naDst), 550 checker.TTL(header.NDPHopLimit), 551 checker.NDPNA( 552 checker.NDPNASolicitedFlag(test.naSolicited), 553 checker.NDPNATargetAddress(nicAddr), 554 checker.NDPNAOptions([]header.NDPOption{ 555 header.NDPTargetLinkLayerAddressOption(nicLinkAddr[:]), 556 }), 557 )) 558 }) 559 } 560 } 561 562 // TestNeighborAdvertisementWithTargetLinkLayerOption tests that receiving a 563 // valid NDP NA message with the Target Link Layer Address option does not 564 // result in a new entry in the neighbor cache for the target of the message. 565 func TestNeighborAdvertisementWithTargetLinkLayerOption(t *testing.T) { 566 const nicID = 1 567 568 tests := []struct { 569 name string 570 optsBuf []byte 571 isValid bool 572 }{ 573 { 574 name: "Valid", 575 optsBuf: []byte{2, 1, 2, 3, 4, 5, 6, 7}, 576 isValid: true, 577 }, 578 { 579 name: "Too Small", 580 optsBuf: []byte{2, 1, 2, 3, 4, 5, 6}, 581 }, 582 { 583 name: "Invalid Length", 584 optsBuf: []byte{2, 2, 2, 3, 4, 5, 6, 7}, 585 }, 586 { 587 name: "Multiple", 588 optsBuf: []byte{ 589 2, 1, 2, 3, 4, 5, 6, 7, 590 2, 1, 2, 3, 4, 5, 6, 8, 591 }, 592 }, 593 } 594 595 for _, test := range tests { 596 t.Run(test.name, func(t *testing.T) { 597 s := stack.New(stack.Options{ 598 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol}, 599 }) 600 e := channel.New(0, 1280, linkAddr0) 601 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 602 if err := s.CreateNIC(nicID, e); err != nil { 603 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 604 } 605 if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil { 606 t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err) 607 } 608 609 ndpNASize := header.ICMPv6NeighborAdvertMinimumSize + len(test.optsBuf) 610 hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNASize) 611 pkt := header.ICMPv6(hdr.Prepend(ndpNASize)) 612 pkt.SetType(header.ICMPv6NeighborAdvert) 613 ns := header.NDPNeighborAdvert(pkt.MessageBody()) 614 ns.SetTargetAddress(lladdr1) 615 opts := ns.Options() 616 copy(opts, test.optsBuf) 617 pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 618 Header: pkt, 619 Src: lladdr1, 620 Dst: lladdr0, 621 })) 622 payloadLength := hdr.UsedLength() 623 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 624 ip.Encode(&header.IPv6Fields{ 625 PayloadLength: uint16(payloadLength), 626 TransportProtocol: header.ICMPv6ProtocolNumber, 627 HopLimit: 255, 628 SrcAddr: lladdr1, 629 DstAddr: lladdr0, 630 }) 631 632 invalid := s.Stats().ICMP.V6.PacketsReceived.Invalid 633 634 // Invalid count should initially be 0. 635 if got := invalid.Value(); got != 0 { 636 t.Fatalf("got invalid = %d, want = 0", got) 637 } 638 639 e.InjectInbound(ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ 640 Data: hdr.View().ToVectorisedView(), 641 })) 642 643 neighbors, err := s.Neighbors(nicID, ProtocolNumber) 644 if err != nil { 645 t.Fatalf("s.Neighbors(%d, %d): %s", nicID, ProtocolNumber, err) 646 } 647 648 neighborByAddr := make(map[tcpip.Address]stack.NeighborEntry) 649 for _, n := range neighbors { 650 if existing, ok := neighborByAddr[n.Addr]; ok { 651 if diff := cmp.Diff(existing, n); diff != "" { 652 t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry (-existing +got):\n%s", nicID, ProtocolNumber, diff) 653 } 654 t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry: %#v", nicID, ProtocolNumber, existing) 655 } 656 neighborByAddr[n.Addr] = n 657 } 658 659 if neigh, ok := neighborByAddr[lladdr1]; ok { 660 t.Fatalf("unexpectedly got neighbor entry: %#v", neigh) 661 } 662 663 if test.isValid { 664 // Invalid count should not have increased. 665 if got := invalid.Value(); got != 0 { 666 t.Errorf("got invalid = %d, want = 0", got) 667 } 668 } else { 669 // Invalid count should have increased. 670 if got := invalid.Value(); got != 1 { 671 t.Errorf("got invalid = %d, want = 1", got) 672 } 673 } 674 }) 675 } 676 } 677 678 func TestNDPValidation(t *testing.T) { 679 const nicID = 1 680 681 handleIPv6Payload := func(payload buffer.View, hopLimit uint8, atomicFragment bool, ep stack.NetworkEndpoint) { 682 var extHdrs header.IPv6ExtHdrSerializer 683 if atomicFragment { 684 extHdrs = append(extHdrs, &header.IPv6SerializableFragmentExtHdr{}) 685 } 686 extHdrsLen := extHdrs.Length() 687 688 ip := buffer.NewView(header.IPv6MinimumSize + extHdrsLen) 689 header.IPv6(ip).Encode(&header.IPv6Fields{ 690 PayloadLength: uint16(len(payload) + extHdrsLen), 691 TransportProtocol: header.ICMPv6ProtocolNumber, 692 HopLimit: hopLimit, 693 SrcAddr: lladdr1, 694 DstAddr: lladdr0, 695 ExtensionHeaders: extHdrs, 696 }) 697 vv := ip.ToVectorisedView() 698 vv.AppendView(payload) 699 ep.HandlePacket(stack.NewPacketBuffer(stack.PacketBufferOptions{ 700 Data: vv, 701 })) 702 } 703 704 var tllData [header.NDPLinkLayerAddressSize]byte 705 header.NDPOptions(tllData[:]).Serialize(header.NDPOptionsSerializer{ 706 header.NDPTargetLinkLayerAddressOption(linkAddr1), 707 }) 708 709 var sllData [header.NDPLinkLayerAddressSize]byte 710 header.NDPOptions(sllData[:]).Serialize(header.NDPOptionsSerializer{ 711 header.NDPSourceLinkLayerAddressOption(linkAddr1), 712 }) 713 714 types := []struct { 715 name string 716 typ header.ICMPv6Type 717 size int 718 extraData []byte 719 statCounter func(tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter 720 routerOnly bool 721 }{ 722 { 723 name: "RouterSolicit", 724 typ: header.ICMPv6RouterSolicit, 725 size: header.ICMPv6MinimumSize, 726 statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter { 727 return stats.RouterSolicit 728 }, 729 routerOnly: true, 730 }, 731 { 732 name: "RouterAdvert", 733 typ: header.ICMPv6RouterAdvert, 734 size: header.ICMPv6HeaderSize + header.NDPRAMinimumSize, 735 statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter { 736 return stats.RouterAdvert 737 }, 738 }, 739 { 740 name: "NeighborSolicit", 741 typ: header.ICMPv6NeighborSolicit, 742 size: header.ICMPv6NeighborSolicitMinimumSize, 743 extraData: sllData[:], 744 statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter { 745 return stats.NeighborSolicit 746 }, 747 }, 748 { 749 name: "NeighborAdvert", 750 typ: header.ICMPv6NeighborAdvert, 751 size: header.ICMPv6NeighborAdvertMinimumSize, 752 extraData: tllData[:], 753 statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter { 754 return stats.NeighborAdvert 755 }, 756 }, 757 { 758 name: "RedirectMsg", 759 typ: header.ICMPv6RedirectMsg, 760 size: header.ICMPv6MinimumSize, 761 statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter { 762 return stats.RedirectMsg 763 }, 764 }, 765 } 766 767 subTests := []struct { 768 name string 769 atomicFragment bool 770 hopLimit uint8 771 code header.ICMPv6Code 772 valid bool 773 }{ 774 { 775 name: "Valid", 776 atomicFragment: false, 777 hopLimit: header.NDPHopLimit, 778 code: 0, 779 valid: true, 780 }, 781 { 782 name: "Fragmented", 783 atomicFragment: true, 784 hopLimit: header.NDPHopLimit, 785 code: 0, 786 valid: false, 787 }, 788 { 789 name: "Invalid hop limit", 790 atomicFragment: false, 791 hopLimit: header.NDPHopLimit - 1, 792 code: 0, 793 valid: false, 794 }, 795 { 796 name: "Invalid ICMPv6 code", 797 atomicFragment: false, 798 hopLimit: header.NDPHopLimit, 799 code: 1, 800 valid: false, 801 }, 802 } 803 804 subnet, err := tcpip.NewSubnet(lladdr1, tcpip.AddressMask(strings.Repeat("\xff", len(lladdr0)))) 805 if err != nil { 806 t.Fatal(err) 807 } 808 809 for _, typ := range types { 810 for _, isRouter := range []bool{false, true} { 811 name := typ.name 812 if isRouter { 813 name += " (Router)" 814 } 815 816 t.Run(name, func(t *testing.T) { 817 for _, test := range subTests { 818 t.Run(test.name, func(t *testing.T) { 819 s := stack.New(stack.Options{ 820 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol}, 821 TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6}, 822 }) 823 824 if isRouter { 825 if err := s.SetForwardingDefaultAndAllNICs(ProtocolNumber, true); err != nil { 826 t.Fatalf("SetForwardingDefaultAndAllNICs(%d, true): %s", ProtocolNumber, err) 827 } 828 } 829 830 if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil { 831 t.Fatalf("CreateNIC(%d, _): %s", nicID, err) 832 } 833 834 if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil { 835 t.Fatalf("AddAddress(%d, %d, %s): %s", nicID, ProtocolNumber, lladdr0, err) 836 } 837 838 ep, err := s.GetNetworkEndpoint(nicID, ProtocolNumber) 839 if err != nil { 840 t.Fatal("cannot find network endpoint instance for IPv6") 841 } 842 843 s.SetRouteTable([]tcpip.Route{{ 844 Destination: subnet, 845 NIC: nicID, 846 }}) 847 848 stats := s.Stats().ICMP.V6.PacketsReceived 849 invalid := stats.Invalid 850 routerOnly := stats.RouterOnlyPacketsDroppedByHost 851 typStat := typ.statCounter(stats) 852 853 icmpH := header.ICMPv6(buffer.NewView(typ.size + len(typ.extraData))) 854 copy(icmpH[typ.size:], typ.extraData) 855 icmpH.SetType(typ.typ) 856 icmpH.SetCode(test.code) 857 icmpH.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 858 Header: icmpH[:typ.size], 859 Src: lladdr0, 860 Dst: lladdr1, 861 PayloadCsum: header.Checksum(typ.extraData /* initial */, 0), 862 PayloadLen: len(typ.extraData), 863 })) 864 865 // Rx count of the NDP message should initially be 0. 866 if got := typStat.Value(); got != 0 { 867 t.Errorf("got %s = %d, want = 0", typ.name, got) 868 } 869 870 // Invalid count should initially be 0. 871 if got := invalid.Value(); got != 0 { 872 t.Errorf("got invalid.Value() = %d, want = 0", got) 873 } 874 875 // Should initially not have dropped any packets. 876 if got := routerOnly.Value(); got != 0 { 877 t.Errorf("got routerOnly.Value() = %d, want = 0", got) 878 } 879 880 if t.Failed() { 881 t.FailNow() 882 } 883 884 handleIPv6Payload(buffer.View(icmpH), test.hopLimit, test.atomicFragment, ep) 885 886 // Rx count of the NDP packet should have increased. 887 if got := typStat.Value(); got != 1 { 888 t.Errorf("got %s = %d, want = 1", typ.name, got) 889 } 890 891 want := uint64(0) 892 if !test.valid { 893 // Invalid count should have increased. 894 want = 1 895 } 896 if got := invalid.Value(); got != want { 897 t.Errorf("got invalid.Value() = %d, want = %d", got, want) 898 } 899 900 want = 0 901 if test.valid && !isRouter && typ.routerOnly { 902 // Router only packets are expected to be dropped when operating 903 // as a host. 904 want = 1 905 } 906 if got := routerOnly.Value(); got != want { 907 t.Errorf("got routerOnly.Value() = %d, want = %d", got, want) 908 } 909 }) 910 } 911 }) 912 } 913 } 914 } 915 916 // TestNeighborAdvertisementValidation tests that the NIC validates received 917 // Neighbor Advertisements. 918 // 919 // In particular, if the IP Destination Address is a multicast address, and the 920 // Solicited flag is not zero, the Neighbor Advertisement is invalid and should 921 // be discarded. 922 func TestNeighborAdvertisementValidation(t *testing.T) { 923 tests := []struct { 924 name string 925 ipDstAddr tcpip.Address 926 solicitedFlag bool 927 valid bool 928 }{ 929 { 930 name: "Multicast IP destination address with Solicited flag set", 931 ipDstAddr: header.IPv6AllNodesMulticastAddress, 932 solicitedFlag: true, 933 valid: false, 934 }, 935 { 936 name: "Multicast IP destination address with Solicited flag unset", 937 ipDstAddr: header.IPv6AllNodesMulticastAddress, 938 solicitedFlag: false, 939 valid: true, 940 }, 941 { 942 name: "Unicast IP destination address with Solicited flag set", 943 ipDstAddr: lladdr0, 944 solicitedFlag: true, 945 valid: true, 946 }, 947 { 948 name: "Unicast IP destination address with Solicited flag unset", 949 ipDstAddr: lladdr0, 950 solicitedFlag: false, 951 valid: true, 952 }, 953 } 954 955 for _, test := range tests { 956 t.Run(test.name, func(t *testing.T) { 957 s := stack.New(stack.Options{ 958 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol}, 959 }) 960 e := channel.New(0, header.IPv6MinimumMTU, linkAddr0) 961 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 962 if err := s.CreateNIC(nicID, e); err != nil { 963 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 964 } 965 if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil { 966 t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err) 967 } 968 969 ndpNASize := header.ICMPv6NeighborAdvertMinimumSize 970 hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNASize) 971 pkt := header.ICMPv6(hdr.Prepend(ndpNASize)) 972 pkt.SetType(header.ICMPv6NeighborAdvert) 973 na := header.NDPNeighborAdvert(pkt.MessageBody()) 974 na.SetTargetAddress(lladdr1) 975 na.SetSolicitedFlag(test.solicitedFlag) 976 pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 977 Header: pkt, 978 Src: lladdr1, 979 Dst: test.ipDstAddr, 980 })) 981 payloadLength := hdr.UsedLength() 982 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 983 ip.Encode(&header.IPv6Fields{ 984 PayloadLength: uint16(payloadLength), 985 TransportProtocol: header.ICMPv6ProtocolNumber, 986 HopLimit: 255, 987 SrcAddr: lladdr1, 988 DstAddr: test.ipDstAddr, 989 }) 990 991 stats := s.Stats().ICMP.V6.PacketsReceived 992 invalid := stats.Invalid 993 rxNA := stats.NeighborAdvert 994 995 if got := rxNA.Value(); got != 0 { 996 t.Fatalf("got rxNA = %d, want = 0", got) 997 } 998 if got := invalid.Value(); got != 0 { 999 t.Fatalf("got invalid = %d, want = 0", got) 1000 } 1001 1002 e.InjectInbound(header.IPv6ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ 1003 Data: hdr.View().ToVectorisedView(), 1004 })) 1005 1006 if got := rxNA.Value(); got != 1 { 1007 t.Fatalf("got rxNA = %d, want = 1", got) 1008 } 1009 var wantInvalid uint64 = 1 1010 if test.valid { 1011 wantInvalid = 0 1012 } 1013 if got := invalid.Value(); got != wantInvalid { 1014 t.Fatalf("got invalid = %d, want = %d", got, wantInvalid) 1015 } 1016 // As per RFC 4861 section 7.2.5: 1017 // When a valid Neighbor Advertisement is received ... 1018 // If no entry exists, the advertisement SHOULD be silently discarded. 1019 // There is no need to create an entry if none exists, since the 1020 // recipient has apparently not initiated any communication with the 1021 // target. 1022 if neighbors, err := s.Neighbors(nicID, ProtocolNumber); err != nil { 1023 t.Fatalf("s.Neighbors(%d, %d): %s", nicID, ProtocolNumber, err) 1024 } else if len(neighbors) != 0 { 1025 t.Fatalf("got len(neighbors) = %d, want = 0; neighbors = %#v", len(neighbors), neighbors) 1026 } 1027 }) 1028 } 1029 } 1030 1031 // TestRouterAdvertValidation tests that when the NIC is configured to handle 1032 // NDP Router Advertisement packets, it validates the Router Advertisement 1033 // properly before handling them. 1034 func TestRouterAdvertValidation(t *testing.T) { 1035 tests := []struct { 1036 name string 1037 src tcpip.Address 1038 hopLimit uint8 1039 code header.ICMPv6Code 1040 ndpPayload []byte 1041 expectedSuccess bool 1042 }{ 1043 { 1044 "OK", 1045 lladdr0, 1046 255, 1047 0, 1048 []byte{ 1049 0, 0, 0, 0, 1050 0, 0, 0, 0, 1051 0, 0, 0, 0, 1052 }, 1053 true, 1054 }, 1055 { 1056 "NonLinkLocalSourceAddr", 1057 addr1, 1058 255, 1059 0, 1060 []byte{ 1061 0, 0, 0, 0, 1062 0, 0, 0, 0, 1063 0, 0, 0, 0, 1064 }, 1065 false, 1066 }, 1067 { 1068 "HopLimitNot255", 1069 lladdr0, 1070 254, 1071 0, 1072 []byte{ 1073 0, 0, 0, 0, 1074 0, 0, 0, 0, 1075 0, 0, 0, 0, 1076 }, 1077 false, 1078 }, 1079 { 1080 "NonZeroCode", 1081 lladdr0, 1082 255, 1083 1, 1084 []byte{ 1085 0, 0, 0, 0, 1086 0, 0, 0, 0, 1087 0, 0, 0, 0, 1088 }, 1089 false, 1090 }, 1091 { 1092 "NDPPayloadTooSmall", 1093 lladdr0, 1094 255, 1095 0, 1096 []byte{ 1097 0, 0, 0, 0, 1098 0, 0, 0, 0, 1099 0, 0, 0, 1100 }, 1101 false, 1102 }, 1103 { 1104 "OKWithOptions", 1105 lladdr0, 1106 255, 1107 0, 1108 []byte{ 1109 // RA payload 1110 0, 0, 0, 0, 1111 0, 0, 0, 0, 1112 0, 0, 0, 0, 1113 1114 // Option #1 (TargetLinkLayerAddress) 1115 2, 1, 0, 0, 0, 0, 0, 0, 1116 1117 // Option #2 (unrecognized) 1118 255, 1, 0, 0, 0, 0, 0, 0, 1119 1120 // Option #3 (PrefixInformation) 1121 3, 4, 0, 0, 0, 0, 0, 0, 1122 0, 0, 0, 0, 0, 0, 0, 0, 1123 0, 0, 0, 0, 0, 0, 0, 0, 1124 0, 0, 0, 0, 0, 0, 0, 0, 1125 }, 1126 true, 1127 }, 1128 { 1129 "OptionWithZeroLength", 1130 lladdr0, 1131 255, 1132 0, 1133 []byte{ 1134 // RA payload 1135 0, 0, 0, 0, 1136 0, 0, 0, 0, 1137 0, 0, 0, 0, 1138 1139 // Option #1 (TargetLinkLayerAddress) 1140 // Invalid as it has 0 length. 1141 2, 0, 0, 0, 0, 0, 0, 0, 1142 1143 // Option #2 (unrecognized) 1144 255, 1, 0, 0, 0, 0, 0, 0, 1145 1146 // Option #3 (PrefixInformation) 1147 3, 4, 0, 0, 0, 0, 0, 0, 1148 0, 0, 0, 0, 0, 0, 0, 0, 1149 0, 0, 0, 0, 0, 0, 0, 0, 1150 0, 0, 0, 0, 0, 0, 0, 0, 1151 }, 1152 false, 1153 }, 1154 } 1155 1156 for _, test := range tests { 1157 t.Run(test.name, func(t *testing.T) { 1158 e := channel.New(10, 1280, linkAddr1) 1159 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 1160 s := stack.New(stack.Options{ 1161 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol}, 1162 }) 1163 1164 if err := s.CreateNIC(1, e); err != nil { 1165 t.Fatalf("CreateNIC(_) = %s", err) 1166 } 1167 1168 icmpSize := header.ICMPv6HeaderSize + len(test.ndpPayload) 1169 hdr := buffer.NewPrependable(header.IPv6MinimumSize + icmpSize) 1170 pkt := header.ICMPv6(hdr.Prepend(icmpSize)) 1171 pkt.SetType(header.ICMPv6RouterAdvert) 1172 pkt.SetCode(test.code) 1173 copy(pkt.MessageBody(), test.ndpPayload) 1174 payloadLength := hdr.UsedLength() 1175 pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 1176 Header: pkt, 1177 Src: test.src, 1178 Dst: header.IPv6AllNodesMulticastAddress, 1179 })) 1180 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 1181 ip.Encode(&header.IPv6Fields{ 1182 PayloadLength: uint16(payloadLength), 1183 TransportProtocol: icmp.ProtocolNumber6, 1184 HopLimit: test.hopLimit, 1185 SrcAddr: test.src, 1186 DstAddr: header.IPv6AllNodesMulticastAddress, 1187 }) 1188 1189 stats := s.Stats().ICMP.V6.PacketsReceived 1190 invalid := stats.Invalid 1191 rxRA := stats.RouterAdvert 1192 1193 if got := invalid.Value(); got != 0 { 1194 t.Fatalf("got invalid = %d, want = 0", got) 1195 } 1196 if got := rxRA.Value(); got != 0 { 1197 t.Fatalf("got rxRA = %d, want = 0", got) 1198 } 1199 1200 e.InjectInbound(header.IPv6ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ 1201 Data: hdr.View().ToVectorisedView(), 1202 })) 1203 1204 if got := rxRA.Value(); got != 1 { 1205 t.Fatalf("got rxRA = %d, want = 1", got) 1206 } 1207 1208 if test.expectedSuccess { 1209 if got := invalid.Value(); got != 0 { 1210 t.Fatalf("got invalid = %d, want = 0", got) 1211 } 1212 } else { 1213 if got := invalid.Value(); got != 1 { 1214 t.Fatalf("got invalid = %d, want = 1", got) 1215 } 1216 } 1217 }) 1218 } 1219 } 1220 1221 // TestCheckDuplicateAddress checks that calls to CheckDuplicateAddress and DAD 1222 // performed when adding new addresses do not interfere with each other. 1223 func TestCheckDuplicateAddress(t *testing.T) { 1224 const nicID = 1 1225 1226 clock := faketime.NewManualClock() 1227 dadConfigs := stack.DADConfigurations{ 1228 DupAddrDetectTransmits: 1, 1229 RetransmitTimer: time.Second, 1230 } 1231 1232 nonces := [...][]byte{ 1233 {1, 2, 3, 4, 5, 6}, 1234 {7, 8, 9, 10, 11, 12}, 1235 } 1236 1237 var secureRNGBytes []byte 1238 for _, n := range nonces { 1239 secureRNGBytes = append(secureRNGBytes, n...) 1240 } 1241 var secureRNG bytes.Reader 1242 secureRNG.Reset(secureRNGBytes[:]) 1243 s := stack.New(stack.Options{ 1244 Clock: clock, 1245 RandSource: rand.NewSource(time.Now().UnixNano()), 1246 SecureRNG: &secureRNG, 1247 NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocolWithOptions(Options{ 1248 DADConfigs: dadConfigs, 1249 })}, 1250 }) 1251 // This test is expected to send at max 2 DAD messages. We allow an extra 1252 // packet to be stored to catch unexpected packets. 1253 e := channel.New(3, header.IPv6MinimumMTU, linkAddr0) 1254 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 1255 if err := s.CreateNIC(nicID, e); err != nil { 1256 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 1257 } 1258 1259 dadPacketsSent := 0 1260 snmc := header.SolicitedNodeAddr(lladdr0) 1261 remoteLinkAddr := header.EthernetAddressFromMulticastIPv6Address(snmc) 1262 checkDADMsg := func() { 1263 clock.RunImmediatelyScheduledJobs() 1264 p, ok := e.Read() 1265 if !ok { 1266 t.Fatalf("expected %d-th DAD message", dadPacketsSent) 1267 } 1268 1269 if p.Proto != header.IPv6ProtocolNumber { 1270 t.Errorf("(i=%d) got p.Proto = %d, want = %d", dadPacketsSent, p.Proto, header.IPv6ProtocolNumber) 1271 } 1272 1273 if p.Route.RemoteLinkAddress != remoteLinkAddr { 1274 t.Errorf("(i=%d) got p.Route.RemoteLinkAddress = %s, want = %s", dadPacketsSent, p.Route.RemoteLinkAddress, remoteLinkAddr) 1275 } 1276 1277 checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()), 1278 checker.SrcAddr(header.IPv6Any), 1279 checker.DstAddr(snmc), 1280 checker.TTL(header.NDPHopLimit), 1281 checker.NDPNS( 1282 checker.NDPNSTargetAddress(lladdr0), 1283 checker.NDPNSOptions([]header.NDPOption{header.NDPNonceOption(nonces[dadPacketsSent])}), 1284 )) 1285 } 1286 if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil { 1287 t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err) 1288 } 1289 checkDADMsg() 1290 1291 // Start DAD for the address we just added. 1292 // 1293 // Even though the stack will perform DAD before the added address transitions 1294 // from tentative to assigned, this DAD request should be independent of that. 1295 ch := make(chan stack.DADResult, 3) 1296 dadRequestsMade := 1 1297 dadPacketsSent++ 1298 if res, err := s.CheckDuplicateAddress(nicID, ProtocolNumber, lladdr0, func(r stack.DADResult) { 1299 ch <- r 1300 }); err != nil { 1301 t.Fatalf("s.CheckDuplicateAddress(%d, %d, %s, _): %s", nicID, ProtocolNumber, lladdr0, err) 1302 } else if res != stack.DADStarting { 1303 t.Fatalf("got s.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", nicID, ProtocolNumber, lladdr0, res, stack.DADStarting) 1304 } 1305 checkDADMsg() 1306 1307 // Remove the address and make sure our DAD request was not stopped. 1308 if err := s.RemoveAddress(nicID, lladdr0); err != nil { 1309 t.Fatalf("RemoveAddress(%d, %s): %s", nicID, lladdr0, err) 1310 } 1311 // Should not restart DAD since we already requested DAD above - the handler 1312 // should be called when the original request completes so we should not send 1313 // an extra DAD message here. 1314 dadRequestsMade++ 1315 if res, err := s.CheckDuplicateAddress(nicID, ProtocolNumber, lladdr0, func(r stack.DADResult) { 1316 ch <- r 1317 }); err != nil { 1318 t.Fatalf("s.CheckDuplicateAddress(%d, %d, %s, _): %s", nicID, ProtocolNumber, lladdr0, err) 1319 } else if res != stack.DADAlreadyRunning { 1320 t.Fatalf("got s.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", nicID, ProtocolNumber, lladdr0, res, stack.DADAlreadyRunning) 1321 } 1322 1323 // Wait for DAD to complete. 1324 clock.Advance(time.Duration(dadConfigs.DupAddrDetectTransmits) * dadConfigs.RetransmitTimer) 1325 for i := 0; i < dadRequestsMade; i++ { 1326 if diff := cmp.Diff(&stack.DADSucceeded{}, <-ch); diff != "" { 1327 t.Errorf("(i=%d) DAD result mismatch (-want +got):\n%s", i, diff) 1328 } 1329 } 1330 // Should have no more results. 1331 select { 1332 case r := <-ch: 1333 t.Errorf("unexpectedly got an extra DAD result; r = %#v", r) 1334 default: 1335 } 1336 1337 // Should have no more packets. 1338 if p, ok := e.Read(); ok { 1339 t.Errorf("got unexpected packet = %#v", p) 1340 } 1341 }