github.com/flowerwrong/netstack@v0.0.0-20191009141956-e5848263af28/tcpip/network/ip_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ip_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/FlowerWrong/netstack/tcpip"
    21  	"github.com/FlowerWrong/netstack/tcpip/buffer"
    22  	"github.com/FlowerWrong/netstack/tcpip/header"
    23  	"github.com/FlowerWrong/netstack/tcpip/link/loopback"
    24  	"github.com/FlowerWrong/netstack/tcpip/network/ipv4"
    25  	"github.com/FlowerWrong/netstack/tcpip/network/ipv6"
    26  	"github.com/FlowerWrong/netstack/tcpip/stack"
    27  	"github.com/FlowerWrong/netstack/tcpip/transport/tcp"
    28  	"github.com/FlowerWrong/netstack/tcpip/transport/udp"
    29  )
    30  
    31  const (
    32  	localIpv4Addr      = "\x0a\x00\x00\x01"
    33  	localIpv4PrefixLen = 24
    34  	remoteIpv4Addr     = "\x0a\x00\x00\x02"
    35  	ipv4SubnetAddr     = "\x0a\x00\x00\x00"
    36  	ipv4SubnetMask     = "\xff\xff\xff\x00"
    37  	ipv4Gateway        = "\x0a\x00\x00\x03"
    38  	localIpv6Addr      = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
    39  	localIpv6PrefixLen = 120
    40  	remoteIpv6Addr     = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
    41  	ipv6SubnetAddr     = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    42  	ipv6SubnetMask     = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00"
    43  	ipv6Gateway        = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03"
    44  )
    45  
    46  // testObject implements two interfaces: LinkEndpoint and TransportDispatcher.
    47  // The former is used to pretend that it's a link endpoint so that we can
    48  // inspect packets written by the network endpoints. The latter is used to
    49  // pretend that it's the network stack so that it can inspect incoming packets
    50  // that have been handled by the network endpoints.
    51  //
    52  // Packets are checked by comparing their fields/values against the expected
    53  // values stored in the test object itself.
    54  type testObject struct {
    55  	t        *testing.T
    56  	protocol tcpip.TransportProtocolNumber
    57  	contents []byte
    58  	srcAddr  tcpip.Address
    59  	dstAddr  tcpip.Address
    60  	v4       bool
    61  	typ      stack.ControlType
    62  	extra    uint32
    63  
    64  	dataCalls    int
    65  	controlCalls int
    66  }
    67  
    68  // checkValues verifies that the transport protocol, data contents, src & dst
    69  // addresses of a packet match what's expected. If any field doesn't match, the
    70  // test fails.
    71  func (t *testObject) checkValues(protocol tcpip.TransportProtocolNumber, vv buffer.VectorisedView, srcAddr, dstAddr tcpip.Address) {
    72  	v := vv.ToView()
    73  	if protocol != t.protocol {
    74  		t.t.Errorf("protocol = %v, want %v", protocol, t.protocol)
    75  	}
    76  
    77  	if srcAddr != t.srcAddr {
    78  		t.t.Errorf("srcAddr = %v, want %v", srcAddr, t.srcAddr)
    79  	}
    80  
    81  	if dstAddr != t.dstAddr {
    82  		t.t.Errorf("dstAddr = %v, want %v", dstAddr, t.dstAddr)
    83  	}
    84  
    85  	if len(v) != len(t.contents) {
    86  		t.t.Fatalf("len(payload) = %v, want %v", len(v), len(t.contents))
    87  	}
    88  
    89  	for i := range t.contents {
    90  		if t.contents[i] != v[i] {
    91  			t.t.Fatalf("payload[%v] = %v, want %v", i, v[i], t.contents[i])
    92  		}
    93  	}
    94  }
    95  
    96  // DeliverTransportPacket is called by network endpoints after parsing incoming
    97  // packets. This is used by the test object to verify that the results of the
    98  // parsing are expected.
    99  func (t *testObject) DeliverTransportPacket(r *stack.Route, protocol tcpip.TransportProtocolNumber, netHeader buffer.View, vv buffer.VectorisedView) {
   100  	t.checkValues(protocol, vv, r.RemoteAddress, r.LocalAddress)
   101  	t.dataCalls++
   102  }
   103  
   104  // DeliverTransportControlPacket is called by network endpoints after parsing
   105  // incoming control (ICMP) packets. This is used by the test object to verify
   106  // that the results of the parsing are expected.
   107  func (t *testObject) DeliverTransportControlPacket(local, remote tcpip.Address, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, typ stack.ControlType, extra uint32, vv buffer.VectorisedView) {
   108  	t.checkValues(trans, vv, remote, local)
   109  	if typ != t.typ {
   110  		t.t.Errorf("typ = %v, want %v", typ, t.typ)
   111  	}
   112  	if extra != t.extra {
   113  		t.t.Errorf("extra = %v, want %v", extra, t.extra)
   114  	}
   115  	t.controlCalls++
   116  }
   117  
   118  // Attach is only implemented to satisfy the LinkEndpoint interface.
   119  func (*testObject) Attach(stack.NetworkDispatcher) {}
   120  
   121  // IsAttached implements stack.LinkEndpoint.IsAttached.
   122  func (*testObject) IsAttached() bool {
   123  	return true
   124  }
   125  
   126  // MTU implements stack.LinkEndpoint.MTU. It just returns a constant that
   127  // matches the linux loopback MTU.
   128  func (*testObject) MTU() uint32 {
   129  	return 65536
   130  }
   131  
   132  // Capabilities implements stack.LinkEndpoint.Capabilities.
   133  func (*testObject) Capabilities() stack.LinkEndpointCapabilities {
   134  	return 0
   135  }
   136  
   137  // MaxHeaderLength is only implemented to satisfy the LinkEndpoint interface.
   138  func (*testObject) MaxHeaderLength() uint16 {
   139  	return 0
   140  }
   141  
   142  // LinkAddress returns the link address of this endpoint.
   143  func (*testObject) LinkAddress() tcpip.LinkAddress {
   144  	return ""
   145  }
   146  
   147  // Wait implements stack.LinkEndpoint.Wait.
   148  func (*testObject) Wait() {}
   149  
   150  // WritePacket is called by network endpoints after producing a packet and
   151  // writing it to the link endpoint. This is used by the test object to verify
   152  // that the produced packet is as expected.
   153  func (t *testObject) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
   154  	var prot tcpip.TransportProtocolNumber
   155  	var srcAddr tcpip.Address
   156  	var dstAddr tcpip.Address
   157  
   158  	if t.v4 {
   159  		h := header.IPv4(hdr.View())
   160  		prot = tcpip.TransportProtocolNumber(h.Protocol())
   161  		srcAddr = h.SourceAddress()
   162  		dstAddr = h.DestinationAddress()
   163  
   164  	} else {
   165  		h := header.IPv6(hdr.View())
   166  		prot = tcpip.TransportProtocolNumber(h.NextHeader())
   167  		srcAddr = h.SourceAddress()
   168  		dstAddr = h.DestinationAddress()
   169  	}
   170  	t.checkValues(prot, payload, srcAddr, dstAddr)
   171  	return nil
   172  }
   173  
   174  func buildIPv4Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
   175  	s := stack.New(stack.Options{
   176  		NetworkProtocols:   []stack.NetworkProtocol{ipv4.NewProtocol()},
   177  		TransportProtocols: []stack.TransportProtocol{udp.NewProtocol(), tcp.NewProtocol()},
   178  	})
   179  	s.CreateNIC(1, loopback.New())
   180  	s.AddAddress(1, ipv4.ProtocolNumber, local)
   181  	s.SetRouteTable([]tcpip.Route{{
   182  		Destination: header.IPv4EmptySubnet,
   183  		Gateway:     ipv4Gateway,
   184  		NIC:         1,
   185  	}})
   186  
   187  	return s.FindRoute(1, local, remote, ipv4.ProtocolNumber, false /* multicastLoop */)
   188  }
   189  
   190  func buildIPv6Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
   191  	s := stack.New(stack.Options{
   192  		NetworkProtocols:   []stack.NetworkProtocol{ipv6.NewProtocol()},
   193  		TransportProtocols: []stack.TransportProtocol{udp.NewProtocol(), tcp.NewProtocol()},
   194  	})
   195  	s.CreateNIC(1, loopback.New())
   196  	s.AddAddress(1, ipv6.ProtocolNumber, local)
   197  	s.SetRouteTable([]tcpip.Route{{
   198  		Destination: header.IPv6EmptySubnet,
   199  		Gateway:     ipv6Gateway,
   200  		NIC:         1,
   201  	}})
   202  
   203  	return s.FindRoute(1, local, remote, ipv6.ProtocolNumber, false /* multicastLoop */)
   204  }
   205  
   206  func TestIPv4Send(t *testing.T) {
   207  	o := testObject{t: t, v4: true}
   208  	proto := ipv4.NewProtocol()
   209  	ep, err := proto.NewEndpoint(1, tcpip.AddressWithPrefix{localIpv4Addr, localIpv4PrefixLen}, nil, nil, &o)
   210  	if err != nil {
   211  		t.Fatalf("NewEndpoint failed: %v", err)
   212  	}
   213  
   214  	// Allocate and initialize the payload view.
   215  	payload := buffer.NewView(100)
   216  	for i := 0; i < len(payload); i++ {
   217  		payload[i] = uint8(i)
   218  	}
   219  
   220  	// Allocate the header buffer.
   221  	hdr := buffer.NewPrependable(int(ep.MaxHeaderLength()))
   222  
   223  	// Issue the write.
   224  	o.protocol = 123
   225  	o.srcAddr = localIpv4Addr
   226  	o.dstAddr = remoteIpv4Addr
   227  	o.contents = payload
   228  
   229  	r, err := buildIPv4Route(localIpv4Addr, remoteIpv4Addr)
   230  	if err != nil {
   231  		t.Fatalf("could not find route: %v", err)
   232  	}
   233  	if err := ep.WritePacket(&r, nil /* gso */, hdr, payload.ToVectorisedView(), 123, 123, stack.PacketOut); err != nil {
   234  		t.Fatalf("WritePacket failed: %v", err)
   235  	}
   236  }
   237  
   238  func TestIPv4Receive(t *testing.T) {
   239  	o := testObject{t: t, v4: true}
   240  	proto := ipv4.NewProtocol()
   241  	ep, err := proto.NewEndpoint(1, tcpip.AddressWithPrefix{localIpv4Addr, localIpv4PrefixLen}, nil, &o, nil)
   242  	if err != nil {
   243  		t.Fatalf("NewEndpoint failed: %v", err)
   244  	}
   245  
   246  	totalLen := header.IPv4MinimumSize + 30
   247  	view := buffer.NewView(totalLen)
   248  	ip := header.IPv4(view)
   249  	ip.Encode(&header.IPv4Fields{
   250  		IHL:         header.IPv4MinimumSize,
   251  		TotalLength: uint16(totalLen),
   252  		TTL:         20,
   253  		Protocol:    10,
   254  		SrcAddr:     remoteIpv4Addr,
   255  		DstAddr:     localIpv4Addr,
   256  	})
   257  
   258  	// Make payload be non-zero.
   259  	for i := header.IPv4MinimumSize; i < totalLen; i++ {
   260  		view[i] = uint8(i)
   261  	}
   262  
   263  	// Give packet to ipv4 endpoint, dispatcher will validate that it's ok.
   264  	o.protocol = 10
   265  	o.srcAddr = remoteIpv4Addr
   266  	o.dstAddr = localIpv4Addr
   267  	o.contents = view[header.IPv4MinimumSize:totalLen]
   268  
   269  	r, err := buildIPv4Route(localIpv4Addr, remoteIpv4Addr)
   270  	if err != nil {
   271  		t.Fatalf("could not find route: %v", err)
   272  	}
   273  	ep.HandlePacket(&r, view.ToVectorisedView())
   274  	if o.dataCalls != 1 {
   275  		t.Fatalf("Bad number of data calls: got %x, want 1", o.dataCalls)
   276  	}
   277  }
   278  
   279  func TestIPv4ReceiveControl(t *testing.T) {
   280  	const mtu = 0xbeef - header.IPv4MinimumSize
   281  	cases := []struct {
   282  		name           string
   283  		expectedCount  int
   284  		fragmentOffset uint16
   285  		code           uint8
   286  		expectedTyp    stack.ControlType
   287  		expectedExtra  uint32
   288  		trunc          int
   289  	}{
   290  		{"FragmentationNeeded", 1, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, 0},
   291  		{"Truncated (10 bytes missing)", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, 10},
   292  		{"Truncated (missing IPv4 header)", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, header.IPv4MinimumSize + 8},
   293  		{"Truncated (missing 'extra info')", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, 4 + header.IPv4MinimumSize + 8},
   294  		{"Truncated (missing ICMP header)", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, header.ICMPv4MinimumSize + header.IPv4MinimumSize + 8},
   295  		{"Port unreachable", 1, 0, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 0},
   296  		{"Non-zero fragment offset", 0, 100, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 0},
   297  		{"Zero-length packet", 0, 0, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv4MinimumSize + header.ICMPv4MinimumSize + 8},
   298  	}
   299  	r, err := buildIPv4Route(localIpv4Addr, "\x0a\x00\x00\xbb")
   300  	if err != nil {
   301  		t.Fatal(err)
   302  	}
   303  	for _, c := range cases {
   304  		t.Run(c.name, func(t *testing.T) {
   305  			o := testObject{t: t}
   306  			proto := ipv4.NewProtocol()
   307  			ep, err := proto.NewEndpoint(1, tcpip.AddressWithPrefix{localIpv4Addr, localIpv4PrefixLen}, nil, &o, nil)
   308  			if err != nil {
   309  				t.Fatalf("NewEndpoint failed: %v", err)
   310  			}
   311  			defer ep.Close()
   312  
   313  			const dataOffset = header.IPv4MinimumSize*2 + header.ICMPv4MinimumSize
   314  			view := buffer.NewView(dataOffset + 8)
   315  
   316  			// Create the outer IPv4 header.
   317  			ip := header.IPv4(view)
   318  			ip.Encode(&header.IPv4Fields{
   319  				IHL:         header.IPv4MinimumSize,
   320  				TotalLength: uint16(len(view) - c.trunc),
   321  				TTL:         20,
   322  				Protocol:    uint8(header.ICMPv4ProtocolNumber),
   323  				SrcAddr:     "\x0a\x00\x00\xbb",
   324  				DstAddr:     localIpv4Addr,
   325  			})
   326  
   327  			// Create the ICMP header.
   328  			icmp := header.ICMPv4(view[header.IPv4MinimumSize:])
   329  			icmp.SetType(header.ICMPv4DstUnreachable)
   330  			icmp.SetCode(c.code)
   331  			icmp.SetIdent(0xdead)
   332  			icmp.SetSequence(0xbeef)
   333  
   334  			// Create the inner IPv4 header.
   335  			ip = header.IPv4(view[header.IPv4MinimumSize+header.ICMPv4MinimumSize:])
   336  			ip.Encode(&header.IPv4Fields{
   337  				IHL:            header.IPv4MinimumSize,
   338  				TotalLength:    100,
   339  				TTL:            20,
   340  				Protocol:       10,
   341  				FragmentOffset: c.fragmentOffset,
   342  				SrcAddr:        localIpv4Addr,
   343  				DstAddr:        remoteIpv4Addr,
   344  			})
   345  
   346  			// Make payload be non-zero.
   347  			for i := dataOffset; i < len(view); i++ {
   348  				view[i] = uint8(i)
   349  			}
   350  
   351  			// Give packet to IPv4 endpoint, dispatcher will validate that
   352  			// it's ok.
   353  			o.protocol = 10
   354  			o.srcAddr = remoteIpv4Addr
   355  			o.dstAddr = localIpv4Addr
   356  			o.contents = view[dataOffset:]
   357  			o.typ = c.expectedTyp
   358  			o.extra = c.expectedExtra
   359  
   360  			vv := view[:len(view)-c.trunc].ToVectorisedView()
   361  			ep.HandlePacket(&r, vv)
   362  			if want := c.expectedCount; o.controlCalls != want {
   363  				t.Fatalf("Bad number of control calls for %q case: got %v, want %v", c.name, o.controlCalls, want)
   364  			}
   365  		})
   366  	}
   367  }
   368  
   369  func TestIPv4FragmentationReceive(t *testing.T) {
   370  	o := testObject{t: t, v4: true}
   371  	proto := ipv4.NewProtocol()
   372  	ep, err := proto.NewEndpoint(1, tcpip.AddressWithPrefix{localIpv4Addr, localIpv4PrefixLen}, nil, &o, nil)
   373  	if err != nil {
   374  		t.Fatalf("NewEndpoint failed: %v", err)
   375  	}
   376  
   377  	totalLen := header.IPv4MinimumSize + 24
   378  
   379  	frag1 := buffer.NewView(totalLen)
   380  	ip1 := header.IPv4(frag1)
   381  	ip1.Encode(&header.IPv4Fields{
   382  		IHL:            header.IPv4MinimumSize,
   383  		TotalLength:    uint16(totalLen),
   384  		TTL:            20,
   385  		Protocol:       10,
   386  		FragmentOffset: 0,
   387  		Flags:          header.IPv4FlagMoreFragments,
   388  		SrcAddr:        remoteIpv4Addr,
   389  		DstAddr:        localIpv4Addr,
   390  	})
   391  	// Make payload be non-zero.
   392  	for i := header.IPv4MinimumSize; i < totalLen; i++ {
   393  		frag1[i] = uint8(i)
   394  	}
   395  
   396  	frag2 := buffer.NewView(totalLen)
   397  	ip2 := header.IPv4(frag2)
   398  	ip2.Encode(&header.IPv4Fields{
   399  		IHL:            header.IPv4MinimumSize,
   400  		TotalLength:    uint16(totalLen),
   401  		TTL:            20,
   402  		Protocol:       10,
   403  		FragmentOffset: 24,
   404  		SrcAddr:        remoteIpv4Addr,
   405  		DstAddr:        localIpv4Addr,
   406  	})
   407  	// Make payload be non-zero.
   408  	for i := header.IPv4MinimumSize; i < totalLen; i++ {
   409  		frag2[i] = uint8(i)
   410  	}
   411  
   412  	// Give packet to ipv4 endpoint, dispatcher will validate that it's ok.
   413  	o.protocol = 10
   414  	o.srcAddr = remoteIpv4Addr
   415  	o.dstAddr = localIpv4Addr
   416  	o.contents = append(frag1[header.IPv4MinimumSize:totalLen], frag2[header.IPv4MinimumSize:totalLen]...)
   417  
   418  	r, err := buildIPv4Route(localIpv4Addr, remoteIpv4Addr)
   419  	if err != nil {
   420  		t.Fatalf("could not find route: %v", err)
   421  	}
   422  
   423  	// Send first segment.
   424  	ep.HandlePacket(&r, frag1.ToVectorisedView())
   425  	if o.dataCalls != 0 {
   426  		t.Fatalf("Bad number of data calls: got %x, want 0", o.dataCalls)
   427  	}
   428  
   429  	// Send second segment.
   430  	ep.HandlePacket(&r, frag2.ToVectorisedView())
   431  	if o.dataCalls != 1 {
   432  		t.Fatalf("Bad number of data calls: got %x, want 1", o.dataCalls)
   433  	}
   434  }
   435  
   436  func TestIPv6Send(t *testing.T) {
   437  	o := testObject{t: t}
   438  	proto := ipv6.NewProtocol()
   439  	ep, err := proto.NewEndpoint(1, tcpip.AddressWithPrefix{localIpv6Addr, localIpv6PrefixLen}, nil, nil, &o)
   440  	if err != nil {
   441  		t.Fatalf("NewEndpoint failed: %v", err)
   442  	}
   443  
   444  	// Allocate and initialize the payload view.
   445  	payload := buffer.NewView(100)
   446  	for i := 0; i < len(payload); i++ {
   447  		payload[i] = uint8(i)
   448  	}
   449  
   450  	// Allocate the header buffer.
   451  	hdr := buffer.NewPrependable(int(ep.MaxHeaderLength()))
   452  
   453  	// Issue the write.
   454  	o.protocol = 123
   455  	o.srcAddr = localIpv6Addr
   456  	o.dstAddr = remoteIpv6Addr
   457  	o.contents = payload
   458  
   459  	r, err := buildIPv6Route(localIpv6Addr, remoteIpv6Addr)
   460  	if err != nil {
   461  		t.Fatalf("could not find route: %v", err)
   462  	}
   463  	if err := ep.WritePacket(&r, nil /* gso */, hdr, payload.ToVectorisedView(), 123, 123, stack.PacketOut); err != nil {
   464  		t.Fatalf("WritePacket failed: %v", err)
   465  	}
   466  }
   467  
   468  func TestIPv6Receive(t *testing.T) {
   469  	o := testObject{t: t}
   470  	proto := ipv6.NewProtocol()
   471  	ep, err := proto.NewEndpoint(1, tcpip.AddressWithPrefix{localIpv6Addr, localIpv6PrefixLen}, nil, &o, nil)
   472  	if err != nil {
   473  		t.Fatalf("NewEndpoint failed: %v", err)
   474  	}
   475  
   476  	totalLen := header.IPv6MinimumSize + 30
   477  	view := buffer.NewView(totalLen)
   478  	ip := header.IPv6(view)
   479  	ip.Encode(&header.IPv6Fields{
   480  		PayloadLength: uint16(totalLen - header.IPv6MinimumSize),
   481  		NextHeader:    10,
   482  		HopLimit:      20,
   483  		SrcAddr:       remoteIpv6Addr,
   484  		DstAddr:       localIpv6Addr,
   485  	})
   486  
   487  	// Make payload be non-zero.
   488  	for i := header.IPv6MinimumSize; i < totalLen; i++ {
   489  		view[i] = uint8(i)
   490  	}
   491  
   492  	// Give packet to ipv6 endpoint, dispatcher will validate that it's ok.
   493  	o.protocol = 10
   494  	o.srcAddr = remoteIpv6Addr
   495  	o.dstAddr = localIpv6Addr
   496  	o.contents = view[header.IPv6MinimumSize:totalLen]
   497  
   498  	r, err := buildIPv6Route(localIpv6Addr, remoteIpv6Addr)
   499  	if err != nil {
   500  		t.Fatalf("could not find route: %v", err)
   501  	}
   502  
   503  	ep.HandlePacket(&r, view.ToVectorisedView())
   504  	if o.dataCalls != 1 {
   505  		t.Fatalf("Bad number of data calls: got %x, want 1", o.dataCalls)
   506  	}
   507  }
   508  
   509  func TestIPv6ReceiveControl(t *testing.T) {
   510  	newUint16 := func(v uint16) *uint16 { return &v }
   511  
   512  	const mtu = 0xffff
   513  	cases := []struct {
   514  		name           string
   515  		expectedCount  int
   516  		fragmentOffset *uint16
   517  		typ            header.ICMPv6Type
   518  		code           uint8
   519  		expectedTyp    stack.ControlType
   520  		expectedExtra  uint32
   521  		trunc          int
   522  	}{
   523  		{"PacketTooBig", 1, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, 0},
   524  		{"Truncated (10 bytes missing)", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, 10},
   525  		{"Truncated (missing IPv6 header)", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, header.IPv6MinimumSize + 8},
   526  		{"Truncated PacketTooBig (missing 'extra info')", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, 4 + header.IPv6MinimumSize + 8},
   527  		{"Truncated (missing ICMP header)", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, header.ICMPv6PacketTooBigMinimumSize + header.IPv6MinimumSize + 8},
   528  		{"Port unreachable", 1, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 0},
   529  		{"Truncated DstUnreachable (missing 'extra info')", 0, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 4 + header.IPv6MinimumSize + 8},
   530  		{"Fragmented, zero offset", 1, newUint16(0), header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 0},
   531  		{"Non-zero fragment offset", 0, newUint16(100), header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 0},
   532  		{"Zero-length packet", 0, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv6MinimumSize + header.ICMPv6DstUnreachableMinimumSize + 8},
   533  	}
   534  	r, err := buildIPv6Route(
   535  		localIpv6Addr,
   536  		"\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa",
   537  	)
   538  	if err != nil {
   539  		t.Fatal(err)
   540  	}
   541  	for _, c := range cases {
   542  		t.Run(c.name, func(t *testing.T) {
   543  			o := testObject{t: t}
   544  			proto := ipv6.NewProtocol()
   545  			ep, err := proto.NewEndpoint(1, tcpip.AddressWithPrefix{localIpv6Addr, localIpv6PrefixLen}, nil, &o, nil)
   546  			if err != nil {
   547  				t.Fatalf("NewEndpoint failed: %v", err)
   548  			}
   549  
   550  			defer ep.Close()
   551  
   552  			dataOffset := header.IPv6MinimumSize*2 + header.ICMPv6MinimumSize
   553  			if c.fragmentOffset != nil {
   554  				dataOffset += header.IPv6FragmentHeaderSize
   555  			}
   556  			view := buffer.NewView(dataOffset + 8)
   557  
   558  			// Create the outer IPv6 header.
   559  			ip := header.IPv6(view)
   560  			ip.Encode(&header.IPv6Fields{
   561  				PayloadLength: uint16(len(view) - header.IPv6MinimumSize - c.trunc),
   562  				NextHeader:    uint8(header.ICMPv6ProtocolNumber),
   563  				HopLimit:      20,
   564  				SrcAddr:       "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa",
   565  				DstAddr:       localIpv6Addr,
   566  			})
   567  
   568  			// Create the ICMP header.
   569  			icmp := header.ICMPv6(view[header.IPv6MinimumSize:])
   570  			icmp.SetType(c.typ)
   571  			icmp.SetCode(c.code)
   572  			icmp.SetIdent(0xdead)
   573  			icmp.SetSequence(0xbeef)
   574  
   575  			// Create the inner IPv6 header.
   576  			ip = header.IPv6(view[header.IPv6MinimumSize+header.ICMPv6PayloadOffset:])
   577  			ip.Encode(&header.IPv6Fields{
   578  				PayloadLength: 100,
   579  				NextHeader:    10,
   580  				HopLimit:      20,
   581  				SrcAddr:       localIpv6Addr,
   582  				DstAddr:       remoteIpv6Addr,
   583  			})
   584  
   585  			// Build the fragmentation header if needed.
   586  			if c.fragmentOffset != nil {
   587  				ip.SetNextHeader(header.IPv6FragmentHeader)
   588  				frag := header.IPv6Fragment(view[2*header.IPv6MinimumSize+header.ICMPv6MinimumSize:])
   589  				frag.Encode(&header.IPv6FragmentFields{
   590  					NextHeader:     10,
   591  					FragmentOffset: *c.fragmentOffset,
   592  					M:              true,
   593  					Identification: 0x12345678,
   594  				})
   595  			}
   596  
   597  			// Make payload be non-zero.
   598  			for i := dataOffset; i < len(view); i++ {
   599  				view[i] = uint8(i)
   600  			}
   601  
   602  			// Give packet to IPv6 endpoint, dispatcher will validate that
   603  			// it's ok.
   604  			o.protocol = 10
   605  			o.srcAddr = remoteIpv6Addr
   606  			o.dstAddr = localIpv6Addr
   607  			o.contents = view[dataOffset:]
   608  			o.typ = c.expectedTyp
   609  			o.extra = c.expectedExtra
   610  
   611  			vv := view[:len(view)-c.trunc].ToVectorisedView()
   612  			ep.HandlePacket(&r, vv)
   613  			if want := c.expectedCount; o.controlCalls != want {
   614  				t.Fatalf("Bad number of control calls for %q case: got %v, want %v", c.name, o.controlCalls, want)
   615  			}
   616  		})
   617  	}
   618  }