mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Extract udp testing utilities
The ICMP and RAW transport endpoint are currently untested. These testing utilities can be reused to write tests for these endpoints. Updates #5623 PiperOrigin-RevId: 423359295
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "context",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"context.go",
|
||||
"flow.go",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/checker",
|
||||
"//pkg/tcpip/faketime",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/channel",
|
||||
"//pkg/tcpip/link/sniffer",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/waiter",
|
||||
"@com_github_google_go_cmp//cmp:go_default_library",
|
||||
"@org_golang_x_time//rate:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,337 @@
|
||||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Package context provides a context used by datagram-based network endpoints
|
||||
// tests. It also defines the TestFlow type to facilitate IP configurations.
|
||||
package context
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/time/rate"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checker"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/faketime"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
// NICID is the id of the nic created by the Context.
|
||||
NICID = 1
|
||||
|
||||
// DefaultMTU is the MTU used by the Context, except where another value is
|
||||
// explicitly specified during initialization. It is chosen to match the MTU
|
||||
// of loopback interfaces on linux systems.
|
||||
DefaultMTU = 65536
|
||||
)
|
||||
|
||||
// Context is a testing context for datagram-based network endpoints.
|
||||
type Context struct {
|
||||
// T is the testing context.
|
||||
T *testing.T
|
||||
|
||||
// LinkEP is the link endpoint that is attached to the stack's NIC.
|
||||
LinkEP *channel.Endpoint
|
||||
|
||||
// Stack is the networking stack owned by the context.
|
||||
Stack *stack.Stack
|
||||
|
||||
// EP is the transport endpoint owned by the context.
|
||||
EP tcpip.Endpoint
|
||||
|
||||
// WQ is the wait queue associated with EP and is used to block for events on
|
||||
// EP.
|
||||
WQ waiter.Queue
|
||||
}
|
||||
|
||||
// Options contains options for creating a new test context.
|
||||
type Options struct {
|
||||
// MTU is the mtu that the link endpoint will be initialized with.
|
||||
MTU uint32
|
||||
|
||||
// HandleLocal specifies if non-loopback interfaces are allowed to loop
|
||||
// packets.
|
||||
HandleLocal bool
|
||||
}
|
||||
|
||||
// New allocates and initializes a test context containing a configured stack.
|
||||
func New(t *testing.T, transportProtocols []stack.TransportProtocolFactory) *Context {
|
||||
t.Helper()
|
||||
|
||||
options := Options{
|
||||
MTU: DefaultMTU,
|
||||
HandleLocal: true,
|
||||
}
|
||||
|
||||
return NewWithOptions(t, transportProtocols, options)
|
||||
}
|
||||
|
||||
// NewWithOptions allocates and initializes a test context containing a
|
||||
// configured stack with the provided options.
|
||||
func NewWithOptions(t *testing.T, transportProtocols []stack.TransportProtocolFactory, options Options) *Context {
|
||||
t.Helper()
|
||||
|
||||
stackOptions := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: transportProtocols,
|
||||
HandleLocal: options.HandleLocal,
|
||||
Clock: &faketime.NullClock{},
|
||||
}
|
||||
|
||||
s := stack.New(stackOptions)
|
||||
// Disable ICMP rate limiter since we're using Null clock, which never
|
||||
// advances time and thus never allows ICMP messages.
|
||||
s.SetICMPLimit(rate.Inf)
|
||||
ep := channel.New(256, options.MTU, "")
|
||||
wep := stack.LinkEndpoint(ep)
|
||||
|
||||
if testing.Verbose() {
|
||||
wep = sniffer.New(ep)
|
||||
}
|
||||
if err := s.CreateNIC(NICID, wep); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _): %s", NICID, err)
|
||||
}
|
||||
|
||||
protocolAddrV4 := tcpip.ProtocolAddress{
|
||||
Protocol: ipv4.ProtocolNumber,
|
||||
AddressWithPrefix: tcpip.Address(StackAddr).WithPrefix(),
|
||||
}
|
||||
if err := s.AddProtocolAddress(NICID, protocolAddrV4, stack.AddressProperties{}); err != nil {
|
||||
t.Fatalf("AddProtocolAddress(%d, %#v, {}): %s", NICID, protocolAddrV4, err)
|
||||
}
|
||||
|
||||
protocolAddrV6 := tcpip.ProtocolAddress{
|
||||
Protocol: ipv6.ProtocolNumber,
|
||||
AddressWithPrefix: tcpip.Address(StackV6Addr).WithPrefix(),
|
||||
}
|
||||
if err := s.AddProtocolAddress(NICID, protocolAddrV6, stack.AddressProperties{}); err != nil {
|
||||
t.Fatalf("AddProtocolAddress(%d, %#v, {}): %s", NICID, protocolAddrV6, err)
|
||||
}
|
||||
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
{
|
||||
Destination: header.IPv4EmptySubnet,
|
||||
NIC: NICID,
|
||||
},
|
||||
{
|
||||
Destination: header.IPv6EmptySubnet,
|
||||
NIC: NICID,
|
||||
},
|
||||
})
|
||||
|
||||
return &Context{
|
||||
T: t,
|
||||
Stack: s,
|
||||
LinkEP: ep,
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup closes the context endpoint if required.
|
||||
func (c *Context) Cleanup() {
|
||||
_ = c.LinkEP.Drain()
|
||||
if c.EP != nil {
|
||||
c.EP.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// CreateEndpoint creates the Context's Endpoint.
|
||||
func (c *Context) CreateEndpoint(network tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber) {
|
||||
c.T.Helper()
|
||||
|
||||
var err tcpip.Error
|
||||
c.EP, err = c.Stack.NewEndpoint(transport, network, &c.WQ)
|
||||
if err != nil {
|
||||
c.T.Fatalf("c.Stack.NewEndpoint(%d, %d, _) failed: %s", transport, network, err)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateEndpointForFlow creates the Context's Endpoint and configured it
|
||||
// according to the given TestFlow.
|
||||
func (c *Context) CreateEndpointForFlow(flow TestFlow, transport tcpip.TransportProtocolNumber) {
|
||||
c.T.Helper()
|
||||
|
||||
c.CreateEndpoint(flow.SockProto(), transport)
|
||||
if flow.isV6Only() {
|
||||
c.EP.SocketOptions().SetV6Only(true)
|
||||
} else if flow.isBroadcast() {
|
||||
c.EP.SocketOptions().SetBroadcast(true)
|
||||
}
|
||||
}
|
||||
|
||||
// CheckEndpointWriteStats checks that the write statistic related to the given
|
||||
// error has been incremented as expected.
|
||||
func (c *Context) CheckEndpointWriteStats(incr uint64, want tcpip.TransportEndpointStats, err tcpip.Error) {
|
||||
got := c.EP.Stats().(*tcpip.TransportEndpointStats).Clone()
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
want.PacketsSent.IncrementBy(incr)
|
||||
case *tcpip.ErrMessageTooLong, *tcpip.ErrInvalidOptionValue:
|
||||
want.WriteErrors.InvalidArgs.IncrementBy(incr)
|
||||
case *tcpip.ErrClosedForSend:
|
||||
want.WriteErrors.WriteClosed.IncrementBy(incr)
|
||||
case *tcpip.ErrInvalidEndpointState:
|
||||
want.WriteErrors.InvalidEndpointState.IncrementBy(incr)
|
||||
case *tcpip.ErrNoRoute, *tcpip.ErrBroadcastDisabled, *tcpip.ErrNetworkUnreachable:
|
||||
want.SendErrors.NoRoute.IncrementBy(incr)
|
||||
default:
|
||||
want.SendErrors.SendToNetworkFailed.IncrementBy(incr)
|
||||
}
|
||||
if got != want {
|
||||
c.T.Errorf("Endpoint stats not matching for error %s: got %#v, want %#v", err, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// CheckEndpointReadStats checks that the read statistic related to the given
|
||||
// error has been incremented as expected.
|
||||
func (c *Context) CheckEndpointReadStats(incr uint64, want tcpip.TransportEndpointStats, err tcpip.Error) {
|
||||
c.T.Helper()
|
||||
|
||||
got := c.EP.Stats().(*tcpip.TransportEndpointStats).Clone()
|
||||
switch err.(type) {
|
||||
case nil, *tcpip.ErrWouldBlock:
|
||||
case *tcpip.ErrClosedForReceive:
|
||||
want.ReadErrors.ReadClosed.IncrementBy(incr)
|
||||
default:
|
||||
c.T.Errorf("Endpoint error missing stats update for err %s", err)
|
||||
}
|
||||
if got != want {
|
||||
c.T.Errorf("Endpoint stats not matching for error %s: got %#v, want %#v", err, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// InjectPacket injects a packet into the context's link endpoint.
|
||||
func (c *Context) InjectPacket(netProto tcpip.NetworkProtocolNumber, buf buffer.View) {
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: buf.ToVectorisedView(),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
c.LinkEP.InjectInbound(netProto, pkt)
|
||||
}
|
||||
|
||||
// readExpectations holds information about the expected outcome when reading
|
||||
// from the context's endpoint.
|
||||
type readExpectations struct {
|
||||
nothingToRead bool
|
||||
payload []byte
|
||||
addresses Header4Tuple
|
||||
readShouldFail bool
|
||||
}
|
||||
|
||||
// readFromEndpoint attempts to read a packet from the endpoint and compares the
|
||||
// outcome with the given expectations.
|
||||
func (c *Context) readFromEndpoint(expectations readExpectations, checkers ...checker.ControlMessagesChecker) {
|
||||
c.T.Helper()
|
||||
|
||||
// Try to receive the data.
|
||||
we, ch := waiter.NewChannelEntry(waiter.ReadableEvents)
|
||||
c.WQ.EventRegister(&we)
|
||||
defer c.WQ.EventUnregister(&we)
|
||||
|
||||
// Take a snapshot of the stats to validate them at the end of the test.
|
||||
epstats := c.EP.Stats().(*tcpip.TransportEndpointStats).Clone()
|
||||
|
||||
var buf bytes.Buffer
|
||||
res, err := c.EP.Read(&buf, tcpip.ReadOptions{NeedRemoteAddr: true})
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||
select {
|
||||
case <-ch:
|
||||
res, err = c.EP.Read(&buf, tcpip.ReadOptions{NeedRemoteAddr: true})
|
||||
default:
|
||||
if expectations.nothingToRead {
|
||||
return
|
||||
}
|
||||
c.T.Fatal("timed out waiting for data")
|
||||
}
|
||||
}
|
||||
|
||||
if expectations.readShouldFail && err != nil {
|
||||
c.CheckEndpointReadStats(1, epstats, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.T.Fatal("Read failed:", err)
|
||||
}
|
||||
|
||||
if expectations.nothingToRead {
|
||||
c.T.Fatalf("Read unexpectedly received data from %s", res.RemoteAddr.Addr)
|
||||
}
|
||||
|
||||
// Check the read result.
|
||||
if diff := cmp.Diff(tcpip.ReadResult{
|
||||
Count: buf.Len(),
|
||||
Total: buf.Len(),
|
||||
RemoteAddr: tcpip.FullAddress{Addr: expectations.addresses.Src.Addr},
|
||||
}, res, checker.IgnoreCmpPath(
|
||||
"ControlMessages", // ControlMessages are checked below.
|
||||
"RemoteAddr.NIC",
|
||||
"RemoteAddr.Port",
|
||||
)); diff != "" {
|
||||
c.T.Fatalf("Read: unexpected result (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// Check the payload.
|
||||
v := buf.Bytes()
|
||||
if !bytes.Equal(expectations.payload, v) {
|
||||
c.T.Fatalf("got payload = %x, want = %x", v, expectations.payload)
|
||||
}
|
||||
|
||||
// Run any checkers against the ControlMessages.
|
||||
for _, f := range checkers {
|
||||
f(c.T, res.ControlMessages)
|
||||
}
|
||||
|
||||
c.CheckEndpointReadStats(1, epstats, err)
|
||||
}
|
||||
|
||||
// ReadFromEndpointExpectSuccess attempts to reads from the endpoint and
|
||||
// performs checks on the received packet, according to the given flow and
|
||||
// checkers.
|
||||
func (c *Context) ReadFromEndpointExpectSuccess(payload []byte, flow TestFlow, checkers ...checker.ControlMessagesChecker) {
|
||||
c.T.Helper()
|
||||
|
||||
c.readFromEndpoint(readExpectations{
|
||||
payload: payload,
|
||||
addresses: flow.MakeHeader4Tuple(Incoming),
|
||||
}, checkers...)
|
||||
}
|
||||
|
||||
// ReadFromEndpointExpectNoPacket reads from the endpoint and checks that no
|
||||
// packets was received.
|
||||
func (c *Context) ReadFromEndpointExpectNoPacket() {
|
||||
c.T.Helper()
|
||||
|
||||
c.readFromEndpoint(readExpectations{
|
||||
nothingToRead: true,
|
||||
})
|
||||
}
|
||||
|
||||
// ReadFromEndpointExpectError reads from the endpoint and checks that an
|
||||
// error was returned.
|
||||
func (c *Context) ReadFromEndpointExpectError() {
|
||||
c.T.Helper()
|
||||
|
||||
c.readFromEndpoint(readExpectations{
|
||||
readShouldFail: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checker"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
)
|
||||
|
||||
const (
|
||||
v4MappedAddrPrefix = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"
|
||||
|
||||
// StackPort is the port TestFlow uses with StackAddr.
|
||||
StackPort = 1234
|
||||
|
||||
// TestPort is the port TestFlow uses with TestAddr.
|
||||
TestPort = 4096
|
||||
|
||||
// StackAddr is the IPv4 address assigned to the stack's NIC and is used by
|
||||
// TestFlow as the local address.
|
||||
StackAddr = "\x0a\x00\x00\x01"
|
||||
|
||||
// StackV4MappedAddr is the IPv4-mapped IPv6 StackAddr.
|
||||
StackV4MappedAddr = v4MappedAddrPrefix + StackAddr
|
||||
|
||||
// TestAddr is the IPv4 address used by TestFlow as the remote address.
|
||||
TestAddr = "\x0a\x00\x00\x02"
|
||||
|
||||
// TestV4MappedAddr is the IPv4-mapped IPv6 TestAddr.
|
||||
TestV4MappedAddr = v4MappedAddrPrefix + TestAddr
|
||||
|
||||
// MulticastAddr is the IPv4 multicast address used by IPv4 multicast
|
||||
// TestFlow.
|
||||
MulticastAddr = "\xe8\x2b\xd3\xea"
|
||||
|
||||
// MulticastV4MappedAddr is the IPv4-mapped IPv6 MulticastAddr.
|
||||
MulticastV4MappedAddr = v4MappedAddrPrefix + MulticastAddr
|
||||
|
||||
// BroadcastAddr is the IPv4 broadcast address.
|
||||
BroadcastAddr = header.IPv4Broadcast
|
||||
|
||||
// BroadcastV4MappedAddr is the IPv4-mapped IPv6 BroadcastAddr.
|
||||
BroadcastV4MappedAddr = v4MappedAddrPrefix + BroadcastAddr
|
||||
|
||||
// V4MappedWildcardAddr is the IPv4-mapped IPv6 wildcard (any) address.
|
||||
V4MappedWildcardAddr = v4MappedAddrPrefix + "\x00\x00\x00\x00"
|
||||
|
||||
// StackV6Addr is the IPv6 address assigned to the stack's NIC and is used by
|
||||
// TestFlow as the local address.
|
||||
StackV6Addr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
|
||||
|
||||
// TestV6Addr is the IPv6 address used by TestFlow as the remote address.
|
||||
TestV6Addr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
|
||||
// MulticastV6Addr is the IPv6 multicast address used by IPv6 multicast
|
||||
// TestFlow.
|
||||
MulticastV6Addr = "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
|
||||
// Header4Tuple stores the 4-tuple {src-IP, src-port, dst-IP, dst-port} used in
|
||||
// a packet header. These values are used to populate a header or verify one.
|
||||
// Note that because they are used in packet headers, the addresses are never in
|
||||
// a V4-mapped format.
|
||||
type Header4Tuple struct {
|
||||
Src tcpip.FullAddress
|
||||
Dst tcpip.FullAddress
|
||||
}
|
||||
|
||||
// TestFlow implements a helper type used for sending and receiving test
|
||||
// packets. A given test TestFlow value defines 1) the socket endpoint used for
|
||||
// the test and 2) the type of packet send or received on the endpoint. E.g., a
|
||||
// MulticastV6Only TestFlow is a IPv6 multicast packet passing through a V6-only
|
||||
// endpoint. The type provides helper methods to characterize the TestFlow
|
||||
// (e.g., IsV4) as well as return a proper Header4Tuple for it.
|
||||
type TestFlow int
|
||||
|
||||
const (
|
||||
_ TestFlow = iota
|
||||
|
||||
// UnicastV4 is IPv4 unicast on an IPv4 socket
|
||||
UnicastV4
|
||||
|
||||
// UnicastV4in6 is IPv4-mapped IPv6 unicast on an IPv6 dual socket
|
||||
UnicastV4in6
|
||||
|
||||
// UnicastV6 is IPv6 unicast on an IPv6 socket
|
||||
UnicastV6
|
||||
|
||||
// UnicastV6Only is IPv6 unicast on an IPv6-only socket
|
||||
UnicastV6Only
|
||||
|
||||
// MulticastV4 is IPv4 multicast on an IPv4 socket
|
||||
MulticastV4
|
||||
|
||||
// MulticastV4in6 is IPv4-mapped IPv6 multicast on an IPv6 dual socket
|
||||
MulticastV4in6
|
||||
|
||||
// MulticastV6 is IPv6 multicast on an IPv6 socket
|
||||
MulticastV6
|
||||
|
||||
// MulticastV6Only IPv6 multicast on an IPv6-only socket
|
||||
MulticastV6Only
|
||||
|
||||
// Broadcast is IPv4 broadcast on an IPv4 socket
|
||||
Broadcast
|
||||
|
||||
// BroadcastIn6 is IPv4-mapped IPv6 broadcast on an IPv6 dual socket
|
||||
BroadcastIn6
|
||||
|
||||
// ReverseMulticastV4 is IPv4 multicast src. Must fail.
|
||||
ReverseMulticastV4
|
||||
|
||||
// ReverseMulticastV6 is IPv6 multicast src. Must fail.
|
||||
ReverseMulticastV6
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer interface.
|
||||
func (flow TestFlow) String() string {
|
||||
switch flow {
|
||||
case UnicastV4:
|
||||
return "UnicastV4"
|
||||
case UnicastV6:
|
||||
return "UnicastV6"
|
||||
case UnicastV6Only:
|
||||
return "UnicastV6Only"
|
||||
case UnicastV4in6:
|
||||
return "UnicastV4in6"
|
||||
case MulticastV4:
|
||||
return "MulticastV4"
|
||||
case MulticastV6:
|
||||
return "MulticastV6"
|
||||
case MulticastV6Only:
|
||||
return "MulticastV6Only"
|
||||
case MulticastV4in6:
|
||||
return "MulticastV4in6"
|
||||
case Broadcast:
|
||||
return "Broadcast"
|
||||
case BroadcastIn6:
|
||||
return "BroadcastIn6"
|
||||
case ReverseMulticastV4:
|
||||
return "ReverseMulticastV4"
|
||||
case ReverseMulticastV6:
|
||||
return "ReverseMulticastV6"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// PacketDirection specifies the direction of a TestFlow.
|
||||
type PacketDirection int
|
||||
|
||||
const (
|
||||
_ PacketDirection = iota
|
||||
|
||||
// Incoming indicates the direction from Test*Addr to Stack*Addr.
|
||||
Incoming
|
||||
|
||||
// Outgoing indicates the direction from Test*Addr to Stack*Addr.
|
||||
Outgoing
|
||||
)
|
||||
|
||||
// MakeHeader4Tuple returns the Header4Tuple for the given TestFlow and direction. Note
|
||||
// that the tuple contains no mapped addresses as those only exist at the socket
|
||||
// level but not at the packet header level.
|
||||
func (flow TestFlow) MakeHeader4Tuple(direction PacketDirection) Header4Tuple {
|
||||
var h Header4Tuple
|
||||
if flow.IsV4() {
|
||||
switch direction {
|
||||
case Outgoing:
|
||||
h = Header4Tuple{
|
||||
Src: tcpip.FullAddress{Addr: StackAddr, Port: StackPort},
|
||||
Dst: tcpip.FullAddress{Addr: TestAddr, Port: TestPort},
|
||||
}
|
||||
case Incoming:
|
||||
h = Header4Tuple{
|
||||
Src: tcpip.FullAddress{Addr: TestAddr, Port: TestPort},
|
||||
Dst: tcpip.FullAddress{Addr: StackAddr, Port: StackPort},
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown direction %d", direction))
|
||||
}
|
||||
|
||||
if flow.IsMulticast() {
|
||||
h.Dst.Addr = MulticastAddr
|
||||
} else if flow.isBroadcast() {
|
||||
h.Dst.Addr = BroadcastAddr
|
||||
}
|
||||
} else { // IPv6
|
||||
switch direction {
|
||||
case Outgoing:
|
||||
h = Header4Tuple{
|
||||
Src: tcpip.FullAddress{Addr: StackV6Addr, Port: StackPort},
|
||||
Dst: tcpip.FullAddress{Addr: TestV6Addr, Port: TestPort},
|
||||
}
|
||||
case Incoming:
|
||||
h = Header4Tuple{
|
||||
Src: tcpip.FullAddress{Addr: TestV6Addr, Port: TestPort},
|
||||
Dst: tcpip.FullAddress{Addr: StackV6Addr, Port: StackPort},
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown direction %d", direction))
|
||||
}
|
||||
|
||||
if flow.IsMulticast() {
|
||||
h.Dst.Addr = MulticastV6Addr
|
||||
}
|
||||
}
|
||||
|
||||
if flow.isReverseMulticast() {
|
||||
h.Src.Addr = flow.GetMulticastAddr()
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// GetMulticastAddr returns the multicast address of a TestFlow.
|
||||
func (flow TestFlow) GetMulticastAddr() tcpip.Address {
|
||||
if flow.IsV4() {
|
||||
return MulticastAddr
|
||||
}
|
||||
return MulticastV6Addr
|
||||
}
|
||||
|
||||
// MapAddrIfApplicable converts the given IPv4 address into its V4-mapped
|
||||
// version if it is applicable to the TestFlow.
|
||||
func (flow TestFlow) MapAddrIfApplicable(v4Addr tcpip.Address) tcpip.Address {
|
||||
if flow.isMapped() {
|
||||
return v4MappedAddrPrefix + v4Addr
|
||||
}
|
||||
return v4Addr
|
||||
}
|
||||
|
||||
// NetProto returns the network protocol of a TestFlow.
|
||||
func (flow TestFlow) NetProto() tcpip.NetworkProtocolNumber {
|
||||
if flow.IsV4() {
|
||||
return ipv4.ProtocolNumber
|
||||
}
|
||||
return ipv6.ProtocolNumber
|
||||
}
|
||||
|
||||
// SockProto returns the network protocol number a socket must be configured
|
||||
// with to support a given TestFlow.
|
||||
func (flow TestFlow) SockProto() tcpip.NetworkProtocolNumber {
|
||||
switch flow {
|
||||
case UnicastV4in6, UnicastV6, UnicastV6Only, MulticastV4in6, MulticastV6, MulticastV6Only, BroadcastIn6, ReverseMulticastV6:
|
||||
return ipv6.ProtocolNumber
|
||||
case UnicastV4, MulticastV4, Broadcast, ReverseMulticastV4:
|
||||
return ipv4.ProtocolNumber
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
|
||||
}
|
||||
}
|
||||
|
||||
// CheckerFn returns the correct network checker for the current TestFlow.
|
||||
func (flow TestFlow) CheckerFn() func(*testing.T, []byte, ...checker.NetworkChecker) {
|
||||
if flow.IsV4() {
|
||||
return checker.IPv4
|
||||
}
|
||||
return checker.IPv6
|
||||
}
|
||||
|
||||
// IsV4 returns true for IPv4 TestFlow's.
|
||||
func (flow TestFlow) IsV4() bool {
|
||||
return flow.SockProto() == ipv4.ProtocolNumber || flow.isMapped()
|
||||
}
|
||||
|
||||
// IsV6 returns true for IPv6 TestFlow's.
|
||||
func (flow TestFlow) IsV6() bool { return !flow.IsV4() }
|
||||
|
||||
func (flow TestFlow) isV6Only() bool {
|
||||
switch flow {
|
||||
case UnicastV6Only, MulticastV6Only:
|
||||
return true
|
||||
case UnicastV4, UnicastV4in6, UnicastV6, MulticastV4, MulticastV4in6, MulticastV6, Broadcast, BroadcastIn6, ReverseMulticastV4, ReverseMulticastV6:
|
||||
return false
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
|
||||
}
|
||||
}
|
||||
|
||||
// IsMulticast returns true if the TestFlow is multicast.
|
||||
func (flow TestFlow) IsMulticast() bool {
|
||||
switch flow {
|
||||
case MulticastV4, MulticastV4in6, MulticastV6, MulticastV6Only:
|
||||
return true
|
||||
case UnicastV4, UnicastV4in6, UnicastV6, UnicastV6Only, Broadcast, BroadcastIn6, ReverseMulticastV4, ReverseMulticastV6:
|
||||
return false
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
|
||||
}
|
||||
}
|
||||
|
||||
func (flow TestFlow) isBroadcast() bool {
|
||||
switch flow {
|
||||
case Broadcast, BroadcastIn6:
|
||||
return true
|
||||
case UnicastV4, UnicastV4in6, UnicastV6, UnicastV6Only, MulticastV4, MulticastV4in6, MulticastV6, MulticastV6Only, ReverseMulticastV4, ReverseMulticastV6:
|
||||
return false
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
|
||||
}
|
||||
}
|
||||
|
||||
func (flow TestFlow) isMapped() bool {
|
||||
switch flow {
|
||||
case UnicastV4in6, MulticastV4in6, BroadcastIn6:
|
||||
return true
|
||||
case UnicastV4, UnicastV6, UnicastV6Only, MulticastV4, MulticastV6, MulticastV6Only, Broadcast, ReverseMulticastV4, ReverseMulticastV6:
|
||||
return false
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid TestFlow given: %d", flow))
|
||||
}
|
||||
}
|
||||
|
||||
func (flow TestFlow) isReverseMulticast() bool {
|
||||
switch flow {
|
||||
case ReverseMulticastV4, ReverseMulticastV6:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -58,14 +58,12 @@ go_test(
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/channel",
|
||||
"//pkg/tcpip/link/loopback",
|
||||
"//pkg/tcpip/link/sniffer",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/testutil",
|
||||
"//pkg/tcpip/transport/icmp",
|
||||
"//pkg/tcpip/transport/testing/context",
|
||||
"//pkg/waiter",
|
||||
"@com_github_google_go_cmp//cmp:go_default_library",
|
||||
"@org_golang_x_time//rate:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
+644
-1139
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user