diff options
Diffstat (limited to 'pkg/tcpip/tests')
-rw-r--r-- | pkg/tcpip/tests/integration/BUILD | 131 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/forward_test.go | 314 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/iptables_test.go | 647 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/link_resolution_test.go | 1301 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/loopback_test.go | 765 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/multicast_broadcast_test.go | 719 | ||||
-rw-r--r-- | pkg/tcpip/tests/integration/route_test.go | 432 | ||||
-rw-r--r-- | pkg/tcpip/tests/utils/BUILD | 22 | ||||
-rw-r--r-- | pkg/tcpip/tests/utils/utils.go | 374 |
9 files changed, 0 insertions, 4705 deletions
diff --git a/pkg/tcpip/tests/integration/BUILD b/pkg/tcpip/tests/integration/BUILD deleted file mode 100644 index 3cc8c36f1..000000000 --- a/pkg/tcpip/tests/integration/BUILD +++ /dev/null @@ -1,131 +0,0 @@ -load("//tools:defs.bzl", "go_test") - -package(licenses = ["notice"]) - -go_test( - name = "forward_test", - size = "small", - srcs = ["forward_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/checker", - "//pkg/tcpip/network/arp", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/tests/utils", - "//pkg/tcpip/transport/tcp", - "//pkg/tcpip/transport/udp", - "//pkg/waiter", - "@com_github_google_go_cmp//cmp:go_default_library", - ], -) - -go_test( - name = "iptables_test", - size = "small", - srcs = ["iptables_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/header", - "//pkg/tcpip/link/channel", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/tests/utils", - "//pkg/tcpip/transport/udp", - ], -) - -go_test( - name = "link_resolution_test", - size = "small", - srcs = ["link_resolution_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/checker", - "//pkg/tcpip/faketime", - "//pkg/tcpip/header", - "//pkg/tcpip/link/pipe", - "//pkg/tcpip/network/arp", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/tests/utils", - "//pkg/tcpip/transport/icmp", - "//pkg/tcpip/transport/tcp", - "//pkg/tcpip/transport/udp", - "//pkg/waiter", - "@com_github_google_go_cmp//cmp:go_default_library", - "@com_github_google_go_cmp//cmp/cmpopts:go_default_library", - ], -) - -go_test( - name = "loopback_test", - size = "small", - srcs = ["loopback_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/checker", - "//pkg/tcpip/header", - "//pkg/tcpip/link/channel", - "//pkg/tcpip/link/loopback", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/tests/utils", - "//pkg/tcpip/transport/icmp", - "//pkg/tcpip/transport/tcp", - "//pkg/tcpip/transport/udp", - "//pkg/waiter", - "@com_github_google_go_cmp//cmp:go_default_library", - ], -) - -go_test( - name = "multicast_broadcast_test", - size = "small", - srcs = ["multicast_broadcast_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/checker", - "//pkg/tcpip/header", - "//pkg/tcpip/link/channel", - "//pkg/tcpip/link/loopback", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/tests/utils", - "//pkg/tcpip/transport/icmp", - "//pkg/tcpip/transport/udp", - "//pkg/waiter", - "@com_github_google_go_cmp//cmp:go_default_library", - ], -) - -go_test( - name = "route_test", - size = "small", - srcs = ["route_test.go"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/checker", - "//pkg/tcpip/header", - "//pkg/tcpip/link/channel", - "//pkg/tcpip/link/loopback", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/tests/utils", - "//pkg/tcpip/transport/icmp", - "//pkg/tcpip/transport/udp", - "//pkg/waiter", - "@com_github_google_go_cmp//cmp:go_default_library", - ], -) diff --git a/pkg/tcpip/tests/integration/forward_test.go b/pkg/tcpip/tests/integration/forward_test.go deleted file mode 100644 index 38c2f321b..000000000 --- a/pkg/tcpip/tests/integration/forward_test.go +++ /dev/null @@ -1,314 +0,0 @@ -// 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 forward_test - -import ( - "bytes" - "testing" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/checker" - "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" - "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" - "gvisor.dev/gvisor/pkg/tcpip/transport/udp" - "gvisor.dev/gvisor/pkg/waiter" -) - -func TestForwarding(t *testing.T) { - const listenPort = 8080 - - type endpointAndAddresses struct { - serverEP tcpip.Endpoint - serverAddr tcpip.Address - serverReadableCH chan struct{} - - clientEP tcpip.Endpoint - clientAddr tcpip.Address - clientReadableCH chan struct{} - } - - newEP := func(t *testing.T, s *stack.Stack, transProto tcpip.TransportProtocolNumber, netProto tcpip.NetworkProtocolNumber) (tcpip.Endpoint, chan struct{}) { - t.Helper() - var wq waiter.Queue - we, ch := waiter.NewChannelEntry(nil) - wq.EventRegister(&we, waiter.EventIn) - ep, err := s.NewEndpoint(transProto, netProto, &wq) - if err != nil { - t.Fatalf("s.NewEndpoint(%d, %d, _): %s", transProto, netProto, err) - } - - t.Cleanup(func() { - wq.EventUnregister(&we) - }) - - return ep, ch - } - - tests := []struct { - name string - epAndAddrs func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses - }{ - { - name: "IPv4 host1 server with host2 client", - epAndAddrs: func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses { - ep1, ep1WECH := newEP(t, host1Stack, proto, ipv4.ProtocolNumber) - ep2, ep2WECH := newEP(t, host2Stack, proto, ipv4.ProtocolNumber) - return endpointAndAddresses{ - serverEP: ep1, - serverAddr: utils.Host1IPv4Addr.AddressWithPrefix.Address, - serverReadableCH: ep1WECH, - - clientEP: ep2, - clientAddr: utils.Host2IPv4Addr.AddressWithPrefix.Address, - clientReadableCH: ep2WECH, - } - }, - }, - { - name: "IPv6 host2 server with host1 client", - epAndAddrs: func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses { - ep1, ep1WECH := newEP(t, host2Stack, proto, ipv6.ProtocolNumber) - ep2, ep2WECH := newEP(t, host1Stack, proto, ipv6.ProtocolNumber) - return endpointAndAddresses{ - serverEP: ep1, - serverAddr: utils.Host2IPv6Addr.AddressWithPrefix.Address, - serverReadableCH: ep1WECH, - - clientEP: ep2, - clientAddr: utils.Host1IPv6Addr.AddressWithPrefix.Address, - clientReadableCH: ep2WECH, - } - }, - }, - { - name: "IPv4 host2 server with routerNIC1 client", - epAndAddrs: func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses { - ep1, ep1WECH := newEP(t, host2Stack, proto, ipv4.ProtocolNumber) - ep2, ep2WECH := newEP(t, routerStack, proto, ipv4.ProtocolNumber) - return endpointAndAddresses{ - serverEP: ep1, - serverAddr: utils.Host2IPv4Addr.AddressWithPrefix.Address, - serverReadableCH: ep1WECH, - - clientEP: ep2, - clientAddr: utils.RouterNIC1IPv4Addr.AddressWithPrefix.Address, - clientReadableCH: ep2WECH, - } - }, - }, - { - name: "IPv6 routerNIC2 server with host1 client", - epAndAddrs: func(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack, proto tcpip.TransportProtocolNumber) endpointAndAddresses { - ep1, ep1WECH := newEP(t, routerStack, proto, ipv6.ProtocolNumber) - ep2, ep2WECH := newEP(t, host1Stack, proto, ipv6.ProtocolNumber) - return endpointAndAddresses{ - serverEP: ep1, - serverAddr: utils.RouterNIC2IPv6Addr.AddressWithPrefix.Address, - serverReadableCH: ep1WECH, - - clientEP: ep2, - clientAddr: utils.Host1IPv6Addr.AddressWithPrefix.Address, - clientReadableCH: ep2WECH, - } - }, - }, - } - - subTests := []struct { - name string - proto tcpip.TransportProtocolNumber - expectedConnectErr tcpip.Error - setupServer func(t *testing.T, ep tcpip.Endpoint) - setupServerConn func(t *testing.T, ep tcpip.Endpoint, ch <-chan struct{}, clientAddr tcpip.FullAddress) (tcpip.Endpoint, chan struct{}) - needRemoteAddr bool - }{ - { - name: "UDP", - proto: udp.ProtocolNumber, - expectedConnectErr: nil, - setupServerConn: func(t *testing.T, ep tcpip.Endpoint, _ <-chan struct{}, clientAddr tcpip.FullAddress) (tcpip.Endpoint, chan struct{}) { - t.Helper() - - if err := ep.Connect(clientAddr); err != nil { - t.Fatalf("ep.Connect(%#v): %s", clientAddr, err) - } - return nil, nil - }, - needRemoteAddr: true, - }, - { - name: "TCP", - proto: tcp.ProtocolNumber, - expectedConnectErr: &tcpip.ErrConnectStarted{}, - setupServer: func(t *testing.T, ep tcpip.Endpoint) { - t.Helper() - - if err := ep.Listen(1); err != nil { - t.Fatalf("ep.Listen(1): %s", err) - } - }, - setupServerConn: func(t *testing.T, ep tcpip.Endpoint, ch <-chan struct{}, clientAddr tcpip.FullAddress) (tcpip.Endpoint, chan struct{}) { - t.Helper() - - var addr tcpip.FullAddress - for { - newEP, wq, err := ep.Accept(&addr) - if _, ok := err.(*tcpip.ErrWouldBlock); ok { - <-ch - continue - } - if err != nil { - t.Fatalf("ep.Accept(_): %s", err) - } - if diff := cmp.Diff(clientAddr, addr, checker.IgnoreCmpPath( - "NIC", - )); diff != "" { - t.Errorf("accepted address mismatch (-want +got):\n%s", diff) - } - - we, newCH := waiter.NewChannelEntry(nil) - wq.EventRegister(&we, waiter.EventIn) - return newEP, newCH - } - }, - needRemoteAddr: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - for _, subTest := range subTests { - t.Run(subTest.name, func(t *testing.T) { - stackOpts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol, tcp.NewProtocol}, - } - - host1Stack := stack.New(stackOpts) - routerStack := stack.New(stackOpts) - host2Stack := stack.New(stackOpts) - utils.SetupRoutedStacks(t, host1Stack, routerStack, host2Stack) - - epsAndAddrs := test.epAndAddrs(t, host1Stack, routerStack, host2Stack, subTest.proto) - defer epsAndAddrs.serverEP.Close() - defer epsAndAddrs.clientEP.Close() - - serverAddr := tcpip.FullAddress{Addr: epsAndAddrs.serverAddr, Port: listenPort} - if err := epsAndAddrs.serverEP.Bind(serverAddr); err != nil { - t.Fatalf("epsAndAddrs.serverEP.Bind(%#v): %s", serverAddr, err) - } - clientAddr := tcpip.FullAddress{Addr: epsAndAddrs.clientAddr} - if err := epsAndAddrs.clientEP.Bind(clientAddr); err != nil { - t.Fatalf("epsAndAddrs.clientEP.Bind(%#v): %s", clientAddr, err) - } - - if subTest.setupServer != nil { - subTest.setupServer(t, epsAndAddrs.serverEP) - } - { - err := epsAndAddrs.clientEP.Connect(serverAddr) - if diff := cmp.Diff(subTest.expectedConnectErr, err); diff != "" { - t.Fatalf("unexpected error from epsAndAddrs.clientEP.Connect(%#v), (-want, +got):\n%s", serverAddr, diff) - } - } - if addr, err := epsAndAddrs.clientEP.GetLocalAddress(); err != nil { - t.Fatalf("epsAndAddrs.clientEP.GetLocalAddress(): %s", err) - } else { - clientAddr = addr - clientAddr.NIC = 0 - } - - serverEP := epsAndAddrs.serverEP - serverCH := epsAndAddrs.serverReadableCH - if ep, ch := subTest.setupServerConn(t, serverEP, serverCH, clientAddr); ep != nil { - defer ep.Close() - serverEP = ep - serverCH = ch - } - - write := func(ep tcpip.Endpoint, data []byte) { - t.Helper() - - var r bytes.Reader - r.Reset(data) - var wOpts tcpip.WriteOptions - n, err := ep.Write(&r, wOpts) - if err != nil { - t.Fatalf("ep.Write(_, %#v): %s", wOpts, err) - } - if want := int64(len(data)); n != want { - t.Fatalf("got ep.Write(_, %#v) = (%d, _), want = (%d, _)", wOpts, n, want) - } - } - - data := []byte{1, 2, 3, 4} - write(epsAndAddrs.clientEP, data) - - read := func(ch chan struct{}, ep tcpip.Endpoint, data []byte, expectedFrom tcpip.FullAddress) { - t.Helper() - - var buf bytes.Buffer - var res tcpip.ReadResult - for { - var err tcpip.Error - opts := tcpip.ReadOptions{NeedRemoteAddr: subTest.needRemoteAddr} - res, err = ep.Read(&buf, opts) - if _, ok := err.(*tcpip.ErrWouldBlock); ok { - <-ch - continue - } - if err != nil { - t.Fatalf("ep.Read(_, %d, %#v): %s", len(data), opts, err) - } - break - } - - readResult := tcpip.ReadResult{ - Count: len(data), - Total: len(data), - } - if subTest.needRemoteAddr { - readResult.RemoteAddr = expectedFrom - } - if diff := cmp.Diff(readResult, res, checker.IgnoreCmpPath( - "ControlMessages", - "RemoteAddr.NIC", - )); diff != "" { - t.Errorf("ep.Read: unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(buf.Bytes(), data); diff != "" { - t.Errorf("received data mismatch (-want +got):\n%s", diff) - } - - if t.Failed() { - t.FailNow() - } - } - - read(serverCH, serverEP, data, clientAddr) - - data = []byte{5, 6, 7, 8, 9, 10, 11, 12} - write(serverEP, data) - read(epsAndAddrs.clientReadableCH, epsAndAddrs.clientEP, data, serverAddr) - }) - } - }) - } -} diff --git a/pkg/tcpip/tests/integration/iptables_test.go b/pkg/tcpip/tests/integration/iptables_test.go deleted file mode 100644 index 480174070..000000000 --- a/pkg/tcpip/tests/integration/iptables_test.go +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright 2021 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 iptables_test - -import ( - "testing" - - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/link/channel" - "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" - "gvisor.dev/gvisor/pkg/tcpip/transport/udp" -) - -type inputIfNameMatcher struct { - name string -} - -var _ stack.Matcher = (*inputIfNameMatcher)(nil) - -func (*inputIfNameMatcher) Name() string { - return "inputIfNameMatcher" -} - -func (im *inputIfNameMatcher) Match(hook stack.Hook, _ *stack.PacketBuffer, inNicName, _ string) (bool, bool) { - return (hook == stack.Input && im.name != "" && im.name == inNicName), false -} - -const ( - nicID = 1 - nicName = "nic1" - anotherNicName = "nic2" - linkAddr = tcpip.LinkAddress("\x0a\x0b\x0c\x0d\x0e\x0e") - srcAddrV4 = "\x0a\x00\x00\x01" - dstAddrV4 = "\x0a\x00\x00\x02" - srcAddrV6 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" - dstAddrV6 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02" - payloadSize = 20 -) - -func genStackV6(t *testing.T) (*stack.Stack, *channel.Endpoint) { - t.Helper() - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, - }) - e := channel.New(0, header.IPv6MinimumMTU, linkAddr) - nicOpts := stack.NICOptions{Name: nicName} - if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil { - t.Fatalf("CreateNICWithOptions(%d, _, %#v) = %s", nicID, nicOpts, err) - } - if err := s.AddAddress(nicID, header.IPv6ProtocolNumber, dstAddrV6); err != nil { - t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, header.IPv6ProtocolNumber, dstAddrV6, err) - } - return s, e -} - -func genStackV4(t *testing.T) (*stack.Stack, *channel.Endpoint) { - t.Helper() - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol}, - }) - e := channel.New(0, header.IPv4MinimumMTU, linkAddr) - nicOpts := stack.NICOptions{Name: nicName} - if err := s.CreateNICWithOptions(nicID, e, nicOpts); err != nil { - t.Fatalf("CreateNICWithOptions(%d, _, %#v) = %s", nicID, nicOpts, err) - } - if err := s.AddAddress(nicID, header.IPv4ProtocolNumber, dstAddrV4); err != nil { - t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, header.IPv4ProtocolNumber, dstAddrV4, err) - } - return s, e -} - -func genPacketV6() *stack.PacketBuffer { - pktSize := header.IPv6MinimumSize + payloadSize - hdr := buffer.NewPrependable(pktSize) - ip := header.IPv6(hdr.Prepend(pktSize)) - ip.Encode(&header.IPv6Fields{ - PayloadLength: payloadSize, - TransportProtocol: 99, - HopLimit: 255, - SrcAddr: srcAddrV6, - DstAddr: dstAddrV6, - }) - vv := hdr.View().ToVectorisedView() - return stack.NewPacketBuffer(stack.PacketBufferOptions{Data: vv}) -} - -func genPacketV4() *stack.PacketBuffer { - pktSize := header.IPv4MinimumSize + payloadSize - hdr := buffer.NewPrependable(pktSize) - ip := header.IPv4(hdr.Prepend(pktSize)) - ip.Encode(&header.IPv4Fields{ - TOS: 0, - TotalLength: uint16(pktSize), - ID: 1, - Flags: 0, - FragmentOffset: 16, - TTL: 48, - Protocol: 99, - SrcAddr: srcAddrV4, - DstAddr: dstAddrV4, - }) - ip.SetChecksum(0) - ip.SetChecksum(^ip.CalculateChecksum()) - vv := hdr.View().ToVectorisedView() - return stack.NewPacketBuffer(stack.PacketBufferOptions{Data: vv}) -} - -func TestIPTablesStatsForInput(t *testing.T) { - tests := []struct { - name string - setupStack func(*testing.T) (*stack.Stack, *channel.Endpoint) - setupFilter func(*testing.T, *stack.Stack) - genPacket func() *stack.PacketBuffer - proto tcpip.NetworkProtocolNumber - expectReceived int - expectInputDropped int - }{ - { - name: "IPv6 Accept", - setupStack: genStackV6, - setupFilter: func(*testing.T, *stack.Stack) { /* no filter */ }, - genPacket: genPacketV6, - proto: header.IPv6ProtocolNumber, - expectReceived: 1, - expectInputDropped: 0, - }, - { - name: "IPv4 Accept", - setupStack: genStackV4, - setupFilter: func(*testing.T, *stack.Stack) { /* no filter */ }, - genPacket: genPacketV4, - proto: header.IPv4ProtocolNumber, - expectReceived: 1, - expectInputDropped: 0, - }, - { - name: "IPv6 Drop (input interface matches)", - setupStack: genStackV6, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, true /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{InputInterface: nicName} - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{nicName}} - // Make sure the packet is not dropped by the next rule. - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err) - } - }, - genPacket: genPacketV6, - proto: header.IPv6ProtocolNumber, - expectReceived: 1, - expectInputDropped: 1, - }, - { - name: "IPv4 Drop (input interface matches)", - setupStack: genStackV4, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, false /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{InputInterface: nicName} - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{nicName}} - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err) - } - }, - genPacket: genPacketV4, - proto: header.IPv4ProtocolNumber, - expectReceived: 1, - expectInputDropped: 1, - }, - { - name: "IPv6 Accept (input interface does not match)", - setupStack: genStackV6, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, true /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{InputInterface: anotherNicName} - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err) - } - }, - genPacket: genPacketV6, - proto: header.IPv6ProtocolNumber, - expectReceived: 1, - expectInputDropped: 0, - }, - { - name: "IPv4 Accept (input interface does not match)", - setupStack: genStackV4, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, false /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{InputInterface: anotherNicName} - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err) - } - }, - genPacket: genPacketV4, - proto: header.IPv4ProtocolNumber, - expectReceived: 1, - expectInputDropped: 0, - }, - { - name: "IPv6 Drop (input interface does not match but invert is true)", - setupStack: genStackV6, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, true /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{ - InputInterface: anotherNicName, - InputInterfaceInvert: true, - } - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err) - } - }, - genPacket: genPacketV6, - proto: header.IPv6ProtocolNumber, - expectReceived: 1, - expectInputDropped: 1, - }, - { - name: "IPv4 Drop (input interface does not match but invert is true)", - setupStack: genStackV4, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, false /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Filter = stack.IPHeaderFilter{ - InputInterface: anotherNicName, - InputInterfaceInvert: true, - } - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err) - } - }, - genPacket: genPacketV4, - proto: header.IPv4ProtocolNumber, - expectReceived: 1, - expectInputDropped: 1, - }, - { - name: "IPv6 Accept (input interface does not match using a matcher)", - setupStack: genStackV6, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, true /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{anotherNicName}} - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err) - } - }, - genPacket: genPacketV6, - proto: header.IPv6ProtocolNumber, - expectReceived: 1, - expectInputDropped: 0, - }, - { - name: "IPv4 Accept (input interface does not match using a matcher)", - setupStack: genStackV4, - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - ipt := s.IPTables() - filter := ipt.GetTable(stack.FilterID, false /* ipv6 */) - ruleIdx := filter.BuiltinChains[stack.Input] - filter.Rules[ruleIdx].Target = &stack.DropTarget{} - filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{anotherNicName}} - filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{} - if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil { - t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err) - } - }, - genPacket: genPacketV4, - proto: header.IPv4ProtocolNumber, - expectReceived: 1, - expectInputDropped: 0, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s, e := test.setupStack(t) - test.setupFilter(t, s) - e.InjectInbound(test.proto, test.genPacket()) - - if got := int(s.Stats().IP.PacketsReceived.Value()); got != test.expectReceived { - t.Errorf("got PacketReceived = %d, want = %d", got, test.expectReceived) - } - if got := int(s.Stats().IP.IPTablesInputDropped.Value()); got != test.expectInputDropped { - t.Errorf("got IPTablesInputDropped = %d, want = %d", got, test.expectInputDropped) - } - }) - } -} - -var _ stack.LinkEndpoint = (*channelEndpointWithoutWritePacket)(nil) - -// channelEndpointWithoutWritePacket is a channel endpoint that does not support -// stack.LinkEndpoint.WritePacket. -type channelEndpointWithoutWritePacket struct { - *channel.Endpoint - - t *testing.T -} - -func (c *channelEndpointWithoutWritePacket) WritePacket(stack.RouteInfo, *stack.GSO, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) tcpip.Error { - c.t.Error("unexpectedly called WritePacket; all writes should go through WritePackets") - return &tcpip.ErrNotSupported{} -} - -var _ stack.Matcher = (*udpSourcePortMatcher)(nil) - -type udpSourcePortMatcher struct { - port uint16 -} - -func (*udpSourcePortMatcher) Name() string { - return "udpSourcePortMatcher" -} - -func (m *udpSourcePortMatcher) Match(_ stack.Hook, pkt *stack.PacketBuffer, _, _ string) (matches, hotdrop bool) { - udp := header.UDP(pkt.TransportHeader().View()) - if len(udp) < header.UDPMinimumSize { - // Drop immediately as the packet is invalid. - return false, true - } - - return udp.SourcePort() == m.port, false -} - -func TestIPTableWritePackets(t *testing.T) { - const ( - nicID = 1 - - dropLocalPort = utils.LocalPort - 1 - acceptPackets = 2 - dropPackets = 3 - ) - - udpHdr := func(hdr buffer.View, srcAddr, dstAddr tcpip.Address, srcPort, dstPort uint16) { - u := header.UDP(hdr) - u.Encode(&header.UDPFields{ - SrcPort: srcPort, - DstPort: dstPort, - Length: header.UDPMinimumSize, - }) - sum := header.PseudoHeaderChecksum(udp.ProtocolNumber, srcAddr, dstAddr, header.UDPMinimumSize) - sum = header.Checksum(hdr, sum) - u.SetChecksum(^u.CalculateChecksum(sum)) - } - - tests := []struct { - name string - setupFilter func(*testing.T, *stack.Stack) - genPacket func(*stack.Route) stack.PacketBufferList - proto tcpip.NetworkProtocolNumber - remoteAddr tcpip.Address - expectSent uint64 - expectOutputDropped uint64 - }{ - { - name: "IPv4 Accept", - setupFilter: func(*testing.T, *stack.Stack) { /* no filter */ }, - genPacket: func(r *stack.Route) stack.PacketBufferList { - var pkts stack.PacketBufferList - - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength() + header.UDPMinimumSize), - }) - hdr := pkt.TransportHeader().Push(header.UDPMinimumSize) - udpHdr(hdr, r.LocalAddress, r.RemoteAddress, utils.LocalPort, utils.RemotePort) - pkts.PushFront(pkt) - - return pkts - }, - proto: header.IPv4ProtocolNumber, - remoteAddr: dstAddrV4, - expectSent: 1, - expectOutputDropped: 0, - }, - { - name: "IPv4 Drop Other Port", - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - - table := stack.Table{ - Rules: []stack.Rule{ - { - Target: &stack.AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}, - }, - { - Target: &stack.AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}, - }, - { - Matchers: []stack.Matcher{&udpSourcePortMatcher{port: dropLocalPort}}, - Target: &stack.DropTarget{NetworkProtocol: header.IPv4ProtocolNumber}, - }, - { - Target: &stack.AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}, - }, - { - Target: &stack.ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}, - }, - }, - BuiltinChains: [stack.NumHooks]int{ - stack.Prerouting: stack.HookUnset, - stack.Input: 0, - stack.Forward: 1, - stack.Output: 2, - stack.Postrouting: stack.HookUnset, - }, - Underflows: [stack.NumHooks]int{ - stack.Prerouting: stack.HookUnset, - stack.Input: 0, - stack.Forward: 1, - stack.Output: 2, - stack.Postrouting: stack.HookUnset, - }, - } - - if err := s.IPTables().ReplaceTable(stack.FilterID, table, false /* ipv4 */); err != nil { - t.Fatalf("RelaceTable(%d, _, false): %s", stack.FilterID, err) - } - }, - genPacket: func(r *stack.Route) stack.PacketBufferList { - var pkts stack.PacketBufferList - - for i := 0; i < acceptPackets; i++ { - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength() + header.UDPMinimumSize), - }) - hdr := pkt.TransportHeader().Push(header.UDPMinimumSize) - udpHdr(hdr, r.LocalAddress, r.RemoteAddress, utils.LocalPort, utils.RemotePort) - pkts.PushFront(pkt) - } - for i := 0; i < dropPackets; i++ { - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength() + header.UDPMinimumSize), - }) - hdr := pkt.TransportHeader().Push(header.UDPMinimumSize) - udpHdr(hdr, r.LocalAddress, r.RemoteAddress, dropLocalPort, utils.RemotePort) - pkts.PushFront(pkt) - } - - return pkts - }, - proto: header.IPv4ProtocolNumber, - remoteAddr: dstAddrV4, - expectSent: acceptPackets, - expectOutputDropped: dropPackets, - }, - { - name: "IPv6 Accept", - setupFilter: func(*testing.T, *stack.Stack) { /* no filter */ }, - genPacket: func(r *stack.Route) stack.PacketBufferList { - var pkts stack.PacketBufferList - - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength() + header.UDPMinimumSize), - }) - hdr := pkt.TransportHeader().Push(header.UDPMinimumSize) - udpHdr(hdr, r.LocalAddress, r.RemoteAddress, utils.LocalPort, utils.RemotePort) - pkts.PushFront(pkt) - - return pkts - }, - proto: header.IPv6ProtocolNumber, - remoteAddr: dstAddrV6, - expectSent: 1, - expectOutputDropped: 0, - }, - { - name: "IPv6 Drop Other Port", - setupFilter: func(t *testing.T, s *stack.Stack) { - t.Helper() - - table := stack.Table{ - Rules: []stack.Rule{ - { - Target: &stack.AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}, - }, - { - Target: &stack.AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}, - }, - { - Matchers: []stack.Matcher{&udpSourcePortMatcher{port: dropLocalPort}}, - Target: &stack.DropTarget{NetworkProtocol: header.IPv6ProtocolNumber}, - }, - { - Target: &stack.AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}, - }, - { - Target: &stack.ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}, - }, - }, - BuiltinChains: [stack.NumHooks]int{ - stack.Prerouting: stack.HookUnset, - stack.Input: 0, - stack.Forward: 1, - stack.Output: 2, - stack.Postrouting: stack.HookUnset, - }, - Underflows: [stack.NumHooks]int{ - stack.Prerouting: stack.HookUnset, - stack.Input: 0, - stack.Forward: 1, - stack.Output: 2, - stack.Postrouting: stack.HookUnset, - }, - } - - if err := s.IPTables().ReplaceTable(stack.FilterID, table, true /* ipv6 */); err != nil { - t.Fatalf("RelaceTable(%d, _, true): %s", stack.FilterID, err) - } - }, - genPacket: func(r *stack.Route) stack.PacketBufferList { - var pkts stack.PacketBufferList - - for i := 0; i < acceptPackets; i++ { - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength() + header.UDPMinimumSize), - }) - hdr := pkt.TransportHeader().Push(header.UDPMinimumSize) - udpHdr(hdr, r.LocalAddress, r.RemoteAddress, utils.LocalPort, utils.RemotePort) - pkts.PushFront(pkt) - } - for i := 0; i < dropPackets; i++ { - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength() + header.UDPMinimumSize), - }) - hdr := pkt.TransportHeader().Push(header.UDPMinimumSize) - udpHdr(hdr, r.LocalAddress, r.RemoteAddress, dropLocalPort, utils.RemotePort) - pkts.PushFront(pkt) - } - - return pkts - }, - proto: header.IPv6ProtocolNumber, - remoteAddr: dstAddrV6, - expectSent: acceptPackets, - expectOutputDropped: dropPackets, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, - }) - e := channelEndpointWithoutWritePacket{ - Endpoint: channel.New(4, header.IPv6MinimumMTU, linkAddr), - t: t, - } - if err := s.CreateNIC(nicID, &e); err != nil { - t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) - } - if err := s.AddAddress(nicID, header.IPv6ProtocolNumber, srcAddrV6); err != nil { - t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, header.IPv6ProtocolNumber, srcAddrV6, err) - } - if err := s.AddAddress(nicID, header.IPv4ProtocolNumber, srcAddrV4); err != nil { - t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, header.IPv4ProtocolNumber, srcAddrV4, err) - } - - s.SetRouteTable([]tcpip.Route{ - { - Destination: header.IPv4EmptySubnet, - NIC: nicID, - }, - { - Destination: header.IPv6EmptySubnet, - NIC: nicID, - }, - }) - - test.setupFilter(t, s) - - r, err := s.FindRoute(nicID, "", test.remoteAddr, test.proto, false) - if err != nil { - t.Fatalf("FindRoute(%d, '', %s, %d, false): %s", nicID, test.remoteAddr, test.proto, err) - } - defer r.Release() - - pkts := test.genPacket(r) - pktsLen := pkts.Len() - if n, err := r.WritePackets(nil /* gso */, pkts, stack.NetworkHeaderParams{ - Protocol: header.UDPProtocolNumber, - TTL: 64, - }); err != nil { - t.Fatalf("WritePackets(...): %s", err) - } else if n != pktsLen { - t.Fatalf("got WritePackets(...) = %d, want = %d", n, pktsLen) - } - - if got := s.Stats().IP.PacketsSent.Value(); got != test.expectSent { - t.Errorf("got PacketSent = %d, want = %d", got, test.expectSent) - } - if got := s.Stats().IP.IPTablesOutputDropped.Value(); got != test.expectOutputDropped { - t.Errorf("got IPTablesOutputDropped = %d, want = %d", got, test.expectOutputDropped) - } - }) - } -} diff --git a/pkg/tcpip/tests/integration/link_resolution_test.go b/pkg/tcpip/tests/integration/link_resolution_test.go deleted file mode 100644 index 095623789..000000000 --- a/pkg/tcpip/tests/integration/link_resolution_test.go +++ /dev/null @@ -1,1301 +0,0 @@ -// 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" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/checker" - "gvisor.dev/gvisor/pkg/tcpip/faketime" - "gvisor.dev/gvisor/pkg/tcpip/header" - "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" - "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) { - host1Stack := stack.New(stackOpts) - host2Stack := stack.New(stackOpts) - - host1NIC, host2NIC := pipe.New(utils.LinkAddr1, utils.LinkAddr2) - - 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); err != nil { - t.Fatalf("host1Stack.AddProtocolAddress(%d, %#v): %s", host1NICID, utils.Ipv4Addr1, err) - } - if err := host2Stack.AddProtocolAddress(host2NICID, utils.Ipv4Addr2); err != nil { - t.Fatalf("host2Stack.AddProtocolAddress(%d, %#v): %s", host2NICID, utils.Ipv4Addr2, err) - } - if err := host1Stack.AddProtocolAddress(host1NICID, utils.Ipv6Addr1); err != nil { - t.Fatalf("host1Stack.AddProtocolAddress(%d, %#v): %s", host1NICID, utils.Ipv6Addr1, err) - } - if err := host2Stack.AddProtocolAddress(host2NICID, utils.Ipv6Addr2); 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 eachother 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, _ := setupStack(t, stackOpts, host1NICID, host2NICID) - - var wq waiter.Queue - we, waiterCH := waiter.NewChannelEntry(nil) - wq.EventRegister(&we, waiter.EventIn) - 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.ErrNoRoute{}, - sockError: tcpip.SockError{ - Err: &tcpip.ErrNoRoute{}, - 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.ErrNoRoute{}, - sockError: tcpip.SockError{ - Err: &tcpip.ErrNoRoute{}, - 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) { - stackOpts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol}, - } - - host1Stack, host2Stack := setupStack(t, stackOpts, host1NICID, host2NICID) - - 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(nil) - clientWQ.EventRegister(&we, waiter.EventOut|waiter.EventErr) - 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.SetRecvError(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. - <-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") - } - - 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 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) { - stackOpts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, - } - - host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID) - - 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 - } - if diff := cmp.Diff(wantRes, <-ch); diff != "" { - t.Fatalf("link resolution result mismatch (-want +got):\n%s", diff) - } - }) - } -} - -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) { - stackOpts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{arp.NewProtocol, ipv4.NewProtocol, ipv6.NewProtocol}, - } - - host1Stack, _ := setupStack(t, stackOpts, host1NICID, host2NICID) - 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{}) - } - if diff := cmp.Diff(stack.ResolvedFieldsResult{RouteInfo: wantRouteInfo, Err: test.expectedErr}, <-ch, cmp.AllowUnexported(stack.RouteInfo{})); diff != "" { - t.Errorf("route resolve result mismatch (-want +got):\n%s", diff) - } - - 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) - - var serverWQ waiter.Queue - serverWE, serverCH := waiter.NewChannelEntry(nil) - serverWQ.EventRegister(&serverWE, waiter.EventIn) - 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, "", 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() - - data := []byte{1, 2} - var pkts stack.PacketBufferList - for _, d := range data { - pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()), - Data: buffer.View([]byte{d}).ToVectorisedView(), - }) - pkt.TransportProtocolNumber = udp.ProtocolNumber - length := uint16(pkt.Size()) - 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 = header.ChecksumCombine(xsum, pkt.Data().AsRange().Checksum()) - udpHdr.SetChecksum(^udpHdr.CalculateChecksum(xsum)) - - pkts.PushBack(pkt) - } - - params := stack.NetworkHeaderParams{ - Protocol: udp.ProtocolNumber, - TTL: 64, - TOS: stack.DefaultTOS, - } - - if n, err := r.WritePackets(nil /* gso */, pkts, params); err != nil { - t.Fatalf("r.WritePackets(nil, %#v, _): %s", params, err) - } else if want := pkts.Len(); want != n { - t.Fatalf("got r.WritePackets(nil, %#v, _) = %d, want = %d", n, params, want) - } - - var writer bytes.Buffer - count := 0 - for { - var rOpts tcpip.ReadOptions - res, err := serverEP.Read(&writer, rOpts) - if err != nil { - if _, ok := err.(*tcpip.ErrWouldBlock); ok { - // Should not have anymore bytes to read after we read the sent - // number of bytes. - if count == len(data) { - break - } - - <-serverCH - continue - } - - t.Fatalf("serverEP.Read(_, %#v): %s", rOpts, err) - } - count += res.Count - } - - if got, want := host2Stack.Stats().UDP.PacketsReceived.Value(), uint64(len(data)); 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) waitForEvent(want eventInfo) error { - if diff := cmp.Diff(want, <-d.c, cmp.AllowUnexported(eventInfo{}), cmpopts.IgnoreFields(stack.NeighborEntry{}, "UpdatedAtNanos")); 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, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) - } - - return listenerEP, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) - } - - return listenerEP, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) - } - - return listenerEP, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := host1Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("host1Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) - } - - return listenerEP, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := routerStack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("routerStack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) - } - - return listenerEP, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := routerStack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("routerStack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) - } - - return listenerEP, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := host2Stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv4.ProtocolNumber, err) - } - - return listenerEP, 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, tcpip.Endpoint, <-chan struct{}) { - var listenerWQ waiter.Queue - 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(nil) - clientWQ.EventRegister(&clientWE, waiter.EventOut) - clientEP, err := host2Stack.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &clientWQ) - if err != nil { - listenerEP.Close() - t.Fatalf("host2Stack.NewEndpoint(%d, %d, _): %s", tcp.ProtocolNumber, ipv6.ProtocolNumber, err) - } - - return listenerEP, 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) - routerStack := stack.New(stackOpts) - host2Stack := stack.New(stackOpts) - 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, "", 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.waitForEvent(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.waitForEvent(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.waitForEvent(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, 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) - } - if err := listenerEP.Listen(1); err != nil { - t.Fatalf("listenerEP.Listen(1): %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.waitForEvent(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 err := nudDisp.waitForEvent(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 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.waitForEvent(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) - } - if err := nudDisp.waitForEvent(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.waitForEvent(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) - } - } - if err := nudDisp.waitForEvent(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, _ := setupStack(t, stackOpts, utils.Host1NICID, utils.Host2NICID) - - // 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: - } - }) - } -} diff --git a/pkg/tcpip/tests/integration/loopback_test.go b/pkg/tcpip/tests/integration/loopback_test.go deleted file mode 100644 index 0a9ea1aa8..000000000 --- a/pkg/tcpip/tests/integration/loopback_test.go +++ /dev/null @@ -1,765 +0,0 @@ -// 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 loopback_test - -import ( - "bytes" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/checker" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/link/channel" - "gvisor.dev/gvisor/pkg/tcpip/link/loopback" - "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" - "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" -) - -var _ ipv6.NDPDispatcher = (*ndpDispatcher)(nil) - -type ndpDispatcher struct{} - -func (*ndpDispatcher) OnDuplicateAddressDetectionResult(tcpip.NICID, tcpip.Address, stack.DADResult) { -} - -func (*ndpDispatcher) OnDefaultRouterDiscovered(tcpip.NICID, tcpip.Address) bool { - return false -} - -func (*ndpDispatcher) OnDefaultRouterInvalidated(tcpip.NICID, tcpip.Address) {} - -func (*ndpDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) bool { - return false -} - -func (*ndpDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) {} - -func (*ndpDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) bool { - return true -} - -func (*ndpDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {} - -func (*ndpDispatcher) OnAutoGenAddressInvalidated(tcpip.NICID, tcpip.AddressWithPrefix) {} - -func (*ndpDispatcher) OnRecursiveDNSServerOption(tcpip.NICID, []tcpip.Address, time.Duration) {} - -func (*ndpDispatcher) OnDNSSearchListOption(tcpip.NICID, []string, time.Duration) {} - -func (*ndpDispatcher) OnDHCPv6Configuration(tcpip.NICID, ipv6.DHCPv6ConfigurationFromNDPRA) {} - -// TestInitialLoopbackAddresses tests that the loopback interface does not -// auto-generate a link-local address when it is brought up. -func TestInitialLoopbackAddresses(t *testing.T) { - const nicID = 1 - - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocolWithOptions(ipv6.Options{ - NDPDisp: &ndpDispatcher{}, - AutoGenLinkLocal: true, - OpaqueIIDOpts: ipv6.OpaqueInterfaceIdentifierOptions{ - NICNameFromID: func(nicID tcpip.NICID, nicName string) string { - t.Fatalf("should not attempt to get name for NIC with ID = %d; nicName = %s", nicID, nicName) - return "" - }, - }, - })}, - }) - - if err := s.CreateNIC(nicID, loopback.New()); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID, err) - } - - nicsInfo := s.NICInfo() - if nicInfo, ok := nicsInfo[nicID]; !ok { - t.Fatalf("did not find NIC with ID = %d in s.NICInfo() = %#v", nicID, nicsInfo) - } else if got := len(nicInfo.ProtocolAddresses); got != 0 { - t.Fatalf("got len(nicInfo.ProtocolAddresses) = %d, want = 0; nicInfo.ProtocolAddresses = %#v", got, nicInfo.ProtocolAddresses) - } -} - -// TestLoopbackAcceptAllInSubnetUDP tests that a loopback interface considers -// itself bound to all addresses in the subnet of an assigned address and UDP -// traffic is sent/received correctly. -func TestLoopbackAcceptAllInSubnetUDP(t *testing.T) { - const ( - nicID = 1 - localPort = 80 - ) - - data := []byte{1, 2, 3, 4} - - ipv4ProtocolAddress := tcpip.ProtocolAddress{ - Protocol: header.IPv4ProtocolNumber, - AddressWithPrefix: utils.Ipv4Addr, - } - ipv4Bytes := []byte(ipv4ProtocolAddress.AddressWithPrefix.Address) - ipv4Bytes[len(ipv4Bytes)-1]++ - otherIPv4Address := tcpip.Address(ipv4Bytes) - - ipv6ProtocolAddress := tcpip.ProtocolAddress{ - Protocol: header.IPv6ProtocolNumber, - AddressWithPrefix: utils.Ipv6Addr, - } - ipv6Bytes := []byte(utils.Ipv6Addr.Address) - ipv6Bytes[len(ipv6Bytes)-1]++ - otherIPv6Address := tcpip.Address(ipv6Bytes) - - tests := []struct { - name string - addAddress tcpip.ProtocolAddress - bindAddr tcpip.Address - dstAddr tcpip.Address - expectRx bool - }{ - { - name: "IPv4 bind to wildcard and send to assigned address", - addAddress: ipv4ProtocolAddress, - dstAddr: ipv4ProtocolAddress.AddressWithPrefix.Address, - expectRx: true, - }, - { - name: "IPv4 bind to wildcard and send to other subnet-local address", - addAddress: ipv4ProtocolAddress, - dstAddr: otherIPv4Address, - expectRx: true, - }, - { - name: "IPv4 bind to wildcard send to other address", - addAddress: ipv4ProtocolAddress, - dstAddr: utils.RemoteIPv4Addr, - expectRx: false, - }, - { - name: "IPv4 bind to other subnet-local address and send to assigned address", - addAddress: ipv4ProtocolAddress, - bindAddr: otherIPv4Address, - dstAddr: ipv4ProtocolAddress.AddressWithPrefix.Address, - expectRx: false, - }, - { - name: "IPv4 bind and send to other subnet-local address", - addAddress: ipv4ProtocolAddress, - bindAddr: otherIPv4Address, - dstAddr: otherIPv4Address, - expectRx: true, - }, - { - name: "IPv4 bind to assigned address and send to other subnet-local address", - addAddress: ipv4ProtocolAddress, - bindAddr: ipv4ProtocolAddress.AddressWithPrefix.Address, - dstAddr: otherIPv4Address, - expectRx: false, - }, - - { - name: "IPv6 bind and send to assigned address", - addAddress: ipv6ProtocolAddress, - bindAddr: utils.Ipv6Addr.Address, - dstAddr: utils.Ipv6Addr.Address, - expectRx: true, - }, - { - name: "IPv6 bind to wildcard and send to other subnet-local address", - addAddress: ipv6ProtocolAddress, - dstAddr: otherIPv6Address, - expectRx: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, - }) - if err := s.CreateNIC(nicID, loopback.New()); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID, err) - } - if err := s.AddProtocolAddress(nicID, test.addAddress); err != nil { - t.Fatalf("AddProtocolAddress(%d, %+v): %s", nicID, test.addAddress, err) - } - s.SetRouteTable([]tcpip.Route{ - { - Destination: header.IPv4EmptySubnet, - NIC: nicID, - }, - { - Destination: header.IPv6EmptySubnet, - NIC: nicID, - }, - }) - - var wq waiter.Queue - rep, err := s.NewEndpoint(udp.ProtocolNumber, test.addAddress.Protocol, &wq) - if err != nil { - t.Fatalf("NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, test.addAddress.Protocol, err) - } - defer rep.Close() - - bindAddr := tcpip.FullAddress{Addr: test.bindAddr, Port: localPort} - if err := rep.Bind(bindAddr); err != nil { - t.Fatalf("rep.Bind(%+v): %s", bindAddr, err) - } - - sep, err := s.NewEndpoint(udp.ProtocolNumber, test.addAddress.Protocol, &wq) - if err != nil { - t.Fatalf("NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, test.addAddress.Protocol, err) - } - defer sep.Close() - - wopts := tcpip.WriteOptions{ - To: &tcpip.FullAddress{ - Addr: test.dstAddr, - Port: localPort, - }, - } - var r bytes.Reader - r.Reset(data) - n, err := sep.Write(&r, wopts) - if err != nil { - t.Fatalf("sep.Write(_, _): %s", err) - } - if want := int64(len(data)); n != want { - t.Fatalf("got sep.Write(_, _) = (%d, nil), want = (%d, nil)", n, want) - } - - var buf bytes.Buffer - opts := tcpip.ReadOptions{NeedRemoteAddr: true} - if res, err := rep.Read(&buf, opts); test.expectRx { - if err != nil { - t.Fatalf("rep.Read(_, %#v): %s", opts, err) - } - if diff := cmp.Diff(tcpip.ReadResult{ - Count: buf.Len(), - Total: buf.Len(), - RemoteAddr: tcpip.FullAddress{ - Addr: test.addAddress.AddressWithPrefix.Address, - }, - }, res, - checker.IgnoreCmpPath("ControlMessages", "RemoteAddr.NIC", "RemoteAddr.Port"), - ); diff != "" { - t.Errorf("rep.Read: unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(data, buf.Bytes()); diff != "" { - t.Errorf("got UDP payload mismatch (-want +got):\n%s", diff) - } - } else if _, ok := err.(*tcpip.ErrWouldBlock); !ok { - t.Fatalf("got rep.Read = (%v, %s) [with data %x], want = (_, %s)", res, err, buf.Bytes(), &tcpip.ErrWouldBlock{}) - } - }) - } -} - -// TestLoopbackSubnetLifetimeBoundToAddr tests that the lifetime of an address -// in a loopback interface's associated subnet is bound to the permanently bound -// address. -func TestLoopbackSubnetLifetimeBoundToAddr(t *testing.T) { - const nicID = 1 - - protoAddr := tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: utils.Ipv4Addr, - } - addrBytes := []byte(utils.Ipv4Addr.Address) - addrBytes[len(addrBytes)-1]++ - otherAddr := tcpip.Address(addrBytes) - - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol}, - }) - if err := s.CreateNIC(nicID, loopback.New()); err != nil { - t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err) - } - if err := s.AddProtocolAddress(nicID, protoAddr); err != nil { - t.Fatalf("s.AddProtocolAddress(%d, %#v): %s", nicID, protoAddr, err) - } - s.SetRouteTable([]tcpip.Route{ - { - Destination: header.IPv4EmptySubnet, - NIC: nicID, - }, - }) - - r, err := s.FindRoute(nicID, otherAddr, utils.RemoteIPv4Addr, ipv4.ProtocolNumber, false /* multicastLoop */) - if err != nil { - t.Fatalf("s.FindRoute(%d, %s, %s, %d, false): %s", nicID, otherAddr, utils.RemoteIPv4Addr, ipv4.ProtocolNumber, err) - } - defer r.Release() - - params := stack.NetworkHeaderParams{ - Protocol: 111, - TTL: 64, - TOS: stack.DefaultTOS, - } - data := buffer.View([]byte{1, 2, 3, 4}) - if err := r.WritePacket(nil /* gso */, params, stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength()), - Data: data.ToVectorisedView(), - })); err != nil { - t.Fatalf("r.WritePacket(nil, %#v, _): %s", params, err) - } - - // Removing the address should make the endpoint invalid. - if err := s.RemoveAddress(nicID, protoAddr.AddressWithPrefix.Address); err != nil { - t.Fatalf("s.RemoveAddress(%d, %s): %s", nicID, protoAddr.AddressWithPrefix.Address, err) - } - { - err := r.WritePacket(nil /* gso */, params, stack.NewPacketBuffer(stack.PacketBufferOptions{ - ReserveHeaderBytes: int(r.MaxHeaderLength()), - Data: data.ToVectorisedView(), - })) - if _, ok := err.(*tcpip.ErrInvalidEndpointState); !ok { - t.Fatalf("got r.WritePacket(nil, %#v, _) = %s, want = %s", params, err, &tcpip.ErrInvalidEndpointState{}) - } - } -} - -// TestLoopbackAcceptAllInSubnetTCP tests that a loopback interface considers -// itself bound to all addresses in the subnet of an assigned address and TCP -// traffic is sent/received correctly. -func TestLoopbackAcceptAllInSubnetTCP(t *testing.T) { - const ( - nicID = 1 - localPort = 80 - ) - - ipv4ProtocolAddress := tcpip.ProtocolAddress{ - Protocol: header.IPv4ProtocolNumber, - AddressWithPrefix: utils.Ipv4Addr, - } - ipv4ProtocolAddress.AddressWithPrefix.PrefixLen = 8 - ipv4Bytes := []byte(ipv4ProtocolAddress.AddressWithPrefix.Address) - ipv4Bytes[len(ipv4Bytes)-1]++ - otherIPv4Address := tcpip.Address(ipv4Bytes) - - ipv6ProtocolAddress := tcpip.ProtocolAddress{ - Protocol: header.IPv6ProtocolNumber, - AddressWithPrefix: utils.Ipv6Addr, - } - ipv6Bytes := []byte(utils.Ipv6Addr.Address) - ipv6Bytes[len(ipv6Bytes)-1]++ - otherIPv6Address := tcpip.Address(ipv6Bytes) - - tests := []struct { - name string - addAddress tcpip.ProtocolAddress - bindAddr tcpip.Address - dstAddr tcpip.Address - expectAccept bool - }{ - { - name: "IPv4 bind to wildcard and send to assigned address", - addAddress: ipv4ProtocolAddress, - dstAddr: ipv4ProtocolAddress.AddressWithPrefix.Address, - expectAccept: true, - }, - { - name: "IPv4 bind to wildcard and send to other subnet-local address", - addAddress: ipv4ProtocolAddress, - dstAddr: otherIPv4Address, - expectAccept: true, - }, - { - name: "IPv4 bind to wildcard send to other address", - addAddress: ipv4ProtocolAddress, - dstAddr: utils.RemoteIPv4Addr, - expectAccept: false, - }, - { - name: "IPv4 bind to other subnet-local address and send to assigned address", - addAddress: ipv4ProtocolAddress, - bindAddr: otherIPv4Address, - dstAddr: ipv4ProtocolAddress.AddressWithPrefix.Address, - expectAccept: false, - }, - { - name: "IPv4 bind and send to other subnet-local address", - addAddress: ipv4ProtocolAddress, - bindAddr: otherIPv4Address, - dstAddr: otherIPv4Address, - expectAccept: true, - }, - { - name: "IPv4 bind to assigned address and send to other subnet-local address", - addAddress: ipv4ProtocolAddress, - bindAddr: ipv4ProtocolAddress.AddressWithPrefix.Address, - dstAddr: otherIPv4Address, - expectAccept: false, - }, - - { - name: "IPv6 bind and send to assigned address", - addAddress: ipv6ProtocolAddress, - bindAddr: utils.Ipv6Addr.Address, - dstAddr: utils.Ipv6Addr.Address, - expectAccept: true, - }, - { - name: "IPv6 bind to wildcard and send to other subnet-local address", - addAddress: ipv6ProtocolAddress, - dstAddr: otherIPv6Address, - expectAccept: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol}, - }) - if err := s.CreateNIC(nicID, loopback.New()); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID, err) - } - if err := s.AddProtocolAddress(nicID, test.addAddress); err != nil { - t.Fatalf("AddProtocolAddress(%d, %#v): %s", nicID, test.addAddress, err) - } - s.SetRouteTable([]tcpip.Route{ - { - Destination: header.IPv4EmptySubnet, - NIC: nicID, - }, - { - Destination: header.IPv6EmptySubnet, - NIC: nicID, - }, - }) - - var wq waiter.Queue - we, ch := waiter.NewChannelEntry(nil) - wq.EventRegister(&we, waiter.EventIn) - defer wq.EventUnregister(&we) - listeningEndpoint, err := s.NewEndpoint(tcp.ProtocolNumber, test.addAddress.Protocol, &wq) - if err != nil { - t.Fatalf("NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, test.addAddress.Protocol, err) - } - defer listeningEndpoint.Close() - - bindAddr := tcpip.FullAddress{Addr: test.bindAddr, Port: localPort} - if err := listeningEndpoint.Bind(bindAddr); err != nil { - t.Fatalf("listeningEndpoint.Bind(%#v): %s", bindAddr, err) - } - - if err := listeningEndpoint.Listen(1); err != nil { - t.Fatalf("listeningEndpoint.Listen(1): %s", err) - } - - connectingEndpoint, err := s.NewEndpoint(tcp.ProtocolNumber, test.addAddress.Protocol, &wq) - if err != nil { - t.Fatalf("s.NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, test.addAddress.Protocol, err) - } - defer connectingEndpoint.Close() - - connectAddr := tcpip.FullAddress{ - Addr: test.dstAddr, - Port: localPort, - } - { - err := connectingEndpoint.Connect(connectAddr) - if _, ok := err.(*tcpip.ErrConnectStarted); !ok { - t.Fatalf("connectingEndpoint.Connect(%#v): %s", connectAddr, err) - } - } - - if !test.expectAccept { - _, _, err := listeningEndpoint.Accept(nil) - if _, ok := err.(*tcpip.ErrWouldBlock); !ok { - t.Fatalf("got listeningEndpoint.Accept(nil) = %s, want = %s", err, &tcpip.ErrWouldBlock{}) - } - return - } - - // Wait for the listening endpoint to be "readable". That is, wait for a - // new connection. - <-ch - var addr tcpip.FullAddress - if _, _, err := listeningEndpoint.Accept(&addr); err != nil { - t.Fatalf("listeningEndpoint.Accept(nil): %s", err) - } - if addr.Addr != test.addAddress.AddressWithPrefix.Address { - t.Errorf("got addr.Addr = %s, want = %s", addr.Addr, test.addAddress.AddressWithPrefix.Address) - } - }) - } -} - -func TestExternalLoopbackTraffic(t *testing.T) { - const ( - nicID1 = 1 - nicID2 = 2 - - ipv4Loopback = tcpip.Address("\x7f\x00\x00\x01") - - numPackets = 1 - ) - - loopbackSourcedICMPv4 := func(e *channel.Endpoint) { - utils.RxICMPv4EchoRequest(e, ipv4Loopback, utils.Ipv4Addr.Address) - } - - loopbackSourcedICMPv6 := func(e *channel.Endpoint) { - utils.RxICMPv6EchoRequest(e, header.IPv6Loopback, utils.Ipv6Addr.Address) - } - - loopbackDestinedICMPv4 := func(e *channel.Endpoint) { - utils.RxICMPv4EchoRequest(e, utils.RemoteIPv4Addr, ipv4Loopback) - } - - loopbackDestinedICMPv6 := func(e *channel.Endpoint) { - utils.RxICMPv6EchoRequest(e, utils.RemoteIPv6Addr, header.IPv6Loopback) - } - - invalidSrcAddrStat := func(s tcpip.IPStats) *tcpip.StatCounter { - return s.InvalidSourceAddressesReceived - } - - invalidDestAddrStat := func(s tcpip.IPStats) *tcpip.StatCounter { - return s.InvalidDestinationAddressesReceived - } - - tests := []struct { - name string - dropExternalLoopback bool - forwarding bool - rxICMP func(*channel.Endpoint) - invalidAddressStat func(tcpip.IPStats) *tcpip.StatCounter - shouldAccept bool - }{ - { - name: "IPv4 external loopback sourced traffic without forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: false, - rxICMP: loopbackSourcedICMPv4, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: true, - }, - { - name: "IPv4 external loopback sourced traffic without forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: false, - rxICMP: loopbackSourcedICMPv4, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: false, - }, - { - name: "IPv4 external loopback sourced traffic with forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: true, - rxICMP: loopbackSourcedICMPv4, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: true, - }, - { - name: "IPv4 external loopback sourced traffic with forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: true, - rxICMP: loopbackSourcedICMPv4, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: false, - }, - { - name: "IPv4 external loopback destined traffic without forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: false, - rxICMP: loopbackDestinedICMPv4, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: false, - }, - { - name: "IPv4 external loopback destined traffic without forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: false, - rxICMP: loopbackDestinedICMPv4, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: false, - }, - { - name: "IPv4 external loopback destined traffic with forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: true, - rxICMP: loopbackDestinedICMPv4, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: true, - }, - { - name: "IPv4 external loopback destined traffic with forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: true, - rxICMP: loopbackDestinedICMPv4, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: false, - }, - - { - name: "IPv6 external loopback sourced traffic without forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: false, - rxICMP: loopbackSourcedICMPv6, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: true, - }, - { - name: "IPv6 external loopback sourced traffic without forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: false, - rxICMP: loopbackSourcedICMPv6, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: false, - }, - { - name: "IPv6 external loopback sourced traffic with forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: true, - rxICMP: loopbackSourcedICMPv6, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: true, - }, - { - name: "IPv6 external loopback sourced traffic with forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: true, - rxICMP: loopbackSourcedICMPv6, - invalidAddressStat: invalidSrcAddrStat, - shouldAccept: false, - }, - { - name: "IPv6 external loopback destined traffic without forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: false, - rxICMP: loopbackDestinedICMPv6, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: false, - }, - { - name: "IPv6 external loopback destined traffic without forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: false, - rxICMP: loopbackDestinedICMPv6, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: false, - }, - { - name: "IPv6 external loopback destined traffic with forwarding and drop external loopback disabled", - dropExternalLoopback: false, - forwarding: true, - rxICMP: loopbackDestinedICMPv6, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: true, - }, - { - name: "IPv6 external loopback destined traffic with forwarding and drop external loopback enabled", - dropExternalLoopback: true, - forwarding: true, - rxICMP: loopbackDestinedICMPv6, - invalidAddressStat: invalidDestAddrStat, - shouldAccept: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ - ipv4.NewProtocolWithOptions(ipv4.Options{ - DropExternalLoopbackTraffic: test.dropExternalLoopback, - }), - ipv6.NewProtocolWithOptions(ipv6.Options{ - DropExternalLoopbackTraffic: test.dropExternalLoopback, - }), - }, - TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4, icmp.NewProtocol6}, - }) - e := channel.New(1, header.IPv6MinimumMTU, "") - if err := s.CreateNIC(nicID1, e); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID1, err) - } - if err := s.AddAddressWithPrefix(nicID1, ipv4.ProtocolNumber, utils.Ipv4Addr); err != nil { - t.Fatalf("AddAddressWithPrefix(%d, %d, %s): %s", nicID1, ipv4.ProtocolNumber, utils.Ipv4Addr, err) - } - if err := s.AddAddressWithPrefix(nicID1, ipv6.ProtocolNumber, utils.Ipv6Addr); err != nil { - t.Fatalf("AddAddressWithPrefix(%d, %d, %s): %s", nicID1, ipv6.ProtocolNumber, utils.Ipv6Addr, err) - } - - if err := s.CreateNIC(nicID2, loopback.New()); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID2, err) - } - if err := s.AddAddress(nicID2, ipv4.ProtocolNumber, ipv4Loopback); err != nil { - t.Fatalf("AddAddress(%d, %d, %s): %s", nicID2, ipv4.ProtocolNumber, ipv4Loopback, err) - } - if err := s.AddAddress(nicID2, ipv6.ProtocolNumber, header.IPv6Loopback); err != nil { - t.Fatalf("AddAddress(%d, %d, %s): %s", nicID2, ipv6.ProtocolNumber, header.IPv6Loopback, err) - } - - if test.forwarding { - if err := s.SetForwarding(ipv4.ProtocolNumber, true); err != nil { - t.Fatalf("SetForwarding(%d, true): %s", ipv4.ProtocolNumber, err) - } - if err := s.SetForwarding(ipv6.ProtocolNumber, true); err != nil { - t.Fatalf("SetForwarding(%d, true): %s", ipv6.ProtocolNumber, err) - } - } - - s.SetRouteTable([]tcpip.Route{ - tcpip.Route{ - Destination: header.IPv4EmptySubnet, - NIC: nicID1, - }, - tcpip.Route{ - Destination: header.IPv6EmptySubnet, - NIC: nicID1, - }, - tcpip.Route{ - Destination: ipv4Loopback.WithPrefix().Subnet(), - NIC: nicID2, - }, - tcpip.Route{ - Destination: header.IPv6Loopback.WithPrefix().Subnet(), - NIC: nicID2, - }, - }) - - stats := s.Stats().IP - invalidAddressStat := test.invalidAddressStat(stats) - deliveredPacketsStat := stats.PacketsDelivered - if got := invalidAddressStat.Value(); got != 0 { - t.Fatalf("got invalidAddressStat.Value() = %d, want = 0", got) - } - if got := deliveredPacketsStat.Value(); got != 0 { - t.Fatalf("got deliveredPacketsStat.Value() = %d, want = 0", got) - } - test.rxICMP(e) - var expectedInvalidPackets uint64 - if !test.shouldAccept { - expectedInvalidPackets = numPackets - } - if got := invalidAddressStat.Value(); got != expectedInvalidPackets { - t.Fatalf("got invalidAddressStat.Value() = %d, want = %d", got, expectedInvalidPackets) - } - if got, want := deliveredPacketsStat.Value(), numPackets-expectedInvalidPackets; got != want { - t.Fatalf("got deliveredPacketsStat.Value() = %d, want = %d", got, want) - } - }) - } -} diff --git a/pkg/tcpip/tests/integration/multicast_broadcast_test.go b/pkg/tcpip/tests/integration/multicast_broadcast_test.go deleted file mode 100644 index 77f4a88ec..000000000 --- a/pkg/tcpip/tests/integration/multicast_broadcast_test.go +++ /dev/null @@ -1,719 +0,0 @@ -// 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 multicast_broadcast_test - -import ( - "bytes" - "testing" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/checker" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/link/channel" - "gvisor.dev/gvisor/pkg/tcpip/link/loopback" - "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" - "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" - "gvisor.dev/gvisor/pkg/tcpip/transport/udp" - "gvisor.dev/gvisor/pkg/waiter" -) - -const ( - defaultMTU = 1280 - ttl = 255 -) - -// TestPingMulticastBroadcast tests that responding to an Echo Request destined -// to a multicast or broadcast address uses a unicast source address for the -// reply. -func TestPingMulticastBroadcast(t *testing.T) { - const nicID = 1 - - tests := []struct { - name string - protoNum tcpip.NetworkProtocolNumber - rxICMP func(*channel.Endpoint, tcpip.Address, tcpip.Address) - srcAddr tcpip.Address - dstAddr tcpip.Address - expectedSrc tcpip.Address - }{ - { - name: "IPv4 unicast", - protoNum: header.IPv4ProtocolNumber, - dstAddr: utils.Ipv4Addr.Address, - srcAddr: utils.RemoteIPv4Addr, - rxICMP: utils.RxICMPv4EchoRequest, - expectedSrc: utils.Ipv4Addr.Address, - }, - { - name: "IPv4 directed broadcast", - protoNum: header.IPv4ProtocolNumber, - rxICMP: utils.RxICMPv4EchoRequest, - srcAddr: utils.RemoteIPv4Addr, - dstAddr: utils.Ipv4SubnetBcast, - expectedSrc: utils.Ipv4Addr.Address, - }, - { - name: "IPv4 broadcast", - protoNum: header.IPv4ProtocolNumber, - rxICMP: utils.RxICMPv4EchoRequest, - srcAddr: utils.RemoteIPv4Addr, - dstAddr: header.IPv4Broadcast, - expectedSrc: utils.Ipv4Addr.Address, - }, - { - name: "IPv4 all-systems multicast", - protoNum: header.IPv4ProtocolNumber, - rxICMP: utils.RxICMPv4EchoRequest, - srcAddr: utils.RemoteIPv4Addr, - dstAddr: header.IPv4AllSystems, - expectedSrc: utils.Ipv4Addr.Address, - }, - { - name: "IPv6 unicast", - protoNum: header.IPv6ProtocolNumber, - rxICMP: utils.RxICMPv6EchoRequest, - srcAddr: utils.RemoteIPv6Addr, - dstAddr: utils.Ipv6Addr.Address, - expectedSrc: utils.Ipv6Addr.Address, - }, - { - name: "IPv6 all-nodes multicast", - protoNum: header.IPv6ProtocolNumber, - rxICMP: utils.RxICMPv6EchoRequest, - srcAddr: utils.RemoteIPv6Addr, - dstAddr: header.IPv6AllNodesMulticastAddress, - expectedSrc: utils.Ipv6Addr.Address, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4, icmp.NewProtocol6}, - }) - // We only expect a single packet in response to our ICMP Echo Request. - e := channel.New(1, defaultMTU, "") - if err := s.CreateNIC(nicID, e); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID, err) - } - ipv4ProtoAddr := tcpip.ProtocolAddress{Protocol: header.IPv4ProtocolNumber, AddressWithPrefix: utils.Ipv4Addr} - if err := s.AddProtocolAddress(nicID, ipv4ProtoAddr); err != nil { - t.Fatalf("AddProtocolAddress(%d, %#v): %s", nicID, ipv4ProtoAddr, err) - } - ipv6ProtoAddr := tcpip.ProtocolAddress{Protocol: header.IPv6ProtocolNumber, AddressWithPrefix: utils.Ipv6Addr} - if err := s.AddProtocolAddress(nicID, ipv6ProtoAddr); err != nil { - t.Fatalf("AddProtocolAddress(%d, %#v): %s", nicID, ipv6ProtoAddr, err) - } - - // Default routes for IPv4 and IPv6 so ICMP can find a route to the remote - // node when attempting to send the ICMP Echo Reply. - s.SetRouteTable([]tcpip.Route{ - { - Destination: header.IPv6EmptySubnet, - NIC: nicID, - }, - { - Destination: header.IPv4EmptySubnet, - NIC: nicID, - }, - }) - - test.rxICMP(e, test.srcAddr, test.dstAddr) - pkt, ok := e.Read() - if !ok { - t.Fatal("expected ICMP response") - } - - if pkt.Route.LocalAddress != test.expectedSrc { - t.Errorf("got pkt.Route.LocalAddress = %s, want = %s", pkt.Route.LocalAddress, test.expectedSrc) - } - // The destination of the response packet should be the source of the - // original packet. - if pkt.Route.RemoteAddress != test.srcAddr { - t.Errorf("got pkt.Route.RemoteAddress = %s, want = %s", pkt.Route.RemoteAddress, test.srcAddr) - } - - src, dst := s.NetworkProtocolInstance(test.protoNum).ParseAddresses(stack.PayloadSince(pkt.Pkt.NetworkHeader())) - if src != test.expectedSrc { - t.Errorf("got pkt source = %s, want = %s", src, test.expectedSrc) - } - // The destination of the response packet should be the source of the - // original packet. - if dst != test.srcAddr { - t.Errorf("got pkt destination = %s, want = %s", dst, test.srcAddr) - } - }) - } - -} - -func rxIPv4UDP(e *channel.Endpoint, src, dst tcpip.Address, data []byte) { - payloadLen := header.UDPMinimumSize + len(data) - totalLen := header.IPv4MinimumSize + payloadLen - hdr := buffer.NewPrependable(totalLen) - u := header.UDP(hdr.Prepend(payloadLen)) - u.Encode(&header.UDPFields{ - SrcPort: utils.RemotePort, - DstPort: utils.LocalPort, - Length: uint16(payloadLen), - }) - copy(u.Payload(), data) - sum := header.PseudoHeaderChecksum(udp.ProtocolNumber, src, dst, uint16(payloadLen)) - sum = header.Checksum(data, sum) - u.SetChecksum(^u.CalculateChecksum(sum)) - - ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize)) - ip.Encode(&header.IPv4Fields{ - TotalLength: uint16(totalLen), - Protocol: uint8(udp.ProtocolNumber), - TTL: ttl, - SrcAddr: src, - DstAddr: dst, - }) - ip.SetChecksum(^ip.CalculateChecksum()) - - e.InjectInbound(header.IPv4ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: hdr.View().ToVectorisedView(), - })) -} - -func rxIPv6UDP(e *channel.Endpoint, src, dst tcpip.Address, data []byte) { - payloadLen := header.UDPMinimumSize + len(data) - hdr := buffer.NewPrependable(header.IPv6MinimumSize + payloadLen) - u := header.UDP(hdr.Prepend(payloadLen)) - u.Encode(&header.UDPFields{ - SrcPort: utils.RemotePort, - DstPort: utils.LocalPort, - Length: uint16(payloadLen), - }) - copy(u.Payload(), data) - sum := header.PseudoHeaderChecksum(udp.ProtocolNumber, src, dst, uint16(payloadLen)) - sum = header.Checksum(data, sum) - u.SetChecksum(^u.CalculateChecksum(sum)) - - ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) - ip.Encode(&header.IPv6Fields{ - PayloadLength: uint16(payloadLen), - TransportProtocol: udp.ProtocolNumber, - HopLimit: ttl, - SrcAddr: src, - DstAddr: dst, - }) - - e.InjectInbound(header.IPv6ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: hdr.View().ToVectorisedView(), - })) -} - -// TestIncomingMulticastAndBroadcast tests receiving a packet destined to some -// multicast or broadcast address. -func TestIncomingMulticastAndBroadcast(t *testing.T) { - const nicID = 1 - - data := []byte{1, 2, 3, 4} - - tests := []struct { - name string - proto tcpip.NetworkProtocolNumber - remoteAddr tcpip.Address - localAddr tcpip.AddressWithPrefix - rxUDP func(*channel.Endpoint, tcpip.Address, tcpip.Address, []byte) - bindAddr tcpip.Address - dstAddr tcpip.Address - expectRx bool - }{ - { - name: "IPv4 unicast binding to unicast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: utils.Ipv4Addr.Address, - dstAddr: utils.Ipv4Addr.Address, - expectRx: true, - }, - { - name: "IPv4 unicast binding to broadcast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: header.IPv4Broadcast, - dstAddr: utils.Ipv4Addr.Address, - expectRx: false, - }, - { - name: "IPv4 unicast binding to wildcard", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - dstAddr: utils.Ipv4Addr.Address, - expectRx: true, - }, - - { - name: "IPv4 directed broadcast binding to subnet broadcast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: utils.Ipv4SubnetBcast, - dstAddr: utils.Ipv4SubnetBcast, - expectRx: true, - }, - { - name: "IPv4 directed broadcast binding to broadcast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: header.IPv4Broadcast, - dstAddr: utils.Ipv4SubnetBcast, - expectRx: false, - }, - { - name: "IPv4 directed broadcast binding to wildcard", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - dstAddr: utils.Ipv4SubnetBcast, - expectRx: true, - }, - - { - name: "IPv4 broadcast binding to broadcast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: header.IPv4Broadcast, - dstAddr: header.IPv4Broadcast, - expectRx: true, - }, - { - name: "IPv4 broadcast binding to subnet broadcast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: utils.Ipv4SubnetBcast, - dstAddr: header.IPv4Broadcast, - expectRx: false, - }, - { - name: "IPv4 broadcast binding to wildcard", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - dstAddr: utils.Ipv4SubnetBcast, - expectRx: true, - }, - - { - name: "IPv4 all-systems multicast binding to all-systems multicast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: header.IPv4AllSystems, - dstAddr: header.IPv4AllSystems, - expectRx: true, - }, - { - name: "IPv4 all-systems multicast binding to wildcard", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - dstAddr: header.IPv4AllSystems, - expectRx: true, - }, - { - name: "IPv4 all-systems multicast binding to unicast", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - bindAddr: utils.Ipv4Addr.Address, - dstAddr: header.IPv4AllSystems, - expectRx: false, - }, - - // IPv6 has no notion of a broadcast. - { - name: "IPv6 unicast binding to wildcard", - dstAddr: utils.Ipv6Addr.Address, - proto: header.IPv6ProtocolNumber, - remoteAddr: utils.RemoteIPv6Addr, - localAddr: utils.Ipv6Addr, - rxUDP: rxIPv6UDP, - expectRx: true, - }, - { - name: "IPv6 broadcast-like address binding to wildcard", - dstAddr: utils.Ipv6SubnetBcast, - proto: header.IPv6ProtocolNumber, - remoteAddr: utils.RemoteIPv6Addr, - localAddr: utils.Ipv6Addr, - rxUDP: rxIPv6UDP, - expectRx: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, - }) - e := channel.New(0, defaultMTU, "") - if err := s.CreateNIC(nicID, e); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID, err) - } - protoAddr := tcpip.ProtocolAddress{Protocol: test.proto, AddressWithPrefix: test.localAddr} - if err := s.AddProtocolAddress(nicID, protoAddr); err != nil { - t.Fatalf("AddProtocolAddress(%d, %#v): %s", nicID, protoAddr, err) - } - - var wq waiter.Queue - ep, err := s.NewEndpoint(udp.ProtocolNumber, test.proto, &wq) - if err != nil { - t.Fatalf("NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, test.proto, err) - } - defer ep.Close() - - bindAddr := tcpip.FullAddress{Addr: test.bindAddr, Port: utils.LocalPort} - if err := ep.Bind(bindAddr); err != nil { - t.Fatalf("ep.Bind(%#v): %s", bindAddr, err) - } - - test.rxUDP(e, test.remoteAddr, test.dstAddr, data) - var buf bytes.Buffer - var opts tcpip.ReadOptions - if res, err := ep.Read(&buf, opts); test.expectRx { - if err != nil { - t.Fatalf("ep.Read(_, %#v): %s", opts, err) - } - if diff := cmp.Diff(tcpip.ReadResult{ - Count: buf.Len(), - Total: buf.Len(), - }, res, checker.IgnoreCmpPath("ControlMessages")); diff != "" { - t.Errorf("ep.Read: unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(data, buf.Bytes()); diff != "" { - t.Errorf("got UDP payload mismatch (-want +got):\n%s", diff) - } - } else if _, ok := err.(*tcpip.ErrWouldBlock); !ok { - t.Fatalf("got Read = (%v, %s) [with data %x], want = (_, %s)", res, err, buf.Bytes(), &tcpip.ErrWouldBlock{}) - } - }) - } -} - -// TestReuseAddrAndBroadcast makes sure broadcast packets are received by all -// interested endpoints. -func TestReuseAddrAndBroadcast(t *testing.T) { - const ( - nicID = 1 - localPort = 9000 - loopbackBroadcast = tcpip.Address("\x7f\xff\xff\xff") - ) - - tests := []struct { - name string - broadcastAddr tcpip.Address - }{ - { - name: "Subnet directed broadcast", - broadcastAddr: loopbackBroadcast, - }, - { - name: "IPv4 broadcast", - broadcastAddr: header.IPv4Broadcast, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, - }) - if err := s.CreateNIC(nicID, loopback.New()); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID, err) - } - protoAddr := tcpip.ProtocolAddress{ - Protocol: header.IPv4ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: "\x7f\x00\x00\x01", - PrefixLen: 8, - }, - } - if err := s.AddProtocolAddress(nicID, protoAddr); err != nil { - t.Fatalf("AddProtocolAddress(%d, %#v): %s", nicID, protoAddr, err) - } - - s.SetRouteTable([]tcpip.Route{ - { - // We use the empty subnet instead of just the loopback subnet so we - // also have a route to the IPv4 Broadcast address. - Destination: header.IPv4EmptySubnet, - NIC: nicID, - }, - }) - - type endpointAndWaiter struct { - ep tcpip.Endpoint - ch chan struct{} - } - var eps []endpointAndWaiter - // We create endpoints that bind to both the wildcard address and the - // broadcast address to make sure both of these types of "broadcast - // interested" endpoints receive broadcast packets. - for _, bindWildcard := range []bool{false, true} { - // Create multiple endpoints for each type of "broadcast interested" - // endpoint so we can test that all endpoints receive the broadcast - // packet. - for i := 0; i < 2; i++ { - var wq waiter.Queue - we, ch := waiter.NewChannelEntry(nil) - wq.EventRegister(&we, waiter.EventIn) - ep, err := s.NewEndpoint(udp.ProtocolNumber, ipv4.ProtocolNumber, &wq) - if err != nil { - t.Fatalf("(eps[%d]) NewEndpoint(%d, %d, _): %s", len(eps), udp.ProtocolNumber, ipv4.ProtocolNumber, err) - } - defer ep.Close() - - ep.SocketOptions().SetReuseAddress(true) - ep.SocketOptions().SetBroadcast(true) - - bindAddr := tcpip.FullAddress{Port: localPort} - if bindWildcard { - if err := ep.Bind(bindAddr); err != nil { - t.Fatalf("eps[%d].Bind(%#v): %s", len(eps), bindAddr, err) - } - } else { - bindAddr.Addr = test.broadcastAddr - if err := ep.Bind(bindAddr); err != nil { - t.Fatalf("eps[%d].Bind(%#v): %s", len(eps), bindAddr, err) - } - } - - eps = append(eps, endpointAndWaiter{ep: ep, ch: ch}) - } - } - - for i, wep := range eps { - writeOpts := tcpip.WriteOptions{ - To: &tcpip.FullAddress{ - Addr: test.broadcastAddr, - Port: localPort, - }, - } - data := []byte{byte(i), 2, 3, 4} - var r bytes.Reader - r.Reset(data) - if n, err := wep.ep.Write(&r, writeOpts); err != nil { - t.Fatalf("eps[%d].Write(_, _): %s", i, err) - } else if want := int64(len(data)); n != want { - t.Fatalf("got eps[%d].Write(_, _) = (%d, nil), want = (%d, nil)", i, n, want) - } - - for j, rep := range eps { - // Wait for the endpoint to become readable. - <-rep.ch - - var buf bytes.Buffer - result, err := rep.ep.Read(&buf, tcpip.ReadOptions{}) - if err != nil { - t.Errorf("(eps[%d] write) eps[%d].Read: %s", i, j, err) - continue - } - if diff := cmp.Diff(tcpip.ReadResult{ - Count: buf.Len(), - Total: buf.Len(), - }, result, checker.IgnoreCmpPath("ControlMessages")); diff != "" { - t.Errorf("(eps[%d] write) eps[%d].Read: unexpected result (-want +got):\n%s", i, j, diff) - } - if diff := cmp.Diff([]byte(data), buf.Bytes()); diff != "" { - t.Errorf("(eps[%d] write) got UDP payload from eps[%d] mismatch (-want +got):\n%s", i, j, diff) - } - } - } - }) - } -} - -func TestUDPAddRemoveMembershipSocketOption(t *testing.T) { - const ( - nicID = 1 - ) - - data := []byte{1, 2, 3, 4} - - tests := []struct { - name string - proto tcpip.NetworkProtocolNumber - remoteAddr tcpip.Address - localAddr tcpip.AddressWithPrefix - rxUDP func(*channel.Endpoint, tcpip.Address, tcpip.Address, []byte) - multicastAddr tcpip.Address - }{ - { - name: "IPv4 unicast binding to unicast", - multicastAddr: "\xe0\x01\x02\x03", - proto: header.IPv4ProtocolNumber, - remoteAddr: utils.RemoteIPv4Addr, - localAddr: utils.Ipv4Addr, - rxUDP: rxIPv4UDP, - }, - { - name: "IPv6 broadcast-like address binding to wildcard", - multicastAddr: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04", - proto: header.IPv6ProtocolNumber, - remoteAddr: utils.RemoteIPv6Addr, - localAddr: utils.Ipv6Addr, - rxUDP: rxIPv6UDP, - }, - } - - subTests := []struct { - name string - specifyNICID bool - specifyNICAddr bool - }{ - { - name: "Specify NIC ID and NIC address", - specifyNICID: true, - specifyNICAddr: true, - }, - { - name: "Don't specify NIC ID or NIC address", - specifyNICID: false, - specifyNICAddr: false, - }, - { - name: "Specify NIC ID but don't specify NIC address", - specifyNICID: true, - specifyNICAddr: false, - }, - { - name: "Don't specify NIC ID but specify NIC address", - specifyNICID: false, - specifyNICAddr: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - for _, subTest := range subTests { - t.Run(subTest.name, func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, - }) - e := channel.New(0, defaultMTU, "") - if err := s.CreateNIC(nicID, e); err != nil { - t.Fatalf("CreateNIC(%d, _): %s", nicID, err) - } - protoAddr := tcpip.ProtocolAddress{Protocol: test.proto, AddressWithPrefix: test.localAddr} - if err := s.AddProtocolAddress(nicID, protoAddr); err != nil { - t.Fatalf("AddProtocolAddress(%d, %#v): %s", nicID, protoAddr, err) - } - - // Set the route table so that UDP can find a NIC that is - // routable to the multicast address when the NIC isn't specified. - if !subTest.specifyNICID && !subTest.specifyNICAddr { - s.SetRouteTable([]tcpip.Route{ - { - Destination: header.IPv6EmptySubnet, - NIC: nicID, - }, - { - Destination: header.IPv4EmptySubnet, - NIC: nicID, - }, - }) - } - - var wq waiter.Queue - ep, err := s.NewEndpoint(udp.ProtocolNumber, test.proto, &wq) - if err != nil { - t.Fatalf("NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, test.proto, err) - } - defer ep.Close() - - bindAddr := tcpip.FullAddress{Port: utils.LocalPort} - if err := ep.Bind(bindAddr); err != nil { - t.Fatalf("ep.Bind(%#v): %s", bindAddr, err) - } - - memOpt := tcpip.MembershipOption{MulticastAddr: test.multicastAddr} - if subTest.specifyNICID { - memOpt.NIC = nicID - } - if subTest.specifyNICAddr { - memOpt.InterfaceAddr = test.localAddr.Address - } - - // We should receive UDP packets to the group once we join the - // multicast group. - addOpt := tcpip.AddMembershipOption(memOpt) - if err := ep.SetSockOpt(&addOpt); err != nil { - t.Fatalf("ep.SetSockOpt(&%#v): %s", addOpt, err) - } - test.rxUDP(e, test.remoteAddr, test.multicastAddr, data) - var buf bytes.Buffer - result, err := ep.Read(&buf, tcpip.ReadOptions{}) - if err != nil { - t.Fatalf("ep.Read: %s", err) - } else { - if diff := cmp.Diff(tcpip.ReadResult{ - Count: buf.Len(), - Total: buf.Len(), - }, result, checker.IgnoreCmpPath("ControlMessages")); diff != "" { - t.Errorf("ep.Read: unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(data, buf.Bytes()); diff != "" { - t.Errorf("got UDP payload mismatch (-want +got):\n%s", diff) - } - } - - // We should not receive UDP packets to the group once we leave - // the multicast group. - removeOpt := tcpip.RemoveMembershipOption(memOpt) - if err := ep.SetSockOpt(&removeOpt); err != nil { - t.Fatalf("ep.SetSockOpt(&%#v): %s", removeOpt, err) - } - { - _, err := ep.Read(&buf, tcpip.ReadOptions{}) - if _, ok := err.(*tcpip.ErrWouldBlock); !ok { - t.Fatalf("got ep.Read = (_, %s), want = (_, %s)", err, &tcpip.ErrWouldBlock{}) - } - } - }) - } - }) - } -} diff --git a/pkg/tcpip/tests/integration/route_test.go b/pkg/tcpip/tests/integration/route_test.go deleted file mode 100644 index 568a982bb..000000000 --- a/pkg/tcpip/tests/integration/route_test.go +++ /dev/null @@ -1,432 +0,0 @@ -// 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 route_test - -import ( - "bytes" - "fmt" - "testing" - - "github.com/google/go-cmp/cmp" - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "gvisor.dev/gvisor/pkg/tcpip/checker" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/link/channel" - "gvisor.dev/gvisor/pkg/tcpip/link/loopback" - "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" - "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" - "gvisor.dev/gvisor/pkg/tcpip/transport/udp" - "gvisor.dev/gvisor/pkg/waiter" -) - -// TestLocalPing tests pinging a remote that is local the stack. -// -// This tests that a local route is created and packets do not leave the stack. -func TestLocalPing(t *testing.T) { - const ( - nicID = 1 - ipv4Loopback = tcpip.Address("\x7f\x00\x00\x01") - - // icmpDataOffset is the offset to the data in both ICMPv4 and ICMPv6 echo - // request/reply packets. - icmpDataOffset = 8 - ) - - channelEP := func() stack.LinkEndpoint { return channel.New(1, header.IPv6MinimumMTU, "") } - channelEPCheck := func(t *testing.T, e stack.LinkEndpoint) { - channelEP := e.(*channel.Endpoint) - if n := channelEP.Drain(); n != 0 { - t.Fatalf("got channelEP.Drain() = %d, want = 0", n) - } - } - - ipv4ICMPBuf := func(t *testing.T) buffer.View { - 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 buffer.View(hdr) - } - - ipv6ICMPBuf := func(t *testing.T) buffer.View { - data := [8]byte{1, 2, 3, 4, 5, 6, 7, 9} - 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 buffer.View(hdr) - } - - tests := []struct { - name string - transProto tcpip.TransportProtocolNumber - netProto tcpip.NetworkProtocolNumber - linkEndpoint func() stack.LinkEndpoint - localAddr tcpip.Address - icmpBuf func(*testing.T) buffer.View - expectedConnectErr tcpip.Error - checkLinkEndpoint func(t *testing.T, e stack.LinkEndpoint) - }{ - { - name: "IPv4 loopback", - transProto: icmp.ProtocolNumber4, - netProto: ipv4.ProtocolNumber, - linkEndpoint: loopback.New, - localAddr: ipv4Loopback, - icmpBuf: ipv4ICMPBuf, - checkLinkEndpoint: func(*testing.T, stack.LinkEndpoint) {}, - }, - { - name: "IPv6 loopback", - transProto: icmp.ProtocolNumber6, - netProto: ipv6.ProtocolNumber, - linkEndpoint: loopback.New, - localAddr: header.IPv6Loopback, - icmpBuf: ipv6ICMPBuf, - checkLinkEndpoint: func(*testing.T, stack.LinkEndpoint) {}, - }, - { - name: "IPv4 non-loopback", - transProto: icmp.ProtocolNumber4, - netProto: ipv4.ProtocolNumber, - linkEndpoint: channelEP, - localAddr: utils.Ipv4Addr.Address, - icmpBuf: ipv4ICMPBuf, - checkLinkEndpoint: channelEPCheck, - }, - { - name: "IPv6 non-loopback", - transProto: icmp.ProtocolNumber6, - netProto: ipv6.ProtocolNumber, - linkEndpoint: channelEP, - localAddr: utils.Ipv6Addr.Address, - icmpBuf: ipv6ICMPBuf, - checkLinkEndpoint: channelEPCheck, - }, - { - name: "IPv4 loopback without local address", - transProto: icmp.ProtocolNumber4, - netProto: ipv4.ProtocolNumber, - linkEndpoint: loopback.New, - icmpBuf: ipv4ICMPBuf, - expectedConnectErr: &tcpip.ErrNoRoute{}, - checkLinkEndpoint: func(*testing.T, stack.LinkEndpoint) {}, - }, - { - name: "IPv6 loopback without local address", - transProto: icmp.ProtocolNumber6, - netProto: ipv6.ProtocolNumber, - linkEndpoint: loopback.New, - icmpBuf: ipv6ICMPBuf, - expectedConnectErr: &tcpip.ErrNoRoute{}, - checkLinkEndpoint: func(*testing.T, stack.LinkEndpoint) {}, - }, - { - name: "IPv4 non-loopback without local address", - transProto: icmp.ProtocolNumber4, - netProto: ipv4.ProtocolNumber, - linkEndpoint: channelEP, - icmpBuf: ipv4ICMPBuf, - expectedConnectErr: &tcpip.ErrNoRoute{}, - checkLinkEndpoint: channelEPCheck, - }, - { - name: "IPv6 non-loopback without local address", - transProto: icmp.ProtocolNumber6, - netProto: ipv6.ProtocolNumber, - linkEndpoint: channelEP, - icmpBuf: ipv6ICMPBuf, - expectedConnectErr: &tcpip.ErrNoRoute{}, - checkLinkEndpoint: channelEPCheck, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - for _, dropExternalLoopback := range []bool{true, false} { - t.Run(fmt.Sprintf("DropExternalLoopback=%t", dropExternalLoopback), func(t *testing.T) { - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ - ipv4.NewProtocolWithOptions(ipv4.Options{ - DropExternalLoopbackTraffic: dropExternalLoopback, - }), - ipv6.NewProtocolWithOptions(ipv6.Options{ - DropExternalLoopbackTraffic: dropExternalLoopback, - }), - }, - TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4, icmp.NewProtocol6}, - HandleLocal: true, - }) - e := test.linkEndpoint() - if err := s.CreateNIC(nicID, e); err != nil { - t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err) - } - - if len(test.localAddr) != 0 { - if err := s.AddAddress(nicID, test.netProto, test.localAddr); err != nil { - t.Fatalf("s.AddAddress(%d, %d, %s): %s", nicID, test.netProto, test.localAddr, err) - } - } - - var wq waiter.Queue - we, ch := waiter.NewChannelEntry(nil) - wq.EventRegister(&we, waiter.EventIn) - ep, err := s.NewEndpoint(test.transProto, test.netProto, &wq) - if err != nil { - t.Fatalf("s.NewEndpoint(%d, %d, _): %s", test.transProto, test.netProto, err) - } - defer ep.Close() - - connAddr := tcpip.FullAddress{Addr: test.localAddr} - if err := ep.Connect(connAddr); err != test.expectedConnectErr { - t.Fatalf("got ep.Connect(%#v) = %s, want = %s", connAddr, err, test.expectedConnectErr) - } - - if test.expectedConnectErr != nil { - return - } - - var r bytes.Reader - payload := test.icmpBuf(t) - r.Reset(payload) - var wOpts tcpip.WriteOptions - if n, err := ep.Write(&r, wOpts); err != nil { - t.Fatalf("ep.Write(%#v, %#v): %s", payload, wOpts, err) - } else if n != int64(len(payload)) { - t.Fatalf("got ep.Write(%#v, %#v) = (%d, _, nil), want = (%d, _, nil)", payload, wOpts, n, len(payload)) - } - - // Wait for the endpoint to become readable. - <-ch - - var w bytes.Buffer - rr, err := ep.Read(&w, tcpip.ReadOptions{ - NeedRemoteAddr: true, - }) - if err != nil { - t.Fatalf("ep.Read(...): %s", err) - } - if diff := cmp.Diff(buffer.View(w.Bytes()[icmpDataOffset:]), payload[icmpDataOffset:]); diff != "" { - t.Errorf("received data mismatch (-want +got):\n%s", diff) - } - if rr.RemoteAddr.Addr != test.localAddr { - t.Errorf("got addr.Addr = %s, want = %s", rr.RemoteAddr.Addr, test.localAddr) - } - - test.checkLinkEndpoint(t, e) - }) - } - }) - } -} - -// TestLocalUDP tests sending UDP packets between two endpoints that are local -// to the stack. -// -// This tests that that packets never leave the stack and the addresses -// used when sending a packet. -func TestLocalUDP(t *testing.T) { - const ( - nicID = 1 - ) - - tests := []struct { - name string - canBePrimaryAddr tcpip.ProtocolAddress - firstPrimaryAddr tcpip.ProtocolAddress - }{ - { - name: "IPv4", - canBePrimaryAddr: utils.Ipv4Addr1, - firstPrimaryAddr: utils.Ipv4Addr2, - }, - { - name: "IPv6", - canBePrimaryAddr: utils.Ipv6Addr1, - firstPrimaryAddr: utils.Ipv6Addr2, - }, - } - - subTests := []struct { - name string - addAddress bool - expectedWriteErr tcpip.Error - }{ - { - name: "Unassigned local address", - addAddress: false, - expectedWriteErr: &tcpip.ErrNoRoute{}, - }, - { - name: "Assigned local address", - addAddress: true, - expectedWriteErr: nil, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - for _, subTest := range subTests { - t.Run(subTest.name, func(t *testing.T) { - stackOpts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol}, - HandleLocal: true, - } - - s := stack.New(stackOpts) - ep := channel.New(1, header.IPv6MinimumMTU, "") - - if err := s.CreateNIC(nicID, ep); err != nil { - t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err) - } - - if subTest.addAddress { - if err := s.AddProtocolAddressWithOptions(nicID, test.canBePrimaryAddr, stack.CanBePrimaryEndpoint); err != nil { - t.Fatalf("s.AddProtocolAddressWithOptions(%d, %#v, %d): %s", nicID, test.canBePrimaryAddr, stack.FirstPrimaryEndpoint, err) - } - if err := s.AddProtocolAddressWithOptions(nicID, test.firstPrimaryAddr, stack.FirstPrimaryEndpoint); err != nil { - t.Fatalf("s.AddProtocolAddressWithOptions(%d, %#v, %d): %s", nicID, test.firstPrimaryAddr, stack.FirstPrimaryEndpoint, err) - } - } - - var serverWQ waiter.Queue - serverWE, serverCH := waiter.NewChannelEntry(nil) - serverWQ.EventRegister(&serverWE, waiter.EventIn) - server, err := s.NewEndpoint(udp.ProtocolNumber, test.firstPrimaryAddr.Protocol, &serverWQ) - if err != nil { - t.Fatalf("s.NewEndpoint(%d, %d): %s", udp.ProtocolNumber, test.firstPrimaryAddr.Protocol, err) - } - defer server.Close() - - bindAddr := tcpip.FullAddress{Port: 80} - if err := server.Bind(bindAddr); err != nil { - t.Fatalf("server.Bind(%#v): %s", bindAddr, err) - } - - var clientWQ waiter.Queue - clientWE, clientCH := waiter.NewChannelEntry(nil) - clientWQ.EventRegister(&clientWE, waiter.EventIn) - client, err := s.NewEndpoint(udp.ProtocolNumber, test.firstPrimaryAddr.Protocol, &clientWQ) - if err != nil { - t.Fatalf("s.NewEndpoint(%d, %d): %s", udp.ProtocolNumber, test.firstPrimaryAddr.Protocol, err) - } - defer client.Close() - - serverAddr := tcpip.FullAddress{ - Addr: test.canBePrimaryAddr.AddressWithPrefix.Address, - Port: 80, - } - - clientPayload := []byte{1, 2, 3, 4} - { - var r bytes.Reader - r.Reset(clientPayload) - wOpts := tcpip.WriteOptions{ - To: &serverAddr, - } - if n, err := client.Write(&r, wOpts); err != subTest.expectedWriteErr { - t.Fatalf("got client.Write(%#v, %#v) = (%d, %s), want = (_, %s)", clientPayload, wOpts, n, err, subTest.expectedWriteErr) - } else if subTest.expectedWriteErr != nil { - // Nothing else to test if we expected not to be able to send the - // UDP packet. - return - } else if n != int64(len(clientPayload)) { - t.Fatalf("got client.Write(%#v, %#v) = (%d, nil), want = (%d, nil)", clientPayload, wOpts, n, len(clientPayload)) - } - } - - // Wait for the server endpoint to become readable. - <-serverCH - - var clientAddr tcpip.FullAddress - var readBuf bytes.Buffer - if read, err := server.Read(&readBuf, tcpip.ReadOptions{NeedRemoteAddr: true}); err != nil { - t.Fatalf("server.Read(_): %s", err) - } else { - clientAddr = read.RemoteAddr - - if diff := cmp.Diff(tcpip.ReadResult{ - Count: readBuf.Len(), - Total: readBuf.Len(), - RemoteAddr: tcpip.FullAddress{ - Addr: test.canBePrimaryAddr.AddressWithPrefix.Address, - }, - }, read, checker.IgnoreCmpPath( - "ControlMessages", - "RemoteAddr.NIC", - "RemoteAddr.Port", - )); diff != "" { - t.Errorf("server.Read: unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(buffer.View(clientPayload), buffer.View(readBuf.Bytes())); diff != "" { - t.Errorf("server read clientPayload mismatch (-want +got):\n%s", diff) - } - if t.Failed() { - t.FailNow() - } - } - - serverPayload := []byte{1, 2, 3, 4} - { - var r bytes.Reader - r.Reset(serverPayload) - wOpts := tcpip.WriteOptions{ - To: &clientAddr, - } - if n, err := server.Write(&r, wOpts); err != nil { - t.Fatalf("server.Write(%#v, %#v): %s", serverPayload, wOpts, err) - } else if n != int64(len(serverPayload)) { - t.Fatalf("got server.Write(%#v, %#v) = (%d, nil), want = (%d, nil)", serverPayload, wOpts, n, len(serverPayload)) - } - } - - // Wait for the client endpoint to become readable. - <-clientCH - - readBuf.Reset() - if read, err := client.Read(&readBuf, tcpip.ReadOptions{NeedRemoteAddr: true}); err != nil { - t.Fatalf("client.Read(_): %s", err) - } else { - if diff := cmp.Diff(tcpip.ReadResult{ - Count: readBuf.Len(), - Total: readBuf.Len(), - RemoteAddr: tcpip.FullAddress{Addr: serverAddr.Addr}, - }, read, checker.IgnoreCmpPath( - "ControlMessages", - "RemoteAddr.NIC", - "RemoteAddr.Port", - )); diff != "" { - t.Errorf("client.Read: unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(buffer.View(serverPayload), buffer.View(readBuf.Bytes())); diff != "" { - t.Errorf("client read serverPayload mismatch (-want +got):\n%s", diff) - } - if t.Failed() { - t.FailNow() - } - } - }) - } - }) - } -} diff --git a/pkg/tcpip/tests/utils/BUILD b/pkg/tcpip/tests/utils/BUILD deleted file mode 100644 index a9699a367..000000000 --- a/pkg/tcpip/tests/utils/BUILD +++ /dev/null @@ -1,22 +0,0 @@ -load("//tools:defs.bzl", "go_library") - -package(licenses = ["notice"]) - -go_library( - name = "utils", - srcs = ["utils.go"], - visibility = ["//pkg/tcpip/tests:__subpackages__"], - deps = [ - "//pkg/tcpip", - "//pkg/tcpip/buffer", - "//pkg/tcpip/header", - "//pkg/tcpip/link/channel", - "//pkg/tcpip/link/ethernet", - "//pkg/tcpip/link/nested", - "//pkg/tcpip/link/pipe", - "//pkg/tcpip/network/ipv4", - "//pkg/tcpip/network/ipv6", - "//pkg/tcpip/stack", - "//pkg/tcpip/transport/icmp", - ], -) diff --git a/pkg/tcpip/tests/utils/utils.go b/pkg/tcpip/tests/utils/utils.go deleted file mode 100644 index d1c9f3a94..000000000 --- a/pkg/tcpip/tests/utils/utils.go +++ /dev/null @@ -1,374 +0,0 @@ -// 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 utils holds common testing utilities for tcpip. -package utils - -import ( - "net" - "testing" - - "gvisor.dev/gvisor/pkg/tcpip" - "gvisor.dev/gvisor/pkg/tcpip/buffer" - "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/nested" - "gvisor.dev/gvisor/pkg/tcpip/link/pipe" - "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/transport/icmp" -) - -// Common NIC IDs used by tests. -const ( - Host1NICID = 1 - RouterNICID1 = 2 - RouterNICID2 = 3 - Host2NICID = 4 -) - -// Common link addresses used by tests. -const ( - LinkAddr1 = tcpip.LinkAddress("\x02\x03\x03\x04\x05\x06") - LinkAddr2 = tcpip.LinkAddress("\x02\x03\x03\x04\x05\x07") - LinkAddr3 = tcpip.LinkAddress("\x02\x03\x03\x04\x05\x08") - LinkAddr4 = tcpip.LinkAddress("\x02\x03\x03\x04\x05\x09") -) - -const ( - ttl = 255 -) - -// Common IP addresses used by tests. -var ( - Ipv4Addr = tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("192.168.1.58").To4()), - PrefixLen: 24, - } - Ipv4Subnet = Ipv4Addr.Subnet() - Ipv4SubnetBcast = Ipv4Subnet.Broadcast() - - Ipv6Addr = tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("200a::1").To16()), - PrefixLen: 64, - } - Ipv6Subnet = Ipv6Addr.Subnet() - Ipv6SubnetBcast = Ipv6Subnet.Broadcast() - - Ipv4Addr1 = tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("192.168.0.1").To4()), - PrefixLen: 24, - }, - } - Ipv4Addr2 = tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("192.168.0.2").To4()), - PrefixLen: 8, - }, - } - Ipv4Addr3 = tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("192.168.0.3").To4()), - PrefixLen: 8, - }, - } - Ipv6Addr1 = tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("a::1").To16()), - PrefixLen: 64, - }, - } - Ipv6Addr2 = tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("a::2").To16()), - PrefixLen: 64, - }, - } - Ipv6Addr3 = tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("a::3").To16()), - PrefixLen: 64, - }, - } - - // Remote addrs. - RemoteIPv4Addr = tcpip.Address(net.ParseIP("10.0.0.1").To4()) - RemoteIPv6Addr = tcpip.Address(net.ParseIP("200b::1").To16()) -) - -// Common ports for testing. -const ( - RemotePort = 5555 - LocalPort = 80 -) - -// Common IP addresses used for testing. -var ( - Host1IPv4Addr = tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("192.168.0.2").To4()), - PrefixLen: 24, - }, - } - RouterNIC1IPv4Addr = tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("192.168.0.1").To4()), - PrefixLen: 24, - }, - } - RouterNIC2IPv4Addr = tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("10.0.0.1").To4()), - PrefixLen: 8, - }, - } - Host2IPv4Addr = tcpip.ProtocolAddress{ - Protocol: ipv4.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("10.0.0.2").To4()), - PrefixLen: 8, - }, - } - Host1IPv6Addr = tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("a::2").To16()), - PrefixLen: 64, - }, - } - RouterNIC1IPv6Addr = tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("a::1").To16()), - PrefixLen: 64, - }, - } - RouterNIC2IPv6Addr = tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("b::1").To16()), - PrefixLen: 64, - }, - } - Host2IPv6Addr = tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.AddressWithPrefix{ - Address: tcpip.Address(net.ParseIP("b::2").To16()), - PrefixLen: 64, - }, - } -) - -// NewEthernetEndpoint returns an ethernet link endpoint that wraps an inner -// link endpoint and checks the destination link address before delivering -// network packets to the network dispatcher. -// -// See ethernet.Endpoint for more details. -func NewEthernetEndpoint(ep stack.LinkEndpoint) *EndpointWithDestinationCheck { - var e EndpointWithDestinationCheck - e.Endpoint.Init(ethernet.New(ep), &e) - return &e -} - -// EndpointWithDestinationCheck is a link endpoint that checks the destination -// link address before delivering network packets to the network dispatcher. -type EndpointWithDestinationCheck struct { - nested.Endpoint -} - -var _ stack.NetworkDispatcher = (*EndpointWithDestinationCheck)(nil) -var _ stack.LinkEndpoint = (*EndpointWithDestinationCheck)(nil) - -// DeliverNetworkPacket implements stack.NetworkDispatcher. -func (e *EndpointWithDestinationCheck) DeliverNetworkPacket(src, dst tcpip.LinkAddress, proto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { - if dst == e.Endpoint.LinkAddress() || dst == header.EthernetBroadcastAddress || header.IsMulticastEthernetAddress(dst) { - e.Endpoint.DeliverNetworkPacket(src, dst, proto, pkt) - } -} - -// SetupRoutedStacks creates the NICs, sets forwarding, adds addresses and sets -// the route tables for the passed stacks. -func SetupRoutedStacks(t *testing.T, host1Stack, routerStack, host2Stack *stack.Stack) { - host1NIC, routerNIC1 := pipe.New(LinkAddr1, LinkAddr2) - routerNIC2, host2NIC := pipe.New(LinkAddr3, LinkAddr4) - - if err := host1Stack.CreateNIC(Host1NICID, NewEthernetEndpoint(host1NIC)); err != nil { - t.Fatalf("host1Stack.CreateNIC(%d, _): %s", Host1NICID, err) - } - if err := routerStack.CreateNIC(RouterNICID1, NewEthernetEndpoint(routerNIC1)); err != nil { - t.Fatalf("routerStack.CreateNIC(%d, _): %s", RouterNICID1, err) - } - if err := routerStack.CreateNIC(RouterNICID2, NewEthernetEndpoint(routerNIC2)); err != nil { - t.Fatalf("routerStack.CreateNIC(%d, _): %s", RouterNICID2, err) - } - if err := host2Stack.CreateNIC(Host2NICID, NewEthernetEndpoint(host2NIC)); err != nil { - t.Fatalf("host2Stack.CreateNIC(%d, _): %s", Host2NICID, err) - } - - if err := routerStack.SetForwarding(ipv4.ProtocolNumber, true); err != nil { - t.Fatalf("routerStack.SetForwarding(%d): %s", ipv4.ProtocolNumber, err) - } - if err := routerStack.SetForwarding(ipv6.ProtocolNumber, true); err != nil { - t.Fatalf("routerStack.SetForwarding(%d): %s", ipv6.ProtocolNumber, err) - } - - if err := host1Stack.AddProtocolAddress(Host1NICID, Host1IPv4Addr); err != nil { - t.Fatalf("host1Stack.AddProtocolAddress(%d, %#v): %s", Host1NICID, Host1IPv4Addr, err) - } - if err := routerStack.AddProtocolAddress(RouterNICID1, RouterNIC1IPv4Addr); err != nil { - t.Fatalf("routerStack.AddProtocolAddress(%d, %#v): %s", RouterNICID1, RouterNIC1IPv4Addr, err) - } - if err := routerStack.AddProtocolAddress(RouterNICID2, RouterNIC2IPv4Addr); err != nil { - t.Fatalf("routerStack.AddProtocolAddress(%d, %#v): %s", RouterNICID2, RouterNIC2IPv4Addr, err) - } - if err := host2Stack.AddProtocolAddress(Host2NICID, Host2IPv4Addr); err != nil { - t.Fatalf("host2Stack.AddProtocolAddress(%d, %#v): %s", Host2NICID, Host2IPv4Addr, err) - } - if err := host1Stack.AddProtocolAddress(Host1NICID, Host1IPv6Addr); err != nil { - t.Fatalf("host1Stack.AddProtocolAddress(%d, %#v): %s", Host1NICID, Host1IPv6Addr, err) - } - if err := routerStack.AddProtocolAddress(RouterNICID1, RouterNIC1IPv6Addr); err != nil { - t.Fatalf("routerStack.AddProtocolAddress(%d, %#v): %s", RouterNICID1, RouterNIC1IPv6Addr, err) - } - if err := routerStack.AddProtocolAddress(RouterNICID2, RouterNIC2IPv6Addr); err != nil { - t.Fatalf("routerStack.AddProtocolAddress(%d, %#v): %s", RouterNICID2, RouterNIC2IPv6Addr, err) - } - if err := host2Stack.AddProtocolAddress(Host2NICID, Host2IPv6Addr); err != nil { - t.Fatalf("host2Stack.AddProtocolAddress(%d, %#v): %s", Host2NICID, Host2IPv6Addr, err) - } - - host1Stack.SetRouteTable([]tcpip.Route{ - { - Destination: Host1IPv4Addr.AddressWithPrefix.Subnet(), - NIC: Host1NICID, - }, - { - Destination: Host1IPv6Addr.AddressWithPrefix.Subnet(), - NIC: Host1NICID, - }, - { - Destination: Host2IPv4Addr.AddressWithPrefix.Subnet(), - Gateway: RouterNIC1IPv4Addr.AddressWithPrefix.Address, - NIC: Host1NICID, - }, - { - Destination: Host2IPv6Addr.AddressWithPrefix.Subnet(), - Gateway: RouterNIC1IPv6Addr.AddressWithPrefix.Address, - NIC: Host1NICID, - }, - }) - routerStack.SetRouteTable([]tcpip.Route{ - { - Destination: RouterNIC1IPv4Addr.AddressWithPrefix.Subnet(), - NIC: RouterNICID1, - }, - { - Destination: RouterNIC1IPv6Addr.AddressWithPrefix.Subnet(), - NIC: RouterNICID1, - }, - { - Destination: RouterNIC2IPv4Addr.AddressWithPrefix.Subnet(), - NIC: RouterNICID2, - }, - { - Destination: RouterNIC2IPv6Addr.AddressWithPrefix.Subnet(), - NIC: RouterNICID2, - }, - }) - host2Stack.SetRouteTable([]tcpip.Route{ - { - Destination: Host2IPv4Addr.AddressWithPrefix.Subnet(), - NIC: Host2NICID, - }, - { - Destination: Host2IPv6Addr.AddressWithPrefix.Subnet(), - NIC: Host2NICID, - }, - { - Destination: Host1IPv4Addr.AddressWithPrefix.Subnet(), - Gateway: RouterNIC2IPv4Addr.AddressWithPrefix.Address, - NIC: Host2NICID, - }, - { - Destination: Host1IPv6Addr.AddressWithPrefix.Subnet(), - Gateway: RouterNIC2IPv6Addr.AddressWithPrefix.Address, - NIC: Host2NICID, - }, - }) -} - -// RxICMPv4EchoRequest constructs and injects an ICMPv4 echo request packet on -// the provided endpoint. -func RxICMPv4EchoRequest(e *channel.Endpoint, src, dst tcpip.Address) { - totalLen := header.IPv4MinimumSize + header.ICMPv4MinimumSize - hdr := buffer.NewPrependable(totalLen) - pkt := header.ICMPv4(hdr.Prepend(header.ICMPv4MinimumSize)) - pkt.SetType(header.ICMPv4Echo) - pkt.SetCode(header.ICMPv4UnusedCode) - pkt.SetChecksum(0) - pkt.SetChecksum(^header.Checksum(pkt, 0)) - ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize)) - ip.Encode(&header.IPv4Fields{ - TotalLength: uint16(totalLen), - Protocol: uint8(icmp.ProtocolNumber4), - TTL: ttl, - SrcAddr: src, - DstAddr: dst, - }) - ip.SetChecksum(^ip.CalculateChecksum()) - - e.InjectInbound(header.IPv4ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: hdr.View().ToVectorisedView(), - })) -} - -// RxICMPv6EchoRequest constructs and injects an ICMPv6 echo request packet on -// the provided endpoint. -func RxICMPv6EchoRequest(e *channel.Endpoint, src, dst tcpip.Address) { - totalLen := header.IPv6MinimumSize + header.ICMPv6MinimumSize - hdr := buffer.NewPrependable(totalLen) - pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6MinimumSize)) - pkt.SetType(header.ICMPv6EchoRequest) - pkt.SetCode(header.ICMPv6UnusedCode) - pkt.SetChecksum(0) - pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ - Header: pkt, - Src: src, - Dst: dst, - })) - ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) - ip.Encode(&header.IPv6Fields{ - PayloadLength: header.ICMPv6MinimumSize, - TransportProtocol: icmp.ProtocolNumber6, - HopLimit: ttl, - SrcAddr: src, - DstAddr: dst, - }) - - e.InjectInbound(header.IPv6ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: hdr.View().ToVectorisedView(), - })) -} |