2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-07-09 21:03:03 +00:00
|
|
|
//
|
|
|
|
// 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.
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2018-08-22 20:29:57 +00:00
|
|
|
// +build linux
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
package fdbased
|
|
|
|
|
|
|
|
import (
|
2018-09-14 23:38:45 +00:00
|
|
|
"bytes"
|
2018-04-27 17:37:02 +00:00
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"reflect"
|
|
|
|
"syscall"
|
|
|
|
"testing"
|
|
|
|
"time"
|
2019-03-28 18:02:23 +00:00
|
|
|
"unsafe"
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2020-08-13 20:07:03 +00:00
|
|
|
"github.com/google/go-cmp/cmp"
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
2018-09-19 20:42:55 +00:00
|
|
|
const (
|
2019-03-28 18:02:23 +00:00
|
|
|
mtu = 1500
|
|
|
|
laddr = tcpip.LinkAddress("\x11\x22\x33\x44\x55\x66")
|
|
|
|
raddr = tcpip.LinkAddress("\x77\x88\x99\xaa\xbb\xcc")
|
|
|
|
proto = 10
|
|
|
|
csumOffset = 48
|
|
|
|
gsoMSS = 500
|
2018-09-19 20:42:55 +00:00
|
|
|
)
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
type packetInfo struct {
|
2020-08-13 20:07:03 +00:00
|
|
|
Raddr tcpip.LinkAddress
|
|
|
|
Proto tcpip.NetworkProtocolNumber
|
|
|
|
Contents *stack.PacketBuffer
|
|
|
|
}
|
|
|
|
|
|
|
|
type packetContents struct {
|
|
|
|
LinkHeader buffer.View
|
|
|
|
NetworkHeader buffer.View
|
|
|
|
TransportHeader buffer.View
|
|
|
|
Data buffer.View
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkPacketInfoEqual(t *testing.T, got, want packetInfo) {
|
|
|
|
t.Helper()
|
|
|
|
if diff := cmp.Diff(
|
|
|
|
want, got,
|
|
|
|
cmp.Transformer("ExtractPacketBuffer", func(pk *stack.PacketBuffer) *packetContents {
|
|
|
|
if pk == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &packetContents{
|
|
|
|
LinkHeader: pk.LinkHeader().View(),
|
|
|
|
NetworkHeader: pk.NetworkHeader().View(),
|
|
|
|
TransportHeader: pk.TransportHeader().View(),
|
|
|
|
Data: pk.Data.ToView(),
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
); diff != "" {
|
|
|
|
t.Errorf("unexpected packetInfo (-want +got):\n%s", diff)
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type context struct {
|
2020-03-24 22:33:16 +00:00
|
|
|
t *testing.T
|
|
|
|
readFDs []int
|
|
|
|
writeFDs []int
|
|
|
|
ep stack.LinkEndpoint
|
|
|
|
ch chan packetInfo
|
|
|
|
done chan struct{}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func newContext(t *testing.T, opt *Options) *context {
|
2020-03-24 22:33:16 +00:00
|
|
|
firstFDPair, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Socketpair failed: %v", err)
|
|
|
|
}
|
|
|
|
secondFDPair, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Socketpair failed: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-03-24 22:33:16 +00:00
|
|
|
done := make(chan struct{}, 2)
|
2018-04-27 17:37:02 +00:00
|
|
|
opt.ClosedFunc = func(*tcpip.Error) {
|
|
|
|
done <- struct{}{}
|
|
|
|
}
|
|
|
|
|
2020-03-24 22:33:16 +00:00
|
|
|
opt.FDs = []int{firstFDPair[1], secondFDPair[1]}
|
2019-09-07 00:59:46 +00:00
|
|
|
ep, err := New(opt)
|
2019-04-17 18:14:24 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to create FD endpoint: %v", err)
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
c := &context{
|
2020-03-24 22:33:16 +00:00
|
|
|
t: t,
|
|
|
|
readFDs: []int{firstFDPair[0], secondFDPair[0]},
|
|
|
|
writeFDs: opt.FDs,
|
|
|
|
ep: ep,
|
|
|
|
ch: make(chan packetInfo, 100),
|
|
|
|
done: done,
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ep.Attach(c)
|
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *context) cleanup() {
|
2020-03-24 22:33:16 +00:00
|
|
|
for _, fd := range c.readFDs {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
|
|
|
<-c.done
|
2018-04-27 17:37:02 +00:00
|
|
|
<-c.done
|
2020-03-24 22:33:16 +00:00
|
|
|
for _, fd := range c.writeFDs {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2020-06-03 21:57:57 +00:00
|
|
|
func (c *context) DeliverNetworkPacket(remote tcpip.LinkAddress, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
2019-11-06 22:24:38 +00:00
|
|
|
c.ch <- packetInfo{remote, protocol, pkt}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 22:12:56 +00:00
|
|
|
func (c *context) DeliverOutboundPacket(remote tcpip.LinkAddress, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
|
|
|
panic("unimplemented")
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
func TestNoEthernetProperties(t *testing.T) {
|
|
|
|
c := newContext(t, &Options{MTU: mtu})
|
|
|
|
defer c.cleanup()
|
|
|
|
|
|
|
|
if want, v := uint16(0), c.ep.MaxHeaderLength(); want != v {
|
|
|
|
t.Fatalf("MaxHeaderLength() = %v, want %v", v, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
if want, v := uint32(mtu), c.ep.MTU(); want != v {
|
|
|
|
t.Fatalf("MTU() = %v, want %v", v, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEthernetProperties(t *testing.T) {
|
|
|
|
c := newContext(t, &Options{EthernetHeader: true, MTU: mtu})
|
|
|
|
defer c.cleanup()
|
|
|
|
|
|
|
|
if want, v := uint16(header.EthernetMinimumSize), c.ep.MaxHeaderLength(); want != v {
|
|
|
|
t.Fatalf("MaxHeaderLength() = %v, want %v", v, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
if want, v := uint32(mtu), c.ep.MTU(); want != v {
|
|
|
|
t.Fatalf("MTU() = %v, want %v", v, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddress(t *testing.T) {
|
|
|
|
addrs := []tcpip.LinkAddress{"", "abc", "def"}
|
|
|
|
for _, a := range addrs {
|
|
|
|
t.Run(fmt.Sprintf("Address: %q", a), func(t *testing.T) {
|
|
|
|
c := newContext(t, &Options{Address: a, MTU: mtu})
|
|
|
|
defer c.cleanup()
|
|
|
|
|
|
|
|
if want, v := a, c.ep.LinkAddress(); want != v {
|
|
|
|
t.Fatalf("LinkAddress() = %v, want %v", v, want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-24 22:33:16 +00:00
|
|
|
func testWritePacket(t *testing.T, plen int, eth bool, gsoMaxSize uint32, hash uint32) {
|
2019-03-28 18:02:23 +00:00
|
|
|
c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: eth, GSOMaxSize: gsoMaxSize})
|
|
|
|
defer c.cleanup()
|
|
|
|
|
2021-01-16 00:46:51 +00:00
|
|
|
var r stack.RouteInfo
|
|
|
|
r.RemoteLinkAddress = raddr
|
2019-03-28 18:02:23 +00:00
|
|
|
|
2020-08-13 20:07:03 +00:00
|
|
|
// Build payload.
|
|
|
|
payload := buffer.NewView(plen)
|
|
|
|
if _, err := rand.Read(payload); err != nil {
|
|
|
|
t.Fatalf("rand.Read(payload): %s", err)
|
2019-03-28 18:02:23 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 20:07:03 +00:00
|
|
|
// Build packet buffer.
|
|
|
|
const netHdrLen = 100
|
|
|
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
|
|
ReserveHeaderBytes: int(c.ep.MaxHeaderLength()) + netHdrLen,
|
|
|
|
Data: payload.ToVectorisedView(),
|
|
|
|
})
|
|
|
|
pkt.Hash = hash
|
|
|
|
|
|
|
|
// Build header.
|
|
|
|
b := pkt.NetworkHeader().Push(netHdrLen)
|
|
|
|
if _, err := rand.Read(b); err != nil {
|
|
|
|
t.Fatalf("rand.Read(b): %s", err)
|
2019-03-28 18:02:23 +00:00
|
|
|
}
|
2020-08-13 20:07:03 +00:00
|
|
|
|
|
|
|
// Write.
|
|
|
|
want := append(append(buffer.View(nil), b...), payload...)
|
2019-03-28 18:02:23 +00:00
|
|
|
var gso *stack.GSO
|
|
|
|
if gsoMaxSize != 0 {
|
|
|
|
gso = &stack.GSO{
|
|
|
|
Type: stack.GSOTCPv6,
|
|
|
|
NeedsCsum: true,
|
|
|
|
CsumOffset: csumOffset,
|
|
|
|
MSS: gsoMSS,
|
|
|
|
MaxSize: gsoMaxSize,
|
2019-04-18 18:40:34 +00:00
|
|
|
L3HdrLen: header.IPv4MaximumHeaderSize,
|
2019-03-28 18:02:23 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-16 00:46:51 +00:00
|
|
|
if err := c.ep.WritePacket(r, gso, proto, pkt); err != nil {
|
2019-03-28 18:02:23 +00:00
|
|
|
t.Fatalf("WritePacket failed: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-03-24 22:33:16 +00:00
|
|
|
// Read from the corresponding FD, then compare with what we wrote.
|
2019-03-28 18:02:23 +00:00
|
|
|
b = make([]byte, mtu)
|
2020-03-24 22:33:16 +00:00
|
|
|
fd := c.readFDs[hash%uint32(len(c.readFDs))]
|
|
|
|
n, err := syscall.Read(fd, b)
|
2019-03-28 18:02:23 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Read failed: %v", err)
|
|
|
|
}
|
|
|
|
b = b[:n]
|
|
|
|
if gsoMaxSize != 0 {
|
|
|
|
vnetHdr := *(*virtioNetHdr)(unsafe.Pointer(&b[0]))
|
|
|
|
if vnetHdr.flags&_VIRTIO_NET_HDR_F_NEEDS_CSUM == 0 {
|
|
|
|
t.Fatalf("virtioNetHdr.flags %v doesn't contain %v", vnetHdr.flags, _VIRTIO_NET_HDR_F_NEEDS_CSUM)
|
|
|
|
}
|
|
|
|
csumStart := header.EthernetMinimumSize + gso.L3HdrLen
|
|
|
|
if vnetHdr.csumStart != csumStart {
|
|
|
|
t.Fatalf("vnetHdr.csumStart = %v, want %v", vnetHdr.csumStart, csumStart)
|
|
|
|
}
|
|
|
|
if vnetHdr.csumOffset != csumOffset {
|
|
|
|
t.Fatalf("vnetHdr.csumOffset = %v, want %v", vnetHdr.csumOffset, csumOffset)
|
|
|
|
}
|
|
|
|
gsoType := uint8(0)
|
|
|
|
if int(gso.MSS) < plen {
|
|
|
|
gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
|
|
|
|
}
|
|
|
|
if vnetHdr.gsoType != gsoType {
|
|
|
|
t.Fatalf("vnetHdr.gsoType = %v, want %v", vnetHdr.gsoType, gsoType)
|
|
|
|
}
|
|
|
|
b = b[virtioNetHdrSize:]
|
|
|
|
}
|
|
|
|
if eth {
|
|
|
|
h := header.Ethernet(b)
|
|
|
|
b = b[header.EthernetMinimumSize:]
|
|
|
|
|
|
|
|
if a := h.SourceAddress(); a != laddr {
|
|
|
|
t.Fatalf("SourceAddress() = %v, want %v", a, laddr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if a := h.DestinationAddress(); a != raddr {
|
|
|
|
t.Fatalf("DestinationAddress() = %v, want %v", a, raddr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if et := h.Type(); et != proto {
|
|
|
|
t.Fatalf("Type() = %v, want %v", et, proto)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(b) != len(want) {
|
|
|
|
t.Fatalf("Read returned %v bytes, want %v", len(b), len(want))
|
|
|
|
}
|
|
|
|
if !bytes.Equal(b, want) {
|
|
|
|
t.Fatalf("Read returned %x, want %x", b, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
func TestWritePacket(t *testing.T) {
|
|
|
|
lengths := []int{0, 100, 1000}
|
|
|
|
eths := []bool{true, false}
|
2019-03-28 18:02:23 +00:00
|
|
|
gsos := []uint32{0, 32768}
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
for _, eth := range eths {
|
|
|
|
for _, plen := range lengths {
|
2019-03-28 18:02:23 +00:00
|
|
|
for _, gso := range gsos {
|
|
|
|
t.Run(
|
|
|
|
fmt.Sprintf("Eth=%v,PayloadLen=%v,GSOMaxSize=%v", eth, plen, gso),
|
|
|
|
func(t *testing.T) {
|
2020-03-24 22:33:16 +00:00
|
|
|
testWritePacket(t, plen, eth, gso, 0)
|
2019-03-28 18:02:23 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-24 22:33:16 +00:00
|
|
|
func TestHashedWritePacket(t *testing.T) {
|
|
|
|
lengths := []int{0, 100, 1000}
|
|
|
|
eths := []bool{true, false}
|
|
|
|
gsos := []uint32{0, 32768}
|
|
|
|
hashes := []uint32{0, 1}
|
|
|
|
for _, eth := range eths {
|
|
|
|
for _, plen := range lengths {
|
|
|
|
for _, gso := range gsos {
|
|
|
|
for _, hash := range hashes {
|
|
|
|
t.Run(
|
|
|
|
fmt.Sprintf("Eth=%v,PayloadLen=%v,GSOMaxSize=%v,Hash=%d", eth, plen, gso, hash),
|
|
|
|
func(t *testing.T) {
|
|
|
|
testWritePacket(t, plen, eth, gso, hash)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-19 20:42:55 +00:00
|
|
|
func TestPreserveSrcAddress(t *testing.T) {
|
|
|
|
baddr := tcpip.LinkAddress("\xcc\xbb\xaa\x77\x88\x99")
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2018-09-19 20:42:55 +00:00
|
|
|
c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: true})
|
|
|
|
defer c.cleanup()
|
|
|
|
|
|
|
|
// Set LocalLinkAddress in route to the value of the bridged address.
|
2021-01-16 00:46:51 +00:00
|
|
|
var r stack.RouteInfo
|
Invoke address resolution upon subsequent traffic to Failed neighbor
Removes the period of time in which subseqeuent traffic to a Failed neighbor
immediately fails with ErrNoLinkAddress. A Failed neighbor is one in which
address resolution fails; or in other words, the neighbor's IP address cannot
be translated to a MAC address.
This means removing the Failed state for linkAddrCache and allowing transitiong
out of Failed into Incomplete for neighborCache. Previously, both caches would
transition entries to Failed after address resolution fails. In this state, any
subsequent traffic requested within an unreachable time would immediately fail
with ErrNoLinkAddress. This does not follow RFC 4861 section 7.3.3:
If address resolution fails, the entry SHOULD be deleted, so that subsequent
traffic to that neighbor invokes the next-hop determination procedure again.
Invoking next-hop determination at this point ensures that alternate default
routers are tried.
The API for getting a link address for a given address, whether through the link
address cache or the neighbor table, is updated to optionally take a callback
which will be called when address resolution completes. This allows `Route` to
handle completing link resolution internally, so callers of (*Route).Resolve
(e.g. endpoints) don’t have to keep track of when it completes and update the
Route accordingly.
This change also removes the wakers from LinkAddressCache, NeighborCache, and
Route in favor of the callbacks, and callers that previously used a waker can
now just pass a callback to (*Route).Resolve that will notify the waker on
resolution completion.
Fixes #4796
Startblock:
has LGTM from sbalana
and then
add reviewer ghanan
PiperOrigin-RevId: 348597478
2020-12-22 09:34:41 +00:00
|
|
|
r.LocalLinkAddress = baddr
|
2021-01-16 00:46:51 +00:00
|
|
|
r.RemoteLinkAddress = raddr
|
2018-09-19 20:42:55 +00:00
|
|
|
|
2020-08-13 20:07:03 +00:00
|
|
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
|
|
// WritePacket panics given a prependable with anything less than
|
|
|
|
// the minimum size of the ethernet header.
|
|
|
|
// TODO(b/153685824): Figure out if this should use c.ep.MaxHeaderLength().
|
|
|
|
ReserveHeaderBytes: header.EthernetMinimumSize,
|
|
|
|
Data: buffer.VectorisedView{},
|
|
|
|
})
|
2021-01-16 00:46:51 +00:00
|
|
|
if err := c.ep.WritePacket(r, nil /* gso */, proto, pkt); err != nil {
|
2018-09-19 20:42:55 +00:00
|
|
|
t.Fatalf("WritePacket failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read from the FD, then compare with what we wrote.
|
|
|
|
b := make([]byte, mtu)
|
2020-03-24 22:33:16 +00:00
|
|
|
n, err := syscall.Read(c.readFDs[0], b)
|
2018-09-19 20:42:55 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Read failed: %v", err)
|
|
|
|
}
|
|
|
|
b = b[:n]
|
|
|
|
h := header.Ethernet(b)
|
|
|
|
|
|
|
|
if a := h.SourceAddress(); a != baddr {
|
|
|
|
t.Fatalf("SourceAddress() = %v, want %v", a, baddr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeliverPacket(t *testing.T) {
|
2018-04-27 17:37:02 +00:00
|
|
|
lengths := []int{100, 1000}
|
|
|
|
eths := []bool{true, false}
|
|
|
|
|
|
|
|
for _, eth := range eths {
|
|
|
|
for _, plen := range lengths {
|
|
|
|
t.Run(fmt.Sprintf("Eth=%v,PayloadLen=%v", eth, plen), func(t *testing.T) {
|
|
|
|
c := newContext(t, &Options{Address: laddr, MTU: mtu, EthernetHeader: eth})
|
|
|
|
defer c.cleanup()
|
|
|
|
|
|
|
|
// Build packet.
|
2020-08-13 20:07:03 +00:00
|
|
|
all := make([]byte, plen)
|
|
|
|
if _, err := rand.Read(all); err != nil {
|
|
|
|
t.Fatalf("rand.Read(all): %s", err)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2020-08-13 20:07:03 +00:00
|
|
|
// Make it look like an IPv4 packet.
|
|
|
|
all[0] = 0x40
|
|
|
|
|
|
|
|
wantPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
|
|
ReserveHeaderBytes: header.EthernetMinimumSize,
|
|
|
|
Data: buffer.NewViewFromBytes(all).ToVectorisedView(),
|
|
|
|
})
|
|
|
|
if eth {
|
|
|
|
hdr := header.Ethernet(wantPkt.LinkHeader().Push(header.EthernetMinimumSize))
|
2018-04-27 17:37:02 +00:00
|
|
|
hdr.Encode(&header.EthernetFields{
|
|
|
|
SrcAddr: raddr,
|
|
|
|
DstAddr: laddr,
|
|
|
|
Type: proto,
|
|
|
|
})
|
2020-08-13 20:07:03 +00:00
|
|
|
all = append(hdr, all...)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write packet via the file descriptor.
|
2020-03-24 22:33:16 +00:00
|
|
|
if _, err := syscall.Write(c.readFDs[0], all); err != nil {
|
2018-04-27 17:37:02 +00:00
|
|
|
t.Fatalf("Write failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Receive packet through the endpoint.
|
|
|
|
select {
|
|
|
|
case pi := <-c.ch:
|
|
|
|
want := packetInfo{
|
2020-08-13 20:07:03 +00:00
|
|
|
Raddr: raddr,
|
|
|
|
Proto: proto,
|
|
|
|
Contents: wantPkt,
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
if !eth {
|
2020-08-13 20:07:03 +00:00
|
|
|
want.Proto = header.IPv4ProtocolNumber
|
|
|
|
want.Raddr = ""
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2020-08-13 20:07:03 +00:00
|
|
|
checkPacketInfoEqual(t, pi, want)
|
2018-04-27 17:37:02 +00:00
|
|
|
case <-time.After(10 * time.Second):
|
|
|
|
t.Fatalf("Timed out waiting for packet")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBufConfigMaxLength(t *testing.T) {
|
|
|
|
got := 0
|
|
|
|
for _, i := range BufConfig {
|
|
|
|
got += i
|
|
|
|
}
|
|
|
|
want := header.MaxIPPacketSize // maximum TCP packet size
|
|
|
|
if got < want {
|
|
|
|
t.Errorf("total buffer size is invalid: got %d, want >= %d", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBufConfigFirst(t *testing.T) {
|
|
|
|
// The stack assumes that the TCP/IP header is enterily contained in the first view.
|
|
|
|
// Therefore, the first view needs to be large enough to contain the maximum TCP/IP
|
|
|
|
// header, which is 120 bytes (60 bytes for IP + 60 bytes for TCP).
|
|
|
|
want := 120
|
|
|
|
got := BufConfig[0]
|
|
|
|
if got < want {
|
|
|
|
t.Errorf("first view has an invalid size: got %d, want >= %d", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var capLengthTestCases = []struct {
|
|
|
|
comment string
|
|
|
|
config []int
|
|
|
|
n int
|
|
|
|
wantUsed int
|
|
|
|
wantLengths []int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
comment: "Single slice",
|
|
|
|
config: []int{2},
|
|
|
|
n: 1,
|
|
|
|
wantUsed: 1,
|
|
|
|
wantLengths: []int{1},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
comment: "Multiple slices",
|
|
|
|
config: []int{1, 2},
|
|
|
|
n: 2,
|
|
|
|
wantUsed: 2,
|
|
|
|
wantLengths: []int{1, 1},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
comment: "Entire buffer",
|
|
|
|
config: []int{1, 2},
|
|
|
|
n: 3,
|
|
|
|
wantUsed: 2,
|
|
|
|
wantLengths: []int{1, 2},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
comment: "Entire buffer but not on the last slice",
|
|
|
|
config: []int{1, 2, 3},
|
|
|
|
n: 3,
|
|
|
|
wantUsed: 2,
|
2021-01-26 00:26:10 +00:00
|
|
|
wantLengths: []int{1, 2},
|
2018-04-27 17:37:02 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-01-26 00:26:10 +00:00
|
|
|
func TestIovecBuffer(t *testing.T) {
|
2019-05-21 22:23:12 +00:00
|
|
|
for _, c := range capLengthTestCases {
|
2021-01-26 00:26:10 +00:00
|
|
|
t.Run(c.comment, func(t *testing.T) {
|
|
|
|
b := newIovecBuffer(c.config, false /* skipsVnetHdr */)
|
2019-05-21 22:23:12 +00:00
|
|
|
|
2021-01-26 00:26:10 +00:00
|
|
|
// Test initial allocation.
|
|
|
|
iovecs := b.nextIovecs()
|
|
|
|
if got, want := len(iovecs), len(c.config); got != want {
|
|
|
|
t.Fatalf("len(iovecs) = %d, want %d", got, want)
|
|
|
|
}
|
2019-05-21 22:23:12 +00:00
|
|
|
|
2021-01-26 00:26:10 +00:00
|
|
|
// Make a copy as iovecs points to internal slice. We will need this state
|
|
|
|
// later.
|
|
|
|
oldIovecs := append([]syscall.Iovec(nil), iovecs...)
|
2019-05-21 22:23:12 +00:00
|
|
|
|
2021-01-26 00:26:10 +00:00
|
|
|
// Test the views that get pulled.
|
|
|
|
vv := b.pullViews(c.n)
|
|
|
|
var lengths []int
|
|
|
|
for _, v := range vv.Views() {
|
|
|
|
lengths = append(lengths, len(v))
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(lengths, c.wantLengths) {
|
|
|
|
t.Errorf("Pulled view lengths = %v, want %v", lengths, c.wantLengths)
|
|
|
|
}
|
2019-05-21 22:23:12 +00:00
|
|
|
|
2021-01-26 00:26:10 +00:00
|
|
|
// Test that new views get reallocated.
|
|
|
|
for i, newIov := range b.nextIovecs() {
|
|
|
|
if i < c.wantUsed {
|
|
|
|
if newIov.Base == oldIovecs[i].Base {
|
|
|
|
t.Errorf("b.views[%d] should have been reallocated", i)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if newIov.Base != oldIovecs[i].Base {
|
|
|
|
t.Errorf("b.views[%d] should not have been reallocated", i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2021-01-26 00:26:10 +00:00
|
|
|
func TestIovecBufferSkipVnetHdr(t *testing.T) {
|
|
|
|
for _, test := range []struct {
|
|
|
|
desc string
|
|
|
|
readN int
|
|
|
|
wantLen int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
desc: "nothing read",
|
|
|
|
readN: 0,
|
|
|
|
wantLen: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "smaller than vnet header",
|
|
|
|
readN: virtioNetHdrSize - 1,
|
|
|
|
wantLen: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "header skipped",
|
|
|
|
readN: virtioNetHdrSize + 100,
|
|
|
|
wantLen: 100,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(test.desc, func(t *testing.T) {
|
|
|
|
b := newIovecBuffer([]int{10, 20, 50, 50}, true)
|
|
|
|
// Pretend a read happend.
|
|
|
|
b.nextIovecs()
|
|
|
|
vv := b.pullViews(test.readN)
|
|
|
|
if got, want := vv.Size(), test.wantLen; got != want {
|
|
|
|
t.Errorf("b.pullView(%d).Size() = %d; want %d", test.readN, got, want)
|
|
|
|
}
|
|
|
|
if got, want := len(vv.ToOwnedView()), test.wantLen; got != want {
|
|
|
|
t.Errorf("b.pullView(%d).ToOwnedView() has length %d; want %d", test.readN, got, want)
|
|
|
|
}
|
|
|
|
})
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-13 21:35:11 +00:00
|
|
|
|
|
|
|
// fakeNetworkDispatcher delivers packets to pkts.
|
|
|
|
type fakeNetworkDispatcher struct {
|
|
|
|
pkts []*stack.PacketBuffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *fakeNetworkDispatcher) DeliverNetworkPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
|
|
|
d.pkts = append(d.pkts, pkt)
|
|
|
|
}
|
|
|
|
|
2020-07-22 22:12:56 +00:00
|
|
|
func (d *fakeNetworkDispatcher) DeliverOutboundPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
|
|
|
panic("unimplemented")
|
|
|
|
}
|
|
|
|
|
2020-07-13 21:35:11 +00:00
|
|
|
func TestDispatchPacketFormat(t *testing.T) {
|
|
|
|
for _, test := range []struct {
|
|
|
|
name string
|
|
|
|
newDispatcher func(fd int, e *endpoint) (linkDispatcher, error)
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "readVDispatcher",
|
|
|
|
newDispatcher: newReadVDispatcher,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "recvMMsgDispatcher",
|
|
|
|
newDispatcher: newRecvMMsgDispatcher,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
// Create a socket pair to send/recv.
|
|
|
|
fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer syscall.Close(fds[0])
|
|
|
|
defer syscall.Close(fds[1])
|
|
|
|
|
|
|
|
data := []byte{
|
|
|
|
// Ethernet header.
|
|
|
|
1, 2, 3, 4, 5, 60,
|
|
|
|
1, 2, 3, 4, 5, 61,
|
|
|
|
8, 0,
|
|
|
|
// Mock network header.
|
|
|
|
40, 41, 42, 43,
|
|
|
|
}
|
|
|
|
err = syscall.Sendmsg(fds[1], data, nil, nil, 0)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create and run dispatcher once.
|
|
|
|
sink := &fakeNetworkDispatcher{}
|
|
|
|
d, err := test.newDispatcher(fds[0], &endpoint{
|
|
|
|
hdrSize: header.EthernetMinimumSize,
|
|
|
|
dispatcher: sink,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if ok, err := d.dispatch(); !ok || err != nil {
|
|
|
|
t.Fatalf("d.dispatch() = %v, %v", ok, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify packet.
|
|
|
|
if got, want := len(sink.pkts), 1; got != want {
|
|
|
|
t.Fatalf("len(sink.pkts) = %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
pkt := sink.pkts[0]
|
2020-08-13 20:07:03 +00:00
|
|
|
if got, want := pkt.LinkHeader().View().Size(), header.EthernetMinimumSize; got != want {
|
|
|
|
t.Errorf("pkt.LinkHeader().View().Size() = %d, want %d", got, want)
|
2020-07-13 21:35:11 +00:00
|
|
|
}
|
|
|
|
if got, want := pkt.Data.Size(), 4; got != want {
|
|
|
|
t.Errorf("pkt.Data.Size() = %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|