gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/link/fdbased/endpoint_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  //go:build linux
    16  // +build linux
    17  
    18  package fdbased
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"math/rand"
    24  	"os"
    25  	"slices"
    26  	"testing"
    27  	"time"
    28  	"unsafe"
    29  
    30  	"github.com/google/go-cmp/cmp"
    31  	"golang.org/x/sys/unix"
    32  	"gvisor.dev/gvisor/pkg/buffer"
    33  	"gvisor.dev/gvisor/pkg/refs"
    34  	"gvisor.dev/gvisor/pkg/tcpip"
    35  	"gvisor.dev/gvisor/pkg/tcpip/header"
    36  	"gvisor.dev/gvisor/pkg/tcpip/stack"
    37  )
    38  
    39  const (
    40  	mtu        = 1500
    41  	laddr      = tcpip.LinkAddress("\x11\x22\x33\x44\x55\x66")
    42  	raddr      = tcpip.LinkAddress("\x77\x88\x99\xaa\xbb\xcc")
    43  	proto      = 10
    44  	csumOffset = 48
    45  	gsoMSS     = 500
    46  )
    47  
    48  type packetInfo struct {
    49  	Proto    tcpip.NetworkProtocolNumber
    50  	Contents *stack.PacketBuffer
    51  }
    52  
    53  type packetContents struct {
    54  	LinkHeader      []byte
    55  	NetworkHeader   []byte
    56  	TransportHeader []byte
    57  	Data            []byte
    58  }
    59  
    60  func checkPacketInfoEqual(t *testing.T, got, want packetInfo) {
    61  	t.Helper()
    62  	if diff := cmp.Diff(
    63  		want, got,
    64  		cmp.Transformer("ExtractPacketBuffer", func(pk *stack.PacketBuffer) *packetContents {
    65  			if pk == nil {
    66  				return nil
    67  			}
    68  			return &packetContents{
    69  				LinkHeader:      pk.LinkHeader().Slice(),
    70  				NetworkHeader:   pk.NetworkHeader().Slice(),
    71  				TransportHeader: pk.TransportHeader().Slice(),
    72  				Data:            pk.Data().AsRange().ToSlice(),
    73  			}
    74  		}),
    75  	); diff != "" {
    76  		t.Errorf("unexpected packetInfo (-want +got):\n%s", diff)
    77  	}
    78  }
    79  
    80  type context struct {
    81  	t        *testing.T
    82  	readFDs  []int
    83  	writeFDs []int
    84  	ep       stack.LinkEndpoint
    85  	ch       chan packetInfo
    86  	done     chan struct{}
    87  }
    88  
    89  func newContext(t *testing.T, opt *Options) *context {
    90  	firstFDPair, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_SEQPACKET, 0)
    91  	if err != nil {
    92  		t.Fatalf("Socketpair failed: %v", err)
    93  	}
    94  	secondFDPair, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_SEQPACKET, 0)
    95  	if err != nil {
    96  		t.Fatalf("Socketpair failed: %v", err)
    97  	}
    98  
    99  	done := make(chan struct{}, 2)
   100  	opt.ClosedFunc = func(tcpip.Error) {
   101  		done <- struct{}{}
   102  	}
   103  
   104  	opt.FDs = []int{firstFDPair[1], secondFDPair[1]}
   105  	ep, err := New(opt)
   106  	if err != nil {
   107  		t.Fatalf("Failed to create FD endpoint: %v", err)
   108  	}
   109  
   110  	c := &context{
   111  		t:        t,
   112  		readFDs:  []int{firstFDPair[0], secondFDPair[0]},
   113  		writeFDs: opt.FDs,
   114  		ep:       ep,
   115  		ch:       make(chan packetInfo, 100),
   116  		done:     done,
   117  	}
   118  
   119  	ep.Attach(c)
   120  
   121  	return c
   122  }
   123  
   124  func (c *context) cleanup() {
   125  	for _, fd := range c.readFDs {
   126  		unix.Close(fd)
   127  	}
   128  	<-c.done
   129  	<-c.done
   130  	for _, fd := range c.writeFDs {
   131  		unix.Close(fd)
   132  	}
   133  }
   134  
   135  func (c *context) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
   136  	pkt.IncRef()
   137  	c.ch <- packetInfo{protocol, pkt}
   138  }
   139  
   140  func (c *context) DeliverLinkPacket(tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
   141  	c.t.Fatal("DeliverLinkPacket not implemented")
   142  }
   143  
   144  func TestNoEthernetProperties(t *testing.T) {
   145  	c := newContext(t, &Options{MTU: mtu})
   146  	defer c.cleanup()
   147  
   148  	if want, v := uint16(0), c.ep.MaxHeaderLength(); want != v {
   149  		t.Fatalf("MaxHeaderLength() = %v, want %v", v, want)
   150  	}
   151  
   152  	if want, v := uint32(mtu), c.ep.MTU(); want != v {
   153  		t.Fatalf("MTU() = %v, want %v", v, want)
   154  	}
   155  }
   156  
   157  func TestEthernetProperties(t *testing.T) {
   158  	c := newContext(t, &Options{EthernetHeader: true, MTU: mtu})
   159  	defer c.cleanup()
   160  
   161  	if want, v := uint16(header.EthernetMinimumSize), c.ep.MaxHeaderLength(); want != v {
   162  		t.Fatalf("MaxHeaderLength() = %v, want %v", v, want)
   163  	}
   164  
   165  	if want, v := uint32(mtu), c.ep.MTU(); want != v {
   166  		t.Fatalf("MTU() = %v, want %v", v, want)
   167  	}
   168  }
   169  
   170  func TestAddress(t *testing.T) {
   171  	addrs := []tcpip.LinkAddress{"", "abc", "def"}
   172  	for _, a := range addrs {
   173  		t.Run(fmt.Sprintf("Address: %q", a), func(t *testing.T) {
   174  			c := newContext(t, &Options{Address: a, MTU: mtu})
   175  			defer c.cleanup()
   176  
   177  			if want, v := a, c.ep.LinkAddress(); want != v {
   178  				t.Fatalf("LinkAddress() = %v, want %v", v, want)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func testWritePacket(t *testing.T, plen int, eth bool, gsoMaxSize uint32, hash uint32) {
   185  	c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: eth, GSOMaxSize: gsoMaxSize})
   186  	defer c.cleanup()
   187  
   188  	// Build payload.
   189  	payload := make([]byte, plen)
   190  	if _, err := rand.Read(payload); err != nil {
   191  		t.Fatalf("rand.Read(payload): %s", err)
   192  	}
   193  
   194  	// Build packet buffer.
   195  	const netHdrLen = 100
   196  	pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
   197  		ReserveHeaderBytes: int(c.ep.MaxHeaderLength()) + netHdrLen,
   198  		Payload:            buffer.MakeWithData(payload),
   199  	})
   200  	defer pkt.DecRef()
   201  	pkt.Hash = hash
   202  	// Every PacketBuffer must have these set:
   203  	// See nic.writePacket.
   204  	pkt.EgressRoute.LocalLinkAddress = laddr
   205  	pkt.EgressRoute.RemoteLinkAddress = raddr
   206  	pkt.NetworkProtocolNumber = proto
   207  
   208  	// Build header.
   209  	b := pkt.NetworkHeader().Push(netHdrLen)
   210  	if _, err := rand.Read(b); err != nil {
   211  		t.Fatalf("rand.Read(b): %s", err)
   212  	}
   213  
   214  	// Write.
   215  	want := append(append([]byte{}, b...), payload...)
   216  	const l3HdrLen = header.IPv6MinimumSize
   217  	if gsoMaxSize != 0 {
   218  		pkt.GSOOptions = stack.GSO{
   219  			Type:       stack.GSOTCPv6,
   220  			NeedsCsum:  true,
   221  			CsumOffset: csumOffset,
   222  			MSS:        gsoMSS,
   223  			L3HdrLen:   l3HdrLen,
   224  		}
   225  	}
   226  
   227  	c.ep.AddHeader(pkt)
   228  
   229  	var pkts stack.PacketBufferList
   230  	pkts.PushBack(pkt)
   231  	if _, err := c.ep.WritePackets(pkts); err != nil {
   232  		t.Fatalf("WritePackets failed: %s", err)
   233  	}
   234  
   235  	// Read from the corresponding FD, then compare with what we wrote.
   236  	b = make([]byte, mtu)
   237  	fd := c.readFDs[hash%uint32(len(c.readFDs))]
   238  	n, err := unix.Read(fd, b)
   239  	if err != nil {
   240  		t.Fatalf("Read failed: %v", err)
   241  	}
   242  	b = b[:n]
   243  	if gsoMaxSize != 0 {
   244  		vnetHdr := *(*virtioNetHdr)(unsafe.Pointer(&b[0]))
   245  		if vnetHdr.flags&_VIRTIO_NET_HDR_F_NEEDS_CSUM == 0 {
   246  			t.Fatalf("virtioNetHdr.flags %v  doesn't contain %v", vnetHdr.flags, _VIRTIO_NET_HDR_F_NEEDS_CSUM)
   247  		}
   248  		const csumStart = header.EthernetMinimumSize + l3HdrLen
   249  		if vnetHdr.csumStart != csumStart {
   250  			t.Fatalf("vnetHdr.csumStart = %v, want %v", vnetHdr.csumStart, csumStart)
   251  		}
   252  		if vnetHdr.csumOffset != csumOffset {
   253  			t.Fatalf("vnetHdr.csumOffset = %v, want %v", vnetHdr.csumOffset, csumOffset)
   254  		}
   255  		gsoType := uint8(0)
   256  		if plen > gsoMSS {
   257  			gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
   258  		}
   259  		if vnetHdr.gsoType != gsoType {
   260  			t.Fatalf("vnetHdr.gsoType = %v, want %v", vnetHdr.gsoType, gsoType)
   261  		}
   262  		b = b[virtioNetHdrSize:]
   263  	}
   264  	if eth {
   265  		h := header.Ethernet(b)
   266  		b = b[header.EthernetMinimumSize:]
   267  
   268  		if a := h.SourceAddress(); a != laddr {
   269  			t.Fatalf("SourceAddress() = %v, want %v", a, laddr)
   270  		}
   271  
   272  		if a := h.DestinationAddress(); a != raddr {
   273  			t.Fatalf("DestinationAddress() = %v, want %v", a, raddr)
   274  		}
   275  
   276  		if et := h.Type(); et != proto {
   277  			t.Fatalf("Type() = %v, want %v", et, proto)
   278  		}
   279  	}
   280  	if len(b) != len(want) {
   281  		t.Fatalf("Read returned %v bytes, want %v", len(b), len(want))
   282  	}
   283  	if !bytes.Equal(b, want) {
   284  		t.Fatalf("Read returned %x, want %x", b, want)
   285  	}
   286  }
   287  
   288  func TestWritePacket(t *testing.T) {
   289  	lengths := []int{0, 100, 1000}
   290  	eths := []bool{true, false}
   291  	gsos := []uint32{0, 32768}
   292  
   293  	for _, eth := range eths {
   294  		for _, plen := range lengths {
   295  			for _, gso := range gsos {
   296  				t.Run(
   297  					fmt.Sprintf("Eth=%v,PayloadLen=%v,GSOMaxSize=%v", eth, plen, gso),
   298  					func(t *testing.T) {
   299  						testWritePacket(t, plen, eth, gso, 0)
   300  					},
   301  				)
   302  			}
   303  		}
   304  	}
   305  }
   306  
   307  func TestHashedWritePacket(t *testing.T) {
   308  	lengths := []int{0, 100, 1000}
   309  	eths := []bool{true, false}
   310  	gsos := []uint32{0, 32768}
   311  	hashes := []uint32{0, 1}
   312  	for _, eth := range eths {
   313  		for _, plen := range lengths {
   314  			for _, gso := range gsos {
   315  				for _, hash := range hashes {
   316  					t.Run(
   317  						fmt.Sprintf("Eth=%v,PayloadLen=%v,GSOMaxSize=%v,Hash=%d", eth, plen, gso, hash),
   318  						func(t *testing.T) {
   319  							testWritePacket(t, plen, eth, gso, hash)
   320  						},
   321  					)
   322  				}
   323  			}
   324  		}
   325  	}
   326  }
   327  
   328  func TestPreserveSrcAddress(t *testing.T) {
   329  	baddr := tcpip.LinkAddress("\xcc\xbb\xaa\x77\x88\x99")
   330  
   331  	c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: true})
   332  	defer c.cleanup()
   333  
   334  	pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
   335  		// WritePacket panics given a prependable with anything less than
   336  		// the minimum size of the ethernet header.
   337  		// TODO(b/153685824): Figure out if this should use c.ep.MaxHeaderLength().
   338  		ReserveHeaderBytes: header.EthernetMinimumSize,
   339  	})
   340  	defer pkt.DecRef()
   341  	// Every PacketBuffer must have these set:
   342  	// See nic.writePacket.
   343  	pkt.NetworkProtocolNumber = proto
   344  	// Set LocalLinkAddress in route to the value of the bridged address.
   345  	pkt.EgressRoute.LocalLinkAddress = baddr
   346  	pkt.EgressRoute.RemoteLinkAddress = raddr
   347  	c.ep.AddHeader(pkt)
   348  
   349  	var pkts stack.PacketBufferList
   350  	pkts.PushBack(pkt)
   351  	if _, err := c.ep.WritePackets(pkts); err != nil {
   352  		t.Fatalf("WritePackets failed: %s", err)
   353  	}
   354  
   355  	// Read from the FD, then compare with what we wrote.
   356  	b := make([]byte, mtu)
   357  	n, err := unix.Read(c.readFDs[0], b)
   358  	if err != nil {
   359  		t.Fatalf("Read failed: %v", err)
   360  	}
   361  	b = b[:n]
   362  	h := header.Ethernet(b)
   363  
   364  	if a := h.SourceAddress(); a != baddr {
   365  		t.Fatalf("SourceAddress() = %v, want %v", a, baddr)
   366  	}
   367  }
   368  
   369  func TestDeliverPacket(t *testing.T) {
   370  	lengths := []int{100, 1000}
   371  	eths := []bool{true, false}
   372  
   373  	for _, eth := range eths {
   374  		for _, plen := range lengths {
   375  			t.Run(fmt.Sprintf("Eth=%v,PayloadLen=%v", eth, plen), func(t *testing.T) {
   376  				c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: eth})
   377  				defer c.cleanup()
   378  
   379  				// Build packet.
   380  				all := make([]byte, plen)
   381  				if _, err := rand.Read(all); err != nil {
   382  					t.Fatalf("rand.Read(all): %s", err)
   383  				}
   384  				// Make it look like an IPv4 packet.
   385  				all[0] = 0x40
   386  
   387  				wantPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
   388  					ReserveHeaderBytes: header.EthernetMinimumSize,
   389  					Payload:            buffer.MakeWithData(all),
   390  				})
   391  				defer wantPkt.DecRef()
   392  				if eth {
   393  					hdr := header.Ethernet(wantPkt.LinkHeader().Push(header.EthernetMinimumSize))
   394  					hdr.Encode(&header.EthernetFields{
   395  						SrcAddr: raddr,
   396  						DstAddr: laddr,
   397  						Type:    proto,
   398  					})
   399  					all = append(hdr, all...)
   400  				}
   401  
   402  				// Write packet via the file descriptor.
   403  				if _, err := unix.Write(c.readFDs[0], all); err != nil {
   404  					t.Fatalf("Write failed: %v", err)
   405  				}
   406  
   407  				// Receive packet through the endpoint.
   408  				select {
   409  				case pi := <-c.ch:
   410  					defer pi.Contents.DecRef()
   411  					want := packetInfo{
   412  						Proto:    proto,
   413  						Contents: wantPkt,
   414  					}
   415  					if !eth {
   416  						want.Proto = header.IPv4ProtocolNumber
   417  					}
   418  					checkPacketInfoEqual(t, pi, want)
   419  				case <-time.After(10 * time.Second):
   420  					t.Fatalf("Timed out waiting for packet")
   421  				}
   422  			})
   423  		}
   424  	}
   425  }
   426  
   427  func TestBufConfigMaxLength(t *testing.T) {
   428  	got := 0
   429  	for _, i := range BufConfig {
   430  		got += i
   431  	}
   432  	want := header.MaxIPPacketSize // maximum TCP packet size
   433  	if got < want {
   434  		t.Errorf("total buffer size is invalid: got %d, want >= %d", got, want)
   435  	}
   436  }
   437  
   438  func TestBufConfigFirst(t *testing.T) {
   439  	// The stack assumes that the TCP/IP header is entirely contained in the first view.
   440  	// Therefore, the first view needs to be large enough to contain the maximum TCP/IP
   441  	// header, which is 120 bytes (60 bytes for IP + 60 bytes for TCP).
   442  	want := 120
   443  	got := BufConfig[0]
   444  	if got < want {
   445  		t.Errorf("first view has an invalid size: got %d, want >= %d", got, want)
   446  	}
   447  }
   448  
   449  var capLengthTestCases = []struct {
   450  	comment     string
   451  	config      []int
   452  	n           int
   453  	wantUsed    int
   454  	wantLengths []int
   455  }{
   456  	{
   457  		comment:     "Single slice",
   458  		config:      []int{256},
   459  		n:           128,
   460  		wantUsed:    1,
   461  		wantLengths: []int{128},
   462  	},
   463  	{
   464  		comment:     "Multiple slices",
   465  		config:      []int{128, 256},
   466  		n:           256,
   467  		wantUsed:    2,
   468  		wantLengths: []int{128, 128},
   469  	},
   470  	{
   471  		comment:     "Entire buffer",
   472  		config:      []int{128, 256},
   473  		n:           384,
   474  		wantUsed:    2,
   475  		wantLengths: []int{128, 256},
   476  	},
   477  	{
   478  		comment:     "Entire buffer but not on the last slice",
   479  		config:      []int{128, 256, 512},
   480  		n:           384,
   481  		wantUsed:    2,
   482  		wantLengths: []int{128, 256},
   483  	},
   484  }
   485  
   486  func TestIovecBuffer(t *testing.T) {
   487  	for _, c := range capLengthTestCases {
   488  		t.Run(c.comment, func(t *testing.T) {
   489  			b := newIovecBuffer(c.config, false /* skipsVnetHdr */)
   490  			defer b.release()
   491  
   492  			// Test initial allocation.
   493  			iovecs := b.nextIovecs()
   494  			if got, want := len(iovecs), len(c.config); got != want {
   495  				t.Fatalf("len(iovecs) = %d, want %d", got, want)
   496  			}
   497  
   498  			// Make a copy as iovecs points to internal slice. We will need this state
   499  			// later.
   500  			oldIovecs := append([]unix.Iovec(nil), iovecs...)
   501  
   502  			// Test the buffer that get pulled.
   503  			buf := b.pullBuffer(c.n)
   504  			defer buf.Release()
   505  			var lengths []int
   506  			buf.Apply(func(v *buffer.View) {
   507  				lengths = append(lengths, v.Size())
   508  			})
   509  			if !slices.Equal(lengths, c.wantLengths) {
   510  				t.Errorf("Pulled view lengths = %v, want %v", lengths, c.wantLengths)
   511  			}
   512  
   513  			// Test that new views get reallocated.
   514  			for i, newIov := range b.nextIovecs() {
   515  				if i < c.wantUsed {
   516  					if newIov.Base == oldIovecs[i].Base {
   517  						t.Errorf("b.views[%d] should have been reallocated", i)
   518  					}
   519  				} else {
   520  					if newIov.Base != oldIovecs[i].Base {
   521  						t.Errorf("b.views[%d] should not have been reallocated", i)
   522  					}
   523  				}
   524  			}
   525  		})
   526  	}
   527  }
   528  
   529  func TestIovecBufferSkipVnetHdr(t *testing.T) {
   530  	for _, test := range []struct {
   531  		desc    string
   532  		readN   int
   533  		wantLen int
   534  	}{
   535  		{
   536  			desc:    "nothing read",
   537  			readN:   0,
   538  			wantLen: 0,
   539  		},
   540  		{
   541  			desc:    "smaller than vnet header",
   542  			readN:   virtioNetHdrSize - 1,
   543  			wantLen: 0,
   544  		},
   545  		{
   546  			desc:    "header skipped",
   547  			readN:   virtioNetHdrSize + 512,
   548  			wantLen: 512,
   549  		},
   550  	} {
   551  		t.Run(test.desc, func(t *testing.T) {
   552  			b := newIovecBuffer([]int{128, 256, 512, 1024}, true)
   553  			defer b.release()
   554  			// Pretend a read happened.
   555  			b.nextIovecs()
   556  			buf := b.pullBuffer(test.readN)
   557  			defer buf.Release()
   558  			if got, want := int(buf.Size()), test.wantLen; got != want {
   559  				t.Errorf("b.pullView(%d).Size() = %d; want %d", test.readN, got, want)
   560  			}
   561  			if got, want := len(buf.Flatten()), test.wantLen; got != want {
   562  				t.Errorf("b.pullView(%d).ToOwnedView() has length %d; want %d", test.readN, got, want)
   563  			}
   564  		})
   565  	}
   566  }
   567  
   568  // fakeNetworkDispatcher delivers packets to pkts.
   569  type fakeNetworkDispatcher struct {
   570  	pkts []*stack.PacketBuffer
   571  }
   572  
   573  func (d *fakeNetworkDispatcher) DeliverNetworkPacket(_ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
   574  	pkt.IncRef()
   575  	d.pkts = append(d.pkts, pkt)
   576  }
   577  
   578  func (*fakeNetworkDispatcher) DeliverLinkPacket(tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
   579  	panic("not implemented")
   580  }
   581  
   582  func TestDispatchPacketFormat(t *testing.T) {
   583  	for _, test := range []struct {
   584  		name          string
   585  		newDispatcher func(fd int, e *endpoint, opts *Options) (linkDispatcher, error)
   586  		ethHdr        []byte
   587  		netHdr        []byte
   588  	}{
   589  		{
   590  			name:          "readVDispatcher",
   591  			newDispatcher: newReadVDispatcher,
   592  			ethHdr: []byte{
   593  				1, 2, 3, 4, 5, 60,
   594  				1, 2, 3, 4, 5, 61,
   595  				8, 0,
   596  			},
   597  			netHdr: []byte{0x40, 0, 0, 0},
   598  		},
   599  		{
   600  			name:          "recvMMsgDispatcher",
   601  			newDispatcher: newRecvMMsgDispatcher,
   602  			ethHdr: []byte{
   603  				1, 2, 3, 4, 5, 60,
   604  				1, 2, 3, 4, 5, 61,
   605  				8, 0,
   606  			},
   607  			netHdr: []byte{0x40, 0, 0, 0},
   608  		},
   609  		{
   610  			name:          "readVDispatcherNoEth",
   611  			newDispatcher: newReadVDispatcher,
   612  			netHdr:        []byte{0x40, 0, 0, 0},
   613  		},
   614  		{
   615  			name:          "readVDispatcherNoEthIPv6",
   616  			newDispatcher: newReadVDispatcher,
   617  			netHdr: []byte{
   618  				0x60, 0, 0, 0, // IPv6 Preamble
   619  				0, 0x04, 0, 0, // IPv6 Preamble
   620  				byte(header.IPv6HopByHopOptionsExtHdrIdentifier), // Next header
   621  				0xff,                   // Hop limit
   622  				0, 0, 0, 0, 0, 0, 0, 0, // Src addr
   623  				0, 0, 0, 0, 0, 0, 0, 0,
   624  				0, 0, 0, 0, 0, 0, 0, 0, // Dst addr
   625  				0, 0, 0, 0, 0, 0, 0, 0,
   626  
   627  				// Hop by hop extension header.
   628  				byte(header.IPv6DestinationOptionsExtHdrIdentifier),
   629  				0x8,              // Length.
   630  				0, 0, 0, 0, 0, 0, // Padding.
   631  
   632  				// Destination options extension header.
   633  				0,                // Next header (none)
   634  				0x8,              // Length.
   635  				0, 0, 0, 0, 0, 0, // Padding.
   636  
   637  				// payload data.
   638  				1, 2, 3, 4,
   639  			},
   640  		},
   641  	} {
   642  		t.Run(test.name, func(t *testing.T) {
   643  			// Create a socket pair to send/recv.
   644  			fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
   645  			if err != nil {
   646  				t.Fatal(err)
   647  			}
   648  
   649  			data := append(test.ethHdr, test.netHdr...)
   650  			err = unix.Sendmsg(fds[1], data, nil, nil, 0)
   651  			if err != nil {
   652  				t.Fatal(err)
   653  			}
   654  
   655  			// Create and run dispatcher once.
   656  			sink := &fakeNetworkDispatcher{}
   657  			d, err := test.newDispatcher(fds[0], &endpoint{
   658  				hdrSize:    len(test.ethHdr),
   659  				dispatcher: sink,
   660  			}, &Options{ProcessorsPerChannel: 1})
   661  			if err != nil {
   662  				t.Fatal(err)
   663  			}
   664  			defer d.release()
   665  			if ok, err := d.dispatch(); !ok || err != nil {
   666  				t.Fatalf("d.dispatch() = %v, %v", ok, err)
   667  			}
   668  
   669  			// Verify packet.
   670  			if got, want := len(sink.pkts), 1; got != want {
   671  				t.Fatalf("len(sink.pkts) = %d, want %d", got, want)
   672  			}
   673  			pkt := sink.pkts[0]
   674  			defer pkt.DecRef()
   675  			if len(test.ethHdr) > 0 {
   676  				if got, want := len(pkt.LinkHeader().Slice()), header.EthernetMinimumSize; got != want {
   677  					t.Errorf("pkt.LinkHeader().View().Size() = %d, want %d", got, want)
   678  				}
   679  			}
   680  			if got, want := pkt.Data().Size(), len(test.netHdr); got != want {
   681  				t.Errorf("pkt.Data().Size() = %d, want %d", got, want)
   682  			}
   683  			wantProto := header.IPv4ProtocolNumber
   684  			if header.IPVersion(test.netHdr[:]) == header.IPv6Version {
   685  				wantProto = header.IPv6ProtocolNumber
   686  			}
   687  			if pkt.NetworkProtocolNumber != wantProto {
   688  				t.Errorf("pkt.NetworkProtocolNumber = %d, want %d", pkt.NetworkProtocolNumber, wantProto)
   689  			}
   690  		})
   691  	}
   692  }
   693  
   694  func TestMain(m *testing.M) {
   695  	refs.SetLeakMode(refs.LeaksPanic)
   696  	code := m.Run()
   697  	refs.DoLeakCheck()
   698  	os.Exit(code)
   699  }