func TestFillTxMemory(t *testing.T) {
	const bufferSize = 1500
	c := newTestContext(t, 20000, bufferSize, localLinkAddr)
	defer c.cleanup()
	r := stack.Route{
		RemoteLinkAddress: remoteLinkAddr,
	}
	buf := buffer.NewView(100)
	ids := make(map[uint64]struct{})
	for i := queueDataSize / bufferSize; i > 0; i-- {
		hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()))
		if err := c.ep.WritePacket(&r, nil  , header.IPv4ProtocolNumber, tcpip.PacketBuffer{
			Header: hdr,
			Data:   buf.ToVectorisedView(),
		}); err != nil {
			t.Fatalf("WritePacket failed unexpectedly: %v", err)
		}
		desc := c.txq.tx.Pull()
		pi := queue.DecodeTxPacketHeader(desc)
		if _, ok := ids[pi.ID]; ok {
			t.Fatalf("ID (%v) reused", pi.ID)
		}
		ids[pi.ID] = struct{}{}
		c.txq.tx.Flush()
	}
	hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()))
	err := c.ep.WritePacket(&r, nil  , header.IPv4ProtocolNumber, tcpip.PacketBuffer{
		Header: hdr,
		Data:   buf.ToVectorisedView(),
	})
	if want := tcpip.ErrWouldBlock; err != want {
		t.Fatalf("WritePacket return unexpected result: got %v, want %v", err, want)
	}
}
TestFillTxMemory sends packets until the we run out of shared memory.
package sharedmem
import (
	"bytes"
	"io/ioutil"
	"math/rand"
	"os"
	"strings"
	"sync"
	"syscall"
	"testing"
	"time"
	"github.com/google/netstack/tcpip"
	"github.com/google/netstack/tcpip/buffer"
	"github.com/google/netstack/tcpip/header"
	"github.com/google/netstack/tcpip/link/sharedmem/pipe"
	"github.com/google/netstack/tcpip/link/sharedmem/queue"
	"github.com/google/netstack/tcpip/stack"
)
const (
	localLinkAddr  = "\xde\xad\xbe\xef\x56\x78"
	remoteLinkAddr = "\xde\xad\xbe\xef\x12\x34"
	queueDataSize = 1024 * 1024
	queuePipeSize = 4096
)
type queueBuffers struct {
	data []byte
	rx   pipe.Tx
	tx   pipe.Rx
}
func initQueue(t *testing.T, q *queueBuffers, c *QueueConfig) {
	b, err := getBuffer(c.TxPipeFD)
	if err != nil {
		t.Fatalf("getBuffer failed: %v", err)
	}
	q.tx.Init(b)
	b, err = getBuffer(c.RxPipeFD)
	if err != nil {
		t.Fatalf("getBuffer failed: %v", err)
	}
	q.rx.Init(b)
	q.data, err = getBuffer(c.DataFD)
	if err != nil {
		t.Fatalf("getBuffer failed: %v", err)
	}
}
func (q *queueBuffers) cleanup() {
	syscall.Munmap(q.tx.Bytes())
	syscall.Munmap(q.rx.Bytes())
	syscall.Munmap(q.data)
}
type packetInfo struct {
	addr       tcpip.LinkAddress
	proto      tcpip.NetworkProtocolNumber
	vv         buffer.VectorisedView
	linkHeader buffer.View
}
type testContext struct {
	t     *testing.T
	ep    *endpoint
	txCfg QueueConfig
	rxCfg QueueConfig
	txq   queueBuffers
	rxq   queueBuffers
	packetCh chan struct{}
	mu       sync.Mutex
	packets  []packetInfo
}
func newTestContext(t *testing.T, mtu, bufferSize uint32, addr tcpip.LinkAddress) *testContext {
	var err error
	c := &testContext{
		t:        t,
		packetCh: make(chan struct{}, 1000000),
	}
	c.txCfg = createQueueFDs(t, queueSizes{
		dataSize:       queueDataSize,
		txPipeSize:     queuePipeSize,
		rxPipeSize:     queuePipeSize,
		sharedDataSize: 4096,
	})
	c.rxCfg = createQueueFDs(t, queueSizes{
		dataSize:       queueDataSize,
		txPipeSize:     queuePipeSize,
		rxPipeSize:     queuePipeSize,
		sharedDataSize: 4096,
	})
	initQueue(t, &c.txq, &c.txCfg)
	initQueue(t, &c.rxq, &c.rxCfg)
	ep, err := New(mtu, bufferSize, addr, c.txCfg, c.rxCfg)
	if err != nil {
		t.Fatalf("New failed: %v", err)
	}
	c.ep = ep.(*endpoint)
	c.ep.Attach(c)
	return c
}
func (c *testContext) DeliverNetworkPacket(_ stack.LinkEndpoint, remoteLinkAddr, localLinkAddr tcpip.LinkAddress, proto tcpip.NetworkProtocolNumber, pkt tcpip.PacketBuffer) {
	c.mu.Lock()
	c.packets = append(c.packets, packetInfo{
		addr:  remoteLinkAddr,
		proto: proto,
		vv:    pkt.Data.Clone(nil),
	})
	c.mu.Unlock()
	c.packetCh <- struct{}{}
}
func (c *testContext) cleanup() {
	c.ep.Close()
	closeFDs(&c.txCfg)
	closeFDs(&c.rxCfg)
	c.txq.cleanup()
	c.rxq.cleanup()
}
func (c *testContext) waitForPackets(n int, to <-chan time.Time, errorStr string) {
	for i := 0; i < n; i++ {
		select {
		case <-c.packetCh:
		case <-to:
			c.t.Fatalf(errorStr)
		}
	}
}
func (c *testContext) pushRxCompletion(size uint32, bs []queue.RxBuffer) {
	b := c.rxq.rx.Push(queue.RxCompletionSize(len(bs)))
	queue.EncodeRxCompletion(b, size, 0)
	for i := range bs {
		queue.EncodeRxCompletionBuffer(b, i, queue.RxBuffer{
			Offset: bs[i].Offset,
			Size:   bs[i].Size,
			ID:     bs[i].ID,
		})
	}
}
func randomFill(b []byte) {
	for i := range b {
		b[i] = byte(rand.Intn(256))
	}
}
func shuffle(b []int) {
	for i := len(b) - 1; i >= 0; i-- {
		j := rand.Intn(i + 1)
		b[i], b[j] = b[j], b[i]
	}
}
func createFile(t *testing.T, size int64, initQueue bool) int {
	tmpDir := os.Getenv("TEST_TMPDIR")
	if tmpDir == "" {
		tmpDir = os.Getenv("TMPDIR")
	}
	f, err := ioutil.TempFile(tmpDir, "sharedmem_test")
	if err != nil {
		t.Fatalf("TempFile failed: %v", err)
	}
	defer f.Close()
	syscall.Unlink(f.Name())
	if initQueue {
		_, err := f.WriteAt([]byte{0, 0, 0, 0, 0, 0, 0, 0x80}, 0)
		if err != nil {
			t.Fatalf("WriteAt failed: %v", err)
		}
	}
	fd, err := syscall.Dup(int(f.Fd()))
	if err != nil {
		t.Fatalf("Dup failed: %v", err)
	}
	if err := syscall.Ftruncate(fd, size); err != nil {
		syscall.Close(fd)
		t.Fatalf("Ftruncate failed: %v", err)
	}
	return fd
}
func closeFDs(c *QueueConfig) {
	syscall.Close(c.DataFD)
	syscall.Close(c.EventFD)
	syscall.Close(c.TxPipeFD)
	syscall.Close(c.RxPipeFD)
	syscall.Close(c.SharedDataFD)
}
type queueSizes struct {
	dataSize       int64
	txPipeSize     int64
	rxPipeSize     int64
	sharedDataSize int64
}
func createQueueFDs(t *testing.T, s queueSizes) QueueConfig {
	fd, _, err := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, 0, 0)
	if err != 0 {
		t.Fatalf("eventfd failed: %v", error(err))
	}
	return QueueConfig{
		EventFD:      int(fd),
		DataFD:       createFile(t, s.dataSize, false),
		TxPipeFD:     createFile(t, s.txPipeSize, true),
		RxPipeFD:     createFile(t, s.rxPipeSize, true),
		SharedDataFD: createFile(t, s.sharedDataSize, false),
	}
}
func TestSimpleSend(t *testing.T) {
	c := newTestContext(t, 20000, 1500, localLinkAddr)
	defer c.cleanup()
	r := stack.Route{
		RemoteLinkAddress: remoteLinkAddr,
	}
	for iters := 1000; iters > 0; iters-- {
		func() {
			n := rand.Intn(10000)
			hdr := buffer.NewPrependable(n + int(c.ep.MaxHeaderLength()))
			hdrBuf := hdr.Prepend(n)
			randomFill(hdrBuf)
			n = rand.Intn(10000)
			buf := buffer.NewView(n)
			randomFill(buf)
			proto := tcpip.NetworkProtocolNumber(rand.Intn(0x10000))
			if err := c.ep.WritePacket(&r, nil  , proto, tcpip.PacketBuffer{
				Header: hdr,
				Data:   buf.ToVectorisedView(),
			}); err != nil {
				t.Fatalf("WritePacket failed: %v", err)
			}
			desc := c.txq.tx.Pull()
			pi := queue.DecodeTxPacketHeader(desc)
			if pi.Reserved != 0 {
				t.Fatalf("Reserved value is non-zero: 0x%x", pi.Reserved)
			}
			contents := make([]byte, 0, pi.Size)
			for i := 0; i < pi.BufferCount; i++ {
				bi := queue.DecodeTxBufferHeader(desc, i)
				contents = append(contents, c.txq.data[bi.Offset:][:bi.Size]...)
			}
			c.txq.tx.Flush()
			defer func() {
				b := c.txq.rx.Push(8)
				queue.EncodeTxCompletion(b, pi.ID)
				c.txq.rx.Flush()
			}()
			ethTemplate := make(header.Ethernet, header.EthernetMinimumSize)
			ethTemplate.Encode(&header.EthernetFields{
				SrcAddr: localLinkAddr,
				DstAddr: remoteLinkAddr,
				Type:    proto,
			})
			if got := contents[:header.EthernetMinimumSize]; !bytes.Equal(got, []byte(ethTemplate)) {
				t.Fatalf("Bad ethernet header in packet: got %x, want %x", got, ethTemplate)
			}
			merged := append(hdrBuf, buf...)
			if uint32(len(contents)) < pi.Size {
				t.Fatalf("Sum of buffers is less than packet size: %v < %v", len(contents), pi.Size)
			}
			contents = contents[:pi.Size][header.EthernetMinimumSize:]
			if !bytes.Equal(contents, merged) {
				t.Fatalf("Buffers are different: got %x (%v bytes), want %x (%v bytes)", contents, len(contents), merged, len(merged))
			}
		}()
	}
}
func TestPreserveSrcAddressInSend(t *testing.T) {
	c := newTestContext(t, 20000, 1500, localLinkAddr)
	defer c.cleanup()
	newLocalLinkAddress := tcpip.LinkAddress(strings.Repeat("0xFE", 6))
	r := stack.Route{
		RemoteLinkAddress: remoteLinkAddr,
		LocalLinkAddress:  newLocalLinkAddress,
	}
	hdr := buffer.NewPrependable(header.EthernetMinimumSize)
	proto := tcpip.NetworkProtocolNumber(rand.Intn(0x10000))
	if err := c.ep.WritePacket(&r, nil  , proto, tcpip.PacketBuffer{
		Header: hdr,
	}); err != nil {
		t.Fatalf("WritePacket failed: %v", err)
	}
	desc := c.txq.tx.Pull()
	pi := queue.DecodeTxPacketHeader(desc)
	if pi.Reserved != 0 {
		t.Fatalf("Reserved value is non-zero: 0x%x", pi.Reserved)
	}
	contents := make([]byte, 0, pi.Size)
	for i := 0; i < pi.BufferCount; i++ {
		bi := queue.DecodeTxBufferHeader(desc, i)
		contents = append(contents, c.txq.data[bi.Offset:][:bi.Size]...)
	}
	c.txq.tx.Flush()
	defer func() {
		b := c.txq.rx.Push(8)
		queue.EncodeTxCompletion(b, pi.ID)
		c.txq.rx.Flush()
	}()
	ethTemplate := make(header.Ethernet, header.EthernetMinimumSize)
	ethTemplate.Encode(&header.EthernetFields{
		SrcAddr: newLocalLinkAddress,
		DstAddr: remoteLinkAddr,
		Type:    proto,
	})
	if got := contents[:header.EthernetMinimumSize]; !bytes.Equal(got, []byte(ethTemplate)) {
		t.Fatalf("Bad ethernet header in packet: got %x, want %x", got, ethTemplate)
	}
}
func TestFillTxQueue(t *testing.T) {
	c := newTestContext(t, 20000, 1500, localLinkAddr)
	defer c.cleanup()
	r := stack.Route{
		RemoteLinkAddress: remoteLinkAddr,
	}
	buf := buffer.NewView(100)
	ids := make(map[uint64]struct{})
	for i := queuePipeSize / 40; i > 0; i-- {
		hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()))
		if err := c.ep.WritePacket(&r, nil  , header.IPv4ProtocolNumber, tcpip.PacketBuffer{
			Header: hdr,
			Data:   buf.ToVectorisedView(),
		}); err != nil {
			t.Fatalf("WritePacket failed unexpectedly: %v", err)
		}
		desc := c.txq.tx.Pull()
		pi := queue.DecodeTxPacketHeader(desc)
		if _, ok := ids[pi.ID]; ok {
			t.Fatalf("ID (%v) reused", pi.ID)
		}
		ids[pi.ID] = struct{}{}
	}
	hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()))
	if want, err := tcpip.ErrWouldBlock, c.ep.WritePacket(&r, nil  , header.IPv4ProtocolNumber, tcpip.PacketBuffer{
		Header: hdr,
		Data:   buf.ToVectorisedView(),
	}); err != want {
		t.Fatalf("WritePacket return unexpected result: got %v, want %v", err, want)
	}
}
func TestFillTxQueueAfterBadCompletion(t *testing.T) {
	c := newTestContext(t, 20000, 1500, localLinkAddr)
	defer c.cleanup()
	queue.EncodeTxCompletion(c.txq.rx.Push(8), 1)
	c.txq.rx.Flush()
	r := stack.Route{
		RemoteLinkAddress: remoteLinkAddr,
	}
	buf := buffer.NewView(100)
	for i := 2; i > 0; i-- {
		hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()))
		if err := c.ep.WritePacket(&r, nil  , header.IPv4ProtocolNumber, tcpip.PacketBuffer{
			Header: hdr,
			Data:   buf.ToVectorisedView(),
		}); err != nil {
			t.Fatalf("WritePacket failed unexpectedly: %v", err)
		}
	}
	for i := 2; i > 0; i-- {
		pi := queue.DecodeTxPacketHeader(c.txq.tx.Pull())
		queue.EncodeTxCompletion(c.txq.rx.Push(8), pi.ID)
		queue.EncodeTxCompletion(c.txq.rx.Push(8), pi.ID)
		c.txq.rx.Flush()
	}
	c.txq.tx.Flush()
	ids := make(map[uint64]struct{})
	for i := queuePipeSize / 40; i > 0; i-- {
		hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()))
		if err := c.ep.WritePacket(&r, nil  , header.IPv4ProtocolNumber, tcpip.PacketBuffer{
			Header: hdr,
			Data:   buf.ToVectorisedView(),
		}); err != nil {
			t.Fatalf("WritePacket failed unexpectedly: %v", err)
		}
		desc := c.txq.tx.Pull()
		pi := queue.DecodeTxPacketHeader(desc)
		if _, ok := ids[pi.ID]; ok {
			t.Fatalf("ID (%v) reused", pi.ID)
		}
		ids[pi.ID] = struct{}{}
	}
	hdr := buffer.NewPrependable(int(c.ep.MaxHeaderLength()))
	if want, err := tcpip.ErrWouldBlock, c.ep.WritePacket(&r, nil  , header.IPv4ProtocolNumber, tcpip.PacketBuffer{
		Header: hdr,
		Data:   buf.ToVectorisedView(),
	}); err != want {
		t.Fatalf("WritePacket return unexpected result: got %v, want %v", err, want)
	}
}