// Copyright 2020 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package link_resolution_test import ( "bytes" "fmt" "net" "runtime" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "gvisor.dev/gvisor/pkg/buffer" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/checker" "gvisor.dev/gvisor/pkg/tcpip/checksum" "gvisor.dev/gvisor/pkg/tcpip/faketime" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/link/channel" "gvisor.dev/gvisor/pkg/tcpip/link/ethernet" "gvisor.dev/gvisor/pkg/tcpip/link/pipe" "gvisor.dev/gvisor/pkg/tcpip/network/arp" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/tests/utils" tcptestutil "gvisor.dev/gvisor/pkg/tcpip/testutil" "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp" "gvisor.dev/gvisor/pkg/waiter" ) func setupStack(t *testing.T, stackOpts stack.Options, host1NICID, host2NICID tcpip.NICID) (*stack.Stack, *stack.Stack) { return setupStackWithSeparateOpts(t, stackOpts, stackOpts, host1NICID, host2NICID) } func setupStackWithSeparateOpts(t *testing.T, stack1Opts stack.Options, stack2Opts stack.Options, host1NICID, host2NICID tcpip.NICID) (*stack.Stack, *stack.Stack) { const maxFrameSize = header.IPv6MinimumMTU + header.EthernetMinimumSize host1Stack := stack.New(stack1Opts) host2Stack := stack.New(stack2Opts) host1NIC, host2NIC := pipe.New(utils.LinkAddr1, utils.LinkAddr2, maxFrameSize) if err := host1Stack.CreateNIC(host1NICID, utils.NewEthernetEndpoint(host1NIC)); err != nil { t.Fatalf("host1Stack.CreateNIC(%d, _): %s", host1NICID, err) } if err := host2Stack.CreateNIC(host2NICID, utils.NewEthernetEndpoint(host2NIC)); err != nil { t.Fatalf("host2Stack.CreateNIC(%d, _): %s", host2NICID, err) } if err := host1Stack.AddProtocolAddress(host1NICID, utils.Ipv4Addr1, stack.AddressProperties{}); err != nil { t.Fatalf("host1Stack.AddProtocolAddress(%d, %+v, {}): %s", host1NICID, utils.Ipv4Addr1, err) } if err := host2Stack.AddProtocolAddress(host2NICID, utils.Ipv4Addr2, stack.AddressProperties{}); err != nil { t.Fatalf("host2Stack.AddProtocolAddress(%d, %+v, {}): %s", host2NICID, utils.Ipv4Addr2, err) } if err := host1Stack.AddProtocolAddress(host1NICID, utils.Ipv6Addr1, stack.AddressProperties{}); err != nil { t.Fatalf("host1Stack.AddProtocolAddress(%d, %+v, {}): %s", host1NICID, utils.Ipv6Addr1, err) } if err := host2Stack.AddProtocolAddress(host2NICID, utils.Ipv6Addr2, stack.AddressProperties{}); err != nil { t.Fatalf("host2Stack.AddProtocolAddress(%d, %+v, {}): %s", host2NICID, utils.Ipv6Addr2, err) } host1Stack.SetRouteTable([]tcpip.Route{ { Destination: utils.Ipv4Addr1.AddressWithPrefix.Subnet(), NIC: host1NICID, }, { Destination: utils.Ipv6Addr1.AddressWithPrefix.Subnet(), NIC: host1NICID, }, }) host2Stack.SetRouteTable([]tcpip.Route{ { Destination: utils.Ipv4Addr2.AddressWithPrefix.Subnet(), NIC: host2NICID, }, { Destination: utils.Ipv6Addr2.AddressWithPrefix.Subnet(), NIC: host2NICID, }, }) return host1Stack, host2Stack } // TestPing tests that two hosts can ping each other when link resolution is // enabled. func TestPing(t *testing.T) { const ( host1NICID = 1 host2NICID = 4 // icmpDataOffset is the offset to the data in both ICMPv4 and ICMPv6 echo // request/reply packets. icmpDataOffset = 8 ) tests := []struct { name string transProto tcpip.TransportProtocolNumber netProto tcpip.NetworkProtocolNumber remoteAddr tcpip.Address icmpBuf func(*testing.T) []byte }{ { name: "IPv4 Ping", transProto: icmp.ProtocolNumber4, netProto: ipv4.ProtocolNumber, remoteAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, icmpBuf: func(t *testing.T) []byte { data := [8]byte{1, 2, 3, 4, 5, 6, 7, 8} hdr := header.ICMPv4(make([]byte, header.ICMPv4MinimumSize+len(data))) hdr.SetType(header.ICMPv4Echo) if n := copy(hdr.Payload(), data[:]); n != len(data) { t.Fatalf("copied %d bytes but expected to copy %d bytes", n, len(data)) } return hdr }, }, { name: "IPv6 Ping", transProto: icmp.ProtocolNumber6, netProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, icmpBuf: func(t *testing.T) []byte { data := [8]byte{1, 2, 3, 4, 5, 6, 7, 8} hdr := header.ICMPv6(make([]byte, header.ICMPv6MinimumSize+len(data))) hdr.SetType(header.ICMPv6EchoRequest) if n := copy(hdr.Payload(), data[:]); n != len(data) { t.Fatalf("copied %d bytes but expected to copy %d bytes", n, len(data)) } return hdr }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { stackOpts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4, icmp.NewProtocol6}, } host1Stack, host2Stack := setupStack(t, stackOpts, host1NICID, host2NICID) defer host1Stack.Destroy() defer host2Stack.Destroy() var wq waiter.Queue we, waiterCH := waiter.NewChannelEntry(waiter.ReadableEvents) wq.EventRegister(&we) ep, err := host1Stack.NewEndpoint(test.transProto, test.netProto, &wq) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", test.transProto, test.netProto, err) } defer ep.Close() icmpBuf := test.icmpBuf(t) var r bytes.Reader r.Reset(icmpBuf) wOpts := tcpip.WriteOptions{To: &tcpip.FullAddress{Addr: test.remoteAddr}} if n, err := ep.Write(&r, wOpts); err != nil { t.Fatalf("ep.Write(_, _): %s", err) } else if want := int64(len(icmpBuf)); n != want { t.Fatalf("got ep.Write(_, _) = (%d, _), want = (%d, _)", n, want) } // Wait for the endpoint to be readable. <-waiterCH var buf bytes.Buffer opts := tcpip.ReadOptions{NeedRemoteAddr: true} res, err := ep.Read(&buf, opts) if err != nil { t.Fatalf("ep.Read(_, %d, %#v): %s", len(icmpBuf), opts, err) } if diff := cmp.Diff(tcpip.ReadResult{ Count: buf.Len(), Total: buf.Len(), RemoteAddr: tcpip.FullAddress{Addr: test.remoteAddr}, }, res, checker.IgnoreCmpPath( "ControlMessages", "RemoteAddr.NIC", "RemoteAddr.Port", )); diff != "" { t.Errorf("ep.Read: unexpected result (-want +got):\n%s", diff) } if diff := cmp.Diff(buf.Bytes()[icmpDataOffset:], icmpBuf[icmpDataOffset:]); diff != "" { t.Errorf("received data mismatch (-want +got):\n%s", diff) } }) } } type transportError struct { origin tcpip.SockErrOrigin typ uint8 code uint8 info uint32 kind stack.TransportErrorKind } func TestTCPLinkResolutionFailure(t *testing.T) { const ( host1NICID = 1 host2NICID = 4 ) tests := []struct { name string netProto tcpip.NetworkProtocolNumber remoteAddr tcpip.Address expectedWriteErr tcpip.Error sockError tcpip.SockError transErr transportError }{ { name: "IPv4 with resolvable remote", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, expectedWriteErr: nil, }, { name: "IPv6 with resolvable remote", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, expectedWriteErr: nil, }, { name: "IPv4 without resolvable remote", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Ipv4Addr3.AddressWithPrefix.Address, expectedWriteErr: &tcpip.ErrHostUnreachable{}, sockError: tcpip.SockError{ Err: &tcpip.ErrHostUnreachable{}, Dst: tcpip.FullAddress{ NIC: host1NICID, Addr: utils.Ipv4Addr3.AddressWithPrefix.Address, Port: 1234, }, Offender: tcpip.FullAddress{ NIC: host1NICID, Addr: utils.Ipv4Addr1.AddressWithPrefix.Address, }, NetProto: ipv4.ProtocolNumber, }, transErr: transportError{ origin: tcpip.SockExtErrorOriginICMP, typ: uint8(header.ICMPv4DstUnreachable), code: uint8(header.ICMPv4HostUnreachable), kind: stack.DestinationHostUnreachableTransportError, }, }, { name: "IPv6 without resolvable remote", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr3.AddressWithPrefix.Address, expectedWriteErr: &tcpip.ErrHostUnreachable{}, sockError: tcpip.SockError{ Err: &tcpip.ErrHostUnreachable{}, Dst: tcpip.FullAddress{ NIC: host1NICID, Addr: utils.Ipv6Addr3.AddressWithPrefix.Address, Port: 1234, }, Offender: tcpip.FullAddress{ NIC: host1NICID, Addr: utils.Ipv6Addr1.AddressWithPrefix.Address, }, NetProto: ipv6.ProtocolNumber, }, transErr: transportError{ origin: tcpip.SockExtErrorOriginICMP6, typ: uint8(header.ICMPv6DstUnreachable), code: uint8(header.ICMPv6AddressUnreachable), kind: stack.DestinationHostUnreachableTransportError, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { clock := faketime.NewManualClock() stackOpts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol}, Clock: clock, } host1Stack, host2Stack := setupStack(t, stackOpts, host1NICID, host2NICID) defer host1Stack.Destroy() defer host2Stack.Destroy() var listenerWQ waiter.Queue listenerEP, err := host2Stack.NewEndpoint(tcp.ProtocolNumber, test.netProto, &listenerWQ) if err != nil { t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, test.netProto, err) } defer listenerEP.Close() listenerAddr := tcpip.FullAddress{Port: 1234} if err := listenerEP.Bind(listenerAddr); err != nil { t.Fatalf("listenerEP.Bind(%#v): %s", listenerAddr, err) } if err := listenerEP.Listen(1); err != nil { t.Fatalf("listenerEP.Listen(1): %s", err) } var clientWQ waiter.Queue we, ch := waiter.NewChannelEntry(waiter.WritableEvents | waiter.EventErr) clientWQ.EventRegister(&we) clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, test.netProto, &clientWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, test.netProto, err) } defer clientEP.Close() sockOpts := clientEP.SocketOptions() sockOpts.SetIPv4RecvError(true) sockOpts.SetIPv6RecvError(true) remoteAddr := listenerAddr remoteAddr.Addr = test.remoteAddr { err := clientEP.Connect(remoteAddr) if _, ok := err.(*tcpip.ErrConnectStarted); !ok { t.Fatalf("got clientEP.Connect(%#v) = %s, want = %s", remoteAddr, err, &tcpip.ErrConnectStarted{}) } } // Wait for an error due to link resolution failing, or the endpoint to be // writable. if test.expectedWriteErr != nil { nudConfigs, err := host1Stack.NUDConfigurations(host1NICID, test.netProto) if err != nil { t.Fatalf("host1Stack.NUDConfigurations(%d, %d): %s", host1NICID, test.netProto, err) } clock.Advance(time.Duration(nudConfigs.MaxMulticastProbes) * nudConfigs.RetransmitTimer) } else { clock.RunImmediatelyScheduledJobs() } <-ch { var r bytes.Reader r.Reset([]byte{0}) var wOpts tcpip.WriteOptions _, err := clientEP.Write(&r, wOpts) if diff := cmp.Diff(test.expectedWriteErr, err); diff != "" { t.Errorf("unexpected error from clientEP.Write(_, %#v), (-want, +got):\n%s", wOpts, diff) } } if test.expectedWriteErr == nil { return } sockErr := sockOpts.DequeueErr() if sockErr == nil { t.Fatalf("got sockOpts.DequeueErr() = nil, want = non-nil") } defer sockErr.Payload.Release() sockErrCmpOpts := []cmp.Option{ cmpopts.IgnoreUnexported(tcpip.SockError{}), cmp.Comparer(func(a, b tcpip.Error) bool { // tcpip.Error holds an unexported field but the errors netstack uses // are pre defined so we can simply compare pointers. return a == b }), checker.IgnoreCmpPath( // Ignore the payload since we do not know the TCP seq/ack numbers. "Payload", // Ignore the cause since we will compare its properties separately // since the concrete type of the cause is unknown. "Cause", ), } if addr, err := clientEP.GetLocalAddress(); err != nil { t.Fatalf("clientEP.GetLocalAddress(): %s", err) } else { test.sockError.Offender.Port = addr.Port } if diff := cmp.Diff(&test.sockError, sockErr, sockErrCmpOpts...); diff != "" { t.Errorf("socket error mismatch (-want +got):\n%s", diff) } transErr, ok := sockErr.Cause.(stack.TransportError) if !ok { t.Fatalf("socket error cause is not a transport error; cause = %#v", sockErr.Cause) } if diff := cmp.Diff( test.transErr, transportError{ origin: transErr.Origin(), typ: transErr.Type(), code: transErr.Code(), info: transErr.Info(), kind: transErr.Kind(), }, cmp.AllowUnexported(transportError{}), ); diff != "" { t.Errorf("socket error mismatch (-want +got):\n%s", diff) } }) } } func TestForwardingWithLinkResolutionFailure(t *testing.T) { const ( incomingNICID = 1 outgoingNICID = 2 ttl = 2 expectedHostUnreachableErrorCount = 1 ) outgoingLinkAddr := tcptestutil.MustParseLink("02:03:03:04:05:06") rxICMPv4EchoRequest := func(e *channel.Endpoint, src, dst tcpip.Address) { utils.RxICMPv4EchoRequest(e, src, dst, ttl) } rxICMPv6EchoRequest := func(e *channel.Endpoint, src, dst tcpip.Address) { utils.RxICMPv6EchoRequest(e, src, dst, ttl) } arpChecker := func(t *testing.T, request *stack.PacketBuffer, src, dst tcpip.Address) { if request.NetworkProtocolNumber != arp.ProtocolNumber { t.Errorf("got request.NetworkProtocolNumber = %d, want = %d", request.NetworkProtocolNumber, arp.ProtocolNumber) } if request.EgressRoute.RemoteLinkAddress != header.EthernetBroadcastAddress { t.Errorf("got request.EgressRoute.RemoteLinkAddress = %s, want = %s", request.EgressRoute.RemoteLinkAddress, header.EthernetBroadcastAddress) } rep := header.ARP(request.NetworkHeader().Slice()) if got := rep.Op(); got != header.ARPRequest { t.Errorf("got Op() = %d, want = %d", got, header.ARPRequest) } if got := tcpip.LinkAddress(rep.HardwareAddressSender()); got != outgoingLinkAddr { t.Errorf("got HardwareAddressSender = %s, want = %s", got, outgoingLinkAddr) } if got := tcpip.AddrFromSlice(rep.ProtocolAddressSender()); got != src { t.Errorf("got ProtocolAddressSender = %s, want = %s", got, src) } if got := tcpip.AddrFromSlice(rep.ProtocolAddressTarget()); got != dst { t.Errorf("got ProtocolAddressTarget = %s, want = %s", got, dst) } } ndpChecker := func(t *testing.T, request *stack.PacketBuffer, src, dst tcpip.Address) { if request.NetworkProtocolNumber != header.IPv6ProtocolNumber { t.Fatalf("got Proto = %d, want = %d", request.NetworkProtocolNumber, header.IPv6ProtocolNumber) } snmc := header.SolicitedNodeAddr(dst) if want := header.EthernetAddressFromMulticastIPv6Address(snmc); request.EgressRoute.RemoteLinkAddress != want { t.Errorf("got remote link address = %s, want = %s", request.EgressRoute.RemoteLinkAddress, want) } payload := stack.PayloadSince(request.NetworkHeader()) defer payload.Release() checker.IPv6(t, payload, checker.SrcAddr(src), checker.DstAddr(snmc), checker.TTL(header.NDPHopLimit), checker.NDPNS( checker.NDPNSTargetAddress(dst), )) } icmpv4Checker := func(t *testing.T, v *buffer.View, src, dst tcpip.Address) { checker.IPv4(t, v, checker.SrcAddr(src), checker.DstAddr(dst), checker.TTL(ipv4.DefaultTTL), checker.ICMPv4( checker.ICMPv4Checksum(), checker.ICMPv4Type(header.ICMPv4DstUnreachable), checker.ICMPv4Code(header.ICMPv4HostUnreachable), ), ) } icmpv6Checker := func(t *testing.T, v *buffer.View, src, dst tcpip.Address) { checker.IPv6(t, v, checker.SrcAddr(src), checker.DstAddr(dst), checker.TTL(ipv6.DefaultTTL), checker.ICMPv6( checker.ICMPv6Type(header.ICMPv6DstUnreachable), checker.ICMPv6Code(header.ICMPv6AddressUnreachable), ), ) } tests := []struct { name string networkProtocolFactory []stack.NetworkProtocolFactory networkProtocolNumber tcpip.NetworkProtocolNumber sourceAddr tcpip.Address destAddr tcpip.Address incomingAddr tcpip.AddressWithPrefix outgoingAddr tcpip.AddressWithPrefix transportProtocol func(*stack.Stack) stack.TransportProtocol rx func(*channel.Endpoint, tcpip.Address, tcpip.Address) linkResolutionRequestChecker func(*testing.T, *stack.PacketBuffer, tcpip.Address, tcpip.Address) icmpReplyChecker func(*testing.T, *buffer.View, tcpip.Address, tcpip.Address) mtu uint32 }{ { name: "IPv4 Host unreachable", networkProtocolFactory: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol}, networkProtocolNumber: header.IPv4ProtocolNumber, sourceAddr: tcptestutil.MustParse4("10.0.0.2"), destAddr: tcptestutil.MustParse4("11.0.0.2"), incomingAddr: tcpip.AddressWithPrefix{ Address: tcpip.AddrFromSlice(net.ParseIP("10.0.0.1").To4()), PrefixLen: 8, }, outgoingAddr: tcpip.AddressWithPrefix{ Address: tcpip.AddrFromSlice(net.ParseIP("11.0.0.1").To4()), PrefixLen: 8, }, transportProtocol: icmp.NewProtocol4, linkResolutionRequestChecker: arpChecker, icmpReplyChecker: icmpv4Checker, rx: rxICMPv4EchoRequest, mtu: ipv4.MaxTotalSize, }, { name: "IPv6 Host unreachable", networkProtocolFactory: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, networkProtocolNumber: header.IPv6ProtocolNumber, sourceAddr: tcptestutil.MustParse6("10::2"), destAddr: tcptestutil.MustParse6("11::2"), incomingAddr: tcpip.AddressWithPrefix{ Address: tcpip.AddrFromSlice(net.ParseIP("10::1").To16()), PrefixLen: 64, }, outgoingAddr: tcpip.AddressWithPrefix{ Address: tcpip.AddrFromSlice(net.ParseIP("11::1").To16()), PrefixLen: 64, }, transportProtocol: icmp.NewProtocol6, linkResolutionRequestChecker: ndpChecker, icmpReplyChecker: icmpv6Checker, rx: rxICMPv6EchoRequest, mtu: header.IPv6MinimumMTU, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { clock := faketime.NewManualClock() s := stack.New(stack.Options{ NetworkProtocols: test.networkProtocolFactory, TransportProtocols: []stack.TransportProtocolFactory{test.transportProtocol}, Clock: clock, }) defer s.Destroy() // Set up endpoint through which we will receive packets. incomingEndpoint := channel.New(1, test.mtu, "") if err := s.CreateNIC(incomingNICID, incomingEndpoint); err != nil { t.Fatalf("CreateNIC(%d, _): %s", incomingNICID, err) } incomingProtoAddr := tcpip.ProtocolAddress{ Protocol: test.networkProtocolNumber, AddressWithPrefix: test.incomingAddr, } if err := s.AddProtocolAddress(incomingNICID, incomingProtoAddr, stack.AddressProperties{}); err != nil { t.Fatalf("AddProtocolAddress(%d, %+v, {}): %s", incomingNICID, incomingProtoAddr, err) } // Set up endpoint through which we will attempt to forward packets. outgoingEndpoint := channel.New(1, test.mtu, outgoingLinkAddr) outgoingEndpoint.LinkEPCapabilities |= stack.CapabilityResolutionRequired if err := s.CreateNIC(outgoingNICID, outgoingEndpoint); err != nil { t.Fatalf("CreateNIC(%d, _): %s", outgoingNICID, err) } outgoingProtoAddr := tcpip.ProtocolAddress{ Protocol: test.networkProtocolNumber, AddressWithPrefix: test.outgoingAddr, } if err := s.AddProtocolAddress(outgoingNICID, outgoingProtoAddr, stack.AddressProperties{}); err != nil { t.Fatalf("AddProtocolAddress(%d, %+v, {}): %s", outgoingNICID, outgoingProtoAddr, err) } s.SetRouteTable([]tcpip.Route{ { Destination: test.incomingAddr.Subnet(), NIC: incomingNICID, }, { Destination: test.outgoingAddr.Subnet(), NIC: outgoingNICID, }, }) if err := s.SetForwardingDefaultAndAllNICs(test.networkProtocolNumber, true); err != nil { t.Fatalf("SetForwardingDefaultAndAllNICs(%d, true): %s", test.networkProtocolNumber, err) } test.rx(incomingEndpoint, test.sourceAddr, test.destAddr) nudConfigs, err := s.NUDConfigurations(outgoingNICID, test.networkProtocolNumber) if err != nil { t.Fatalf("s.NUDConfigurations(%d, %d): %s", outgoingNICID, test.networkProtocolNumber, err) } // Trigger the first packet on the endpoint. clock.RunImmediatelyScheduledJobs() for i := 0; i < int(nudConfigs.MaxMulticastProbes); i++ { request := outgoingEndpoint.Read() if request == nil { t.Fatal("expected ARP packet through outgoing NIC") } test.linkResolutionRequestChecker(t, request, test.outgoingAddr.Address, test.destAddr) request.DecRef() // Advance the clock the span of one request timeout. clock.Advance(nudConfigs.RetransmitTimer) } // Next, we make a blocking read to retrieve the error packet. This is // necessary because outgoing packets are dequeued asynchronously when // link resolution fails, and this dequeue is what triggers the ICMP // error. reply := incomingEndpoint.Read() if reply == nil { t.Fatal("expected ICMP packet through incoming NIC") } payload := stack.PayloadSince(reply.NetworkHeader()) defer payload.Release() test.icmpReplyChecker(t, payload, test.incomingAddr.Address, test.sourceAddr) reply.DecRef() // Since link resolution failed, we don't expect the packet to be // forwarded. forwardedPacket := outgoingEndpoint.Read() if forwardedPacket != nil { t.Fatalf("expected no ICMP Echo packet through outgoing NIC, instead found: %#v", forwardedPacket) } if got, want := s.Stats().IP.Forwarding.HostUnreachable.Value(), expectedHostUnreachableErrorCount; int(got) != want { t.Errorf("got rt.Stats().IP.Forwarding.HostUnreachable.Value() = %d, want = %d", got, want) } }) } } func TestGetLinkAddress(t *testing.T) { const ( host1NICID = 1 host2NICID = 4 ) tests := []struct { name string netProto tcpip.NetworkProtocolNumber remoteAddr, localAddr tcpip.Address expectedErr tcpip.Error }{ { name: "IPv4 resolvable", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, expectedErr: nil, }, { name: "IPv6 resolvable", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, expectedErr: nil, }, { name: "IPv4 not resolvable", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Ipv4Addr3.AddressWithPrefix.Address, expectedErr: &tcpip.ErrTimeout{}, }, { name: "IPv6 not resolvable", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr3.AddressWithPrefix.Address, expectedErr: &tcpip.ErrTimeout{}, }, { name: "IPv4 bad local address", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, localAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, expectedErr: &tcpip.ErrBadLocalAddress{}, }, { name: "IPv6 bad local address", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, localAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, expectedErr: &tcpip.ErrBadLocalAddress{}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { clock := faketime.NewManualClock() stackOpts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, Clock: clock, } host1Stack, host2Stack := setupStack(t, stackOpts, host1NICID, host2NICID) defer host1Stack.Destroy() defer host2Stack.Destroy() ch := make(chan stack.LinkResolutionResult, 1) err := host1Stack.GetLinkAddress(host1NICID, test.remoteAddr, test.localAddr, test.netProto, func(r stack.LinkResolutionResult) { ch <- r }) if _, ok := err.(*tcpip.ErrWouldBlock); !ok { t.Fatalf("got host1Stack.GetLinkAddress(%d, %s, '', %d, _) = %s, want = %s", host1NICID, test.remoteAddr, test.netProto, err, &tcpip.ErrWouldBlock{}) } wantRes := stack.LinkResolutionResult{Err: test.expectedErr} if test.expectedErr == nil { wantRes.LinkAddress = utils.LinkAddr2 } nudConfigs, err := host1Stack.NUDConfigurations(host1NICID, test.netProto) if err != nil { t.Fatalf("host1Stack.NUDConfigurations(%d, %d): %s", host1NICID, test.netProto, err) } clock.Advance(time.Duration(nudConfigs.MaxMulticastProbes) * nudConfigs.RetransmitTimer) select { case got := <-ch: if diff := cmp.Diff(wantRes, got); diff != "" { t.Fatalf("link resolution result mismatch (-want +got):\n%s", diff) } default: t.Fatal("event didn't arrive") } }) } } func TestRouteResolvedFields(t *testing.T) { const ( host1NICID = 1 host2NICID = 4 ) tests := []struct { name string netProto tcpip.NetworkProtocolNumber localAddr tcpip.Address remoteAddr tcpip.Address immediatelyResolvable bool expectedErr tcpip.Error expectedLinkAddr tcpip.LinkAddress }{ { name: "IPv4 immediately resolvable", netProto: ipv4.ProtocolNumber, localAddr: utils.Ipv4Addr1.AddressWithPrefix.Address, remoteAddr: header.IPv4AllSystems, immediatelyResolvable: true, expectedErr: nil, expectedLinkAddr: header.EthernetAddressFromMulticastIPv4Address(header.IPv4AllSystems), }, { name: "IPv6 immediately resolvable", netProto: ipv6.ProtocolNumber, localAddr: utils.Ipv6Addr1.AddressWithPrefix.Address, remoteAddr: header.IPv6AllNodesMulticastAddress, immediatelyResolvable: true, expectedErr: nil, expectedLinkAddr: header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllNodesMulticastAddress), }, { name: "IPv4 resolvable", netProto: ipv4.ProtocolNumber, localAddr: utils.Ipv4Addr1.AddressWithPrefix.Address, remoteAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, immediatelyResolvable: false, expectedErr: nil, expectedLinkAddr: utils.LinkAddr2, }, { name: "IPv6 resolvable", netProto: ipv6.ProtocolNumber, localAddr: utils.Ipv6Addr1.AddressWithPrefix.Address, remoteAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, immediatelyResolvable: false, expectedErr: nil, expectedLinkAddr: utils.LinkAddr2, }, { name: "IPv4 not resolvable", netProto: ipv4.ProtocolNumber, localAddr: utils.Ipv4Addr1.AddressWithPrefix.Address, remoteAddr: utils.Ipv4Addr3.AddressWithPrefix.Address, immediatelyResolvable: false, expectedErr: &tcpip.ErrTimeout{}, }, { name: "IPv6 not resolvable", netProto: ipv6.ProtocolNumber, localAddr: utils.Ipv6Addr1.AddressWithPrefix.Address, remoteAddr: utils.Ipv6Addr3.AddressWithPrefix.Address, immediatelyResolvable: false, expectedErr: &tcpip.ErrTimeout{}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { clock := faketime.NewManualClock() stackOpts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, Clock: clock, } host1Stack, host2Stack := setupStack(t, stackOpts, host1NICID, host2NICID) defer host1Stack.Destroy() defer host2Stack.Destroy() r, err := host1Stack.FindRoute(host1NICID, test.localAddr, test.remoteAddr, test.netProto, false /* multicastLoop */) if err != nil { t.Fatalf("host1Stack.FindRoute(%d, %s, %s, %d, false): %s", host1NICID, test.localAddr, test.remoteAddr, test.netProto, err) } defer r.Release() var wantRouteInfo stack.RouteInfo wantRouteInfo.LocalLinkAddress = utils.LinkAddr1 wantRouteInfo.LocalAddress = test.localAddr wantRouteInfo.RemoteAddress = test.remoteAddr wantRouteInfo.NetProto = test.netProto wantRouteInfo.Loop = stack.PacketOut wantRouteInfo.RemoteLinkAddress = test.expectedLinkAddr ch := make(chan stack.ResolvedFieldsResult, 1) if !test.immediatelyResolvable { wantUnresolvedRouteInfo := wantRouteInfo wantUnresolvedRouteInfo.RemoteLinkAddress = "" err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) { ch <- r }) if _, ok := err.(*tcpip.ErrWouldBlock); !ok { t.Errorf("got r.ResolvedFields(_) = %s, want = %s", err, &tcpip.ErrWouldBlock{}) } nudConfigs, err := host1Stack.NUDConfigurations(host1NICID, test.netProto) if err != nil { t.Fatalf("host1Stack.NUDConfigurations(%d, %d): %s", host1NICID, test.netProto, err) } clock.Advance(time.Duration(nudConfigs.MaxMulticastProbes) * nudConfigs.RetransmitTimer) select { case got := <-ch: if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Err: test.expectedErr}, got, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" { t.Errorf("route resolve result mismatch (-want +got):\n%s", diff) } default: t.Fatalf("event didn't arrive") } if test.expectedErr != nil { return } // At this point the neighbor table should be populated so the route // should be immediately resolvable. } if err := r.ResolvedFields(func(r stack.ResolvedFieldsResult) { ch <- r }); err != nil { t.Errorf("r.ResolvedFields(_): %s", err) } select { case routeResolveRes := <-ch: if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Err: nil}, routeResolveRes, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" { t.Errorf("route resolve result from resolved route mismatch (-want +got):\n%s", diff) } default: t.Fatal("expected route to be immediately resolvable") } }) } } func TestWritePacketsLinkResolution(t *testing.T) { const ( host1NICID = 1 host2NICID = 4 ) tests := []struct { name string netProto tcpip.NetworkProtocolNumber remoteAddr tcpip.Address expectedWriteErr tcpip.Error }{ { name: "IPv4", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, expectedWriteErr: nil, }, { name: "IPv6", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, expectedWriteErr: nil, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { stackOpts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, } host1Stack, host2Stack := setupStack(t, stackOpts, host1NICID, host2NICID) defer host1Stack.Destroy() defer host2Stack.Destroy() var serverWQ waiter.Queue serverWE, serverCH := waiter.NewChannelEntry(waiter.ReadableEvents) serverWQ.EventRegister(&serverWE) serverEP, err := host2Stack.NewEndpoint(udp.ProtocolNumber, test.netProto, &serverWQ) if err != nil { t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, test.netProto, err) } defer serverEP.Close() serverAddr := tcpip.FullAddress{Port: 1234} if err := serverEP.Bind(serverAddr); err != nil { t.Fatalf("serverEP.Bind(%#v): %s", serverAddr, err) } r, err := host1Stack.FindRoute(host1NICID, tcpip.Address{}, test.remoteAddr, test.netProto, false /* multicastLoop */) if err != nil { t.Fatalf("host1Stack.FindRoute(%d, '', %s, %d, false): %s", host1NICID, test.remoteAddr, test.netProto, err) } defer r.Release() params := stack.NetworkHeaderParams{ Protocol: udp.ProtocolNumber, TTL: 64, TOS: stack.DefaultTOS, } data := []byte{1, 2} pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()), Payload: buffer.MakeWithData(data), }) pkt.TransportProtocolNumber = udp.ProtocolNumber length := uint16(pkt.Data().Size() + header.UDPMinimumSize) udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize)) udpHdr.Encode(&header.UDPFields{ SrcPort: 5555, DstPort: serverAddr.Port, Length: length, }) xsum := r.PseudoHeaderChecksum(udp.ProtocolNumber, length) xsum = checksum.Combine(xsum, pkt.Data().Checksum()) udpHdr.SetChecksum(^udpHdr.CalculateChecksum(xsum)) if err := r.WritePacket(params, pkt); err != nil { t.Fatalf("WritePacket(...): %s", err) } pkt.DecRef() var writer bytes.Buffer for { var rOpts tcpip.ReadOptions res, err := serverEP.Read(&writer, rOpts) if err != nil { if _, ok := err.(*tcpip.ErrWouldBlock); ok { <-serverCH continue } t.Fatalf("serverEP.Read(_, %#v): %s", rOpts, err) } if res.Count != len(data) { t.Fatalf("got res.Count = %d, want = %d", res.Count, len(data)) } break } if got, want := host2Stack.Stats().UDP.PacketsReceived.Value(), uint64(1); got != want { t.Errorf("got host2Stack.Stats().UDP.PacketsReceived.Value() = %d, want = %d", got, want) } if diff := cmp.Diff(data, writer.Bytes()); diff != "" { t.Errorf("read bytes mismatch (-want +got):\n%s", diff) } }) } } type eventType int const ( entryAdded eventType = iota entryChanged entryRemoved ) func (t eventType) String() string { switch t { case entryAdded: return "add" case entryChanged: return "change" case entryRemoved: return "remove" default: return fmt.Sprintf("unknown (%d)", t) } } type eventInfo struct { eventType eventType nicID tcpip.NICID entry stack.NeighborEntry } func (e eventInfo) String() string { return fmt.Sprintf("%s event for NIC #%d, %#v", e.eventType, e.nicID, e.entry) } var _ stack.NUDDispatcher = (*nudDispatcher)(nil) type nudDispatcher struct { c chan eventInfo } func (d *nudDispatcher) OnNeighborAdded(nicID tcpip.NICID, entry stack.NeighborEntry) { e := eventInfo{ eventType: entryAdded, nicID: nicID, entry: entry, } d.c <- e } func (d *nudDispatcher) OnNeighborChanged(nicID tcpip.NICID, entry stack.NeighborEntry) { e := eventInfo{ eventType: entryChanged, nicID: nicID, entry: entry, } d.c <- e } func (d *nudDispatcher) OnNeighborRemoved(nicID tcpip.NICID, entry stack.NeighborEntry) { e := eventInfo{ eventType: entryRemoved, nicID: nicID, entry: entry, } d.c <- e } func (d *nudDispatcher) expectEvent(want eventInfo) error { if diff := cmp.Diff(want, <-d.c, cmp.AllowUnexported(eventInfo{}), cmpopts.IgnoreFields(stack.NeighborEntry{}, "UpdatedAt")); diff != "" { return fmt.Errorf("got invalid event (-want +got):\n%s", diff) } return nil } // TestTCPConfirmNeighborReachability tests that TCP informs layers beneath it // that the neighbor used for a route is reachable. func TestTCPConfirmNeighborReachability(t *testing.T) { tests := []struct { name string netProto tcpip.NetworkProtocolNumber remoteAddr tcpip.Address neighborAddr tcpip.Address getEndpoints func(*testing.T, *stack.Stack, *stack.Stack, *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) isHost1Listener bool }{ { name: "IPv4 active connection through neighbor", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Host2IPv4Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, _, host2Stack *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := host2Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, }, { name: "IPv6 active connection through neighbor", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Host2IPv6Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, _, host2Stack *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := host2Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, }, { name: "IPv4 active connection to neighbor", netProto: ipv4.ProtocolNumber, remoteAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, routerStack, _ *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := routerStack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("routerStack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, }, { name: "IPv6 active connection to neighbor", netProto: ipv6.ProtocolNumber, remoteAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, routerStack, _ *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := routerStack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("routerStack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, }, { name: "IPv4 passive connection to neighbor", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Host1IPv4Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, routerStack, _ *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := routerStack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("routerStack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, isHost1Listener: true, }, { name: "IPv6 passive connection to neighbor", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Host1IPv6Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, routerStack, _ *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := routerStack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("routerStack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, isHost1Listener: true, }, { name: "IPv4 passive connection through neighbor", netProto: ipv4.ProtocolNumber, remoteAddr: utils.Host1IPv4Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, _, host2Stack *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := host2Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, isHost1Listener: true, }, { name: "IPv6 passive connection through neighbor", netProto: ipv6.ProtocolNumber, remoteAddr: utils.Host1IPv6Addr.AddressWithPrefix.Address, neighborAddr: utils.RouterNIC1IPv6Addr.AddressWithPrefix.Address, getEndpoints: func(t *testing.T, host1Stack, _, host2Stack *stack.Stack) (tcpip.Endpoint, <-chan struct{}, tcpip.Endpoint, <-chan struct{}) { var listenerWQ waiter.Queue listenerWE, listenerCH := waiter.NewChannelEntry(waiter.EventIn) listenerWQ.EventRegister(&listenerWE) listenerEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &listenerWQ) if err != nil { t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } var clientWQ waiter.Queue clientWE, clientCH := waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents) clientWQ.EventRegister(&clientWE) clientEP, err := host2Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) if err != nil { t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) } return listenerEP, listenerCH, clientEP, clientCH }, isHost1Listener: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { clock := faketime.NewManualClock() nudDisp := nudDispatcher{ c: make(chan eventInfo, 3), } stackOpts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol}, Clock: clock, } host1StackOpts := stackOpts host1StackOpts.NUDDisp = &nudDisp host1Stack := stack.New(host1StackOpts) defer host1Stack.Destroy() routerStack := stack.New(stackOpts) defer routerStack.Destroy() host2Stack := stack.New(stackOpts) defer host2Stack.Destroy() utils.SetupRoutedStacks(t, host1Stack, routerStack, host2Stack) // Add a reachable dynamic entry to our neighbor table for the remote. { ch := make(chan stack.LinkResolutionResult, 1) err := host1Stack.GetLinkAddress(utils.Host1NICID, test.neighborAddr, tcpip.Address{}, test.netProto, func(r stack.LinkResolutionResult) { ch <- r }) if _, ok := err.(*tcpip.ErrWouldBlock); !ok { t.Fatalf("got host1Stack.GetLinkAddress(%d, %s, '', %d, _) = %s, want = %s", utils.Host1NICID, test.neighborAddr, test.netProto, err, &tcpip.ErrWouldBlock{}) } if diff := cmp.Diff(stack.LinkResolutionResult{LinkAddress: utils.LinkAddr2, Err: nil}, <-ch); diff != "" { t.Fatalf("link resolution mismatch (-want +got):\n%s", diff) } } if err := nudDisp.expectEvent(eventInfo{ eventType: entryAdded, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Incomplete, Addr: test.neighborAddr}, }); err != nil { t.Fatalf("error waiting for initial NUD event: %s", err) } if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Reachable, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for reachable NUD event: %s", err) } // Wait for the remote's neighbor entry to be stale before creating a // TCP connection from host1 to some remote. nudConfigs, err := host1Stack.NUDConfigurations(utils.Host1NICID, test.netProto) if err != nil { t.Fatalf("host1Stack.NUDConfigurations(%d, %d): %s", utils.Host1NICID, test.netProto, err) } // The maximum reachable time for a neighbor is some maximum random factor // applied to the base reachable time. // // See NUDConfigurations.BaseReachableTime for more information. maxReachableTime := time.Duration(float32(nudConfigs.BaseReachableTime) * nudConfigs.MaxRandomFactor) clock.Advance(maxReachableTime) if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Stale, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for stale NUD event: %s", err) } listenerEP, listenerCH, clientEP, clientCH := test.getEndpoints(t, host1Stack, routerStack, host2Stack) defer listenerEP.Close() defer clientEP.Close() listenerAddr := tcpip.FullAddress{Addr: test.remoteAddr, Port: 1234} if err := listenerEP.Bind(listenerAddr); err != nil { t.Fatalf("listenerEP.Bind(%#v): %s", listenerAddr, err) } // A backlog of 1 results in SYN cookies being used for all passive // connections to make sure the only spot in the accept queue is not // taken by a connection that never completes the handshake. We use // a backlog of 2 to make sure SYN cookies are not used. // // We avoid SYN cookies to make sure that an accepted endpoint is able // to confirm the neighbor's reachability through the cached neighbor // entry in the endpoint's route. When SYN cookies are used, the // accepted endpoint is constructed when the handshake has already been // established and such an endpoint's route will not have a cached // neighbor entry as it was not used to send any of packets for the // handshake. if err := listenerEP.Listen(2); err != nil { t.Fatalf("listenerEP.Listen(2): %s", err) } { err := clientEP.Connect(listenerAddr) if _, ok := err.(*tcpip.ErrConnectStarted); !ok { t.Fatalf("got clientEP.Connect(%#v) = %s, want = %s", listenerAddr, err, &tcpip.ErrConnectStarted{}) } } // Wait for the TCP handshake to complete then make sure the neighbor is // reachable without entering the probe state as TCP should provide NUD // with confirmation that the neighbor is reachable (indicated by a // successful 3-way handshake). <-clientCH if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Delay, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for delay NUD event: %s", err) } <-listenerCH if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Reachable, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for reachable NUD event: %s", err) } peerEP, peerWQ, err := listenerEP.Accept(nil) if err != nil { t.Fatalf("listenerEP.Accept(): %s", err) } defer peerEP.Close() peerWE, peerCH := waiter.NewChannelEntry(waiter.ReadableEvents) peerWQ.EventRegister(&peerWE) // Wait for the neighbor to be stale again then send data to the remote. // // On successful transmission, the neighbor should become reachable // without probing the neighbor as a TCP ACK would be received which is an // indication of the neighbor being reachable. clock.Advance(maxReachableTime) if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Stale, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for stale NUD event: %s", err) } { var r bytes.Reader r.Reset([]byte{0}) var wOpts tcpip.WriteOptions if _, err := clientEP.Write(&r, wOpts); err != nil { t.Errorf("clientEP.Write(_, %#v): %s", wOpts, err) } } // Heads up, there is a race here. // // Incoming TCP segments are handled in // tcp.(*endpoint).handleSegmentLocked: // // - tcp.(*endpoint).rcv.handleRcvdSegment puts the segment on the // segment queue and notifies waiting readers (such as this channel) // // - tcp.(*endpoint).snd.handleRcvdSegment sends an ACK for the segment // and notifies the NUD machinery that the peer is reachable // // Thus we must permit a delay between the readable signal and the // expected NUD event. // // At the time of writing, this race is reliably hit with gotsan. <-peerCH for len(nudDisp.c) == 0 { runtime.Gosched() } if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Delay, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for delay NUD event: %s", err) } if test.isHost1Listener { // If host1 is not the client, host1 does not send any data so TCP // has no way to know it is making forward progress. Because of this, // TCP should not mark the route reachable and NUD should go through the // probe state. clock.Advance(nudConfigs.DelayFirstProbeTime) if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Probe, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for probe NUD event: %s", err) } } { var r bytes.Reader r.Reset([]byte{0}) var wOpts tcpip.WriteOptions if _, err := peerEP.Write(&r, wOpts); err != nil { t.Errorf("peerEP.Write(_, %#v): %s", wOpts, err) } } <-clientCH if err := nudDisp.expectEvent(eventInfo{ eventType: entryChanged, nicID: utils.Host1NICID, entry: stack.NeighborEntry{State: stack.Reachable, Addr: test.neighborAddr, LinkAddr: utils.LinkAddr2}, }); err != nil { t.Fatalf("error waiting for reachable NUD event: %s", err) } }) } } func TestDAD(t *testing.T) { dadConfigs := stack.DADConfigurations{ DupAddrDetectTransmits: 1, RetransmitTimer: time.Second, } tests := []struct { name string netProto tcpip.NetworkProtocolNumber dadNetProto tcpip.NetworkProtocolNumber remoteAddr tcpip.Address expectedResult stack.DADResult }{ { name: "IPv4 own address", netProto: ipv4.ProtocolNumber, dadNetProto: arp.ProtocolNumber, remoteAddr: utils.Ipv4Addr1.AddressWithPrefix.Address, expectedResult: &stack.DADSucceeded{}, }, { name: "IPv6 own address", netProto: ipv6.ProtocolNumber, dadNetProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr1.AddressWithPrefix.Address, expectedResult: &stack.DADSucceeded{}, }, { name: "IPv4 duplicate address", netProto: ipv4.ProtocolNumber, dadNetProto: arp.ProtocolNumber, remoteAddr: utils.Ipv4Addr2.AddressWithPrefix.Address, expectedResult: &stack.DADDupAddrDetected{HolderLinkAddress: utils.LinkAddr2}, }, { name: "IPv6 duplicate address", netProto: ipv6.ProtocolNumber, dadNetProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr2.AddressWithPrefix.Address, expectedResult: &stack.DADDupAddrDetected{HolderLinkAddress: utils.LinkAddr2}, }, { name: "IPv4 no duplicate address", netProto: ipv4.ProtocolNumber, dadNetProto: arp.ProtocolNumber, remoteAddr: utils.Ipv4Addr3.AddressWithPrefix.Address, expectedResult: &stack.DADSucceeded{}, }, { name: "IPv6 no duplicate address", netProto: ipv6.ProtocolNumber, dadNetProto: ipv6.ProtocolNumber, remoteAddr: utils.Ipv6Addr3.AddressWithPrefix.Address, expectedResult: &stack.DADSucceeded{}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { clock := faketime.NewManualClock() stackOpts := stack.Options{ Clock: clock, NetworkProtocols: []stack.NetworkProtocolFactory{ arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol, }, } host1Stack, host2Stack := setupStack(t, stackOpts, utils.Host1NICID, utils.Host2NICID) defer host1Stack.Destroy() defer host2Stack.Destroy() // DAD should be disabled by default. if res, err := host1Stack.CheckDuplicateAddress(utils.Host1NICID, test.netProto, test.remoteAddr, func(r stack.DADResult) { t.Errorf("unexpectedly called DAD completion handler when DAD was supposed to be disabled") }); err != nil { t.Fatalf("host1Stack.CheckDuplicateAddress(%d, %d, %s, _): %s", utils.Host1NICID, test.netProto, test.remoteAddr, err) } else if res != stack.DADDisabled { t.Errorf("got host1Stack.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", utils.Host1NICID, test.netProto, test.remoteAddr, res, stack.DADDisabled) } // Enable DAD then attempt to check if an address is duplicated. netEP, err := host1Stack.GetNetworkEndpoint(utils.Host1NICID, test.dadNetProto) if err != nil { t.Fatalf("host1Stack.GetNetworkEndpoint(%d, %d): %s", utils.Host1NICID, test.dadNetProto, err) } dad, ok := netEP.(stack.DuplicateAddressDetector) if !ok { t.Fatalf("expected %T to implement stack.DuplicateAddressDetector", netEP) } dad.SetDADConfigurations(dadConfigs) ch := make(chan stack.DADResult, 3) if res, err := host1Stack.CheckDuplicateAddress(utils.Host1NICID, test.netProto, test.remoteAddr, func(r stack.DADResult) { ch <- r }); err != nil { t.Fatalf("host1Stack.CheckDuplicateAddress(%d, %d, %s, _): %s", utils.Host1NICID, test.netProto, test.remoteAddr, err) } else if res != stack.DADStarting { t.Errorf("got host1Stack.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", utils.Host1NICID, test.netProto, test.remoteAddr, res, stack.DADStarting) } expectResults := 1 if _, ok := test.expectedResult.(*stack.DADSucceeded); ok { const delta = time.Nanosecond clock.Advance(time.Duration(dadConfigs.DupAddrDetectTransmits)*dadConfigs.RetransmitTimer - delta) select { case r := <-ch: t.Fatalf("unexpectedly got DAD result before the DAD timeout; r = %#v", r) default: } // If we expect the resolve to succeed try requesting DAD again on the // same address. The handler for the new request should be called once // the original DAD request completes. expectResults = 2 if res, err := host1Stack.CheckDuplicateAddress(utils.Host1NICID, test.netProto, test.remoteAddr, func(r stack.DADResult) { ch <- r }); err != nil { t.Fatalf("host1Stack.CheckDuplicateAddress(%d, %d, %s, _): %s", utils.Host1NICID, test.netProto, test.remoteAddr, err) } else if res != stack.DADAlreadyRunning { t.Errorf("got host1Stack.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", utils.Host1NICID, test.netProto, test.remoteAddr, res, stack.DADAlreadyRunning) } clock.Advance(delta) } for i := 0; i < expectResults; i++ { if diff := cmp.Diff(test.expectedResult, <-ch); diff != "" { t.Errorf("(i=%d) DAD result mismatch (-want +got):\n%s", i, diff) } } // Should have no more results. select { case r := <-ch: t.Errorf("unexpectedly got an extra DAD result; r = %#v", r) default: } }) } } type settableLinkEndpoint struct { stack.LinkEndpoint mu sync.Mutex addr tcpip.LinkAddress } func newSettableLinkEndpoint(e stack.LinkEndpoint) *settableLinkEndpoint { return &settableLinkEndpoint{ LinkEndpoint: e, addr: e.LinkAddress(), } } func (e *settableLinkEndpoint) setLinkAddress(addr tcpip.LinkAddress) { e.mu.Lock() defer e.mu.Unlock() e.addr = addr } func (e *settableLinkEndpoint) LinkAddress() tcpip.LinkAddress { e.mu.Lock() defer e.mu.Unlock() return e.addr } type monitorableLinkEndpoint struct { stack.LinkEndpoint ch chan tcpip.LinkAddress } func newMonitorableLinkEndpoint(e stack.LinkEndpoint) *monitorableLinkEndpoint { return &monitorableLinkEndpoint{e, make(chan tcpip.LinkAddress, 1)} } func (e *monitorableLinkEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { for _, pkt := range pkts.AsSlice() { dstAddr := header.Ethernet(pkt.LinkHeader().Slice()).DestinationAddress() e.ch <- dstAddr } e.LinkEndpoint.WritePackets(pkts) return 0, nil } func (e *monitorableLinkEndpoint) waitForLinkAddress(addr tcpip.LinkAddress, wait time.Duration) error { c := time.After(wait) for { select { case sentAddr := <-e.ch: if addr == sentAddr { return nil } case <-c: return fmt.Errorf("timed out waiting for endpoint to send packet with destination address: %v", addr) } } } func TestUpdateCachedNeighborEntry(t *testing.T) { d := []byte{1, 2} params := stack.NetworkHeaderParams{ Protocol: udp.ProtocolNumber, TTL: 64, TOS: stack.DefaultTOS, } writePacket := func(t *testing.T, r *stack.Route) { pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()), Payload: buffer.MakeWithData(d), }) if err := r.WritePacket(params, pkt); err != nil { t.Fatalf("WritePacket(...): %s", err) } pkt.DecRef() } const ( host1NICID = 1 host2NICID = 4 ) stackOpts := stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{ arp.NewProtocol, ipv4.NewProtocolWithOptions(ipv4.Options{ IGMP: ipv4.IGMPOptions{ Enabled: false, }, }), ipv6.NewProtocolWithOptions(ipv6.Options{ MLD: ipv6.MLDOptions{ Enabled: false, }, }), }, TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, } const maxFrameSize = header.IPv6MinimumMTU + header.EthernetMinimumSize host1Stack := stack.New(stackOpts) host2Stack := stack.New(stackOpts) defer host1Stack.Destroy() defer host2Stack.Destroy() host1Pipe, host2Pipe := pipe.New(utils.LinkAddr1, utils.LinkAddr2, maxFrameSize) host1NICMonitorable := newMonitorableLinkEndpoint(ethernet.New(host1Pipe)) host2NICSettable := newSettableLinkEndpoint(host2Pipe) if err := host1Stack.CreateNIC(host1NICID, host1NICMonitorable); err != nil { t.Fatalf("host1Stack.CreateNIC(%d, _): %s", host1NICID, err) } if err := host2Stack.CreateNIC(host2NICID, ethernet.New(host2NICSettable)); err != nil { t.Fatalf("host2Stack.CreateNIC(%d, _): %s", host2NICID, err) } if err := host1Stack.AddProtocolAddress(host1NICID, utils.Ipv4Addr1, stack.AddressProperties{}); err != nil { t.Fatalf("host1Stack.AddProtocolAddress(%d, %+v, {}): %s", host1NICID, utils.Ipv4Addr1, err) } if err := host2Stack.AddProtocolAddress(host2NICID, utils.Ipv4Addr2, stack.AddressProperties{}); err != nil { t.Fatalf("host2Stack.AddProtocolAddress(%d, %+v, {}): %s", host2NICID, utils.Ipv4Addr2, err) } host1Stack.SetRouteTable([]tcpip.Route{ { Destination: utils.Ipv4Addr1.AddressWithPrefix.Subnet(), NIC: host1NICID, }, }) host2Stack.SetRouteTable([]tcpip.Route{ { Destination: utils.Ipv4Addr2.AddressWithPrefix.Subnet(), NIC: host2NICID, }, }) localAddr := utils.Ipv4Addr1.AddressWithPrefix.Address neighborAddr := utils.Ipv4Addr2.AddressWithPrefix.Address // Obtain a route to a neighbor. r, err := host1Stack.FindRoute(host1NICID, localAddr, neighborAddr, header.IPv4ProtocolNumber, false) if err != nil { t.Fatalf("host1Stack.FindRoute(...): %s", err) } // Send packet to neighbor (start link resolution & resolve, then send // packet). Send twice to use cached address the second time. for i := 0; i < 2; i++ { go writePacket(t, r) if err := host1NICMonitorable.waitForLinkAddress(utils.LinkAddr2, time.Second); err != nil { t.Fatalf("host1NIC.waitForLinkAddress(%s): %s", utils.LinkAddr2, err) } } // Neighbor no longer reachable, deleted from the neighbor cache. host1Stack.RemoveNeighbor(host1NICID, header.IPv4ProtocolNumber, neighborAddr) host2Stack.DisableNIC(host2NICID) // Send a packet to the neighbor that's no longer reachable (should fail). go writePacket(t, r) if err := host1NICMonitorable.waitForLinkAddress(utils.LinkAddr2, time.Second); err == nil { t.Fatalf("got host1NIC.waitForLinkAddress(%s) = nil, want err", utils.LinkAddr2) } // Neighbor reachable again with new MAC address. host2Stack.EnableNIC(host2NICID) host2NICSettable.setLinkAddress(utils.LinkAddr3) // Pending packet should eventually reach the new neighbor. if err := host1NICMonitorable.waitForLinkAddress(utils.LinkAddr3, 5*time.Second); err != nil { t.Fatalf("host1NIC.waitForLinkAddress(%s): %s", utils.LinkAddr3, err) } }