github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/transport/icmp/icmp_test.go (about)

     1  // Copyright 2021 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package icmp_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/tcpip"
    21  	"github.com/SagerNet/gvisor/pkg/tcpip/buffer"
    22  	"github.com/SagerNet/gvisor/pkg/tcpip/checker"
    23  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    24  	"github.com/SagerNet/gvisor/pkg/tcpip/link/channel"
    25  	"github.com/SagerNet/gvisor/pkg/tcpip/link/sniffer"
    26  	"github.com/SagerNet/gvisor/pkg/tcpip/network/ipv4"
    27  	"github.com/SagerNet/gvisor/pkg/tcpip/stack"
    28  	"github.com/SagerNet/gvisor/pkg/tcpip/testutil"
    29  	"github.com/SagerNet/gvisor/pkg/tcpip/transport/icmp"
    30  	"github.com/SagerNet/gvisor/pkg/waiter"
    31  )
    32  
    33  // TODO(https://github.com/SagerNet/issues/5623): Finish unit testing the icmp package.
    34  // See the issue for remaining areas of work.
    35  
    36  var (
    37  	localV4Addr1 = testutil.MustParse4("10.0.0.1")
    38  	localV4Addr2 = testutil.MustParse4("10.0.0.2")
    39  	remoteV4Addr = testutil.MustParse4("10.0.0.3")
    40  )
    41  
    42  func addNICWithDefaultRoute(t *testing.T, s *stack.Stack, id tcpip.NICID, name string, addrV4 tcpip.Address) *channel.Endpoint {
    43  	t.Helper()
    44  
    45  	ep := channel.New(1 /* size */, header.IPv4MinimumMTU, "" /* linkAddr */)
    46  	t.Cleanup(ep.Close)
    47  
    48  	wep := stack.LinkEndpoint(ep)
    49  	if testing.Verbose() {
    50  		wep = sniffer.New(ep)
    51  	}
    52  
    53  	opts := stack.NICOptions{Name: name}
    54  	if err := s.CreateNICWithOptions(id, wep, opts); err != nil {
    55  		t.Fatalf("s.CreateNIC(%d, _) = %s", id, err)
    56  	}
    57  
    58  	if err := s.AddAddress(id, ipv4.ProtocolNumber, addrV4); err != nil {
    59  		t.Fatalf("s.AddAddress(%d, %d, %s) = %s", id, ipv4.ProtocolNumber, addrV4, err)
    60  	}
    61  
    62  	s.AddRoute(tcpip.Route{
    63  		Destination: header.IPv4EmptySubnet,
    64  		NIC:         id,
    65  	})
    66  
    67  	return ep
    68  }
    69  
    70  func writePayload(buf []byte) {
    71  	for i := range buf {
    72  		buf[i] = byte(i)
    73  	}
    74  }
    75  
    76  func newICMPv4EchoRequest(payloadSize uint32) buffer.View {
    77  	buf := buffer.NewView(header.ICMPv4MinimumSize + int(payloadSize))
    78  	writePayload(buf[header.ICMPv4MinimumSize:])
    79  
    80  	icmp := header.ICMPv4(buf)
    81  	icmp.SetType(header.ICMPv4Echo)
    82  	// No need to set the checksum; it is reset by the socket before the packet
    83  	// is sent.
    84  
    85  	return buf
    86  }
    87  
    88  // TestWriteUnboundWithBindToDevice exercises writing to an unbound ICMP socket
    89  // when SO_BINDTODEVICE is set to the non-default NIC for that subnet.
    90  //
    91  // Only IPv4 is tested. The logic to determine which NIC to use is agnostic to
    92  // the version of IP.
    93  func TestWriteUnboundWithBindToDevice(t *testing.T) {
    94  	s := stack.New(stack.Options{
    95  		NetworkProtocols:   []stack.NetworkProtocolFactory{ipv4.NewProtocol},
    96  		TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol4},
    97  		HandleLocal:        true,
    98  	})
    99  
   100  	// Add two NICs, both with default routes on the same subnet. The first NIC
   101  	// added will be the default NIC for that subnet.
   102  	defaultEP := addNICWithDefaultRoute(t, s, 1, "default", localV4Addr1)
   103  	alternateEP := addNICWithDefaultRoute(t, s, 2, "alternate", localV4Addr2)
   104  
   105  	socket, err := s.NewEndpoint(icmp.ProtocolNumber4, ipv4.ProtocolNumber, &waiter.Queue{})
   106  	if err != nil {
   107  		t.Fatalf("s.NewEndpoint(%d, %d, _) = %s", icmp.ProtocolNumber4, ipv4.ProtocolNumber, err)
   108  	}
   109  	defer socket.Close()
   110  
   111  	echoPayloadSize := defaultEP.MTU() - header.IPv4MinimumSize - header.ICMPv4MinimumSize
   112  
   113  	// Send a packet without SO_BINDTODEVICE. This verifies that the first NIC
   114  	// to be added is the default NIC to send packets when not explicitly bound.
   115  	{
   116  		buf := newICMPv4EchoRequest(echoPayloadSize)
   117  		r := buf.Reader()
   118  		n, err := socket.Write(&r, tcpip.WriteOptions{
   119  			To: &tcpip.FullAddress{Addr: remoteV4Addr},
   120  		})
   121  		if err != nil {
   122  			t.Fatalf("socket.Write(_, {To:%s}) = %s", remoteV4Addr, err)
   123  		}
   124  		if n != int64(len(buf)) {
   125  			t.Fatalf("got n = %d, want n = %d", n, len(buf))
   126  		}
   127  
   128  		// Verify the packet was sent out the default NIC.
   129  		p, ok := defaultEP.Read()
   130  		if !ok {
   131  			t.Fatalf("got defaultEP.Read(_) = _, false; want = _, true (packet wasn't written out)")
   132  		}
   133  
   134  		vv := buffer.NewVectorisedView(p.Pkt.Size(), p.Pkt.Views())
   135  		b := vv.ToView()
   136  
   137  		checker.IPv4(t, b, []checker.NetworkChecker{
   138  			checker.SrcAddr(localV4Addr1),
   139  			checker.DstAddr(remoteV4Addr),
   140  			checker.ICMPv4(
   141  				checker.ICMPv4Type(header.ICMPv4Echo),
   142  				checker.ICMPv4Payload(buf[header.ICMPv4MinimumSize:]),
   143  			),
   144  		}...)
   145  
   146  		// Verify the packet was not sent out the alternate NIC.
   147  		if p, ok := alternateEP.Read(); ok {
   148  			t.Fatalf("got alternateEP.Read(_) = %+v, true; want = _, false", p)
   149  		}
   150  	}
   151  
   152  	// Send a packet with SO_BINDTODEVICE. This exercises reliance on
   153  	// SO_BINDTODEVICE to route the packet to the alternate NIC.
   154  	{
   155  		// Use SO_BINDTODEVICE to send over the alternate NIC by default.
   156  		socket.SocketOptions().SetBindToDevice(2)
   157  
   158  		buf := newICMPv4EchoRequest(echoPayloadSize)
   159  		r := buf.Reader()
   160  		n, err := socket.Write(&r, tcpip.WriteOptions{
   161  			To: &tcpip.FullAddress{Addr: remoteV4Addr},
   162  		})
   163  		if err != nil {
   164  			t.Fatalf("socket.Write(_, {To:%s}) = %s", tcpip.Address(remoteV4Addr), err)
   165  		}
   166  		if n != int64(len(buf)) {
   167  			t.Fatalf("got n = %d, want n = %d", n, len(buf))
   168  		}
   169  
   170  		// Verify the packet was not sent out the default NIC.
   171  		if p, ok := defaultEP.Read(); ok {
   172  			t.Fatalf("got defaultEP.Read(_) = %+v, true; want = _, false", p)
   173  		}
   174  
   175  		// Verify the packet was sent out the alternate NIC.
   176  		p, ok := alternateEP.Read()
   177  		if !ok {
   178  			t.Fatalf("got alternateEP.Read(_) = _, false; want = _, true (packet wasn't written out)")
   179  		}
   180  
   181  		vv := buffer.NewVectorisedView(p.Pkt.Size(), p.Pkt.Views())
   182  		b := vv.ToView()
   183  
   184  		checker.IPv4(t, b, []checker.NetworkChecker{
   185  			checker.SrcAddr(localV4Addr2),
   186  			checker.DstAddr(remoteV4Addr),
   187  			checker.ICMPv4(
   188  				checker.ICMPv4Type(header.ICMPv4Echo),
   189  				checker.ICMPv4Payload(buf[header.ICMPv4MinimumSize:]),
   190  			),
   191  		}...)
   192  	}
   193  
   194  	// Send a packet with SO_BINDTODEVICE cleared. This verifies that clearing
   195  	// the device binding will fallback to using the default NIC to send
   196  	// packets.
   197  	{
   198  		socket.SocketOptions().SetBindToDevice(0)
   199  
   200  		buf := newICMPv4EchoRequest(echoPayloadSize)
   201  		r := buf.Reader()
   202  		n, err := socket.Write(&r, tcpip.WriteOptions{
   203  			To: &tcpip.FullAddress{Addr: remoteV4Addr},
   204  		})
   205  		if err != nil {
   206  			t.Fatalf("socket.Write(_, {To:%s}) = %s", tcpip.Address(remoteV4Addr), err)
   207  		}
   208  		if n != int64(len(buf)) {
   209  			t.Fatalf("got n = %d, want n = %d", n, len(buf))
   210  		}
   211  
   212  		// Verify the packet was sent out the default NIC.
   213  		p, ok := defaultEP.Read()
   214  		if !ok {
   215  			t.Fatalf("got defaultEP.Read(_) = _, false; want = _, true (packet wasn't written out)")
   216  		}
   217  
   218  		vv := buffer.NewVectorisedView(p.Pkt.Size(), p.Pkt.Views())
   219  		b := vv.ToView()
   220  
   221  		checker.IPv4(t, b, []checker.NetworkChecker{
   222  			checker.SrcAddr(localV4Addr1),
   223  			checker.DstAddr(remoteV4Addr),
   224  			checker.ICMPv4(
   225  				checker.ICMPv4Type(header.ICMPv4Echo),
   226  				checker.ICMPv4Payload(buf[header.ICMPv4MinimumSize:]),
   227  			),
   228  		}...)
   229  
   230  		// Verify the packet was not sent out the alternate NIC.
   231  		if p, ok := alternateEP.Read(); ok {
   232  			t.Fatalf("got alternateEP.Read(_) = %+v, true; want = _, false", p)
   233  		}
   234  	}
   235  }