summaryrefslogtreecommitdiffhomepage
path: root/test/packetimpact
diff options
context:
space:
mode:
Diffstat (limited to 'test/packetimpact')
-rw-r--r--test/packetimpact/README.md702
-rw-r--r--test/packetimpact/dut/BUILD18
-rw-r--r--test/packetimpact/dut/posix_server.cc365
-rw-r--r--test/packetimpact/netdevs/BUILD15
-rw-r--r--test/packetimpact/netdevs/netdevs.go104
-rw-r--r--test/packetimpact/proto/BUILD12
-rw-r--r--test/packetimpact/proto/posix_server.proto230
-rw-r--r--test/packetimpact/runner/BUILD20
-rw-r--r--test/packetimpact/runner/defs.bzl136
-rw-r--r--test/packetimpact/runner/packetimpact_test.go339
-rw-r--r--test/packetimpact/testbench/BUILD46
-rw-r--r--test/packetimpact/testbench/connections.go950
-rw-r--r--test/packetimpact/testbench/dut.go658
-rw-r--r--test/packetimpact/testbench/dut_client.go28
-rw-r--r--test/packetimpact/testbench/layers.go1248
-rw-r--r--test/packetimpact/testbench/layers_test.go507
-rw-r--r--test/packetimpact/testbench/rawsockets.go178
-rw-r--r--test/packetimpact/testbench/testbench.go106
-rw-r--r--test/packetimpact/tests/BUILD265
-rw-r--r--test/packetimpact/tests/fin_wait2_timeout_test.go75
-rw-r--r--test/packetimpact/tests/icmpv6_param_problem_test.go78
-rw-r--r--test/packetimpact/tests/ipv4_id_uniqueness_test.go122
-rw-r--r--test/packetimpact/tests/tcp_close_wait_ack_test.go108
-rw-r--r--test/packetimpact/tests/tcp_cork_mss_test.go84
-rw-r--r--test/packetimpact/tests/tcp_handshake_window_size_test.go66
-rw-r--r--test/packetimpact/tests/tcp_noaccept_close_rst_test.go42
-rw-r--r--test/packetimpact/tests/tcp_outside_the_window_test.go93
-rw-r--r--test/packetimpact/tests/tcp_paws_mechanism_test.go109
-rw-r--r--test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go87
-rw-r--r--test/packetimpact/tests/tcp_reordering_test.go174
-rw-r--r--test/packetimpact/tests/tcp_retransmits_test.go84
-rw-r--r--test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go105
-rw-r--r--test/packetimpact/tests/tcp_splitseg_mss_test.go71
-rw-r--r--test/packetimpact/tests/tcp_synrcvd_reset_test.go52
-rw-r--r--test/packetimpact/tests/tcp_synsent_reset_test.go88
-rw-r--r--test/packetimpact/tests/tcp_user_timeout_test.go105
-rw-r--r--test/packetimpact/tests/tcp_window_shrink_test.go73
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go105
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_test.go112
-rw-r--r--test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go98
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go365
-rw-r--r--test/packetimpact/tests/udp_recv_multicast_test.go40
-rw-r--r--test/packetimpact/tests/udp_send_recv_dgram_test.go90
43 files changed, 0 insertions, 8353 deletions
diff --git a/test/packetimpact/README.md b/test/packetimpact/README.md
deleted file mode 100644
index f46c67a0c..000000000
--- a/test/packetimpact/README.md
+++ /dev/null
@@ -1,702 +0,0 @@
-# Packetimpact
-
-## What is packetimpact?
-
-Packetimpact is a tool for platform-independent network testing. It is heavily
-inspired by [packetdrill](https://github.com/google/packetdrill). It creates two
-docker containers connected by a network. One is for the test bench, which
-operates the test. The other is for the device-under-test (DUT), which is the
-software being tested. The test bench communicates over the network with the DUT
-to check correctness of the network.
-
-### Goals
-
-Packetimpact aims to provide:
-
-* A **multi-platform** solution that can test both Linux and gVisor.
-* **Conciseness** on par with packetdrill scripts.
-* **Control-flow** like for loops, conditionals, and variables.
-* **Flexibilty** to specify every byte in a packet or use multiple sockets.
-
-## How to run packetimpact tests?
-
-Build the test container image by running the following at the root of the
-repository:
-
-```bash
-$ make load-packetimpact
-```
-
-Run a test, e.g. `fin_wait2_timeout`, against Linux:
-
-```bash
-$ bazel test //test/packetimpact/tests:fin_wait2_timeout_linux_test
-```
-
-Run the same test, but against gVisor:
-
-```bash
-$ bazel test //test/packetimpact/tests:fin_wait2_timeout_netstack_test
-```
-
-## When to use packetimpact?
-
-There are a few ways to write networking tests for gVisor currently:
-
-* [Go unit tests](https://github.com/google/gvisor/tree/master/pkg/tcpip)
-* [syscall tests](https://github.com/google/gvisor/tree/master/test/syscalls/linux)
-* [packetdrill tests](https://github.com/google/gvisor/tree/master/test/packetdrill)
-* packetimpact tests
-
-The right choice depends on the needs of the test.
-
-Feature | Go unit test | syscall test | packetdrill | packetimpact
--------------- | ------------ | ------------ | ----------- | ------------
-Multi-platform | no | **YES** | **YES** | **YES**
-Concise | no | somewhat | somewhat | **VERY**
-Control-flow | **YES** | **YES** | no | **YES**
-Flexible | **VERY** | no | somewhat | **VERY**
-
-### Go unit tests
-
-If the test depends on the internals of gVisor and doesn't need to run on Linux
-or other platforms for comparison purposes, a Go unit test can be appropriate.
-They can observe internals of gVisor networking. The downside is that they are
-**not concise** and **not multi-platform**. If you require insight on gVisor
-internals, this is the right choice.
-
-### Syscall tests
-
-Syscall tests are **multi-platform** but cannot examine the internals of gVisor
-networking. They are **concise**. They can use **control-flow** structures like
-conditionals, for loops, and variables. However, they are limited to only what
-the POSIX interface provides so they are **not flexible**. For example, you
-would have difficulty writing a syscall test that intentionally sends a bad IP
-checksum. Or if you did write that test with raw sockets, it would be very
-**verbose** to write a test that intentionally send wrong checksums, wrong
-protocols, wrong sequence numbers, etc.
-
-### Packetdrill tests
-
-Packetdrill tests are **multi-platform** and can run against both Linux and
-gVisor. They are **concise** and use a special packetdrill scripting language.
-They are **more flexible** than a syscall test in that they can send packets
-that a syscall test would have difficulty sending, like a packet with a
-calcuated ACK number. But they are also somewhat limimted in flexibiilty in that
-they can't do tests with multiple sockets. They have **no control-flow** ability
-like variables or conditionals. For example, it isn't possible to send a packet
-that depends on the window size of a previous packet because the packetdrill
-language can't express that. Nor could you branch based on whether or not the
-other side supports window scaling, for example.
-
-### Packetimpact tests
-
-Packetimpact tests are similar to Packetdrill tests except that they are written
-in Go instead of the packetdrill scripting language. That gives them all the
-**control-flow** abilities of Go (loops, functions, variables, etc). They are
-**multi-platform** in the same way as packetdrill tests but even more
-**flexible** because Go is more expressive than the scripting language of
-packetdrill. However, Go is **not as concise** as the packetdrill language. Many
-design decisions below are made to mitigate that.
-
-## How it works
-
-```
- Testbench Device-Under-Test (DUT)
- +-------------------+ +------------------------+
- | | TEST NET | |
- | rawsockets.go <-->| <===========> | <---+ |
- | ^ | | | |
- | | | | | |
- | v | | | |
- | unittest | | | |
- | ^ | | | |
- | | | | | |
- | v | | v |
- | dut.go <========gRPC========> posix server |
- | | CONTROL NET | |
- +-------------------+ +------------------------+
-```
-
-Two docker containers are created by a "runner" script, one for the testbench
-and the other for the device under test (DUT). The script connects the two
-containers with a control network and test network. It also does some other
-tasks like waiting until the DUT is ready before starting the test and disabling
-Linux networking that would interfere with the test bench.
-
-### DUT
-
-The DUT container runs a program called the "posix_server". The posix_server is
-written in c++ for maximum portability. It is compiled on the host. The script
-that starts the containers copies it into the DUT's container and runs it. It's
-job is to receive directions from the test bench on what actions to take. For
-this, the posix_server does three steps in a loop:
-
-1. Listen for a request from the test bench.
-2. Execute a command.
-3. Send the response back to the test bench.
-
-The requests and responses are
-[protobufs](https://developers.google.com/protocol-buffers) and the
-communication is done with [gRPC](https://grpc.io/). The commands run are
-[POSIX socket commands](https://en.wikipedia.org/wiki/Berkeley_sockets#Socket_API_functions),
-with the inputs and outputs converted into protobuf requests and responses. All
-communication is on the control network, so that the test network is unaffected
-by extra packets.
-
-For example, this is the request and response pair to call
-[`socket()`](http://man7.org/linux/man-pages/man2/socket.2.html):
-
-```protocol-buffer
-message SocketRequest {
- int32 domain = 1;
- int32 type = 2;
- int32 protocol = 3;
-}
-
-message SocketResponse {
- int32 fd = 1;
- int32 errno_ = 2;
-}
-```
-
-##### Alternatives considered
-
-* We could have use JSON for communication instead. It would have been a
- lighter-touch than protobuf but protobuf handles all the data type and has
- strict typing to prevent a class of errors. The test bench could be written
- in other languages, too.
-* Instead of mimicking the POSIX interfaces, arguments could have had a more
- natural form, like the `bind()` getting a string IP address instead of bytes
- in a `sockaddr_t`. However, conforming to the existing structures keeps more
- of the complexity in Go and keeps the posix_server simpler and thus more
- likely to compile everywhere.
-
-### Test Bench
-
-The test bench does most of the work in a test. It is a Go program that compiles
-on the host and is copied by the script into test bench's container. It is a
-regular [go unit test](https://golang.org/pkg/testing/) that imports the test
-bench framework. The test bench framwork is based on three basic utilities:
-
-* Commanding the DUT to run POSIX commands and return responses.
-* Sending raw packets to the DUT on the test network.
-* Listening for raw packets from the DUT on the test network.
-
-#### DUT commands
-
-To keep the interface to the DUT consistent and easy-to-use, each POSIX command
-supported by the posix_server is wrapped in functions with signatures similar to
-the ones in the [Go unix package](https://godoc.org/golang.org/x/sys/unix). This
-way all the details of endianess and (un)marshalling of go structs such as
-[unix.Timeval](https://godoc.org/golang.org/x/sys/unix#Timeval) is handled in
-one place. This also makes it straight-forward to convert tests that use `unix.`
-or `syscall.` calls to `dut.` calls.
-
-For example, creating a connection to the DUT and commanding it to make a socket
-looks like this:
-
-```go
-dut := testbench.NewDut(t)
-fd, err := dut.SocketWithErrno(unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_IP)
-if fd < 0 {
- t.Fatalf(...)
-}
-```
-
-Because the usual case is to fail the test when the DUT fails to create a
-socket, there is a concise version of each of the `...WithErrno` functions that
-does that:
-
-```go
-dut := testbench.NewDut(t)
-fd := dut.Socket(unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_IP)
-```
-
-The DUT and other structs in the code store a `*testing.T` so that they can
-provide versions of functions that call `t.Fatalf(...)`. This helps keep tests
-concise.
-
-##### Alternatives considered
-
-* Instead of mimicking the `unix.` go interface, we could have invented a more
- natural one, like using `float64` instead of `Timeval`. However, using the
- same function signatures that `unix.` has makes it easier to convert code to
- `dut.`. Also, using an existing interface ensures that we don't invent an
- interface that isn't extensible. For example, if we invented a function for
- `bind()` that didn't support IPv6 and later we had to add a second `bind6()`
- function.
-
-#### Sending/Receiving Raw Packets
-
-The framework wraps POSIX sockets for sending and receiving raw frames. Both
-send and receive are synchronous commands.
-[SO_RCVTIMEO](http://man7.org/linux/man-pages/man7/socket.7.html) is used to set
-a timeout on the receive commands. For ease of use, these are wrapped in an
-`Injector` and a `Sniffer`. They have functions:
-
-```go
-func (s *Sniffer) Recv(timeout time.Duration) []byte {...}
-func (i *Injector) Send(b []byte) {...}
-```
-
-##### Alternatives considered
-
-* [gopacket](https://github.com/google/gopacket) pcap has raw socket support
- but requires cgo. cgo is not guaranteed to be portable from the host to the
- container and in practice, the container doesn't recognize binaries built on
- the host if they use cgo.
-* Both gVisor and gopacket have the ability to read and write pcap files
- without cgo but that is insufficient here because we can't just replay pcap
- files, we need a more dynamic solution.
-* The sniffer and injector can't share a socket because they need to be bound
- differently.
-* Sniffing could have been done asynchronously with channels, obviating the
- need for `SO_RCVTIMEO`. But that would introduce asynchronous complication.
- `SO_RCVTIMEO` is well supported on the test bench.
-
-#### `Layer` struct
-
-A large part of packetimpact tests is creating packets to send and comparing
-received packets against expectations. To keep tests concise, it is useful to be
-able to specify just the important parts of packets that need to be set. For
-example, sending a packet with default values except for TCP Flags. And for
-packets received, it's useful to be able to compare just the necessary parts of
-received packets and ignore the rest.
-
-To aid in both of those, Go structs with optional fields are created for each
-encapsulation type, such as IPv4, TCP, and Ethernet. This is inspired by
-[scapy](https://scapy.readthedocs.io/en/latest/). For example, here is the
-struct for Ethernet:
-
-```go
-type Ether struct {
- LayerBase
- SrcAddr *tcpip.LinkAddress
- DstAddr *tcpip.LinkAddress
- Type *tcpip.NetworkProtocolNumber
-}
-```
-
-Each struct has the same fields as those in the
-[gVisor headers](https://github.com/google/gvisor/tree/master/pkg/tcpip/header)
-but with a pointer for each field that may be `nil`.
-
-##### Alternatives considered
-
-* Just use []byte like gVisor headers do. The drawback is that it makes the
- tests more verbose.
- * For example, there would be no way to call `Send(myBytes)` concisely and
- indicate if the checksum should be calculated automatically versus
- overridden. The only way would be to add lines to the test to calculate
- it before each Send, which is wordy. Or make multiple versions of Send:
- one that checksums IP, one that doesn't, one that checksums TCP, one
- that does both, etc. That would be many combinations.
- * Filtering inputs would become verbose. Either:
- * large conditionals that need to be repeated many places:
- `h[FlagOffset] == SYN && h[LengthOffset:LengthOffset+2] == ...` or
- * Many functions, one per field, like: `filterByFlag(myBytes, SYN)`,
- `filterByLength(myBytes, 20)`, `filterByNextProto(myBytes, 0x8000)`,
- etc.
- * Using pointers allows us to combine `Layer`s with reflection. So the
- default `Layers` can be overridden by a `Layers` with just the TCP
- conection's src/dst which can be overridden by one with just a test
- specific TCP window size.
- * It's a proven way to separate the details of a packet from the byte
- format as shown by scapy's success.
-* Use packetgo. It's more general than parsing packets with gVisor. However:
- * packetgo doesn't have optional fields so many of the above problems
- still apply.
- * It would be yet another dependency.
- * It's not as well known to engineers that are already writing gVisor
- code.
- * It might be a good candidate for replacing the parsing of packets into
- `Layer`s if all that parsing turns out to be more work than parsing by
- packetgo and converting *that* to `Layer`. packetgo has easier to use
- getters for the layers. This could be done later in a way that doesn't
- break tests.
-
-#### `Layer` methods
-
-The `Layer` structs provide a way to partially specify an encapsulation. They
-also need methods for using those partially specified encapsulation, for example
-to marshal them to bytes or compare them. For those, each encapsulation
-implements the `Layer` interface:
-
-```go
-// Layer is the interface that all encapsulations must implement.
-//
-// A Layer is an encapsulation in a packet, such as TCP, IPv4, IPv6, etc. A
-// Layer contains all the fields of the encapsulation. Each field is a pointer
-// and may be nil.
-type Layer interface {
- // toBytes converts the Layer into bytes. In places where the Layer's field
- // isn't nil, the value that is pointed to is used. When the field is nil, a
- // reasonable default for the Layer is used. For example, "64" for IPv4 TTL
- // and a calculated checksum for TCP or IP. Some layers require information
- // from the previous or next layers in order to compute a default, such as
- // TCP's checksum or Ethernet's type, so each Layer has a doubly-linked list
- // to the layer's neighbors.
- toBytes() ([]byte, error)
-
- // match checks if the current Layer matches the provided Layer. If either
- // Layer has a nil in a given field, that field is considered matching.
- // Otherwise, the values pointed to by the fields must match.
- match(Layer) bool
-
- // length in bytes of the current encapsulation
- length() int
-
- // next gets a pointer to the encapsulated Layer.
- next() Layer
-
- // prev gets a pointer to the Layer encapsulating this one.
- prev() Layer
-
- // setNext sets the pointer to the encapsulated Layer.
- setNext(Layer)
-
- // setPrev sets the pointer to the Layer encapsulating this one.
- setPrev(Layer)
-}
-```
-
-The `next` and `prev` make up a link listed so that each layer can get at the
-information in the layer around it. This is necessary for some protocols, like
-TCP that needs the layer before and payload after to compute the checksum. Any
-sequence of `Layer` structs is valid so long as the parser and `toBytes`
-functions can map from type to protool number and vice-versa. When the mapping
-fails, an error is emitted explaining what functionality is missing. The
-solution is either to fix the ordering or implement the missing protocol.
-
-For each `Layer` there is also a parsing function. For example, this one is for
-Ethernet:
-
-```
-func ParseEther(b []byte) (Layers, error)
-```
-
-The parsing function converts bytes received on the wire into a `Layer`
-(actually `Layers`, see below) which has no `nil`s in it. By using
-`match(Layer)` to compare against another `Layer` that *does* have `nil`s in it,
-the received bytes can be partially compared. The `nil`s behave as
-"don't-cares".
-
-##### Alternatives considered
-
-* Matching against `[]byte` instead of converting to `Layer` first.
- * The downside is that it precludes the use of a `cmp.Equal` one-liner to
- do comparisons.
- * It creates confusion in the code to deal with both representations at
- different times. For example, is the checksum calculated on `[]byte` or
- `Layer` when sending? What about when checking received packets?
-
-#### `Layers`
-
-```
-type Layers []Layer
-
-func (ls *Layers) match(other Layers) bool {...}
-func (ls *Layers) toBytes() ([]byte, error) {...}
-```
-
-`Layers` is an array of `Layer`. It represents a stack of encapsulations, such
-as `Layers{Ether{},IPv4{},TCP{},Payload{}}`. It also has `toBytes()` and
-`match(Layers)`, like `Layer`. The parse functions above actually return
-`Layers` and not `Layer` because they know about the headers below and
-sequentially call each parser on the remaining, encapsulated bytes.
-
-All this leads to the ability to write concise packet processing. For example:
-
-```go
-etherType := 0x8000
-flags = uint8(header.TCPFlagSyn|header.TCPFlagAck)
-toMatch := Layers{Ether{Type: &etherType}, IPv4{}, TCP{Flags: &flags}}
-for {
- recvBytes := sniffer.Recv(time.Second)
- if recvBytes == nil {
- println("Got no packet for 1 second")
- }
- gotPacket, err := ParseEther(recvBytes)
- if err == nil && toMatch.match(gotPacket) {
- println("Got a TCP/IPv4/Eth packet with SYNACK")
- }
-}
-```
-
-##### Alternatives considered
-
-* Don't use previous and next pointers.
- * Each layer may need to be able to interrogate the layers around it, like
- for computing the next protocol number or total length. So *some*
- mechanism is needed for a `Layer` to see neighboring layers.
- * We could pass the entire array `Layers` to the `toBytes()` function.
- Passing an array to a method that includes in the array the function
- receiver itself seems wrong.
-
-#### `layerState`
-
-`Layers` represents the different headers of a packet but a connection includes
-more state. For example, a TCP connection needs to keep track of the next
-expected sequence number and also the next sequence number to send. This is
-stored in a `layerState` struct. This is the `layerState` for TCP:
-
-```go
-// tcpState maintains state about a TCP connection.
-type tcpState struct {
- out, in TCP
- localSeqNum, remoteSeqNum *seqnum.Value
- synAck *TCP
- portPickerFD int
- finSent bool
-}
-```
-
-The next sequence numbers for each side of the connection are stored. `out` and
-`in` have defaults for the TCP header, such as the expected source and
-destination ports for outgoing packets and incoming packets.
-
-##### `layerState` interface
-
-```go
-// layerState stores the state of a layer of a connection.
-type layerState interface {
- // outgoing returns an outgoing layer to be sent in a frame.
- outgoing() Layer
-
- // incoming creates an expected Layer for comparing against a received Layer.
- // Because the expectation can depend on values in the received Layer, it is
- // an input to incoming. For example, the ACK number needs to be checked in a
- // TCP packet but only if the ACK flag is set in the received packet.
- incoming(received Layer) Layer
-
- // sent updates the layerState based on the Layer that was sent. The input is
- // a Layer with all prev and next pointers populated so that the entire frame
- // as it was sent is available.
- sent(sent Layer) error
-
- // received updates the layerState based on a Layer that is receieved. The
- // input is a Layer with all prev and next pointers populated so that the
- // entire frame as it was receieved is available.
- received(received Layer) error
-
- // close frees associated resources held by the LayerState.
- close() error
-}
-```
-
-`outgoing` generates the default Layer for an outgoing packet. For TCP, this
-would be a `TCP` with the source and destination ports populated. Because they
-are static, they are stored inside the `out` member of `tcpState`. However, the
-sequence numbers change frequently so the outgoing sequence number is stored in
-the `localSeqNum` and put into the output of outgoing for each call.
-
-`incoming` does the same functions for packets that arrive but instead of
-generating a packet to send, it generates an expect packet for filtering packets
-that arrive. For example, if a `TCP` header arrives with the wrong ports, it can
-be ignored as belonging to a different connection. `incoming` needs the received
-header itself as an input because the filter may depend on the input. For
-example, the expected sequence number depends on the flags in the TCP header.
-
-`sent` and `received` are run for each header that is actually sent or received
-and used to update the internal state. `incoming` and `outgoing` should *not* be
-used for these purpose. For example, `incoming` is called on every packet that
-arrives but only packets that match ought to actually update the state.
-`outgoing` is called to created outgoing packets and those packets are always
-sent, so unlike `incoming`/`received`, there is one `outgoing` call for each
-`sent` call.
-
-`close` cleans up after the layerState. For example, TCP and UDP need to keep a
-port reserved and then release it.
-
-#### Connections
-
-Using `layerState` above, we can create connections.
-
-```go
-// Connection holds a collection of layer states for maintaining a connection
-// along with sockets for sniffer and injecting packets.
-type Connection struct {
- layerStates []layerState
- injector Injector
- sniffer Sniffer
- t *testing.T
-}
-```
-
-The connection stores an array of `layerState` in the order that the headers
-should be present in the frame to send. For example, Ether then IPv4 then TCP.
-The injector and sniffer are for writing and reading frames. A `*testing.T` is
-stored so that internal errors can be reported directly without code in the unit
-test.
-
-The `Connection` has some useful functions:
-
-```go
-// Close frees associated resources held by the Connection.
-func (conn *Connection) Close() {...}
-// CreateFrame builds a frame for the connection with layer overriding defaults
-// of the innermost layer and additionalLayers added after it.
-func (conn *Connection) CreateFrame(layer Layer, additionalLayers ...Layer) Layers {...}
-// SendFrame sends a frame on the wire and updates the state of all layers.
-func (conn *Connection) SendFrame(frame Layers) {...}
-// Send a packet with reasonable defaults. Potentially override the final layer
-// in the connection with the provided layer and add additionLayers.
-func (conn *Connection) Send(layer Layer, additionalLayers ...Layer) {...}
-// Expect a frame with the final layerStates layer matching the provided Layer
-// within the timeout specified. If it doesn't arrive in time, it returns nil.
-func (conn *Connection) Expect(layer Layer, timeout time.Duration) (Layer, error) {...}
-// ExpectFrame expects a frame that matches the provided Layers within the
-// timeout specified. If it doesn't arrive in time, it returns nil.
-func (conn *Connection) ExpectFrame(layers Layers, timeout time.Duration) (Layers, error) {...}
-// Drain drains the sniffer's receive buffer by receiving packets until there's
-// nothing else to receive.
-func (conn *Connection) Drain() {...}
-```
-
-`CreateFrame` uses the `[]layerState` to create a frame to send. The first
-argument is for overriding defaults in the last header of the frame, because
-this is the most common need. For a TCPIPv4 connection, this would be the TCP
-header. Optional additionalLayers can be specified to add to the frame being
-created, such as a `Payload` for `TCP`.
-
-`SendFrame` sends the frame to the DUT. It is combined with `CreateFrame` to
-make `Send`. For unittests with basic sending needs, `Send` can be used. If more
-control is needed over the frame, it can be made with `CreateFrame`, modified in
-the unit test, and then sent with `SendFrame`.
-
-On the receiving side, there is `Expect` and `ExpectFrame`. Like with the
-sending side, there are two forms of each function, one for just the last header
-and one for the whole frame. The expect functions use the `[]layerState` to
-create a template for the expected incoming frame. That frame is then overridden
-by the values in the first argument. Finally, a loop starts sniffing packets on
-the wire for frames. If a matching frame is found before the timeout, it is
-returned without error. If not, nil is returned and the error contains text of
-all the received frames that didn't match. Exactly one of the outputs will be
-non-nil, even if no frames are received at all.
-
-`Drain` sniffs and discards all the frames that have yet to be received. A
-common way to write a test is:
-
-```go
-conn.Drain() // Discard all outstanding frames.
-conn.Send(...) // Send a frame with overrides.
-// Now expect a frame with a certain header and fail if it doesn't arrive.
-if _, err := conn.Expect(...); err != nil { t.Fatal(...) }
-```
-
-Or for a test where we want to check that no frame arrives:
-
-```go
-if gotOne, _ := conn.Expect(...); gotOne != nil { t.Fatal(...) }
-```
-
-#### Specializing `Connection`
-
-Because there are some common combinations of `layerState` into `Connection`,
-they are defined:
-
-```go
-// TCPIPv4 maintains the state for all the layers in a TCP/IPv4 connection.
-type TCPIPv4 Connection
-// UDPIPv4 maintains the state for all the layers in a UDP/IPv4 connection.
-type UDPIPv4 Connection
-```
-
-Each has a `NewXxx` function to create a new connection with reasonable
-defaults. They also have functions that call the underlying `Connection`
-functions but with specialization and tighter type-checking. For example:
-
-```go
-func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) {
- (*Connection)(conn).Send(&tcp, additionalLayers...)
-}
-func (conn *TCPIPv4) Drain() {
- conn.sniffer.Drain()
-}
-```
-
-They may also have some accessors to get or set the internal state of the
-connection:
-
-```go
-func (conn *TCPIPv4) state() *tcpState {
- state, ok := conn.layerStates[len(conn.layerStates)-1].(*tcpState)
- if !ok {
- conn.t.Fatalf("expected final state of %v to be tcpState", conn.layerStates)
- }
- return state
-}
-func (conn *TCPIPv4) RemoteSeqNum() *seqnum.Value {
- return conn.state().remoteSeqNum
-}
-func (conn *TCPIPv4) LocalSeqNum() *seqnum.Value {
- return conn.state().localSeqNum
-}
-```
-
-Unittests will in practice use these functions and not the functions on
-`Connection`. For example, `NewTCPIPv4()` and then call `Send` on that rather
-than cast is to a `Connection` and call `Send` on that cast result.
-
-##### Alternatives considered
-
-* Instead of storing `outgoing` and `incoming`, store values.
- * There would be many more things to store instead, like `localMac`,
- `remoteMac`, `localIP`, `remoteIP`, `localPort`, and `remotePort`.
- * Construction of a packet would be many lines to copy each of these
- values into a `[]byte`. And there would be slight variations needed for
- each encapsulation stack, like TCPIPv6 and ARP.
- * Filtering incoming packets would be a long sequence:
- * Compare the MACs, then
- * Parse the next header, then
- * Compare the IPs, then
- * Parse the next header, then
- * Compare the TCP ports. Instead it's all just one call to
- `cmp.Equal(...)`, for all sequences.
- * A TCPIPv6 connection could share most of the code. Only the type of the
- IP addresses are different. The types of `outgoing` and `incoming` would
- be remain `Layers`.
- * An ARP connection could share all the Ethernet parts. The IP `Layer`
- could be factored out of `outgoing`. After that, the IPv4 and IPv6
- connections could implement one interface and a single TCP struct could
- have either network protocol through composition.
-
-## Putting it all together
-
-Here's what te start of a packetimpact unit test looks like. This test creates a
-TCP connection with the DUT. There are added comments for explanation in this
-document but a real test might not include them in order to stay even more
-concise.
-
-```go
-func TestMyTcpTest(t *testing.T) {
- // Prepare a DUT for communication.
- dut := testbench.NewDUT(t)
-
- // This does:
- // dut.Socket()
- // dut.Bind()
- // dut.Getsockname() to learn the new port number
- // dut.Listen()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD) // Tell the DUT to close the socket at the end of the test.
-
- // Monitor a new TCP connection with sniffer, injector, sequence number tracking,
- // and reasonable outgoing and incoming packet field default IPs, MACs, and port numbers.
- conn := testbench.NewTCPIPv4(t, dut, remotePort)
-
- // Perform a 3-way handshake: send SYN, expect SYNACK, send ACK.
- conn.Handshake()
-
- // Tell the DUT to accept the new connection.
- acceptFD := dut.Accept(acceptFd)
-}
-```
-
-## Other notes
-
-* The time between receiving a SYN-ACK and replying with an ACK in `Handshake`
- is about 3ms. This is much slower than the native unix response, which is
- about 0.3ms. Packetdrill gets closer to 0.3ms. For tests where timing is
- crucial, packetdrill is faster and more precise.
diff --git a/test/packetimpact/dut/BUILD b/test/packetimpact/dut/BUILD
deleted file mode 100644
index 3ce63c2c6..000000000
--- a/test/packetimpact/dut/BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-load("//tools:defs.bzl", "cc_binary", "grpcpp")
-
-package(
- default_visibility = ["//test/packetimpact:__subpackages__"],
- licenses = ["notice"],
-)
-
-cc_binary(
- name = "posix_server",
- srcs = ["posix_server.cc"],
- linkstatic = 1,
- static = True, # This is needed for running in a docker container.
- deps = [
- grpcpp,
- "//test/packetimpact/proto:posix_server_cc_grpc_proto",
- "//test/packetimpact/proto:posix_server_cc_proto",
- ],
-)
diff --git a/test/packetimpact/dut/posix_server.cc b/test/packetimpact/dut/posix_server.cc
deleted file mode 100644
index a1a5c3612..000000000
--- a/test/packetimpact/dut/posix_server.cc
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright 2020 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.
-
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <unordered_map>
-
-#include "include/grpcpp/security/server_credentials.h"
-#include "include/grpcpp/server_builder.h"
-#include "test/packetimpact/proto/posix_server.grpc.pb.h"
-#include "test/packetimpact/proto/posix_server.pb.h"
-
-// Converts a sockaddr_storage to a Sockaddr message.
-::grpc::Status sockaddr_to_proto(const sockaddr_storage &addr,
- socklen_t addrlen,
- posix_server::Sockaddr *sockaddr_proto) {
- switch (addr.ss_family) {
- case AF_INET: {
- auto addr_in = reinterpret_cast<const sockaddr_in *>(&addr);
- auto response_in = sockaddr_proto->mutable_in();
- response_in->set_family(addr_in->sin_family);
- response_in->set_port(ntohs(addr_in->sin_port));
- response_in->mutable_addr()->assign(
- reinterpret_cast<const char *>(&addr_in->sin_addr.s_addr), 4);
- return ::grpc::Status::OK;
- }
- case AF_INET6: {
- auto addr_in6 = reinterpret_cast<const sockaddr_in6 *>(&addr);
- auto response_in6 = sockaddr_proto->mutable_in6();
- response_in6->set_family(addr_in6->sin6_family);
- response_in6->set_port(ntohs(addr_in6->sin6_port));
- response_in6->set_flowinfo(ntohl(addr_in6->sin6_flowinfo));
- response_in6->mutable_addr()->assign(
- reinterpret_cast<const char *>(&addr_in6->sin6_addr.s6_addr), 16);
- response_in6->set_scope_id(ntohl(addr_in6->sin6_scope_id));
- return ::grpc::Status::OK;
- }
- }
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Unknown Sockaddr");
-}
-
-::grpc::Status proto_to_sockaddr(const posix_server::Sockaddr &sockaddr_proto,
- sockaddr_storage *addr, socklen_t *addr_len) {
- switch (sockaddr_proto.sockaddr_case()) {
- case posix_server::Sockaddr::SockaddrCase::kIn: {
- auto proto_in = sockaddr_proto.in();
- if (proto_in.addr().size() != 4) {
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "IPv4 address must be 4 bytes");
- }
- auto addr_in = reinterpret_cast<sockaddr_in *>(addr);
- addr_in->sin_family = proto_in.family();
- addr_in->sin_port = htons(proto_in.port());
- proto_in.addr().copy(reinterpret_cast<char *>(&addr_in->sin_addr.s_addr),
- 4);
- *addr_len = sizeof(*addr_in);
- break;
- }
- case posix_server::Sockaddr::SockaddrCase::kIn6: {
- auto proto_in6 = sockaddr_proto.in6();
- if (proto_in6.addr().size() != 16) {
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "IPv6 address must be 16 bytes");
- }
- auto addr_in6 = reinterpret_cast<sockaddr_in6 *>(addr);
- addr_in6->sin6_family = proto_in6.family();
- addr_in6->sin6_port = htons(proto_in6.port());
- addr_in6->sin6_flowinfo = htonl(proto_in6.flowinfo());
- proto_in6.addr().copy(
- reinterpret_cast<char *>(&addr_in6->sin6_addr.s6_addr), 16);
- addr_in6->sin6_scope_id = htonl(proto_in6.scope_id());
- *addr_len = sizeof(*addr_in6);
- break;
- }
- case posix_server::Sockaddr::SockaddrCase::SOCKADDR_NOT_SET:
- default:
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Unknown Sockaddr");
- }
- return ::grpc::Status::OK;
-}
-
-class PosixImpl final : public posix_server::Posix::Service {
- ::grpc::Status Accept(grpc_impl::ServerContext *context,
- const ::posix_server::AcceptRequest *request,
- ::posix_server::AcceptResponse *response) override {
- sockaddr_storage addr;
- socklen_t addrlen = sizeof(addr);
- response->set_fd(accept(request->sockfd(),
- reinterpret_cast<sockaddr *>(&addr), &addrlen));
- response->set_errno_(errno);
- return sockaddr_to_proto(addr, addrlen, response->mutable_addr());
- }
-
- ::grpc::Status Bind(grpc_impl::ServerContext *context,
- const ::posix_server::BindRequest *request,
- ::posix_server::BindResponse *response) override {
- if (!request->has_addr()) {
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Missing address");
- }
-
- sockaddr_storage addr;
- socklen_t addr_len;
- auto err = proto_to_sockaddr(request->addr(), &addr, &addr_len);
- if (!err.ok()) {
- return err;
- }
-
- response->set_ret(
- bind(request->sockfd(), reinterpret_cast<sockaddr *>(&addr), addr_len));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status Close(grpc_impl::ServerContext *context,
- const ::posix_server::CloseRequest *request,
- ::posix_server::CloseResponse *response) override {
- response->set_ret(close(request->fd()));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status Connect(grpc_impl::ServerContext *context,
- const ::posix_server::ConnectRequest *request,
- ::posix_server::ConnectResponse *response) override {
- if (!request->has_addr()) {
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Missing address");
- }
- sockaddr_storage addr;
- socklen_t addr_len;
- auto err = proto_to_sockaddr(request->addr(), &addr, &addr_len);
- if (!err.ok()) {
- return err;
- }
-
- response->set_ret(connect(request->sockfd(),
- reinterpret_cast<sockaddr *>(&addr), addr_len));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status Fcntl(grpc_impl::ServerContext *context,
- const ::posix_server::FcntlRequest *request,
- ::posix_server::FcntlResponse *response) override {
- response->set_ret(::fcntl(request->fd(), request->cmd(), request->arg()));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status GetSockName(
- grpc_impl::ServerContext *context,
- const ::posix_server::GetSockNameRequest *request,
- ::posix_server::GetSockNameResponse *response) override {
- sockaddr_storage addr;
- socklen_t addrlen = sizeof(addr);
- response->set_ret(getsockname(
- request->sockfd(), reinterpret_cast<sockaddr *>(&addr), &addrlen));
- response->set_errno_(errno);
- return sockaddr_to_proto(addr, addrlen, response->mutable_addr());
- }
-
- ::grpc::Status GetSockOpt(
- grpc_impl::ServerContext *context,
- const ::posix_server::GetSockOptRequest *request,
- ::posix_server::GetSockOptResponse *response) override {
- switch (request->type()) {
- case ::posix_server::GetSockOptRequest::BYTES: {
- socklen_t optlen = request->optlen();
- std::vector<char> buf(optlen);
- response->set_ret(::getsockopt(request->sockfd(), request->level(),
- request->optname(), buf.data(),
- &optlen));
- if (optlen >= 0) {
- response->mutable_optval()->set_bytesval(buf.data(), optlen);
- }
- break;
- }
- case ::posix_server::GetSockOptRequest::INT: {
- int intval = 0;
- socklen_t optlen = sizeof(intval);
- response->set_ret(::getsockopt(request->sockfd(), request->level(),
- request->optname(), &intval, &optlen));
- response->mutable_optval()->set_intval(intval);
- break;
- }
- case ::posix_server::GetSockOptRequest::TIME: {
- timeval tv;
- socklen_t optlen = sizeof(tv);
- response->set_ret(::getsockopt(request->sockfd(), request->level(),
- request->optname(), &tv, &optlen));
- response->mutable_optval()->mutable_timeval()->set_seconds(tv.tv_sec);
- response->mutable_optval()->mutable_timeval()->set_microseconds(
- tv.tv_usec);
- break;
- }
- default:
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Unknown SockOpt Type");
- }
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status Listen(grpc_impl::ServerContext *context,
- const ::posix_server::ListenRequest *request,
- ::posix_server::ListenResponse *response) override {
- response->set_ret(listen(request->sockfd(), request->backlog()));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status Send(::grpc::ServerContext *context,
- const ::posix_server::SendRequest *request,
- ::posix_server::SendResponse *response) override {
- response->set_ret(::send(request->sockfd(), request->buf().data(),
- request->buf().size(), request->flags()));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status SendTo(::grpc::ServerContext *context,
- const ::posix_server::SendToRequest *request,
- ::posix_server::SendToResponse *response) override {
- if (!request->has_dest_addr()) {
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Missing address");
- }
- sockaddr_storage addr;
- socklen_t addr_len;
- auto err = proto_to_sockaddr(request->dest_addr(), &addr, &addr_len);
- if (!err.ok()) {
- return err;
- }
-
- response->set_ret(::sendto(request->sockfd(), request->buf().data(),
- request->buf().size(), request->flags(),
- reinterpret_cast<sockaddr *>(&addr), addr_len));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status SetSockOpt(
- grpc_impl::ServerContext *context,
- const ::posix_server::SetSockOptRequest *request,
- ::posix_server::SetSockOptResponse *response) override {
- switch (request->optval().val_case()) {
- case ::posix_server::SockOptVal::kBytesval:
- response->set_ret(setsockopt(request->sockfd(), request->level(),
- request->optname(),
- request->optval().bytesval().c_str(),
- request->optval().bytesval().size()));
- break;
- case ::posix_server::SockOptVal::kIntval: {
- int opt = request->optval().intval();
- response->set_ret(::setsockopt(request->sockfd(), request->level(),
- request->optname(), &opt, sizeof(opt)));
- break;
- }
- case ::posix_server::SockOptVal::kTimeval: {
- timeval tv = {.tv_sec = static_cast<__time_t>(
- request->optval().timeval().seconds()),
- .tv_usec = static_cast<__suseconds_t>(
- request->optval().timeval().microseconds())};
- response->set_ret(setsockopt(request->sockfd(), request->level(),
- request->optname(), &tv, sizeof(tv)));
- break;
- }
- default:
- return ::grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Unknown SockOpt Type");
- }
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status Socket(grpc_impl::ServerContext *context,
- const ::posix_server::SocketRequest *request,
- ::posix_server::SocketResponse *response) override {
- response->set_fd(
- socket(request->domain(), request->type(), request->protocol()));
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-
- ::grpc::Status Recv(::grpc::ServerContext *context,
- const ::posix_server::RecvRequest *request,
- ::posix_server::RecvResponse *response) override {
- std::vector<char> buf(request->len());
- response->set_ret(
- recv(request->sockfd(), buf.data(), buf.size(), request->flags()));
- if (response->ret() >= 0) {
- response->set_buf(buf.data(), response->ret());
- }
- response->set_errno_(errno);
- return ::grpc::Status::OK;
- }
-};
-
-// Parse command line options. Returns a pointer to the first argument beyond
-// the options.
-void parse_command_line_options(int argc, char *argv[], std::string *ip,
- int *port) {
- static struct option options[] = {{"ip", required_argument, NULL, 1},
- {"port", required_argument, NULL, 2},
- {0, 0, 0, 0}};
-
- // Parse the arguments.
- int c;
- while ((c = getopt_long(argc, argv, "", options, NULL)) > 0) {
- if (c == 1) {
- *ip = optarg;
- } else if (c == 2) {
- *port = std::stoi(std::string(optarg));
- }
- }
-}
-
-void run_server(const std::string &ip, int port) {
- PosixImpl posix_service;
- grpc::ServerBuilder builder;
- std::string server_address = ip + ":" + std::to_string(port);
- // Set the authentication mechanism.
- std::shared_ptr<grpc::ServerCredentials> creds =
- grpc::InsecureServerCredentials();
- builder.AddListeningPort(server_address, creds);
- builder.RegisterService(&posix_service);
-
- std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
- std::cerr << "Server listening on " << server_address << std::endl;
- server->Wait();
- std::cerr << "posix_server is finished." << std::endl;
-}
-
-int main(int argc, char *argv[]) {
- std::cerr << "posix_server is starting." << std::endl;
- std::string ip;
- int port;
- parse_command_line_options(argc, argv, &ip, &port);
-
- std::cerr << "Got IP " << ip << " and port " << port << "." << std::endl;
- run_server(ip, port);
-}
diff --git a/test/packetimpact/netdevs/BUILD b/test/packetimpact/netdevs/BUILD
deleted file mode 100644
index 422bb9b0c..000000000
--- a/test/packetimpact/netdevs/BUILD
+++ /dev/null
@@ -1,15 +0,0 @@
-load("//tools:defs.bzl", "go_library")
-
-package(
- licenses = ["notice"],
-)
-
-go_library(
- name = "netdevs",
- srcs = ["netdevs.go"],
- visibility = ["//test/packetimpact:__subpackages__"],
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/header",
- ],
-)
diff --git a/test/packetimpact/netdevs/netdevs.go b/test/packetimpact/netdevs/netdevs.go
deleted file mode 100644
index d2c9cfeaf..000000000
--- a/test/packetimpact/netdevs/netdevs.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2020 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 netdevs contains utilities for working with network devices.
-package netdevs
-
-import (
- "fmt"
- "net"
- "regexp"
- "strings"
-
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/header"
-)
-
-// A DeviceInfo represents a network device.
-type DeviceInfo struct {
- MAC net.HardwareAddr
- IPv4Addr net.IP
- IPv4Net *net.IPNet
- IPv6Addr net.IP
- IPv6Net *net.IPNet
-}
-
-var (
- deviceLine = regexp.MustCompile(`^\s*\d+: (\w+)`)
- linkLine = regexp.MustCompile(`^\s*link/\w+ ([0-9a-fA-F:]+)`)
- inetLine = regexp.MustCompile(`^\s*inet ([0-9./]+)`)
- inet6Line = regexp.MustCompile(`^\s*inet6 ([0-9a-fA-Z:/]+)`)
-)
-
-// ParseDevices parses the output from `ip addr show` into a map from device
-// name to information about the device.
-func ParseDevices(cmdOutput string) (map[string]DeviceInfo, error) {
- var currentDevice string
- var currentInfo DeviceInfo
- deviceInfos := make(map[string]DeviceInfo)
- for _, line := range strings.Split(cmdOutput, "\n") {
- if m := deviceLine.FindStringSubmatch(line); m != nil {
- if currentDevice != "" {
- deviceInfos[currentDevice] = currentInfo
- }
- currentInfo = DeviceInfo{}
- currentDevice = m[1]
- } else if m := linkLine.FindStringSubmatch(line); m != nil {
- mac, err := net.ParseMAC(m[1])
- if err != nil {
- return nil, err
- }
- currentInfo.MAC = mac
- } else if m := inetLine.FindStringSubmatch(line); m != nil {
- ipv4Addr, ipv4Net, err := net.ParseCIDR(m[1])
- if err != nil {
- return nil, err
- }
- currentInfo.IPv4Addr = ipv4Addr
- currentInfo.IPv4Net = ipv4Net
- } else if m := inet6Line.FindStringSubmatch(line); m != nil {
- ipv6Addr, ipv6Net, err := net.ParseCIDR(m[1])
- if err != nil {
- return nil, err
- }
- currentInfo.IPv6Addr = ipv6Addr
- currentInfo.IPv6Net = ipv6Net
- }
- }
- if currentDevice != "" {
- deviceInfos[currentDevice] = currentInfo
- }
- return deviceInfos, nil
-}
-
-// MACToIP converts the MAC address to an IPv6 link local address as described
-// in RFC 4291 page 20: https://tools.ietf.org/html/rfc4291#page-20
-func MACToIP(mac net.HardwareAddr) net.IP {
- addr := make([]byte, header.IPv6AddressSize)
- addr[0] = 0xfe
- addr[1] = 0x80
- header.EthernetAdddressToModifiedEUI64IntoBuf(tcpip.LinkAddress(mac), addr[8:])
- return net.IP(addr)
-}
-
-// FindDeviceByIP finds a DeviceInfo and device name from an IP address in the
-// output of ParseDevices.
-func FindDeviceByIP(ip net.IP, devices map[string]DeviceInfo) (string, DeviceInfo, error) {
- for dev, info := range devices {
- if info.IPv4Addr.Equal(ip) {
- return dev, info, nil
- }
- }
- return "", DeviceInfo{}, fmt.Errorf("can't find %s on any interface", ip)
-}
diff --git a/test/packetimpact/proto/BUILD b/test/packetimpact/proto/BUILD
deleted file mode 100644
index 4a4370f42..000000000
--- a/test/packetimpact/proto/BUILD
+++ /dev/null
@@ -1,12 +0,0 @@
-load("//tools:defs.bzl", "proto_library")
-
-package(
- default_visibility = ["//test/packetimpact:__subpackages__"],
- licenses = ["notice"],
-)
-
-proto_library(
- name = "posix_server",
- srcs = ["posix_server.proto"],
- has_services = 1,
-)
diff --git a/test/packetimpact/proto/posix_server.proto b/test/packetimpact/proto/posix_server.proto
deleted file mode 100644
index ccd20b10d..000000000
--- a/test/packetimpact/proto/posix_server.proto
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2020 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.
-
-syntax = "proto3";
-
-package posix_server;
-
-message SockaddrIn {
- int32 family = 1;
- uint32 port = 2;
- bytes addr = 3;
-}
-
-message SockaddrIn6 {
- uint32 family = 1;
- uint32 port = 2;
- uint32 flowinfo = 3;
- bytes addr = 4;
- uint32 scope_id = 5;
-}
-
-message Sockaddr {
- oneof sockaddr {
- SockaddrIn in = 1;
- SockaddrIn6 in6 = 2;
- }
-}
-
-message Timeval {
- int64 seconds = 1;
- int64 microseconds = 2;
-}
-
-message SockOptVal {
- oneof val {
- bytes bytesval = 1;
- int32 intval = 2;
- Timeval timeval = 3;
- }
-}
-
-// Request and Response pairs for each Posix service RPC call, sorted.
-
-message AcceptRequest {
- int32 sockfd = 1;
-}
-
-message AcceptResponse {
- int32 fd = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
- Sockaddr addr = 3;
-}
-
-message BindRequest {
- int32 sockfd = 1;
- Sockaddr addr = 2;
-}
-
-message BindResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message CloseRequest {
- int32 fd = 1;
-}
-
-message CloseResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message ConnectRequest {
- int32 sockfd = 1;
- Sockaddr addr = 2;
-}
-
-message ConnectResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message FcntlRequest {
- int32 fd = 1;
- int32 cmd = 2;
- int32 arg = 3;
-}
-
-message FcntlResponse {
- int32 ret = 1;
- int32 errno_ = 2;
-}
-
-message GetSockNameRequest {
- int32 sockfd = 1;
-}
-
-message GetSockNameResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
- Sockaddr addr = 3;
-}
-
-message GetSockOptRequest {
- int32 sockfd = 1;
- int32 level = 2;
- int32 optname = 3;
- int32 optlen = 4;
- enum SockOptType {
- UNSPECIFIED = 0;
- BYTES = 1;
- INT = 2;
- TIME = 3;
- }
- SockOptType type = 5;
-}
-
-message GetSockOptResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
- SockOptVal optval = 3;
-}
-
-message ListenRequest {
- int32 sockfd = 1;
- int32 backlog = 2;
-}
-
-message ListenResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message SendRequest {
- int32 sockfd = 1;
- bytes buf = 2;
- int32 flags = 3;
-}
-
-message SendResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message SendToRequest {
- int32 sockfd = 1;
- bytes buf = 2;
- int32 flags = 3;
- Sockaddr dest_addr = 4;
-}
-
-message SendToResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message SetSockOptRequest {
- int32 sockfd = 1;
- int32 level = 2;
- int32 optname = 3;
- SockOptVal optval = 4;
-}
-
-message SetSockOptResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message SocketRequest {
- int32 domain = 1;
- int32 type = 2;
- int32 protocol = 3;
-}
-
-message SocketResponse {
- int32 fd = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
-}
-
-message RecvRequest {
- int32 sockfd = 1;
- int32 len = 2;
- int32 flags = 3;
-}
-
-message RecvResponse {
- int32 ret = 1;
- int32 errno_ = 2; // "errno" may fail to compile in c++.
- bytes buf = 3;
-}
-
-service Posix {
- // Call accept() on the DUT.
- rpc Accept(AcceptRequest) returns (AcceptResponse);
- // Call bind() on the DUT.
- rpc Bind(BindRequest) returns (BindResponse);
- // Call close() on the DUT.
- rpc Close(CloseRequest) returns (CloseResponse);
- // Call connect() on the DUT.
- rpc Connect(ConnectRequest) returns (ConnectResponse);
- // Call fcntl() on the DUT.
- rpc Fcntl(FcntlRequest) returns (FcntlResponse);
- // Call getsockname() on the DUT.
- rpc GetSockName(GetSockNameRequest) returns (GetSockNameResponse);
- // Call getsockopt() on the DUT.
- rpc GetSockOpt(GetSockOptRequest) returns (GetSockOptResponse);
- // Call listen() on the DUT.
- rpc Listen(ListenRequest) returns (ListenResponse);
- // Call send() on the DUT.
- rpc Send(SendRequest) returns (SendResponse);
- // Call sendto() on the DUT.
- rpc SendTo(SendToRequest) returns (SendToResponse);
- // Call setsockopt() on the DUT.
- rpc SetSockOpt(SetSockOptRequest) returns (SetSockOptResponse);
- // Call socket() on the DUT.
- rpc Socket(SocketRequest) returns (SocketResponse);
- // Call recv() on the DUT.
- rpc Recv(RecvRequest) returns (RecvResponse);
-}
diff --git a/test/packetimpact/runner/BUILD b/test/packetimpact/runner/BUILD
deleted file mode 100644
index 0b68a760a..000000000
--- a/test/packetimpact/runner/BUILD
+++ /dev/null
@@ -1,20 +0,0 @@
-load("//tools:defs.bzl", "go_test")
-
-package(
- default_visibility = ["//test/packetimpact:__subpackages__"],
- licenses = ["notice"],
-)
-
-go_test(
- name = "packetimpact_test",
- srcs = ["packetimpact_test.go"],
- tags = [
- # Not intended to be run directly.
- "local",
- "manual",
- ],
- deps = [
- "//pkg/test/dockerutil",
- "//test/packetimpact/netdevs",
- ],
-)
diff --git a/test/packetimpact/runner/defs.bzl b/test/packetimpact/runner/defs.bzl
deleted file mode 100644
index ea66b9756..000000000
--- a/test/packetimpact/runner/defs.bzl
+++ /dev/null
@@ -1,136 +0,0 @@
-"""Defines rules for packetimpact test targets."""
-
-load("//tools:defs.bzl", "go_test")
-
-def _packetimpact_test_impl(ctx):
- test_runner = ctx.executable._test_runner
- bench = ctx.actions.declare_file("%s-bench" % ctx.label.name)
- bench_content = "\n".join([
- "#!/bin/bash",
- # This test will run part in a distinct user namespace. This can cause
- # permission problems, because all runfiles may not be owned by the
- # current user, and no other users will be mapped in that namespace.
- # Make sure that everything is readable here.
- "find . -type f -or -type d -exec chmod a+rx {} \\;",
- "%s %s --testbench_binary %s $@\n" % (
- test_runner.short_path,
- " ".join(ctx.attr.flags),
- ctx.files.testbench_binary[0].short_path,
- ),
- ])
- ctx.actions.write(bench, bench_content, is_executable = True)
-
- transitive_files = depset()
- if hasattr(ctx.attr._test_runner, "data_runfiles"):
- transitive_files = depset(ctx.attr._test_runner.data_runfiles.files)
- runfiles = ctx.runfiles(
- files = [test_runner] + ctx.files.testbench_binary + ctx.files._posix_server_binary,
- transitive_files = transitive_files,
- collect_default = True,
- collect_data = True,
- )
- return [DefaultInfo(executable = bench, runfiles = runfiles)]
-
-_packetimpact_test = rule(
- attrs = {
- "_test_runner": attr.label(
- executable = True,
- cfg = "target",
- default = ":packetimpact_test",
- ),
- "_posix_server_binary": attr.label(
- cfg = "target",
- default = "//test/packetimpact/dut:posix_server",
- ),
- "testbench_binary": attr.label(
- cfg = "target",
- mandatory = True,
- ),
- "flags": attr.string_list(
- mandatory = False,
- default = [],
- ),
- },
- test = True,
- implementation = _packetimpact_test_impl,
-)
-
-PACKETIMPACT_TAGS = ["local", "manual"]
-
-def packetimpact_linux_test(
- name,
- testbench_binary,
- expect_failure = False,
- **kwargs):
- """Add a packetimpact test on linux.
-
- Args:
- name: name of the test
- testbench_binary: the testbench binary
- expect_failure: the test must fail
- **kwargs: all the other args, forwarded to _packetimpact_test
- """
- expect_failure_flag = ["--expect_failure"] if expect_failure else []
- _packetimpact_test(
- name = name + "_linux_test",
- testbench_binary = testbench_binary,
- flags = ["--dut_platform", "linux"] + expect_failure_flag,
- tags = PACKETIMPACT_TAGS + ["packetimpact"],
- **kwargs
- )
-
-def packetimpact_netstack_test(
- name,
- testbench_binary,
- expect_failure = False,
- **kwargs):
- """Add a packetimpact test on netstack.
-
- Args:
- name: name of the test
- testbench_binary: the testbench binary
- expect_failure: the test must fail
- **kwargs: all the other args, forwarded to _packetimpact_test
- """
- expect_failure_flag = []
- if expect_failure:
- expect_failure_flag = ["--expect_failure"]
- _packetimpact_test(
- name = name + "_netstack_test",
- testbench_binary = testbench_binary,
- # This is the default runtime unless
- # "--test_arg=--runtime=OTHER_RUNTIME" is used to override the value.
- flags = ["--dut_platform", "netstack", "--runtime=runsc-d"] + expect_failure_flag,
- tags = PACKETIMPACT_TAGS + ["packetimpact"],
- **kwargs
- )
-
-def packetimpact_go_test(name, size = "small", pure = True, expect_linux_failure = False, expect_netstack_failure = False, **kwargs):
- """Add packetimpact tests written in go.
-
- Args:
- name: name of the test
- size: size of the test
- pure: make a static go binary
- expect_linux_failure: the test must fail for Linux
- expect_netstack_failure: the test must fail for Netstack
- **kwargs: all the other args, forwarded to go_test
- """
- testbench_binary = name + "_test"
- go_test(
- name = testbench_binary,
- size = size,
- pure = pure,
- tags = PACKETIMPACT_TAGS,
- **kwargs
- )
- packetimpact_linux_test(
- name = name,
- expect_failure = expect_linux_failure,
- testbench_binary = testbench_binary,
- )
- packetimpact_netstack_test(
- name = name,
- expect_failure = expect_netstack_failure,
- testbench_binary = testbench_binary,
- )
diff --git a/test/packetimpact/runner/packetimpact_test.go b/test/packetimpact/runner/packetimpact_test.go
deleted file mode 100644
index 3cac5915f..000000000
--- a/test/packetimpact/runner/packetimpact_test.go
+++ /dev/null
@@ -1,339 +0,0 @@
-// Copyright 2020 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.
-
-// The runner starts docker containers and networking for a packetimpact test.
-package packetimpact_test
-
-import (
- "flag"
- "fmt"
- "io/ioutil"
- "log"
- "math/rand"
- "net"
- "os"
- "os/exec"
- "path"
- "strings"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/test/dockerutil"
- "gvisor.dev/gvisor/test/packetimpact/netdevs"
-)
-
-// stringList implements flag.Value.
-type stringList []string
-
-// String implements flag.Value.String.
-func (l *stringList) String() string {
- return strings.Join(*l, ",")
-}
-
-// Set implements flag.Value.Set.
-func (l *stringList) Set(value string) error {
- *l = append(*l, value)
- return nil
-}
-
-var (
- dutPlatform = flag.String("dut_platform", "", "either \"linux\" or \"netstack\"")
- testbenchBinary = flag.String("testbench_binary", "", "path to the testbench binary")
- tshark = flag.Bool("tshark", false, "use more verbose tshark in logs instead of tcpdump")
- extraTestArgs = stringList{}
- expectFailure = flag.Bool("expect_failure", false, "expect that the test will fail when run")
-
- dutAddr = net.IPv4(0, 0, 0, 10)
- testbenchAddr = net.IPv4(0, 0, 0, 20)
-)
-
-const ctrlPort = "40000"
-
-// logger implements testutil.Logger.
-//
-// Labels logs based on their source and formats multi-line logs.
-type logger string
-
-// Name implements testutil.Logger.Name.
-func (l logger) Name() string {
- return string(l)
-}
-
-// Logf implements testutil.Logger.Logf.
-func (l logger) Logf(format string, args ...interface{}) {
- lines := strings.Split(fmt.Sprintf(format, args...), "\n")
- log.Printf("%s: %s", l, lines[0])
- for _, line := range lines[1:] {
- log.Printf("%*s %s", len(l), "", line)
- }
-}
-
-func TestOne(t *testing.T) {
- flag.Var(&extraTestArgs, "extra_test_arg", "extra arguments to pass to the testbench")
- flag.Parse()
- if *dutPlatform != "linux" && *dutPlatform != "netstack" {
- t.Fatal("--dut_platform should be either linux or netstack")
- }
- if *testbenchBinary == "" {
- t.Fatal("--testbench_binary is missing")
- }
- if *dutPlatform == "netstack" {
- if _, err := dockerutil.RuntimePath(); err != nil {
- t.Fatal("--runtime is missing or invalid with --dut_platform=netstack:", err)
- }
- }
- dockerutil.EnsureSupportedDockerVersion()
-
- // Create the networks needed for the test. One control network is needed for
- // the gRPC control packets and one test network on which to transmit the test
- // packets.
- ctrlNet := dockerutil.NewDockerNetwork(logger("ctrlNet"))
- testNet := dockerutil.NewDockerNetwork(logger("testNet"))
- for _, dn := range []*dockerutil.DockerNetwork{ctrlNet, testNet} {
- for {
- if err := createDockerNetwork(dn); err != nil {
- t.Log("creating docker network:", err)
- const wait = 100 * time.Millisecond
- t.Logf("sleeping %s and will try creating docker network again", wait)
- // This can fail if another docker network claimed the same IP so we'll
- // just try again.
- time.Sleep(wait)
- continue
- }
- break
- }
- defer func(dn *dockerutil.DockerNetwork) {
- if err := dn.Cleanup(); err != nil {
- t.Errorf("unable to cleanup container %s: %s", dn.Name, err)
- }
- }(dn)
- }
-
- tmpDir, err := ioutil.TempDir("", "container-output")
- if err != nil {
- t.Fatal("creating temp dir:", err)
- }
- defer os.RemoveAll(tmpDir)
-
- const testOutputDir = "/tmp/testoutput"
-
- runOpts := dockerutil.RunOpts{
- Image: "packetimpact",
- CapAdd: []string{"NET_ADMIN"},
- Extra: []string{"--sysctl", "net.ipv6.conf.all.disable_ipv6=0", "--rm", "-v", tmpDir + ":" + testOutputDir},
- Foreground: true,
- }
-
- // Create the Docker container for the DUT.
- dut := dockerutil.MakeDocker(logger("dut"))
- if *dutPlatform == "linux" {
- dut.Runtime = ""
- }
-
- const containerPosixServerBinary = "/packetimpact/posix_server"
- dut.CopyFiles(&runOpts, "/packetimpact", "/test/packetimpact/dut/posix_server")
-
- if err := dut.Create(runOpts, containerPosixServerBinary, "--ip=0.0.0.0", "--port="+ctrlPort); err != nil {
- t.Fatalf("unable to create container %s: %s", dut.Name, err)
- }
- defer dut.CleanUp()
-
- // Add ctrlNet as eth1 and testNet as eth2.
- const testNetDev = "eth2"
- if err := addNetworks(dut, dutAddr, []*dockerutil.DockerNetwork{ctrlNet, testNet}); err != nil {
- t.Fatal(err)
- }
-
- if err := dut.Start(); err != nil {
- t.Fatalf("unable to start container %s: %s", dut.Name, err)
- }
-
- if _, err := dut.WaitForOutput("Server listening.*\n", 60*time.Second); err != nil {
- t.Fatalf("%s on container %s never listened: %s", containerPosixServerBinary, dut.Name, err)
- }
-
- dutTestDevice, dutDeviceInfo, err := deviceByIP(dut, addressInSubnet(dutAddr, *testNet.Subnet))
- if err != nil {
- t.Fatal(err)
- }
-
- remoteMAC := dutDeviceInfo.MAC
- remoteIPv6 := dutDeviceInfo.IPv6Addr
- // Netstack as DUT doesn't assign IPv6 addresses automatically so do it if
- // needed.
- if remoteIPv6 == nil {
- if _, err := dut.Exec(dockerutil.RunOpts{}, "ip", "addr", "add", netdevs.MACToIP(remoteMAC).String(), "scope", "link", "dev", dutTestDevice); err != nil {
- t.Fatalf("unable to ip addr add on container %s: %s", dut.Name, err)
- }
- // Now try again, to make sure that it worked.
- _, dutDeviceInfo, err = deviceByIP(dut, addressInSubnet(dutAddr, *testNet.Subnet))
- if err != nil {
- t.Fatal(err)
- }
- remoteIPv6 = dutDeviceInfo.IPv6Addr
- if remoteIPv6 == nil {
- t.Fatal("unable to set IPv6 address on container", dut.Name)
- }
- }
-
- // Create the Docker container for the testbench.
- testbench := dockerutil.MakeDocker(logger("testbench"))
- testbench.Runtime = "" // The testbench always runs on Linux.
-
- tbb := path.Base(*testbenchBinary)
- containerTestbenchBinary := "/packetimpact/" + tbb
- runOpts = dockerutil.RunOpts{
- Image: "packetimpact",
- CapAdd: []string{"NET_ADMIN"},
- Extra: []string{"--sysctl", "net.ipv6.conf.all.disable_ipv6=0", "--rm", "-v", tmpDir + ":" + testOutputDir},
- Foreground: true,
- }
- testbench.CopyFiles(&runOpts, "/packetimpact", "/test/packetimpact/tests/"+tbb)
-
- // Run tcpdump in the test bench unbuffered, without DNS resolution, just on
- // the interface with the test packets.
- snifferArgs := []string{
- "tcpdump",
- "-S", "-vvv", "-U", "-n",
- "-i", testNetDev,
- "-w", testOutputDir + "/dump.pcap",
- }
- snifferRegex := "tcpdump: listening.*\n"
- if *tshark {
- // Run tshark in the test bench unbuffered, without DNS resolution, just on
- // the interface with the test packets.
- snifferArgs = []string{
- "tshark", "-V", "-l", "-n", "-i", testNetDev,
- "-o", "tcp.check_checksum:TRUE",
- "-o", "udp.check_checksum:TRUE",
- }
- snifferRegex = "Capturing on.*\n"
- }
-
- defer func() {
- if err := exec.Command("/bin/cp", "-r", tmpDir, os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")).Run(); err != nil {
- t.Error("unable to copy container output files:", err)
- }
- }()
-
- if err := testbench.Create(runOpts, snifferArgs...); err != nil {
- t.Fatalf("unable to create container %s: %s", testbench.Name, err)
- }
- defer testbench.CleanUp()
-
- // Add ctrlNet as eth1 and testNet as eth2.
- if err := addNetworks(testbench, testbenchAddr, []*dockerutil.DockerNetwork{ctrlNet, testNet}); err != nil {
- t.Fatal(err)
- }
-
- if err := testbench.Start(); err != nil {
- t.Fatalf("unable to start container %s: %s", testbench.Name, err)
- }
-
- // Kill so that it will flush output.
- defer testbench.Exec(dockerutil.RunOpts{}, "killall", snifferArgs[0])
-
- if _, err := testbench.WaitForOutput(snifferRegex, 60*time.Second); err != nil {
- t.Fatalf("sniffer on %s never listened: %s", dut.Name, err)
- }
-
- // Because the Linux kernel receives the SYN-ACK but didn't send the SYN it
- // will issue a RST. To prevent this IPtables can be used to filter out all
- // incoming packets. The raw socket that packetimpact tests use will still see
- // everything.
- if _, err := testbench.Exec(dockerutil.RunOpts{}, "iptables", "-A", "INPUT", "-i", testNetDev, "-j", "DROP"); err != nil {
- t.Fatalf("unable to Exec iptables on container %s: %s", testbench.Name, err)
- }
-
- // FIXME(b/156449515): Some piece of the system has a race. The old
- // bash script version had a sleep, so we have one too. The race should
- // be fixed and this sleep removed.
- time.Sleep(time.Second)
-
- // Start a packetimpact test on the test bench. The packetimpact test sends
- // and receives packets and also sends POSIX socket commands to the
- // posix_server to be executed on the DUT.
- testArgs := []string{containerTestbenchBinary}
- testArgs = append(testArgs, extraTestArgs...)
- testArgs = append(testArgs,
- "--posix_server_ip", addressInSubnet(dutAddr, *ctrlNet.Subnet).String(),
- "--posix_server_port", ctrlPort,
- "--remote_ipv4", addressInSubnet(dutAddr, *testNet.Subnet).String(),
- "--local_ipv4", addressInSubnet(testbenchAddr, *testNet.Subnet).String(),
- "--remote_ipv6", remoteIPv6.String(),
- "--remote_mac", remoteMAC.String(),
- "--device", testNetDev,
- "--dut_type", *dutPlatform,
- )
- _, err = testbench.Exec(dockerutil.RunOpts{}, testArgs...)
- if !*expectFailure && err != nil {
- t.Fatal("test failed:", err)
- }
- if *expectFailure && err == nil {
- t.Fatal("test failure expected but the test succeeded, enable the test and mark the corresponding bug as fixed")
- }
-}
-
-func addNetworks(d *dockerutil.Docker, addr net.IP, networks []*dockerutil.DockerNetwork) error {
- for _, dn := range networks {
- ip := addressInSubnet(addr, *dn.Subnet)
- // Connect to the network with the specified IP address.
- if err := dn.Connect(d, "--ip", ip.String()); err != nil {
- return fmt.Errorf("unable to connect container %s to network %s: %w", d.Name, dn.Name, err)
- }
- }
- return nil
-}
-
-// addressInSubnet combines the subnet provided with the address and returns a
-// new address. The return address bits come from the subnet where the mask is 1
-// and from the ip address where the mask is 0.
-func addressInSubnet(addr net.IP, subnet net.IPNet) net.IP {
- var octets []byte
- for i := 0; i < 4; i++ {
- octets = append(octets, (subnet.IP.To4()[i]&subnet.Mask[i])+(addr.To4()[i]&(^subnet.Mask[i])))
- }
- return net.IP(octets)
-}
-
-// makeDockerNetwork makes a randomly-named network that will start with the
-// namePrefix. The network will be a random /24 subnet.
-func createDockerNetwork(n *dockerutil.DockerNetwork) error {
- randSource := rand.NewSource(time.Now().UnixNano())
- r1 := rand.New(randSource)
- // Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
- ip := net.IPv4(byte(r1.Intn(224-192)+192), byte(r1.Intn(256)), byte(r1.Intn(256)), 0)
- n.Subnet = &net.IPNet{
- IP: ip,
- Mask: ip.DefaultMask(),
- }
- return n.Create()
-}
-
-// deviceByIP finds a deviceInfo and device name from an IP address.
-func deviceByIP(d *dockerutil.Docker, ip net.IP) (string, netdevs.DeviceInfo, error) {
- out, err := d.Exec(dockerutil.RunOpts{}, "ip", "addr", "show")
- if err != nil {
- return "", netdevs.DeviceInfo{}, fmt.Errorf("listing devices on %s container: %w", d.Name, err)
- }
- devs, err := netdevs.ParseDevices(out)
- if err != nil {
- return "", netdevs.DeviceInfo{}, fmt.Errorf("parsing devices from %s container: %w", d.Name, err)
- }
- testDevice, deviceInfo, err := netdevs.FindDeviceByIP(ip, devs)
- if err != nil {
- return "", netdevs.DeviceInfo{}, fmt.Errorf("can't find deviceInfo for container %s: %w", d.Name, err)
- }
- return testDevice, deviceInfo, nil
-}
diff --git a/test/packetimpact/testbench/BUILD b/test/packetimpact/testbench/BUILD
deleted file mode 100644
index d19ec07d4..000000000
--- a/test/packetimpact/testbench/BUILD
+++ /dev/null
@@ -1,46 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(
- default_visibility = ["//test/packetimpact:__subpackages__"],
- licenses = ["notice"],
-)
-
-go_library(
- name = "testbench",
- srcs = [
- "connections.go",
- "dut.go",
- "dut_client.go",
- "layers.go",
- "rawsockets.go",
- "testbench.go",
- ],
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/buffer",
- "//pkg/tcpip/header",
- "//pkg/tcpip/seqnum",
- "//pkg/usermem",
- "//test/packetimpact/netdevs",
- "//test/packetimpact/proto:posix_server_go_proto",
- "@com_github_google_go-cmp//cmp:go_default_library",
- "@com_github_google_go-cmp//cmp/cmpopts:go_default_library",
- "@com_github_mohae_deepcopy//:go_default_library",
- "@org_golang_google_grpc//:go_default_library",
- "@org_golang_google_grpc//keepalive:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- "@org_uber_go_multierr//:go_default_library",
- ],
-)
-
-go_test(
- name = "testbench_test",
- size = "small",
- srcs = ["layers_test.go"],
- library = ":testbench",
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/header",
- "@com_github_mohae_deepcopy//:go_default_library",
- ],
-)
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go
deleted file mode 100644
index 8b4a4d905..000000000
--- a/test/packetimpact/testbench/connections.go
+++ /dev/null
@@ -1,950 +0,0 @@
-// Copyright 2020 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 testbench has utilities to send and receive packets and also command
-// the DUT to run POSIX functions.
-package testbench
-
-import (
- "fmt"
- "math/rand"
- "net"
- "testing"
- "time"
-
- "github.com/mohae/deepcopy"
- "go.uber.org/multierr"
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
-)
-
-func portFromSockaddr(sa unix.Sockaddr) (uint16, error) {
- switch sa := sa.(type) {
- case *unix.SockaddrInet4:
- return uint16(sa.Port), nil
- case *unix.SockaddrInet6:
- return uint16(sa.Port), nil
- }
- return 0, fmt.Errorf("sockaddr type %T does not contain port", sa)
-}
-
-// pickPort makes a new socket and returns the socket FD and port. The domain should be AF_INET or AF_INET6. The caller must close the FD when done with
-// the port if there is no error.
-func pickPort(domain, typ int) (int, uint16, error) {
- fd, err := unix.Socket(domain, typ, 0)
- if err != nil {
- return -1, 0, err
- }
- defer func() {
- if err != nil {
- err = multierr.Append(err, unix.Close(fd))
- }
- }()
- var sa unix.Sockaddr
- switch domain {
- case unix.AF_INET:
- var sa4 unix.SockaddrInet4
- copy(sa4.Addr[:], net.ParseIP(LocalIPv4).To4())
- sa = &sa4
- case unix.AF_INET6:
- var sa6 unix.SockaddrInet6
- copy(sa6.Addr[:], net.ParseIP(LocalIPv6).To16())
- sa = &sa6
- default:
- return -1, 0, fmt.Errorf("invalid domain %d, it should be one of unix.AF_INET or unix.AF_INET6", domain)
- }
- if err = unix.Bind(fd, sa); err != nil {
- return -1, 0, err
- }
- sa, err = unix.Getsockname(fd)
- if err != nil {
- return -1, 0, err
- }
- port, err := portFromSockaddr(sa)
- if err != nil {
- return -1, 0, err
- }
- return fd, port, nil
-}
-
-// layerState stores the state of a layer of a connection.
-type layerState interface {
- // outgoing returns an outgoing layer to be sent in a frame. It should not
- // update layerState, that is done in layerState.sent.
- outgoing() Layer
-
- // incoming creates an expected Layer for comparing against a received Layer.
- // Because the expectation can depend on values in the received Layer, it is
- // an input to incoming. For example, the ACK number needs to be checked in a
- // TCP packet but only if the ACK flag is set in the received packet. It
- // should not update layerState, that is done in layerState.received. The
- // caller takes ownership of the returned Layer.
- incoming(received Layer) Layer
-
- // sent updates the layerState based on the Layer that was sent. The input is
- // a Layer with all prev and next pointers populated so that the entire frame
- // as it was sent is available.
- sent(sent Layer) error
-
- // received updates the layerState based on a Layer that is receieved. The
- // input is a Layer with all prev and next pointers populated so that the
- // entire frame as it was receieved is available.
- received(received Layer) error
-
- // close frees associated resources held by the LayerState.
- close() error
-}
-
-// etherState maintains state about an Ethernet connection.
-type etherState struct {
- out, in Ether
-}
-
-var _ layerState = (*etherState)(nil)
-
-// newEtherState creates a new etherState.
-func newEtherState(out, in Ether) (*etherState, error) {
- lMAC, err := tcpip.ParseMACAddress(LocalMAC)
- if err != nil {
- return nil, fmt.Errorf("parsing local MAC: %q: %w", LocalMAC, err)
- }
-
- rMAC, err := tcpip.ParseMACAddress(RemoteMAC)
- if err != nil {
- return nil, fmt.Errorf("parsing remote MAC: %q: %w", RemoteMAC, err)
- }
- s := etherState{
- out: Ether{SrcAddr: &lMAC, DstAddr: &rMAC},
- in: Ether{SrcAddr: &rMAC, DstAddr: &lMAC},
- }
- if err := s.out.merge(&out); err != nil {
- return nil, err
- }
- if err := s.in.merge(&in); err != nil {
- return nil, err
- }
- return &s, nil
-}
-
-func (s *etherState) outgoing() Layer {
- return deepcopy.Copy(&s.out).(Layer)
-}
-
-// incoming implements layerState.incoming.
-func (s *etherState) incoming(Layer) Layer {
- return deepcopy.Copy(&s.in).(Layer)
-}
-
-func (*etherState) sent(Layer) error {
- return nil
-}
-
-func (*etherState) received(Layer) error {
- return nil
-}
-
-func (*etherState) close() error {
- return nil
-}
-
-// ipv4State maintains state about an IPv4 connection.
-type ipv4State struct {
- out, in IPv4
-}
-
-var _ layerState = (*ipv4State)(nil)
-
-// newIPv4State creates a new ipv4State.
-func newIPv4State(out, in IPv4) (*ipv4State, error) {
- lIP := tcpip.Address(net.ParseIP(LocalIPv4).To4())
- rIP := tcpip.Address(net.ParseIP(RemoteIPv4).To4())
- s := ipv4State{
- out: IPv4{SrcAddr: &lIP, DstAddr: &rIP},
- in: IPv4{SrcAddr: &rIP, DstAddr: &lIP},
- }
- if err := s.out.merge(&out); err != nil {
- return nil, err
- }
- if err := s.in.merge(&in); err != nil {
- return nil, err
- }
- return &s, nil
-}
-
-func (s *ipv4State) outgoing() Layer {
- return deepcopy.Copy(&s.out).(Layer)
-}
-
-// incoming implements layerState.incoming.
-func (s *ipv4State) incoming(Layer) Layer {
- return deepcopy.Copy(&s.in).(Layer)
-}
-
-func (*ipv4State) sent(Layer) error {
- return nil
-}
-
-func (*ipv4State) received(Layer) error {
- return nil
-}
-
-func (*ipv4State) close() error {
- return nil
-}
-
-// ipv6State maintains state about an IPv6 connection.
-type ipv6State struct {
- out, in IPv6
-}
-
-var _ layerState = (*ipv6State)(nil)
-
-// newIPv6State creates a new ipv6State.
-func newIPv6State(out, in IPv6) (*ipv6State, error) {
- lIP := tcpip.Address(net.ParseIP(LocalIPv6).To16())
- rIP := tcpip.Address(net.ParseIP(RemoteIPv6).To16())
- s := ipv6State{
- out: IPv6{SrcAddr: &lIP, DstAddr: &rIP},
- in: IPv6{SrcAddr: &rIP, DstAddr: &lIP},
- }
- if err := s.out.merge(&out); err != nil {
- return nil, err
- }
- if err := s.in.merge(&in); err != nil {
- return nil, err
- }
- return &s, nil
-}
-
-// outgoing returns an outgoing layer to be sent in a frame.
-func (s *ipv6State) outgoing() Layer {
- return deepcopy.Copy(&s.out).(Layer)
-}
-
-func (s *ipv6State) incoming(Layer) Layer {
- return deepcopy.Copy(&s.in).(Layer)
-}
-
-func (s *ipv6State) sent(Layer) error {
- // Nothing to do.
- return nil
-}
-
-func (s *ipv6State) received(Layer) error {
- // Nothing to do.
- return nil
-}
-
-// close cleans up any resources held.
-func (s *ipv6State) close() error {
- return nil
-}
-
-// tcpState maintains state about a TCP connection.
-type tcpState struct {
- out, in TCP
- localSeqNum, remoteSeqNum *seqnum.Value
- synAck *TCP
- portPickerFD int
- finSent bool
-}
-
-var _ layerState = (*tcpState)(nil)
-
-// SeqNumValue is a helper routine that allocates a new seqnum.Value value to
-// store v and returns a pointer to it.
-func SeqNumValue(v seqnum.Value) *seqnum.Value {
- return &v
-}
-
-// newTCPState creates a new TCPState.
-func newTCPState(domain int, out, in TCP) (*tcpState, error) {
- portPickerFD, localPort, err := pickPort(domain, unix.SOCK_STREAM)
- if err != nil {
- return nil, err
- }
- s := tcpState{
- out: TCP{SrcPort: &localPort},
- in: TCP{DstPort: &localPort},
- localSeqNum: SeqNumValue(seqnum.Value(rand.Uint32())),
- portPickerFD: portPickerFD,
- finSent: false,
- }
- if err := s.out.merge(&out); err != nil {
- return nil, err
- }
- if err := s.in.merge(&in); err != nil {
- return nil, err
- }
- return &s, nil
-}
-
-func (s *tcpState) outgoing() Layer {
- newOutgoing := deepcopy.Copy(s.out).(TCP)
- if s.localSeqNum != nil {
- newOutgoing.SeqNum = Uint32(uint32(*s.localSeqNum))
- }
- if s.remoteSeqNum != nil {
- newOutgoing.AckNum = Uint32(uint32(*s.remoteSeqNum))
- }
- return &newOutgoing
-}
-
-// incoming implements layerState.incoming.
-func (s *tcpState) incoming(received Layer) Layer {
- tcpReceived, ok := received.(*TCP)
- if !ok {
- return nil
- }
- newIn := deepcopy.Copy(s.in).(TCP)
- if s.remoteSeqNum != nil {
- newIn.SeqNum = Uint32(uint32(*s.remoteSeqNum))
- }
- if s.localSeqNum != nil && (*tcpReceived.Flags&header.TCPFlagAck) != 0 {
- // The caller didn't specify an AckNum so we'll expect the calculated one,
- // but only if the ACK flag is set because the AckNum is not valid in a
- // header if ACK is not set.
- newIn.AckNum = Uint32(uint32(*s.localSeqNum))
- }
- return &newIn
-}
-
-func (s *tcpState) sent(sent Layer) error {
- tcp, ok := sent.(*TCP)
- if !ok {
- return fmt.Errorf("can't update tcpState with %T Layer", sent)
- }
- if !s.finSent {
- // update localSeqNum by the payload only when FIN is not yet sent by us
- for current := tcp.next(); current != nil; current = current.next() {
- s.localSeqNum.UpdateForward(seqnum.Size(current.length()))
- }
- }
- if tcp.Flags != nil && *tcp.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 {
- s.localSeqNum.UpdateForward(1)
- }
- if *tcp.Flags&(header.TCPFlagFin) != 0 {
- s.finSent = true
- }
- return nil
-}
-
-func (s *tcpState) received(l Layer) error {
- tcp, ok := l.(*TCP)
- if !ok {
- return fmt.Errorf("can't update tcpState with %T Layer", l)
- }
- s.remoteSeqNum = SeqNumValue(seqnum.Value(*tcp.SeqNum))
- if *tcp.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 {
- s.remoteSeqNum.UpdateForward(1)
- }
- for current := tcp.next(); current != nil; current = current.next() {
- s.remoteSeqNum.UpdateForward(seqnum.Size(current.length()))
- }
- return nil
-}
-
-// close frees the port associated with this connection.
-func (s *tcpState) close() error {
- if err := unix.Close(s.portPickerFD); err != nil {
- return err
- }
- s.portPickerFD = -1
- return nil
-}
-
-// udpState maintains state about a UDP connection.
-type udpState struct {
- out, in UDP
- portPickerFD int
-}
-
-var _ layerState = (*udpState)(nil)
-
-// newUDPState creates a new udpState.
-func newUDPState(domain int, out, in UDP) (*udpState, error) {
- portPickerFD, localPort, err := pickPort(domain, unix.SOCK_DGRAM)
- if err != nil {
- return nil, err
- }
- s := udpState{
- out: UDP{SrcPort: &localPort},
- in: UDP{DstPort: &localPort},
- portPickerFD: portPickerFD,
- }
- if err := s.out.merge(&out); err != nil {
- return nil, err
- }
- if err := s.in.merge(&in); err != nil {
- return nil, err
- }
- return &s, nil
-}
-
-func (s *udpState) outgoing() Layer {
- return deepcopy.Copy(&s.out).(Layer)
-}
-
-// incoming implements layerState.incoming.
-func (s *udpState) incoming(Layer) Layer {
- return deepcopy.Copy(&s.in).(Layer)
-}
-
-func (*udpState) sent(l Layer) error {
- return nil
-}
-
-func (*udpState) received(l Layer) error {
- return nil
-}
-
-// close frees the port associated with this connection.
-func (s *udpState) close() error {
- if err := unix.Close(s.portPickerFD); err != nil {
- return err
- }
- s.portPickerFD = -1
- return nil
-}
-
-// Connection holds a collection of layer states for maintaining a connection
-// along with sockets for sniffer and injecting packets.
-type Connection struct {
- layerStates []layerState
- injector Injector
- sniffer Sniffer
- t *testing.T
-}
-
-// Returns the default incoming frame against which to match. If received is
-// longer than layerStates then that may still count as a match. The reverse is
-// never a match and nil is returned.
-func (conn *Connection) incoming(received Layers) Layers {
- if len(received) < len(conn.layerStates) {
- return nil
- }
- in := Layers{}
- for i, s := range conn.layerStates {
- toMatch := s.incoming(received[i])
- if toMatch == nil {
- return nil
- }
- in = append(in, toMatch)
- }
- return in
-}
-
-func (conn *Connection) match(override, received Layers) bool {
- toMatch := conn.incoming(received)
- if toMatch == nil {
- return false // Not enough layers in gotLayers for matching.
- }
- if err := toMatch.merge(override); err != nil {
- return false // Failing to merge is not matching.
- }
- return toMatch.match(received)
-}
-
-// Close frees associated resources held by the Connection.
-func (conn *Connection) Close() {
- errs := multierr.Combine(conn.sniffer.close(), conn.injector.close())
- for _, s := range conn.layerStates {
- if err := s.close(); err != nil {
- errs = multierr.Append(errs, fmt.Errorf("unable to close %+v: %s", s, err))
- }
- }
- if errs != nil {
- conn.t.Fatalf("unable to close %+v: %s", conn, errs)
- }
-}
-
-// CreateFrame builds a frame for the connection with defaults overriden
-// from the innermost layer out, and additionalLayers added after it.
-//
-// Note that overrideLayers can have a length that is less than the number
-// of layers in this connection, and in such cases the innermost layers are
-// overriden first. As an example, valid values of overrideLayers for a TCP-
-// over-IPv4-over-Ethernet connection are: nil, [TCP], [IPv4, TCP], and
-// [Ethernet, IPv4, TCP].
-func (conn *Connection) CreateFrame(overrideLayers Layers, additionalLayers ...Layer) Layers {
- var layersToSend Layers
- for i, s := range conn.layerStates {
- layer := s.outgoing()
- // overrideLayers and conn.layerStates have their tails aligned, so
- // to find the index we move backwards by the distance i is to the
- // end.
- if j := len(overrideLayers) - (len(conn.layerStates) - i); j >= 0 {
- if err := layer.merge(overrideLayers[j]); err != nil {
- conn.t.Fatalf("can't merge %+v into %+v: %s", layer, overrideLayers[j], err)
- }
- }
- layersToSend = append(layersToSend, layer)
- }
- layersToSend = append(layersToSend, additionalLayers...)
- return layersToSend
-}
-
-// SendFrameStateless sends a frame without updating any of the layer states.
-//
-// This method is useful for sending out-of-band control messages such as
-// ICMP packets, where it would not make sense to update the transport layer's
-// state using the ICMP header.
-func (conn *Connection) SendFrameStateless(frame Layers) {
- outBytes, err := frame.ToBytes()
- if err != nil {
- conn.t.Fatalf("can't build outgoing packet: %s", err)
- }
- conn.injector.Send(outBytes)
-}
-
-// SendFrame sends a frame on the wire and updates the state of all layers.
-func (conn *Connection) SendFrame(frame Layers) {
- outBytes, err := frame.ToBytes()
- if err != nil {
- conn.t.Fatalf("can't build outgoing packet: %s", err)
- }
- conn.injector.Send(outBytes)
-
- // frame might have nil values where the caller wanted to use default values.
- // sentFrame will have no nil values in it because it comes from parsing the
- // bytes that were actually sent.
- sentFrame := parse(parseEther, outBytes)
- // Update the state of each layer based on what was sent.
- for i, s := range conn.layerStates {
- if err := s.sent(sentFrame[i]); err != nil {
- conn.t.Fatalf("Unable to update the state of %+v with %s: %s", s, sentFrame[i], err)
- }
- }
-}
-
-// send sends a packet, possibly with layers of this connection overridden and
-// additional layers added.
-//
-// Types defined with Connection as the underlying type should expose
-// type-safe versions of this method.
-func (conn *Connection) send(overrideLayers Layers, additionalLayers ...Layer) {
- conn.SendFrame(conn.CreateFrame(overrideLayers, additionalLayers...))
-}
-
-// recvFrame gets the next successfully parsed frame (of type Layers) within the
-// timeout provided. If no parsable frame arrives before the timeout, it returns
-// nil.
-func (conn *Connection) recvFrame(timeout time.Duration) Layers {
- if timeout <= 0 {
- return nil
- }
- b := conn.sniffer.Recv(timeout)
- if b == nil {
- return nil
- }
- return parse(parseEther, b)
-}
-
-// layersError stores the Layers that we got and the Layers that we wanted to
-// match.
-type layersError struct {
- got, want Layers
-}
-
-func (e *layersError) Error() string {
- return e.got.diff(e.want)
-}
-
-// Expect expects a frame with the final layerStates layer matching the
-// provided Layer within the timeout specified. If it doesn't arrive in time,
-// an error is returned.
-func (conn *Connection) Expect(layer Layer, timeout time.Duration) (Layer, error) {
- // Make a frame that will ignore all but the final layer.
- layers := make([]Layer, len(conn.layerStates))
- layers[len(layers)-1] = layer
-
- gotFrame, err := conn.ExpectFrame(layers, timeout)
- if err != nil {
- return nil, err
- }
- if len(conn.layerStates)-1 < len(gotFrame) {
- return gotFrame[len(conn.layerStates)-1], nil
- }
- conn.t.Fatal("the received frame should be at least as long as the expected layers")
- panic("unreachable")
-}
-
-// ExpectFrame expects a frame that matches the provided Layers within the
-// timeout specified. If one arrives in time, the Layers is returned without an
-// error. If it doesn't arrive in time, it returns nil and error is non-nil.
-func (conn *Connection) ExpectFrame(layers Layers, timeout time.Duration) (Layers, error) {
- deadline := time.Now().Add(timeout)
- var errs error
- for {
- var gotLayers Layers
- if timeout = time.Until(deadline); timeout > 0 {
- gotLayers = conn.recvFrame(timeout)
- }
- if gotLayers == nil {
- if errs == nil {
- return nil, fmt.Errorf("got no frames matching %v during %s", layers, timeout)
- }
- return nil, fmt.Errorf("got no frames matching %v during %s: got %w", layers, timeout, errs)
- }
- if conn.match(layers, gotLayers) {
- for i, s := range conn.layerStates {
- if err := s.received(gotLayers[i]); err != nil {
- conn.t.Fatal(err)
- }
- }
- return gotLayers, nil
- }
- errs = multierr.Combine(errs, &layersError{got: gotLayers, want: conn.incoming(gotLayers)})
- }
-}
-
-// Drain drains the sniffer's receive buffer by receiving packets until there's
-// nothing else to receive.
-func (conn *Connection) Drain() {
- conn.sniffer.Drain()
-}
-
-// TCPIPv4 maintains the state for all the layers in a TCP/IPv4 connection.
-type TCPIPv4 Connection
-
-// NewTCPIPv4 creates a new TCPIPv4 connection with reasonable defaults.
-func NewTCPIPv4(t *testing.T, outgoingTCP, incomingTCP TCP) TCPIPv4 {
- etherState, err := newEtherState(Ether{}, Ether{})
- if err != nil {
- t.Fatalf("can't make etherState: %s", err)
- }
- ipv4State, err := newIPv4State(IPv4{}, IPv4{})
- if err != nil {
- t.Fatalf("can't make ipv4State: %s", err)
- }
- tcpState, err := newTCPState(unix.AF_INET, outgoingTCP, incomingTCP)
- if err != nil {
- t.Fatalf("can't make tcpState: %s", err)
- }
- injector, err := NewInjector(t)
- if err != nil {
- t.Fatalf("can't make injector: %s", err)
- }
- sniffer, err := NewSniffer(t)
- if err != nil {
- t.Fatalf("can't make sniffer: %s", err)
- }
-
- return TCPIPv4{
- layerStates: []layerState{etherState, ipv4State, tcpState},
- injector: injector,
- sniffer: sniffer,
- t: t,
- }
-}
-
-// Connect performs a TCP 3-way handshake. The input Connection should have a
-// final TCP Layer.
-func (conn *TCPIPv4) Connect() {
- conn.t.Helper()
-
- // Send the SYN.
- conn.Send(TCP{Flags: Uint8(header.TCPFlagSyn)})
-
- // Wait for the SYN-ACK.
- synAck, err := conn.Expect(TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
- if err != nil {
- conn.t.Fatalf("didn't get synack during handshake: %s", err)
- }
- conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck
-
- // Send an ACK.
- conn.Send(TCP{Flags: Uint8(header.TCPFlagAck)})
-}
-
-// ConnectWithOptions performs a TCP 3-way handshake with given TCP options.
-// The input Connection should have a final TCP Layer.
-func (conn *TCPIPv4) ConnectWithOptions(options []byte) {
- conn.t.Helper()
-
- // Send the SYN.
- conn.Send(TCP{Flags: Uint8(header.TCPFlagSyn), Options: options})
-
- // Wait for the SYN-ACK.
- synAck, err := conn.Expect(TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
- if err != nil {
- conn.t.Fatalf("didn't get synack during handshake: %s", err)
- }
- conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck
-
- // Send an ACK.
- conn.Send(TCP{Flags: Uint8(header.TCPFlagAck)})
-}
-
-// ExpectData is a convenient method that expects a Layer and the Layer after
-// it. If it doens't arrive in time, it returns nil.
-func (conn *TCPIPv4) ExpectData(tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) {
- expected := make([]Layer, len(conn.layerStates))
- expected[len(expected)-1] = tcp
- if payload != nil {
- expected = append(expected, payload)
- }
- return (*Connection)(conn).ExpectFrame(expected, timeout)
-}
-
-// ExpectNextData attempts to receive the next incoming segment for the
-// connection and expects that to match the given layers.
-//
-// It differs from ExpectData() in that here we are only interested in the next
-// received segment, while ExpectData() can receive multiple segments for the
-// connection until there is a match with given layers or a timeout.
-func (conn *TCPIPv4) ExpectNextData(tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) {
- // Receive the first incoming TCP segment for this connection.
- got, err := conn.ExpectData(&TCP{}, nil, timeout)
- if err != nil {
- return nil, err
- }
-
- expected := make([]Layer, len(conn.layerStates))
- expected[len(expected)-1] = tcp
- if payload != nil {
- expected = append(expected, payload)
- tcp.SeqNum = Uint32(uint32(*conn.RemoteSeqNum()) - uint32(payload.Length()))
- }
- if !(*Connection)(conn).match(expected, got) {
- return nil, fmt.Errorf("next frame is not matching %s during %s: got %s", expected, timeout, got)
- }
- return got, nil
-}
-
-// Send a packet with reasonable defaults. Potentially override the TCP layer in
-// the connection with the provided layer and add additionLayers.
-func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) {
- (*Connection)(conn).send(Layers{&tcp}, additionalLayers...)
-}
-
-// Close frees associated resources held by the TCPIPv4 connection.
-func (conn *TCPIPv4) Close() {
- (*Connection)(conn).Close()
-}
-
-// Expect expects a frame with the TCP layer matching the provided TCP within
-// the timeout specified. If it doesn't arrive in time, an error is returned.
-func (conn *TCPIPv4) Expect(tcp TCP, timeout time.Duration) (*TCP, error) {
- layer, err := (*Connection)(conn).Expect(&tcp, timeout)
- if layer == nil {
- return nil, err
- }
- gotTCP, ok := layer.(*TCP)
- if !ok {
- conn.t.Fatalf("expected %s to be TCP", layer)
- }
- return gotTCP, err
-}
-
-func (conn *TCPIPv4) tcpState() *tcpState {
- state, ok := conn.layerStates[2].(*tcpState)
- if !ok {
- conn.t.Fatalf("got transport-layer state type=%T, expected tcpState", conn.layerStates[2])
- }
- return state
-}
-
-func (conn *TCPIPv4) ipv4State() *ipv4State {
- state, ok := conn.layerStates[1].(*ipv4State)
- if !ok {
- conn.t.Fatalf("expected network-layer state type=%T, expected ipv4State", conn.layerStates[1])
- }
- return state
-}
-
-// RemoteSeqNum returns the next expected sequence number from the DUT.
-func (conn *TCPIPv4) RemoteSeqNum() *seqnum.Value {
- return conn.tcpState().remoteSeqNum
-}
-
-// LocalSeqNum returns the next sequence number to send from the testbench.
-func (conn *TCPIPv4) LocalSeqNum() *seqnum.Value {
- return conn.tcpState().localSeqNum
-}
-
-// SynAck returns the SynAck that was part of the handshake.
-func (conn *TCPIPv4) SynAck() *TCP {
- return conn.tcpState().synAck
-}
-
-// LocalAddr gets the local socket address of this connection.
-func (conn *TCPIPv4) LocalAddr() *unix.SockaddrInet4 {
- sa := &unix.SockaddrInet4{Port: int(*conn.tcpState().out.SrcPort)}
- copy(sa.Addr[:], *conn.ipv4State().out.SrcAddr)
- return sa
-}
-
-// Drain drains the sniffer's receive buffer by receiving packets until there's
-// nothing else to receive.
-func (conn *TCPIPv4) Drain() {
- conn.sniffer.Drain()
-}
-
-// IPv6Conn maintains the state for all the layers in a IPv6 connection.
-type IPv6Conn Connection
-
-// NewIPv6Conn creates a new IPv6Conn connection with reasonable defaults.
-func NewIPv6Conn(t *testing.T, outgoingIPv6, incomingIPv6 IPv6) IPv6Conn {
- etherState, err := newEtherState(Ether{}, Ether{})
- if err != nil {
- t.Fatalf("can't make EtherState: %s", err)
- }
- ipv6State, err := newIPv6State(outgoingIPv6, incomingIPv6)
- if err != nil {
- t.Fatalf("can't make IPv6State: %s", err)
- }
-
- injector, err := NewInjector(t)
- if err != nil {
- t.Fatalf("can't make injector: %s", err)
- }
- sniffer, err := NewSniffer(t)
- if err != nil {
- t.Fatalf("can't make sniffer: %s", err)
- }
-
- return IPv6Conn{
- layerStates: []layerState{etherState, ipv6State},
- injector: injector,
- sniffer: sniffer,
- t: t,
- }
-}
-
-// Send sends a frame with ipv6 overriding the IPv6 layer defaults and
-// additionalLayers added after it.
-func (conn *IPv6Conn) Send(ipv6 IPv6, additionalLayers ...Layer) {
- (*Connection)(conn).send(Layers{&ipv6}, additionalLayers...)
-}
-
-// Close to clean up any resources held.
-func (conn *IPv6Conn) Close() {
- (*Connection)(conn).Close()
-}
-
-// ExpectFrame expects a frame that matches the provided Layers within the
-// timeout specified. If it doesn't arrive in time, an error is returned.
-func (conn *IPv6Conn) ExpectFrame(frame Layers, timeout time.Duration) (Layers, error) {
- return (*Connection)(conn).ExpectFrame(frame, timeout)
-}
-
-// UDPIPv4 maintains the state for all the layers in a UDP/IPv4 connection.
-type UDPIPv4 Connection
-
-// NewUDPIPv4 creates a new UDPIPv4 connection with reasonable defaults.
-func NewUDPIPv4(t *testing.T, outgoingUDP, incomingUDP UDP) UDPIPv4 {
- etherState, err := newEtherState(Ether{}, Ether{})
- if err != nil {
- t.Fatalf("can't make etherState: %s", err)
- }
- ipv4State, err := newIPv4State(IPv4{}, IPv4{})
- if err != nil {
- t.Fatalf("can't make ipv4State: %s", err)
- }
- udpState, err := newUDPState(unix.AF_INET, outgoingUDP, incomingUDP)
- if err != nil {
- t.Fatalf("can't make udpState: %s", err)
- }
- injector, err := NewInjector(t)
- if err != nil {
- t.Fatalf("can't make injector: %s", err)
- }
- sniffer, err := NewSniffer(t)
- if err != nil {
- t.Fatalf("can't make sniffer: %s", err)
- }
-
- return UDPIPv4{
- layerStates: []layerState{etherState, ipv4State, udpState},
- injector: injector,
- sniffer: sniffer,
- t: t,
- }
-}
-
-func (conn *UDPIPv4) udpState() *udpState {
- state, ok := conn.layerStates[2].(*udpState)
- if !ok {
- conn.t.Fatalf("got transport-layer state type=%T, expected udpState", conn.layerStates[2])
- }
- return state
-}
-
-func (conn *UDPIPv4) ipv4State() *ipv4State {
- state, ok := conn.layerStates[1].(*ipv4State)
- if !ok {
- conn.t.Fatalf("got network-layer state type=%T, expected ipv4State", conn.layerStates[1])
- }
- return state
-}
-
-// LocalAddr gets the local socket address of this connection.
-func (conn *UDPIPv4) LocalAddr() *unix.SockaddrInet4 {
- sa := &unix.SockaddrInet4{Port: int(*conn.udpState().out.SrcPort)}
- copy(sa.Addr[:], *conn.ipv4State().out.SrcAddr)
- return sa
-}
-
-// Send sends a packet with reasonable defaults, potentially overriding the UDP
-// layer and adding additionLayers.
-func (conn *UDPIPv4) Send(udp UDP, additionalLayers ...Layer) {
- (*Connection)(conn).send(Layers{&udp}, additionalLayers...)
-}
-
-// SendIP sends a packet with reasonable defaults, potentially overriding the
-// UDP and IPv4 headers and adding additionLayers.
-func (conn *UDPIPv4) SendIP(ip IPv4, udp UDP, additionalLayers ...Layer) {
- (*Connection)(conn).send(Layers{&ip, &udp}, additionalLayers...)
-}
-
-// Expect expects a frame with the UDP layer matching the provided UDP within
-// the timeout specified. If it doesn't arrive in time, an error is returned.
-func (conn *UDPIPv4) Expect(udp UDP, timeout time.Duration) (*UDP, error) {
- conn.t.Helper()
- layer, err := (*Connection)(conn).Expect(&udp, timeout)
- if layer == nil {
- return nil, err
- }
- gotUDP, ok := layer.(*UDP)
- if !ok {
- conn.t.Fatalf("expected %s to be UDP", layer)
- }
- return gotUDP, err
-}
-
-// ExpectData is a convenient method that expects a Layer and the Layer after
-// it. If it doens't arrive in time, it returns nil.
-func (conn *UDPIPv4) ExpectData(udp UDP, payload Payload, timeout time.Duration) (Layers, error) {
- conn.t.Helper()
- expected := make([]Layer, len(conn.layerStates))
- expected[len(expected)-1] = &udp
- if payload.length() != 0 {
- expected = append(expected, &payload)
- }
- return (*Connection)(conn).ExpectFrame(expected, timeout)
-}
-
-// Close frees associated resources held by the UDPIPv4 connection.
-func (conn *UDPIPv4) Close() {
- (*Connection)(conn).Close()
-}
-
-// Drain drains the sniffer's receive buffer by receiving packets until there's
-// nothing else to receive.
-func (conn *UDPIPv4) Drain() {
- conn.sniffer.Drain()
-}
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
deleted file mode 100644
index 2a2afecb5..000000000
--- a/test/packetimpact/testbench/dut.go
+++ /dev/null
@@ -1,658 +0,0 @@
-// Copyright 2020 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 testbench
-
-import (
- "context"
- "flag"
- "net"
- "strconv"
- "syscall"
- "testing"
-
- pb "gvisor.dev/gvisor/test/packetimpact/proto/posix_server_go_proto"
-
- "golang.org/x/sys/unix"
- "google.golang.org/grpc"
- "google.golang.org/grpc/keepalive"
-)
-
-// DUT communicates with the DUT to force it to make POSIX calls.
-type DUT struct {
- t *testing.T
- conn *grpc.ClientConn
- posixServer POSIXClient
-}
-
-// NewDUT creates a new connection with the DUT over gRPC.
-func NewDUT(t *testing.T) DUT {
- flag.Parse()
- if err := genPseudoFlags(); err != nil {
- t.Fatal("generating psuedo flags:", err)
- }
-
- posixServerAddress := POSIXServerIP + ":" + strconv.Itoa(POSIXServerPort)
- conn, err := grpc.Dial(posixServerAddress, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{Timeout: RPCKeepalive}))
- if err != nil {
- t.Fatalf("failed to grpc.Dial(%s): %s", posixServerAddress, err)
- }
- posixServer := NewPOSIXClient(conn)
- return DUT{
- t: t,
- conn: conn,
- posixServer: posixServer,
- }
-}
-
-// TearDown closes the underlying connection.
-func (dut *DUT) TearDown() {
- dut.conn.Close()
-}
-
-func (dut *DUT) sockaddrToProto(sa unix.Sockaddr) *pb.Sockaddr {
- dut.t.Helper()
- switch s := sa.(type) {
- case *unix.SockaddrInet4:
- return &pb.Sockaddr{
- Sockaddr: &pb.Sockaddr_In{
- In: &pb.SockaddrIn{
- Family: unix.AF_INET,
- Port: uint32(s.Port),
- Addr: s.Addr[:],
- },
- },
- }
- case *unix.SockaddrInet6:
- return &pb.Sockaddr{
- Sockaddr: &pb.Sockaddr_In6{
- In6: &pb.SockaddrIn6{
- Family: unix.AF_INET6,
- Port: uint32(s.Port),
- Flowinfo: 0,
- ScopeId: s.ZoneId,
- Addr: s.Addr[:],
- },
- },
- }
- }
- dut.t.Fatalf("can't parse Sockaddr: %+v", sa)
- return nil
-}
-
-func (dut *DUT) protoToSockaddr(sa *pb.Sockaddr) unix.Sockaddr {
- dut.t.Helper()
- switch s := sa.Sockaddr.(type) {
- case *pb.Sockaddr_In:
- ret := unix.SockaddrInet4{
- Port: int(s.In.GetPort()),
- }
- copy(ret.Addr[:], s.In.GetAddr())
- return &ret
- case *pb.Sockaddr_In6:
- ret := unix.SockaddrInet6{
- Port: int(s.In6.GetPort()),
- ZoneId: s.In6.GetScopeId(),
- }
- copy(ret.Addr[:], s.In6.GetAddr())
- }
- dut.t.Fatalf("can't parse Sockaddr: %+v", sa)
- return nil
-}
-
-// CreateBoundSocket makes a new socket on the DUT, with type typ and protocol
-// proto, and bound to the IP address addr. Returns the new file descriptor and
-// the port that was selected on the DUT.
-func (dut *DUT) CreateBoundSocket(typ, proto int32, addr net.IP) (int32, uint16) {
- dut.t.Helper()
- var fd int32
- if addr.To4() != nil {
- fd = dut.Socket(unix.AF_INET, typ, proto)
- sa := unix.SockaddrInet4{}
- copy(sa.Addr[:], addr.To4())
- dut.Bind(fd, &sa)
- } else if addr.To16() != nil {
- fd = dut.Socket(unix.AF_INET6, typ, proto)
- sa := unix.SockaddrInet6{}
- copy(sa.Addr[:], addr.To16())
- dut.Bind(fd, &sa)
- } else {
- dut.t.Fatalf("unknown ip addr type for remoteIP")
- }
- sa := dut.GetSockName(fd)
- var port int
- switch s := sa.(type) {
- case *unix.SockaddrInet4:
- port = s.Port
- case *unix.SockaddrInet6:
- port = s.Port
- default:
- dut.t.Fatalf("unknown sockaddr type from getsockname: %t", sa)
- }
- return fd, uint16(port)
-}
-
-// CreateListener makes a new TCP connection. If it fails, the test ends.
-func (dut *DUT) CreateListener(typ, proto, backlog int32) (int32, uint16) {
- fd, remotePort := dut.CreateBoundSocket(typ, proto, net.ParseIP(RemoteIPv4))
- dut.Listen(fd, backlog)
- return fd, remotePort
-}
-
-// All the functions that make gRPC calls to the POSIX service are below, sorted
-// alphabetically.
-
-// Accept calls accept on the DUT and causes a fatal test failure if it doesn't
-// succeed. If more control over the timeout or error handling is needed, use
-// AcceptWithErrno.
-func (dut *DUT) Accept(sockfd int32) (int32, unix.Sockaddr) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- fd, sa, err := dut.AcceptWithErrno(ctx, sockfd)
- if fd < 0 {
- dut.t.Fatalf("failed to accept: %s", err)
- }
- return fd, sa
-}
-
-// AcceptWithErrno calls accept on the DUT.
-func (dut *DUT) AcceptWithErrno(ctx context.Context, sockfd int32) (int32, unix.Sockaddr, error) {
- dut.t.Helper()
- req := pb.AcceptRequest{
- Sockfd: sockfd,
- }
- resp, err := dut.posixServer.Accept(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Accept: %s", err)
- }
- return resp.GetFd(), dut.protoToSockaddr(resp.GetAddr()), syscall.Errno(resp.GetErrno_())
-}
-
-// Bind calls bind on the DUT and causes a fatal test failure if it doesn't
-// succeed. If more control over the timeout or error handling is
-// needed, use BindWithErrno.
-func (dut *DUT) Bind(fd int32, sa unix.Sockaddr) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.BindWithErrno(ctx, fd, sa)
- if ret != 0 {
- dut.t.Fatalf("failed to bind socket: %s", err)
- }
-}
-
-// BindWithErrno calls bind on the DUT.
-func (dut *DUT) BindWithErrno(ctx context.Context, fd int32, sa unix.Sockaddr) (int32, error) {
- dut.t.Helper()
- req := pb.BindRequest{
- Sockfd: fd,
- Addr: dut.sockaddrToProto(sa),
- }
- resp, err := dut.posixServer.Bind(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Bind: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// Close calls close on the DUT and causes a fatal test failure if it doesn't
-// succeed. If more control over the timeout or error handling is needed, use
-// CloseWithErrno.
-func (dut *DUT) Close(fd int32) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.CloseWithErrno(ctx, fd)
- if ret != 0 {
- dut.t.Fatalf("failed to close: %s", err)
- }
-}
-
-// CloseWithErrno calls close on the DUT.
-func (dut *DUT) CloseWithErrno(ctx context.Context, fd int32) (int32, error) {
- dut.t.Helper()
- req := pb.CloseRequest{
- Fd: fd,
- }
- resp, err := dut.posixServer.Close(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Close: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// Connect calls connect on the DUT and causes a fatal test failure if it
-// doesn't succeed. If more control over the timeout or error handling is
-// needed, use ConnectWithErrno.
-func (dut *DUT) Connect(fd int32, sa unix.Sockaddr) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ConnectWithErrno(ctx, fd, sa)
- // Ignore 'operation in progress' error that can be returned when the socket
- // is non-blocking.
- if err != syscall.Errno(unix.EINPROGRESS) && ret != 0 {
- dut.t.Fatalf("failed to connect socket: %s", err)
- }
-}
-
-// ConnectWithErrno calls bind on the DUT.
-func (dut *DUT) ConnectWithErrno(ctx context.Context, fd int32, sa unix.Sockaddr) (int32, error) {
- dut.t.Helper()
- req := pb.ConnectRequest{
- Sockfd: fd,
- Addr: dut.sockaddrToProto(sa),
- }
- resp, err := dut.posixServer.Connect(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Connect: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// Fcntl calls fcntl on the DUT and causes a fatal test failure if it
-// doesn't succeed. If more control over the timeout or error handling is
-// needed, use FcntlWithErrno.
-func (dut *DUT) Fcntl(fd, cmd, arg int32) int32 {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.FcntlWithErrno(ctx, fd, cmd, arg)
- if ret == -1 {
- dut.t.Fatalf("failed to Fcntl: ret=%d, errno=%s", ret, err)
- }
- return ret
-}
-
-// FcntlWithErrno calls fcntl on the DUT.
-func (dut *DUT) FcntlWithErrno(ctx context.Context, fd, cmd, arg int32) (int32, error) {
- dut.t.Helper()
- req := pb.FcntlRequest{
- Fd: fd,
- Cmd: cmd,
- Arg: arg,
- }
- resp, err := dut.posixServer.Fcntl(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Fcntl: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// GetSockName calls getsockname on the DUT and causes a fatal test failure if
-// it doesn't succeed. If more control over the timeout or error handling is
-// needed, use GetSockNameWithErrno.
-func (dut *DUT) GetSockName(sockfd int32) unix.Sockaddr {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, sa, err := dut.GetSockNameWithErrno(ctx, sockfd)
- if ret != 0 {
- dut.t.Fatalf("failed to getsockname: %s", err)
- }
- return sa
-}
-
-// GetSockNameWithErrno calls getsockname on the DUT.
-func (dut *DUT) GetSockNameWithErrno(ctx context.Context, sockfd int32) (int32, unix.Sockaddr, error) {
- dut.t.Helper()
- req := pb.GetSockNameRequest{
- Sockfd: sockfd,
- }
- resp, err := dut.posixServer.GetSockName(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Bind: %s", err)
- }
- return resp.GetRet(), dut.protoToSockaddr(resp.GetAddr()), syscall.Errno(resp.GetErrno_())
-}
-
-func (dut *DUT) getSockOpt(ctx context.Context, sockfd, level, optname, optlen int32, typ pb.GetSockOptRequest_SockOptType) (int32, *pb.SockOptVal, error) {
- dut.t.Helper()
- req := pb.GetSockOptRequest{
- Sockfd: sockfd,
- Level: level,
- Optname: optname,
- Optlen: optlen,
- Type: typ,
- }
- resp, err := dut.posixServer.GetSockOpt(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call GetSockOpt: %s", err)
- }
- optval := resp.GetOptval()
- if optval == nil {
- dut.t.Fatalf("GetSockOpt response does not contain a value")
- }
- return resp.GetRet(), optval, syscall.Errno(resp.GetErrno_())
-}
-
-// GetSockOpt calls getsockopt on the DUT and causes a fatal test failure if it
-// doesn't succeed. If more control over the timeout or error handling is
-// needed, use GetSockOptWithErrno. Because endianess and the width of values
-// might differ between the testbench and DUT architectures, prefer to use a
-// more specific GetSockOptXxx function.
-func (dut *DUT) GetSockOpt(sockfd, level, optname, optlen int32) []byte {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, optval, err := dut.GetSockOptWithErrno(ctx, sockfd, level, optname, optlen)
- if ret != 0 {
- dut.t.Fatalf("failed to GetSockOpt: %s", err)
- }
- return optval
-}
-
-// GetSockOptWithErrno calls getsockopt on the DUT. Because endianess and the
-// width of values might differ between the testbench and DUT architectures,
-// prefer to use a more specific GetSockOptXxxWithErrno function.
-func (dut *DUT) GetSockOptWithErrno(ctx context.Context, sockfd, level, optname, optlen int32) (int32, []byte, error) {
- dut.t.Helper()
- ret, optval, errno := dut.getSockOpt(ctx, sockfd, level, optname, optlen, pb.GetSockOptRequest_BYTES)
- bytesval, ok := optval.Val.(*pb.SockOptVal_Bytesval)
- if !ok {
- dut.t.Fatalf("GetSockOpt got value type: %T, want bytes", optval)
- }
- return ret, bytesval.Bytesval, errno
-}
-
-// GetSockOptInt calls getsockopt on the DUT and causes a fatal test failure
-// if it doesn't succeed. If more control over the int optval or error handling
-// is needed, use GetSockOptIntWithErrno.
-func (dut *DUT) GetSockOptInt(sockfd, level, optname int32) int32 {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, intval, err := dut.GetSockOptIntWithErrno(ctx, sockfd, level, optname)
- if ret != 0 {
- dut.t.Fatalf("failed to GetSockOptInt: %s", err)
- }
- return intval
-}
-
-// GetSockOptIntWithErrno calls getsockopt with an integer optval.
-func (dut *DUT) GetSockOptIntWithErrno(ctx context.Context, sockfd, level, optname int32) (int32, int32, error) {
- dut.t.Helper()
- ret, optval, errno := dut.getSockOpt(ctx, sockfd, level, optname, 0, pb.GetSockOptRequest_INT)
- intval, ok := optval.Val.(*pb.SockOptVal_Intval)
- if !ok {
- dut.t.Fatalf("GetSockOpt got value type: %T, want int", optval)
- }
- return ret, intval.Intval, errno
-}
-
-// GetSockOptTimeval calls getsockopt on the DUT and causes a fatal test failure
-// if it doesn't succeed. If more control over the timeout or error handling is
-// needed, use GetSockOptTimevalWithErrno.
-func (dut *DUT) GetSockOptTimeval(sockfd, level, optname int32) unix.Timeval {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, timeval, err := dut.GetSockOptTimevalWithErrno(ctx, sockfd, level, optname)
- if ret != 0 {
- dut.t.Fatalf("failed to GetSockOptTimeval: %s", err)
- }
- return timeval
-}
-
-// GetSockOptTimevalWithErrno calls getsockopt and returns a timeval.
-func (dut *DUT) GetSockOptTimevalWithErrno(ctx context.Context, sockfd, level, optname int32) (int32, unix.Timeval, error) {
- dut.t.Helper()
- ret, optval, errno := dut.getSockOpt(ctx, sockfd, level, optname, 0, pb.GetSockOptRequest_TIME)
- tv, ok := optval.Val.(*pb.SockOptVal_Timeval)
- if !ok {
- dut.t.Fatalf("GetSockOpt got value type: %T, want timeval", optval)
- }
- timeval := unix.Timeval{
- Sec: tv.Timeval.Seconds,
- Usec: tv.Timeval.Microseconds,
- }
- return ret, timeval, errno
-}
-
-// Listen calls listen on the DUT and causes a fatal test failure if it doesn't
-// succeed. If more control over the timeout or error handling is needed, use
-// ListenWithErrno.
-func (dut *DUT) Listen(sockfd, backlog int32) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ListenWithErrno(ctx, sockfd, backlog)
- if ret != 0 {
- dut.t.Fatalf("failed to listen: %s", err)
- }
-}
-
-// ListenWithErrno calls listen on the DUT.
-func (dut *DUT) ListenWithErrno(ctx context.Context, sockfd, backlog int32) (int32, error) {
- dut.t.Helper()
- req := pb.ListenRequest{
- Sockfd: sockfd,
- Backlog: backlog,
- }
- resp, err := dut.posixServer.Listen(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Listen: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// Send calls send on the DUT and causes a fatal test failure if it doesn't
-// succeed. If more control over the timeout or error handling is needed, use
-// SendWithErrno.
-func (dut *DUT) Send(sockfd int32, buf []byte, flags int32) int32 {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SendWithErrno(ctx, sockfd, buf, flags)
- if ret == -1 {
- dut.t.Fatalf("failed to send: %s", err)
- }
- return ret
-}
-
-// SendWithErrno calls send on the DUT.
-func (dut *DUT) SendWithErrno(ctx context.Context, sockfd int32, buf []byte, flags int32) (int32, error) {
- dut.t.Helper()
- req := pb.SendRequest{
- Sockfd: sockfd,
- Buf: buf,
- Flags: flags,
- }
- resp, err := dut.posixServer.Send(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Send: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// SendTo calls sendto on the DUT and causes a fatal test failure if it doesn't
-// succeed. If more control over the timeout or error handling is needed, use
-// SendToWithErrno.
-func (dut *DUT) SendTo(sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) int32 {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SendToWithErrno(ctx, sockfd, buf, flags, destAddr)
- if ret == -1 {
- dut.t.Fatalf("failed to sendto: %s", err)
- }
- return ret
-}
-
-// SendToWithErrno calls sendto on the DUT.
-func (dut *DUT) SendToWithErrno(ctx context.Context, sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) (int32, error) {
- dut.t.Helper()
- req := pb.SendToRequest{
- Sockfd: sockfd,
- Buf: buf,
- Flags: flags,
- DestAddr: dut.sockaddrToProto(destAddr),
- }
- resp, err := dut.posixServer.SendTo(ctx, &req)
- if err != nil {
- dut.t.Fatalf("faled to call SendTo: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// SetNonBlocking will set O_NONBLOCK flag for fd if nonblocking
-// is true, otherwise it will clear the flag.
-func (dut *DUT) SetNonBlocking(fd int32, nonblocking bool) {
- dut.t.Helper()
- flags := dut.Fcntl(fd, unix.F_GETFL, 0)
- if nonblocking {
- flags |= unix.O_NONBLOCK
- } else {
- flags &= ^unix.O_NONBLOCK
- }
- dut.Fcntl(fd, unix.F_SETFL, flags)
-}
-
-func (dut *DUT) setSockOpt(ctx context.Context, sockfd, level, optname int32, optval *pb.SockOptVal) (int32, error) {
- dut.t.Helper()
- req := pb.SetSockOptRequest{
- Sockfd: sockfd,
- Level: level,
- Optname: optname,
- Optval: optval,
- }
- resp, err := dut.posixServer.SetSockOpt(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call SetSockOpt: %s", err)
- }
- return resp.GetRet(), syscall.Errno(resp.GetErrno_())
-}
-
-// SetSockOpt calls setsockopt on the DUT and causes a fatal test failure if it
-// doesn't succeed. If more control over the timeout or error handling is
-// needed, use SetSockOptWithErrno. Because endianess and the width of values
-// might differ between the testbench and DUT architectures, prefer to use a
-// more specific SetSockOptXxx function.
-func (dut *DUT) SetSockOpt(sockfd, level, optname int32, optval []byte) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptWithErrno(ctx, sockfd, level, optname, optval)
- if ret != 0 {
- dut.t.Fatalf("failed to SetSockOpt: %s", err)
- }
-}
-
-// SetSockOptWithErrno calls setsockopt on the DUT. Because endianess and the
-// width of values might differ between the testbench and DUT architectures,
-// prefer to use a more specific SetSockOptXxxWithErrno function.
-func (dut *DUT) SetSockOptWithErrno(ctx context.Context, sockfd, level, optname int32, optval []byte) (int32, error) {
- dut.t.Helper()
- return dut.setSockOpt(ctx, sockfd, level, optname, &pb.SockOptVal{Val: &pb.SockOptVal_Bytesval{optval}})
-}
-
-// SetSockOptInt calls setsockopt on the DUT and causes a fatal test failure
-// if it doesn't succeed. If more control over the int optval or error handling
-// is needed, use SetSockOptIntWithErrno.
-func (dut *DUT) SetSockOptInt(sockfd, level, optname, optval int32) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptIntWithErrno(ctx, sockfd, level, optname, optval)
- if ret != 0 {
- dut.t.Fatalf("failed to SetSockOptInt: %s", err)
- }
-}
-
-// SetSockOptIntWithErrno calls setsockopt with an integer optval.
-func (dut *DUT) SetSockOptIntWithErrno(ctx context.Context, sockfd, level, optname, optval int32) (int32, error) {
- dut.t.Helper()
- return dut.setSockOpt(ctx, sockfd, level, optname, &pb.SockOptVal{Val: &pb.SockOptVal_Intval{optval}})
-}
-
-// SetSockOptTimeval calls setsockopt on the DUT and causes a fatal test failure
-// if it doesn't succeed. If more control over the timeout or error handling is
-// needed, use SetSockOptTimevalWithErrno.
-func (dut *DUT) SetSockOptTimeval(sockfd, level, optname int32, tv *unix.Timeval) {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptTimevalWithErrno(ctx, sockfd, level, optname, tv)
- if ret != 0 {
- dut.t.Fatalf("failed to SetSockOptTimeval: %s", err)
- }
-}
-
-// SetSockOptTimevalWithErrno calls setsockopt with the timeval converted to
-// bytes.
-func (dut *DUT) SetSockOptTimevalWithErrno(ctx context.Context, sockfd, level, optname int32, tv *unix.Timeval) (int32, error) {
- dut.t.Helper()
- timeval := pb.Timeval{
- Seconds: int64(tv.Sec),
- Microseconds: int64(tv.Usec),
- }
- return dut.setSockOpt(ctx, sockfd, level, optname, &pb.SockOptVal{Val: &pb.SockOptVal_Timeval{&timeval}})
-}
-
-// Socket calls socket on the DUT and returns the file descriptor. If socket
-// fails on the DUT, the test ends.
-func (dut *DUT) Socket(domain, typ, proto int32) int32 {
- dut.t.Helper()
- fd, err := dut.SocketWithErrno(domain, typ, proto)
- if fd < 0 {
- dut.t.Fatalf("failed to create socket: %s", err)
- }
- return fd
-}
-
-// SocketWithErrno calls socket on the DUT and returns the fd and errno.
-func (dut *DUT) SocketWithErrno(domain, typ, proto int32) (int32, error) {
- dut.t.Helper()
- req := pb.SocketRequest{
- Domain: domain,
- Type: typ,
- Protocol: proto,
- }
- ctx := context.Background()
- resp, err := dut.posixServer.Socket(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Socket: %s", err)
- }
- return resp.GetFd(), syscall.Errno(resp.GetErrno_())
-}
-
-// Recv calls recv on the DUT and causes a fatal test failure if it doesn't
-// succeed. If more control over the timeout or error handling is needed, use
-// RecvWithErrno.
-func (dut *DUT) Recv(sockfd, len, flags int32) []byte {
- dut.t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, buf, err := dut.RecvWithErrno(ctx, sockfd, len, flags)
- if ret == -1 {
- dut.t.Fatalf("failed to recv: %s", err)
- }
- return buf
-}
-
-// RecvWithErrno calls recv on the DUT.
-func (dut *DUT) RecvWithErrno(ctx context.Context, sockfd, len, flags int32) (int32, []byte, error) {
- dut.t.Helper()
- req := pb.RecvRequest{
- Sockfd: sockfd,
- Len: len,
- Flags: flags,
- }
- resp, err := dut.posixServer.Recv(ctx, &req)
- if err != nil {
- dut.t.Fatalf("failed to call Recv: %s", err)
- }
- return resp.GetRet(), resp.GetBuf(), syscall.Errno(resp.GetErrno_())
-}
diff --git a/test/packetimpact/testbench/dut_client.go b/test/packetimpact/testbench/dut_client.go
deleted file mode 100644
index d0e68c5da..000000000
--- a/test/packetimpact/testbench/dut_client.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2020 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 testbench
-
-import (
- "google.golang.org/grpc"
- pb "gvisor.dev/gvisor/test/packetimpact/proto/posix_server_go_proto"
-)
-
-// PosixClient is a gRPC client for the Posix service.
-type POSIXClient pb.PosixClient
-
-// NewPOSIXClient makes a new gRPC client for the POSIX service.
-func NewPOSIXClient(c grpc.ClientConnInterface) POSIXClient {
- return pb.NewPosixClient(c)
-}
diff --git a/test/packetimpact/testbench/layers.go b/test/packetimpact/testbench/layers.go
deleted file mode 100644
index 560c4111b..000000000
--- a/test/packetimpact/testbench/layers.go
+++ /dev/null
@@ -1,1248 +0,0 @@
-// Copyright 2020 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 testbench
-
-import (
- "encoding/hex"
- "fmt"
- "reflect"
- "strings"
-
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
- "go.uber.org/multierr"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/buffer"
- "gvisor.dev/gvisor/pkg/tcpip/header"
-)
-
-// Layer is the interface that all encapsulations must implement.
-//
-// A Layer is an encapsulation in a packet, such as TCP, IPv4, IPv6, etc. A
-// Layer contains all the fields of the encapsulation. Each field is a pointer
-// and may be nil.
-type Layer interface {
- fmt.Stringer
-
- // ToBytes converts the Layer into bytes. In places where the Layer's field
- // isn't nil, the value that is pointed to is used. When the field is nil, a
- // reasonable default for the Layer is used. For example, "64" for IPv4 TTL
- // and a calculated checksum for TCP or IP. Some layers require information
- // from the previous or next layers in order to compute a default, such as
- // TCP's checksum or Ethernet's type, so each Layer has a doubly-linked list
- // to the layer's neighbors.
- ToBytes() ([]byte, error)
-
- // match checks if the current Layer matches the provided Layer. If either
- // Layer has a nil in a given field, that field is considered matching.
- // Otherwise, the values pointed to by the fields must match. The LayerBase is
- // ignored.
- match(Layer) bool
-
- // length in bytes of the current encapsulation
- length() int
-
- // next gets a pointer to the encapsulated Layer.
- next() Layer
-
- // prev gets a pointer to the Layer encapsulating this one.
- Prev() Layer
-
- // setNext sets the pointer to the encapsulated Layer.
- setNext(Layer)
-
- // setPrev sets the pointer to the Layer encapsulating this one.
- setPrev(Layer)
-
- // merge overrides the values in the interface with the provided values.
- merge(Layer) error
-}
-
-// LayerBase is the common elements of all layers.
-type LayerBase struct {
- nextLayer Layer
- prevLayer Layer
-}
-
-func (lb *LayerBase) next() Layer {
- return lb.nextLayer
-}
-
-// Prev returns the previous layer.
-func (lb *LayerBase) Prev() Layer {
- return lb.prevLayer
-}
-
-func (lb *LayerBase) setNext(l Layer) {
- lb.nextLayer = l
-}
-
-func (lb *LayerBase) setPrev(l Layer) {
- lb.prevLayer = l
-}
-
-// equalLayer compares that two Layer structs match while ignoring field in
-// which either input has a nil and also ignoring the LayerBase of the inputs.
-func equalLayer(x, y Layer) bool {
- if x == nil || y == nil {
- return true
- }
- // opt ignores comparison pairs where either of the inputs is a nil.
- opt := cmp.FilterValues(func(x, y interface{}) bool {
- for _, l := range []interface{}{x, y} {
- v := reflect.ValueOf(l)
- if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice) && v.IsNil() {
- return true
- }
- }
- return false
- }, cmp.Ignore())
- return cmp.Equal(x, y, opt, cmpopts.IgnoreTypes(LayerBase{}))
-}
-
-// mergeLayer merges y into x. Any fields for which y has a non-nil value, that
-// value overwrite the corresponding fields in x.
-func mergeLayer(x, y Layer) error {
- if y == nil {
- return nil
- }
- if reflect.TypeOf(x) != reflect.TypeOf(y) {
- return fmt.Errorf("can't merge %T into %T", y, x)
- }
- vx := reflect.ValueOf(x).Elem()
- vy := reflect.ValueOf(y).Elem()
- t := vy.Type()
- for i := 0; i < vy.NumField(); i++ {
- t := t.Field(i)
- if t.Anonymous {
- // Ignore the LayerBase in the Layer struct.
- continue
- }
- v := vy.Field(i)
- if v.IsNil() {
- continue
- }
- vx.Field(i).Set(v)
- }
- return nil
-}
-
-func stringLayer(l Layer) string {
- v := reflect.ValueOf(l).Elem()
- t := v.Type()
- var ret []string
- for i := 0; i < v.NumField(); i++ {
- t := t.Field(i)
- if t.Anonymous {
- // Ignore the LayerBase in the Layer struct.
- continue
- }
- v := v.Field(i)
- if v.IsNil() {
- continue
- }
- v = reflect.Indirect(v)
- if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 {
- ret = append(ret, fmt.Sprintf("%s:\n%v", t.Name, hex.Dump(v.Bytes())))
- } else {
- ret = append(ret, fmt.Sprintf("%s:%v", t.Name, v))
- }
- }
- return fmt.Sprintf("&%s{%s}", t, strings.Join(ret, " "))
-}
-
-// Ether can construct and match an ethernet encapsulation.
-type Ether struct {
- LayerBase
- SrcAddr *tcpip.LinkAddress
- DstAddr *tcpip.LinkAddress
- Type *tcpip.NetworkProtocolNumber
-}
-
-func (l *Ether) String() string {
- return stringLayer(l)
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *Ether) ToBytes() ([]byte, error) {
- b := make([]byte, header.EthernetMinimumSize)
- h := header.Ethernet(b)
- fields := &header.EthernetFields{}
- if l.SrcAddr != nil {
- fields.SrcAddr = *l.SrcAddr
- }
- if l.DstAddr != nil {
- fields.DstAddr = *l.DstAddr
- }
- if l.Type != nil {
- fields.Type = *l.Type
- } else {
- switch n := l.next().(type) {
- case *IPv4:
- fields.Type = header.IPv4ProtocolNumber
- case *IPv6:
- fields.Type = header.IPv6ProtocolNumber
- default:
- return nil, fmt.Errorf("ethernet header's next layer is unrecognized: %#v", n)
- }
- }
- h.Encode(fields)
- return h, nil
-}
-
-// LinkAddress is a helper routine that allocates a new tcpip.LinkAddress value
-// to store v and returns a pointer to it.
-func LinkAddress(v tcpip.LinkAddress) *tcpip.LinkAddress {
- return &v
-}
-
-// NetworkProtocolNumber is a helper routine that allocates a new
-// tcpip.NetworkProtocolNumber value to store v and returns a pointer to it.
-func NetworkProtocolNumber(v tcpip.NetworkProtocolNumber) *tcpip.NetworkProtocolNumber {
- return &v
-}
-
-// layerParser parses the input bytes and returns a Layer along with the next
-// layerParser to run. If there is no more parsing to do, the returned
-// layerParser is nil.
-type layerParser func([]byte) (Layer, layerParser)
-
-// parse parses bytes starting with the first layerParser and using successive
-// layerParsers until all the bytes are parsed.
-func parse(parser layerParser, b []byte) Layers {
- var layers Layers
- for {
- var layer Layer
- layer, parser = parser(b)
- layers = append(layers, layer)
- if parser == nil {
- break
- }
- b = b[layer.length():]
- }
- layers.linkLayers()
- return layers
-}
-
-// parseEther parses the bytes assuming that they start with an ethernet header
-// and continues parsing further encapsulations.
-func parseEther(b []byte) (Layer, layerParser) {
- h := header.Ethernet(b)
- ether := Ether{
- SrcAddr: LinkAddress(h.SourceAddress()),
- DstAddr: LinkAddress(h.DestinationAddress()),
- Type: NetworkProtocolNumber(h.Type()),
- }
- var nextParser layerParser
- switch h.Type() {
- case header.IPv4ProtocolNumber:
- nextParser = parseIPv4
- case header.IPv6ProtocolNumber:
- nextParser = parseIPv6
- default:
- // Assume that the rest is a payload.
- nextParser = parsePayload
- }
- return &ether, nextParser
-}
-
-func (l *Ether) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *Ether) length() int {
- return header.EthernetMinimumSize
-}
-
-// merge implements Layer.merge.
-func (l *Ether) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// IPv4 can construct and match an IPv4 encapsulation.
-type IPv4 struct {
- LayerBase
- IHL *uint8
- TOS *uint8
- TotalLength *uint16
- ID *uint16
- Flags *uint8
- FragmentOffset *uint16
- TTL *uint8
- Protocol *uint8
- Checksum *uint16
- SrcAddr *tcpip.Address
- DstAddr *tcpip.Address
-}
-
-func (l *IPv4) String() string {
- return stringLayer(l)
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *IPv4) ToBytes() ([]byte, error) {
- b := make([]byte, header.IPv4MinimumSize)
- h := header.IPv4(b)
- fields := &header.IPv4Fields{
- IHL: 20,
- TOS: 0,
- TotalLength: 0,
- ID: 0,
- Flags: 0,
- FragmentOffset: 0,
- TTL: 64,
- Protocol: 0,
- Checksum: 0,
- SrcAddr: tcpip.Address(""),
- DstAddr: tcpip.Address(""),
- }
- if l.TOS != nil {
- fields.TOS = *l.TOS
- }
- if l.TotalLength != nil {
- fields.TotalLength = *l.TotalLength
- } else {
- fields.TotalLength = uint16(l.length())
- current := l.next()
- for current != nil {
- fields.TotalLength += uint16(current.length())
- current = current.next()
- }
- }
- if l.ID != nil {
- fields.ID = *l.ID
- }
- if l.Flags != nil {
- fields.Flags = *l.Flags
- }
- if l.FragmentOffset != nil {
- fields.FragmentOffset = *l.FragmentOffset
- }
- if l.TTL != nil {
- fields.TTL = *l.TTL
- }
- if l.Protocol != nil {
- fields.Protocol = *l.Protocol
- } else {
- switch n := l.next().(type) {
- case *TCP:
- fields.Protocol = uint8(header.TCPProtocolNumber)
- case *UDP:
- fields.Protocol = uint8(header.UDPProtocolNumber)
- case *ICMPv4:
- fields.Protocol = uint8(header.ICMPv4ProtocolNumber)
- default:
- // TODO(b/150301488): Support more protocols as needed.
- return nil, fmt.Errorf("ipv4 header's next layer is unrecognized: %#v", n)
- }
- }
- if l.SrcAddr != nil {
- fields.SrcAddr = *l.SrcAddr
- }
- if l.DstAddr != nil {
- fields.DstAddr = *l.DstAddr
- }
- if l.Checksum != nil {
- fields.Checksum = *l.Checksum
- }
- h.Encode(fields)
- if l.Checksum == nil {
- h.SetChecksum(^h.CalculateChecksum())
- }
- return h, nil
-}
-
-// Uint16 is a helper routine that allocates a new
-// uint16 value to store v and returns a pointer to it.
-func Uint16(v uint16) *uint16 {
- return &v
-}
-
-// Uint8 is a helper routine that allocates a new
-// uint8 value to store v and returns a pointer to it.
-func Uint8(v uint8) *uint8 {
- return &v
-}
-
-// Address is a helper routine that allocates a new tcpip.Address value to store
-// v and returns a pointer to it.
-func Address(v tcpip.Address) *tcpip.Address {
- return &v
-}
-
-// parseIPv4 parses the bytes assuming that they start with an ipv4 header and
-// continues parsing further encapsulations.
-func parseIPv4(b []byte) (Layer, layerParser) {
- h := header.IPv4(b)
- tos, _ := h.TOS()
- ipv4 := IPv4{
- IHL: Uint8(h.HeaderLength()),
- TOS: &tos,
- TotalLength: Uint16(h.TotalLength()),
- ID: Uint16(h.ID()),
- Flags: Uint8(h.Flags()),
- FragmentOffset: Uint16(h.FragmentOffset()),
- TTL: Uint8(h.TTL()),
- Protocol: Uint8(h.Protocol()),
- Checksum: Uint16(h.Checksum()),
- SrcAddr: Address(h.SourceAddress()),
- DstAddr: Address(h.DestinationAddress()),
- }
- var nextParser layerParser
- switch h.TransportProtocol() {
- case header.TCPProtocolNumber:
- nextParser = parseTCP
- case header.UDPProtocolNumber:
- nextParser = parseUDP
- case header.ICMPv4ProtocolNumber:
- nextParser = parseICMPv4
- default:
- // Assume that the rest is a payload.
- nextParser = parsePayload
- }
- return &ipv4, nextParser
-}
-
-func (l *IPv4) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *IPv4) length() int {
- if l.IHL == nil {
- return header.IPv4MinimumSize
- }
- return int(*l.IHL)
-}
-
-// merge implements Layer.merge.
-func (l *IPv4) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// IPv6 can construct and match an IPv6 encapsulation.
-type IPv6 struct {
- LayerBase
- TrafficClass *uint8
- FlowLabel *uint32
- PayloadLength *uint16
- NextHeader *uint8
- HopLimit *uint8
- SrcAddr *tcpip.Address
- DstAddr *tcpip.Address
-}
-
-func (l *IPv6) String() string {
- return stringLayer(l)
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *IPv6) ToBytes() ([]byte, error) {
- b := make([]byte, header.IPv6MinimumSize)
- h := header.IPv6(b)
- fields := &header.IPv6Fields{
- HopLimit: 64,
- }
- if l.TrafficClass != nil {
- fields.TrafficClass = *l.TrafficClass
- }
- if l.FlowLabel != nil {
- fields.FlowLabel = *l.FlowLabel
- }
- if l.PayloadLength != nil {
- fields.PayloadLength = *l.PayloadLength
- } else {
- for current := l.next(); current != nil; current = current.next() {
- fields.PayloadLength += uint16(current.length())
- }
- }
- if l.NextHeader != nil {
- fields.NextHeader = *l.NextHeader
- } else {
- switch n := l.next().(type) {
- case *TCP:
- fields.NextHeader = uint8(header.TCPProtocolNumber)
- case *UDP:
- fields.NextHeader = uint8(header.UDPProtocolNumber)
- case *ICMPv6:
- fields.NextHeader = uint8(header.ICMPv6ProtocolNumber)
- default:
- // TODO(b/150301488): Support more protocols as needed.
- return nil, fmt.Errorf("ToBytes can't deduce the IPv6 header's next protocol: %#v", n)
- }
- }
- if l.HopLimit != nil {
- fields.HopLimit = *l.HopLimit
- }
- if l.SrcAddr != nil {
- fields.SrcAddr = *l.SrcAddr
- }
- if l.DstAddr != nil {
- fields.DstAddr = *l.DstAddr
- }
- h.Encode(fields)
- return h, nil
-}
-
-// parseIPv6 parses the bytes assuming that they start with an ipv6 header and
-// continues parsing further encapsulations.
-func parseIPv6(b []byte) (Layer, layerParser) {
- h := header.IPv6(b)
- tos, flowLabel := h.TOS()
- ipv6 := IPv6{
- TrafficClass: &tos,
- FlowLabel: &flowLabel,
- PayloadLength: Uint16(h.PayloadLength()),
- NextHeader: Uint8(h.NextHeader()),
- HopLimit: Uint8(h.HopLimit()),
- SrcAddr: Address(h.SourceAddress()),
- DstAddr: Address(h.DestinationAddress()),
- }
- var nextParser layerParser
- switch h.TransportProtocol() {
- case header.TCPProtocolNumber:
- nextParser = parseTCP
- case header.UDPProtocolNumber:
- nextParser = parseUDP
- case header.ICMPv6ProtocolNumber:
- nextParser = parseICMPv6
- default:
- // Assume that the rest is a payload.
- nextParser = parsePayload
- }
- return &ipv6, nextParser
-}
-
-func (l *IPv6) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *IPv6) length() int {
- return header.IPv6MinimumSize
-}
-
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
-func (l *IPv6) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// ICMPv6 can construct and match an ICMPv6 encapsulation.
-type ICMPv6 struct {
- LayerBase
- Type *header.ICMPv6Type
- Code *byte
- Checksum *uint16
- NDPPayload []byte
-}
-
-func (l *ICMPv6) String() string {
- // TODO(eyalsoha): Do something smarter here when *l.Type is ParameterProblem?
- // We could parse the contents of the Payload as if it were an IPv6 packet.
- return stringLayer(l)
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *ICMPv6) ToBytes() ([]byte, error) {
- b := make([]byte, header.ICMPv6HeaderSize+len(l.NDPPayload))
- h := header.ICMPv6(b)
- if l.Type != nil {
- h.SetType(*l.Type)
- }
- if l.Code != nil {
- h.SetCode(*l.Code)
- }
- copy(h.NDPPayload(), l.NDPPayload)
- if l.Checksum != nil {
- h.SetChecksum(*l.Checksum)
- } else {
- ipv6 := l.Prev().(*IPv6)
- h.SetChecksum(header.ICMPv6Checksum(h, *ipv6.SrcAddr, *ipv6.DstAddr, buffer.VectorisedView{}))
- }
- return h, nil
-}
-
-// ICMPv6Type is a helper routine that allocates a new ICMPv6Type value to store
-// v and returns a pointer to it.
-func ICMPv6Type(v header.ICMPv6Type) *header.ICMPv6Type {
- return &v
-}
-
-// Byte is a helper routine that allocates a new byte value to store
-// v and returns a pointer to it.
-func Byte(v byte) *byte {
- return &v
-}
-
-// parseICMPv6 parses the bytes assuming that they start with an ICMPv6 header.
-func parseICMPv6(b []byte) (Layer, layerParser) {
- h := header.ICMPv6(b)
- icmpv6 := ICMPv6{
- Type: ICMPv6Type(h.Type()),
- Code: Byte(h.Code()),
- Checksum: Uint16(h.Checksum()),
- NDPPayload: h.NDPPayload(),
- }
- return &icmpv6, nil
-}
-
-func (l *ICMPv6) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *ICMPv6) length() int {
- return header.ICMPv6HeaderSize + len(l.NDPPayload)
-}
-
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
-func (l *ICMPv6) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// ICMPv4Type is a helper routine that allocates a new header.ICMPv4Type value
-// to store t and returns a pointer to it.
-func ICMPv4Type(t header.ICMPv4Type) *header.ICMPv4Type {
- return &t
-}
-
-// ICMPv4 can construct and match an ICMPv4 encapsulation.
-type ICMPv4 struct {
- LayerBase
- Type *header.ICMPv4Type
- Code *uint8
- Checksum *uint16
-}
-
-func (l *ICMPv4) String() string {
- return stringLayer(l)
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *ICMPv4) ToBytes() ([]byte, error) {
- b := make([]byte, header.ICMPv4MinimumSize)
- h := header.ICMPv4(b)
- if l.Type != nil {
- h.SetType(*l.Type)
- }
- if l.Code != nil {
- h.SetCode(byte(*l.Code))
- }
- if l.Checksum != nil {
- h.SetChecksum(*l.Checksum)
- return h, nil
- }
- payload, err := payload(l)
- if err != nil {
- return nil, err
- }
- h.SetChecksum(header.ICMPv4Checksum(h, payload))
- return h, nil
-}
-
-// parseICMPv4 parses the bytes as an ICMPv4 header, returning a Layer and a
-// parser for the encapsulated payload.
-func parseICMPv4(b []byte) (Layer, layerParser) {
- h := header.ICMPv4(b)
- icmpv4 := ICMPv4{
- Type: ICMPv4Type(h.Type()),
- Code: Uint8(h.Code()),
- Checksum: Uint16(h.Checksum()),
- }
- return &icmpv4, parsePayload
-}
-
-func (l *ICMPv4) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *ICMPv4) length() int {
- return header.ICMPv4MinimumSize
-}
-
-// merge overrides the values in l with the values from other but only in fields
-// where the value is not nil.
-func (l *ICMPv4) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// TCP can construct and match a TCP encapsulation.
-type TCP struct {
- LayerBase
- SrcPort *uint16
- DstPort *uint16
- SeqNum *uint32
- AckNum *uint32
- DataOffset *uint8
- Flags *uint8
- WindowSize *uint16
- Checksum *uint16
- UrgentPointer *uint16
- Options []byte
-}
-
-func (l *TCP) String() string {
- return stringLayer(l)
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *TCP) ToBytes() ([]byte, error) {
- b := make([]byte, l.length())
- h := header.TCP(b)
- if l.SrcPort != nil {
- h.SetSourcePort(*l.SrcPort)
- }
- if l.DstPort != nil {
- h.SetDestinationPort(*l.DstPort)
- }
- if l.SeqNum != nil {
- h.SetSequenceNumber(*l.SeqNum)
- }
- if l.AckNum != nil {
- h.SetAckNumber(*l.AckNum)
- }
- if l.DataOffset != nil {
- h.SetDataOffset(*l.DataOffset)
- } else {
- h.SetDataOffset(uint8(l.length()))
- }
- if l.Flags != nil {
- h.SetFlags(*l.Flags)
- }
- if l.WindowSize != nil {
- h.SetWindowSize(*l.WindowSize)
- } else {
- h.SetWindowSize(32768)
- }
- if l.UrgentPointer != nil {
- h.SetUrgentPoiner(*l.UrgentPointer)
- }
- copy(b[header.TCPMinimumSize:], l.Options)
- header.AddTCPOptionPadding(b[header.TCPMinimumSize:], len(l.Options))
- if l.Checksum != nil {
- h.SetChecksum(*l.Checksum)
- return h, nil
- }
- if err := setTCPChecksum(&h, l); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// totalLength returns the length of the provided layer and all following
-// layers.
-func totalLength(l Layer) int {
- var totalLength int
- for ; l != nil; l = l.next() {
- totalLength += l.length()
- }
- return totalLength
-}
-
-// payload returns a buffer.VectorisedView of l's payload.
-func payload(l Layer) (buffer.VectorisedView, error) {
- var payloadBytes buffer.VectorisedView
- for current := l.next(); current != nil; current = current.next() {
- payload, err := current.ToBytes()
- if err != nil {
- return buffer.VectorisedView{}, fmt.Errorf("can't get bytes for next header: %s", payload)
- }
- payloadBytes.AppendView(payload)
- }
- return payloadBytes, nil
-}
-
-// layerChecksum calculates the checksum of the Layer header, including the
-// peusdeochecksum of the layer before it and all the bytes after it.
-func layerChecksum(l Layer, protoNumber tcpip.TransportProtocolNumber) (uint16, error) {
- totalLength := uint16(totalLength(l))
- var xsum uint16
- switch s := l.Prev().(type) {
- case *IPv4:
- xsum = header.PseudoHeaderChecksum(protoNumber, *s.SrcAddr, *s.DstAddr, totalLength)
- default:
- // TODO(b/150301488): Support more protocols, like IPv6.
- return 0, fmt.Errorf("can't get src and dst addr from previous layer: %#v", s)
- }
- payloadBytes, err := payload(l)
- if err != nil {
- return 0, err
- }
- xsum = header.ChecksumVV(payloadBytes, xsum)
- return xsum, nil
-}
-
-// setTCPChecksum calculates the checksum of the TCP header and sets it in h.
-func setTCPChecksum(h *header.TCP, tcp *TCP) error {
- h.SetChecksum(0)
- xsum, err := layerChecksum(tcp, header.TCPProtocolNumber)
- if err != nil {
- return err
- }
- h.SetChecksum(^h.CalculateChecksum(xsum))
- return nil
-}
-
-// Uint32 is a helper routine that allocates a new
-// uint32 value to store v and returns a pointer to it.
-func Uint32(v uint32) *uint32 {
- return &v
-}
-
-// parseTCP parses the bytes assuming that they start with a tcp header and
-// continues parsing further encapsulations.
-func parseTCP(b []byte) (Layer, layerParser) {
- h := header.TCP(b)
- tcp := TCP{
- SrcPort: Uint16(h.SourcePort()),
- DstPort: Uint16(h.DestinationPort()),
- SeqNum: Uint32(h.SequenceNumber()),
- AckNum: Uint32(h.AckNumber()),
- DataOffset: Uint8(h.DataOffset()),
- Flags: Uint8(h.Flags()),
- WindowSize: Uint16(h.WindowSize()),
- Checksum: Uint16(h.Checksum()),
- UrgentPointer: Uint16(h.UrgentPointer()),
- Options: b[header.TCPMinimumSize:h.DataOffset()],
- }
- return &tcp, parsePayload
-}
-
-func (l *TCP) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *TCP) length() int {
- if l.DataOffset == nil {
- // TCP header including the options must end on a 32-bit
- // boundary; the user could potentially give us a slice
- // whose length is not a multiple of 4 bytes, so we have
- // to do the alignment here.
- optlen := (len(l.Options) + 3) & ^3
- return header.TCPMinimumSize + optlen
- }
- return int(*l.DataOffset)
-}
-
-// merge implements Layer.merge.
-func (l *TCP) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// UDP can construct and match a UDP encapsulation.
-type UDP struct {
- LayerBase
- SrcPort *uint16
- DstPort *uint16
- Length *uint16
- Checksum *uint16
-}
-
-func (l *UDP) String() string {
- return stringLayer(l)
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *UDP) ToBytes() ([]byte, error) {
- b := make([]byte, header.UDPMinimumSize)
- h := header.UDP(b)
- if l.SrcPort != nil {
- h.SetSourcePort(*l.SrcPort)
- }
- if l.DstPort != nil {
- h.SetDestinationPort(*l.DstPort)
- }
- if l.Length != nil {
- h.SetLength(*l.Length)
- } else {
- h.SetLength(uint16(totalLength(l)))
- }
- if l.Checksum != nil {
- h.SetChecksum(*l.Checksum)
- return h, nil
- }
- if err := setUDPChecksum(&h, l); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// setUDPChecksum calculates the checksum of the UDP header and sets it in h.
-func setUDPChecksum(h *header.UDP, udp *UDP) error {
- h.SetChecksum(0)
- xsum, err := layerChecksum(udp, header.UDPProtocolNumber)
- if err != nil {
- return err
- }
- h.SetChecksum(^h.CalculateChecksum(xsum))
- return nil
-}
-
-// parseUDP parses the bytes assuming that they start with a udp header and
-// returns the parsed layer and the next parser to use.
-func parseUDP(b []byte) (Layer, layerParser) {
- h := header.UDP(b)
- udp := UDP{
- SrcPort: Uint16(h.SourcePort()),
- DstPort: Uint16(h.DestinationPort()),
- Length: Uint16(h.Length()),
- Checksum: Uint16(h.Checksum()),
- }
- return &udp, parsePayload
-}
-
-func (l *UDP) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *UDP) length() int {
- return header.UDPMinimumSize
-}
-
-// merge implements Layer.merge.
-func (l *UDP) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// Payload has bytes beyond OSI layer 4.
-type Payload struct {
- LayerBase
- Bytes []byte
-}
-
-func (l *Payload) String() string {
- return stringLayer(l)
-}
-
-// parsePayload parses the bytes assuming that they start with a payload and
-// continue to the end. There can be no further encapsulations.
-func parsePayload(b []byte) (Layer, layerParser) {
- payload := Payload{
- Bytes: b,
- }
- return &payload, nil
-}
-
-// ToBytes implements Layer.ToBytes.
-func (l *Payload) ToBytes() ([]byte, error) {
- return l.Bytes, nil
-}
-
-// Length returns payload byte length.
-func (l *Payload) Length() int {
- return l.length()
-}
-
-func (l *Payload) match(other Layer) bool {
- return equalLayer(l, other)
-}
-
-func (l *Payload) length() int {
- return len(l.Bytes)
-}
-
-// merge implements Layer.merge.
-func (l *Payload) merge(other Layer) error {
- return mergeLayer(l, other)
-}
-
-// Layers is an array of Layer and supports similar functions to Layer.
-type Layers []Layer
-
-// linkLayers sets the linked-list ponters in ls.
-func (ls *Layers) linkLayers() {
- for i, l := range *ls {
- if i > 0 {
- l.setPrev((*ls)[i-1])
- } else {
- l.setPrev(nil)
- }
- if i+1 < len(*ls) {
- l.setNext((*ls)[i+1])
- } else {
- l.setNext(nil)
- }
- }
-}
-
-// ToBytes converts the Layers into bytes. It creates a linked list of the Layer
-// structs and then concatentates the output of ToBytes on each Layer.
-func (ls *Layers) ToBytes() ([]byte, error) {
- ls.linkLayers()
- outBytes := []byte{}
- for _, l := range *ls {
- layerBytes, err := l.ToBytes()
- if err != nil {
- return nil, err
- }
- outBytes = append(outBytes, layerBytes...)
- }
- return outBytes, nil
-}
-
-func (ls *Layers) match(other Layers) bool {
- if len(*ls) > len(other) {
- return false
- }
- for i, l := range *ls {
- if !equalLayer(l, other[i]) {
- return false
- }
- }
- return true
-}
-
-// layerDiff stores the diffs for each field along with the label for the Layer.
-// If rows is nil, that means that there was no diff.
-type layerDiff struct {
- label string
- rows []layerDiffRow
-}
-
-// layerDiffRow stores the fields and corresponding values for two got and want
-// layers. If the value was nil then the string stored is the empty string.
-type layerDiffRow struct {
- field, got, want string
-}
-
-// diffLayer extracts all differing fields between two layers.
-func diffLayer(got, want Layer) []layerDiffRow {
- vGot := reflect.ValueOf(got).Elem()
- vWant := reflect.ValueOf(want).Elem()
- if vGot.Type() != vWant.Type() {
- return nil
- }
- t := vGot.Type()
- var result []layerDiffRow
- for i := 0; i < t.NumField(); i++ {
- t := t.Field(i)
- if t.Anonymous {
- // Ignore the LayerBase in the Layer struct.
- continue
- }
- vGot := vGot.Field(i)
- vWant := vWant.Field(i)
- gotString := ""
- if !vGot.IsNil() {
- gotString = fmt.Sprint(reflect.Indirect(vGot))
- }
- wantString := ""
- if !vWant.IsNil() {
- wantString = fmt.Sprint(reflect.Indirect(vWant))
- }
- result = append(result, layerDiffRow{t.Name, gotString, wantString})
- }
- return result
-}
-
-// layerType returns a concise string describing the type of the Layer, like
-// "TCP", or "IPv6".
-func layerType(l Layer) string {
- return reflect.TypeOf(l).Elem().Name()
-}
-
-// diff compares Layers and returns a representation of the difference. Each
-// Layer in the Layers is pairwise compared. If an element in either is nil, it
-// is considered a match with the other Layer. If two Layers have differing
-// types, they don't match regardless of the contents. If two Layers have the
-// same type then the fields in the Layer are pairwise compared. Fields that are
-// nil always match. Two non-nil fields only match if they point to equal
-// values. diff returns an empty string if and only if *ls and other match.
-func (ls *Layers) diff(other Layers) string {
- var allDiffs []layerDiff
- // Check the cases where one list is longer than the other, where one or both
- // elements are nil, where the sides have different types, and where the sides
- // have the same type.
- for i := 0; i < len(*ls) || i < len(other); i++ {
- if i >= len(*ls) {
- // Matching ls against other where other is longer than ls. missing
- // matches everything so we just include a label without any rows. Having
- // no rows is a sign that there was no diff.
- allDiffs = append(allDiffs, layerDiff{
- label: "missing matches " + layerType(other[i]),
- })
- continue
- }
-
- if i >= len(other) {
- // Matching ls against other where ls is longer than other. missing
- // matches everything so we just include a label without any rows. Having
- // no rows is a sign that there was no diff.
- allDiffs = append(allDiffs, layerDiff{
- label: layerType((*ls)[i]) + " matches missing",
- })
- continue
- }
-
- if (*ls)[i] == nil && other[i] == nil {
- // Matching ls against other where both elements are nil. nil matches
- // everything so we just include a label without any rows. Having no rows
- // is a sign that there was no diff.
- allDiffs = append(allDiffs, layerDiff{
- label: "nil matches nil",
- })
- continue
- }
-
- if (*ls)[i] == nil {
- // Matching ls against other where the element in ls is nil. nil matches
- // everything so we just include a label without any rows. Having no rows
- // is a sign that there was no diff.
- allDiffs = append(allDiffs, layerDiff{
- label: "nil matches " + layerType(other[i]),
- })
- continue
- }
-
- if other[i] == nil {
- // Matching ls against other where the element in other is nil. nil
- // matches everything so we just include a label without any rows. Having
- // no rows is a sign that there was no diff.
- allDiffs = append(allDiffs, layerDiff{
- label: layerType((*ls)[i]) + " matches nil",
- })
- continue
- }
-
- if reflect.TypeOf((*ls)[i]) == reflect.TypeOf(other[i]) {
- // Matching ls against other where both elements have the same type. Match
- // each field pairwise and only report a diff if there is a mismatch,
- // which is only when both sides are non-nil and have differring values.
- diff := diffLayer((*ls)[i], other[i])
- var layerDiffRows []layerDiffRow
- for _, d := range diff {
- if d.got == "" || d.want == "" || d.got == d.want {
- continue
- }
- layerDiffRows = append(layerDiffRows, layerDiffRow{
- d.field,
- d.got,
- d.want,
- })
- }
- if len(layerDiffRows) > 0 {
- allDiffs = append(allDiffs, layerDiff{
- label: layerType((*ls)[i]),
- rows: layerDiffRows,
- })
- } else {
- allDiffs = append(allDiffs, layerDiff{
- label: layerType((*ls)[i]) + " matches " + layerType(other[i]),
- // Having no rows is a sign that there was no diff.
- })
- }
- continue
- }
- // Neither side is nil and the types are different, so we'll display one
- // side then the other.
- allDiffs = append(allDiffs, layerDiff{
- label: layerType((*ls)[i]) + " doesn't match " + layerType(other[i]),
- })
- diff := diffLayer((*ls)[i], (*ls)[i])
- layerDiffRows := []layerDiffRow{}
- for _, d := range diff {
- if len(d.got) == 0 {
- continue
- }
- layerDiffRows = append(layerDiffRows, layerDiffRow{
- d.field,
- d.got,
- "",
- })
- }
- allDiffs = append(allDiffs, layerDiff{
- label: layerType((*ls)[i]),
- rows: layerDiffRows,
- })
-
- layerDiffRows = []layerDiffRow{}
- diff = diffLayer(other[i], other[i])
- for _, d := range diff {
- if len(d.want) == 0 {
- continue
- }
- layerDiffRows = append(layerDiffRows, layerDiffRow{
- d.field,
- "",
- d.want,
- })
- }
- allDiffs = append(allDiffs, layerDiff{
- label: layerType(other[i]),
- rows: layerDiffRows,
- })
- }
-
- output := ""
- // These are for output formatting.
- maxLabelLen, maxFieldLen, maxGotLen, maxWantLen := 0, 0, 0, 0
- foundOne := false
- for _, l := range allDiffs {
- if len(l.label) > maxLabelLen && len(l.rows) > 0 {
- maxLabelLen = len(l.label)
- }
- if l.rows != nil {
- foundOne = true
- }
- for _, r := range l.rows {
- if len(r.field) > maxFieldLen {
- maxFieldLen = len(r.field)
- }
- if l := len(fmt.Sprint(r.got)); l > maxGotLen {
- maxGotLen = l
- }
- if l := len(fmt.Sprint(r.want)); l > maxWantLen {
- maxWantLen = l
- }
- }
- }
- if !foundOne {
- return ""
- }
- for _, l := range allDiffs {
- if len(l.rows) == 0 {
- output += "(" + l.label + ")\n"
- continue
- }
- for i, r := range l.rows {
- var label string
- if i == 0 {
- label = l.label + ":"
- }
- output += fmt.Sprintf(
- "%*s %*s %*v %*v\n",
- maxLabelLen+1, label,
- maxFieldLen+1, r.field+":",
- maxGotLen, r.got,
- maxWantLen, r.want,
- )
- }
- }
- return output
-}
-
-// merge merges the other Layers into ls. If the other Layers is longer, those
-// additional Layer structs are added to ls. The errors from merging are
-// collected and returned.
-func (ls *Layers) merge(other Layers) error {
- var errs error
- for i, o := range other {
- if i < len(*ls) {
- errs = multierr.Combine(errs, (*ls)[i].merge(o))
- } else {
- *ls = append(*ls, o)
- }
- }
- return errs
-}
diff --git a/test/packetimpact/testbench/layers_test.go b/test/packetimpact/testbench/layers_test.go
deleted file mode 100644
index c7f00e70d..000000000
--- a/test/packetimpact/testbench/layers_test.go
+++ /dev/null
@@ -1,507 +0,0 @@
-// Copyright 2020 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 testbench
-
-import (
- "bytes"
- "net"
- "testing"
-
- "github.com/mohae/deepcopy"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/header"
-)
-
-func TestLayerMatch(t *testing.T) {
- var nilPayload *Payload
- noPayload := &Payload{}
- emptyPayload := &Payload{Bytes: []byte{}}
- fullPayload := &Payload{Bytes: []byte{1, 2, 3}}
- emptyTCP := &TCP{SrcPort: Uint16(1234), LayerBase: LayerBase{nextLayer: emptyPayload}}
- fullTCP := &TCP{SrcPort: Uint16(1234), LayerBase: LayerBase{nextLayer: fullPayload}}
- for _, tt := range []struct {
- a, b Layer
- want bool
- }{
- {nilPayload, nilPayload, true},
- {nilPayload, noPayload, true},
- {nilPayload, emptyPayload, true},
- {nilPayload, fullPayload, true},
- {noPayload, noPayload, true},
- {noPayload, emptyPayload, true},
- {noPayload, fullPayload, true},
- {emptyPayload, emptyPayload, true},
- {emptyPayload, fullPayload, false},
- {fullPayload, fullPayload, true},
- {emptyTCP, fullTCP, true},
- } {
- if got := tt.a.match(tt.b); got != tt.want {
- t.Errorf("%s.match(%s) = %t, want %t", tt.a, tt.b, got, tt.want)
- }
- if got := tt.b.match(tt.a); got != tt.want {
- t.Errorf("%s.match(%s) = %t, want %t", tt.b, tt.a, got, tt.want)
- }
- }
-}
-
-func TestLayerMergeMismatch(t *testing.T) {
- tcp := &TCP{}
- otherTCP := &TCP{}
- ipv4 := &IPv4{}
- ether := &Ether{}
- for _, tt := range []struct {
- a, b Layer
- success bool
- }{
- {tcp, tcp, true},
- {tcp, otherTCP, true},
- {tcp, ipv4, false},
- {tcp, ether, false},
- {tcp, nil, true},
-
- {otherTCP, otherTCP, true},
- {otherTCP, ipv4, false},
- {otherTCP, ether, false},
- {otherTCP, nil, true},
-
- {ipv4, ipv4, true},
- {ipv4, ether, false},
- {ipv4, nil, true},
-
- {ether, ether, true},
- {ether, nil, true},
- } {
- if err := tt.a.merge(tt.b); (err == nil) != tt.success {
- t.Errorf("%s.merge(%s) got %s, wanted the opposite", tt.a, tt.b, err)
- }
- if tt.b != nil {
- if err := tt.b.merge(tt.a); (err == nil) != tt.success {
- t.Errorf("%s.merge(%s) got %s, wanted the opposite", tt.b, tt.a, err)
- }
- }
- }
-}
-
-func TestLayerMerge(t *testing.T) {
- zero := Uint32(0)
- one := Uint32(1)
- two := Uint32(2)
- empty := []byte{}
- foo := []byte("foo")
- bar := []byte("bar")
- for _, tt := range []struct {
- a, b Layer
- want Layer
- }{
- {&TCP{AckNum: nil}, &TCP{AckNum: nil}, &TCP{AckNum: nil}},
- {&TCP{AckNum: nil}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
- {&TCP{AckNum: nil}, &TCP{AckNum: one}, &TCP{AckNum: one}},
- {&TCP{AckNum: nil}, &TCP{AckNum: two}, &TCP{AckNum: two}},
- {&TCP{AckNum: nil}, nil, &TCP{AckNum: nil}},
-
- {&TCP{AckNum: zero}, &TCP{AckNum: nil}, &TCP{AckNum: zero}},
- {&TCP{AckNum: zero}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
- {&TCP{AckNum: zero}, &TCP{AckNum: one}, &TCP{AckNum: one}},
- {&TCP{AckNum: zero}, &TCP{AckNum: two}, &TCP{AckNum: two}},
- {&TCP{AckNum: zero}, nil, &TCP{AckNum: zero}},
-
- {&TCP{AckNum: one}, &TCP{AckNum: nil}, &TCP{AckNum: one}},
- {&TCP{AckNum: one}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
- {&TCP{AckNum: one}, &TCP{AckNum: one}, &TCP{AckNum: one}},
- {&TCP{AckNum: one}, &TCP{AckNum: two}, &TCP{AckNum: two}},
- {&TCP{AckNum: one}, nil, &TCP{AckNum: one}},
-
- {&TCP{AckNum: two}, &TCP{AckNum: nil}, &TCP{AckNum: two}},
- {&TCP{AckNum: two}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
- {&TCP{AckNum: two}, &TCP{AckNum: one}, &TCP{AckNum: one}},
- {&TCP{AckNum: two}, &TCP{AckNum: two}, &TCP{AckNum: two}},
- {&TCP{AckNum: two}, nil, &TCP{AckNum: two}},
-
- {&Payload{Bytes: nil}, &Payload{Bytes: nil}, &Payload{Bytes: nil}},
- {&Payload{Bytes: nil}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
- {&Payload{Bytes: nil}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
- {&Payload{Bytes: nil}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
- {&Payload{Bytes: nil}, nil, &Payload{Bytes: nil}},
-
- {&Payload{Bytes: empty}, &Payload{Bytes: nil}, &Payload{Bytes: empty}},
- {&Payload{Bytes: empty}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
- {&Payload{Bytes: empty}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
- {&Payload{Bytes: empty}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
- {&Payload{Bytes: empty}, nil, &Payload{Bytes: empty}},
-
- {&Payload{Bytes: foo}, &Payload{Bytes: nil}, &Payload{Bytes: foo}},
- {&Payload{Bytes: foo}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
- {&Payload{Bytes: foo}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
- {&Payload{Bytes: foo}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
- {&Payload{Bytes: foo}, nil, &Payload{Bytes: foo}},
-
- {&Payload{Bytes: bar}, &Payload{Bytes: nil}, &Payload{Bytes: bar}},
- {&Payload{Bytes: bar}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
- {&Payload{Bytes: bar}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
- {&Payload{Bytes: bar}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
- {&Payload{Bytes: bar}, nil, &Payload{Bytes: bar}},
- } {
- a := deepcopy.Copy(tt.a).(Layer)
- if err := a.merge(tt.b); err != nil {
- t.Errorf("%s.merge(%s) = %s, wanted nil", tt.a, tt.b, err)
- continue
- }
- if a.String() != tt.want.String() {
- t.Errorf("%s.merge(%s) merge result got %s, want %s", tt.a, tt.b, a, tt.want)
- }
- }
-}
-
-func TestLayerStringFormat(t *testing.T) {
- for _, tt := range []struct {
- name string
- l Layer
- want string
- }{
- {
- name: "TCP",
- l: &TCP{
- SrcPort: Uint16(34785),
- DstPort: Uint16(47767),
- SeqNum: Uint32(3452155723),
- AckNum: Uint32(2596996163),
- DataOffset: Uint8(5),
- Flags: Uint8(20),
- WindowSize: Uint16(64240),
- Checksum: Uint16(0x2e2b),
- },
- want: "&testbench.TCP{" +
- "SrcPort:34785 " +
- "DstPort:47767 " +
- "SeqNum:3452155723 " +
- "AckNum:2596996163 " +
- "DataOffset:5 " +
- "Flags:20 " +
- "WindowSize:64240 " +
- "Checksum:11819" +
- "}",
- },
- {
- name: "UDP",
- l: &UDP{
- SrcPort: Uint16(34785),
- DstPort: Uint16(47767),
- Length: Uint16(12),
- },
- want: "&testbench.UDP{" +
- "SrcPort:34785 " +
- "DstPort:47767 " +
- "Length:12" +
- "}",
- },
- {
- name: "IPv4",
- l: &IPv4{
- IHL: Uint8(5),
- TOS: Uint8(0),
- TotalLength: Uint16(44),
- ID: Uint16(0),
- Flags: Uint8(2),
- FragmentOffset: Uint16(0),
- TTL: Uint8(64),
- Protocol: Uint8(6),
- Checksum: Uint16(0x2e2b),
- SrcAddr: Address(tcpip.Address([]byte{197, 34, 63, 10})),
- DstAddr: Address(tcpip.Address([]byte{197, 34, 63, 20})),
- },
- want: "&testbench.IPv4{" +
- "IHL:5 " +
- "TOS:0 " +
- "TotalLength:44 " +
- "ID:0 " +
- "Flags:2 " +
- "FragmentOffset:0 " +
- "TTL:64 " +
- "Protocol:6 " +
- "Checksum:11819 " +
- "SrcAddr:197.34.63.10 " +
- "DstAddr:197.34.63.20" +
- "}",
- },
- {
- name: "Ether",
- l: &Ether{
- SrcAddr: LinkAddress(tcpip.LinkAddress([]byte{0x02, 0x42, 0xc5, 0x22, 0x3f, 0x0a})),
- DstAddr: LinkAddress(tcpip.LinkAddress([]byte{0x02, 0x42, 0xc5, 0x22, 0x3f, 0x14})),
- Type: NetworkProtocolNumber(4),
- },
- want: "&testbench.Ether{" +
- "SrcAddr:02:42:c5:22:3f:0a " +
- "DstAddr:02:42:c5:22:3f:14 " +
- "Type:4" +
- "}",
- },
- {
- name: "Payload",
- l: &Payload{
- Bytes: []byte("Hooray for packetimpact."),
- },
- want: "&testbench.Payload{Bytes:\n" +
- "00000000 48 6f 6f 72 61 79 20 66 6f 72 20 70 61 63 6b 65 |Hooray for packe|\n" +
- "00000010 74 69 6d 70 61 63 74 2e |timpact.|\n" +
- "}",
- },
- } {
- t.Run(tt.name, func(t *testing.T) {
- if got := tt.l.String(); got != tt.want {
- t.Errorf("%s.String() = %s, want: %s", tt.name, got, tt.want)
- }
- })
- }
-}
-
-func TestConnectionMatch(t *testing.T) {
- conn := Connection{
- layerStates: []layerState{&etherState{}},
- }
- protoNum0 := tcpip.NetworkProtocolNumber(0)
- protoNum1 := tcpip.NetworkProtocolNumber(1)
- for _, tt := range []struct {
- description string
- override, received Layers
- wantMatch bool
- }{
- {
- description: "shorter override",
- override: []Layer{&Ether{}},
- received: []Layer{&Ether{}, &Payload{Bytes: []byte("hello")}},
- wantMatch: true,
- },
- {
- description: "longer override",
- override: []Layer{&Ether{}, &Payload{Bytes: []byte("hello")}},
- received: []Layer{&Ether{}},
- wantMatch: false,
- },
- {
- description: "ether layer mismatch",
- override: []Layer{&Ether{Type: &protoNum0}},
- received: []Layer{&Ether{Type: &protoNum1}},
- wantMatch: false,
- },
- {
- description: "both nil",
- override: nil,
- received: nil,
- wantMatch: false,
- },
- {
- description: "nil override",
- override: nil,
- received: []Layer{&Ether{}},
- wantMatch: true,
- },
- } {
- t.Run(tt.description, func(t *testing.T) {
- if gotMatch := conn.match(tt.override, tt.received); gotMatch != tt.wantMatch {
- t.Fatalf("conn.match(%s, %s) = %t, want %t", tt.override, tt.received, gotMatch, tt.wantMatch)
- }
- })
- }
-}
-
-func TestLayersDiff(t *testing.T) {
- for _, tt := range []struct {
- x, y Layers
- want string
- }{
- {
- Layers{&Ether{Type: NetworkProtocolNumber(12)}, &TCP{DataOffset: Uint8(5), SeqNum: Uint32(5)}},
- Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
- "Ether: Type: 12 13\n" +
- " TCP: SeqNum: 5 6\n" +
- " DataOffset: 5 7\n",
- },
- {
- Layers{&Ether{Type: NetworkProtocolNumber(12)}, &UDP{SrcPort: Uint16(123)}},
- Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
- "Ether: Type: 12 13\n" +
- "(UDP doesn't match TCP)\n" +
- " UDP: SrcPort: 123 \n" +
- " TCP: SeqNum: 6\n" +
- " DataOffset: 7\n",
- },
- {
- Layers{&UDP{SrcPort: Uint16(123)}},
- Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
- "(UDP doesn't match Ether)\n" +
- " UDP: SrcPort: 123 \n" +
- "Ether: Type: 13\n" +
- "(missing matches TCP)\n",
- },
- {
- Layers{nil, &UDP{SrcPort: Uint16(123)}},
- Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
- "(nil matches Ether)\n" +
- "(UDP doesn't match TCP)\n" +
- "UDP: SrcPort: 123 \n" +
- "TCP: SeqNum: 6\n" +
- " DataOffset: 7\n",
- },
- {
- Layers{&Ether{Type: NetworkProtocolNumber(13)}, &IPv4{IHL: Uint8(4)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
- Layers{&Ether{Type: NetworkProtocolNumber(13)}, &IPv4{IHL: Uint8(6)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
- "(Ether matches Ether)\n" +
- "IPv4: IHL: 4 6\n" +
- "(TCP matches TCP)\n",
- },
- {
- Layers{&Payload{Bytes: []byte("foo")}},
- Layers{&Payload{Bytes: []byte("bar")}},
- "Payload: Bytes: [102 111 111] [98 97 114]\n",
- },
- {
- Layers{&Payload{Bytes: []byte("")}},
- Layers{&Payload{}},
- "",
- },
- {
- Layers{&Payload{Bytes: []byte("")}},
- Layers{&Payload{Bytes: []byte("")}},
- "",
- },
- {
- Layers{&UDP{}},
- Layers{&TCP{}},
- "(UDP doesn't match TCP)\n" +
- "(UDP)\n" +
- "(TCP)\n",
- },
- } {
- if got := tt.x.diff(tt.y); got != tt.want {
- t.Errorf("%s.diff(%s) = %q, want %q", tt.x, tt.y, got, tt.want)
- }
- if tt.x.match(tt.y) != (tt.x.diff(tt.y) == "") {
- t.Errorf("match and diff of %s and %s disagree", tt.x, tt.y)
- }
- if tt.y.match(tt.x) != (tt.y.diff(tt.x) == "") {
- t.Errorf("match and diff of %s and %s disagree", tt.y, tt.x)
- }
- }
-}
-
-func TestTCPOptions(t *testing.T) {
- for _, tt := range []struct {
- description string
- wantBytes []byte
- wantLayers Layers
- }{
- {
- description: "without payload",
- wantBytes: []byte{
- // IPv4 Header
- 0x45, 0x00, 0x00, 0x2c, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06,
- 0xf9, 0x77, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01,
- // TCP Header
- 0x30, 0x39, 0xd4, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x60, 0x02, 0x20, 0x00, 0xf5, 0x1c, 0x00, 0x00,
- // WindowScale Option
- 0x03, 0x03, 0x02,
- // NOP Option
- 0x00,
- },
- wantLayers: []Layer{
- &IPv4{
- IHL: Uint8(20),
- TOS: Uint8(0),
- TotalLength: Uint16(44),
- ID: Uint16(1),
- Flags: Uint8(0),
- FragmentOffset: Uint16(0),
- TTL: Uint8(64),
- Protocol: Uint8(uint8(header.TCPProtocolNumber)),
- Checksum: Uint16(0xf977),
- SrcAddr: Address(tcpip.Address(net.ParseIP("192.168.0.2").To4())),
- DstAddr: Address(tcpip.Address(net.ParseIP("192.168.0.1").To4())),
- },
- &TCP{
- SrcPort: Uint16(12345),
- DstPort: Uint16(54321),
- SeqNum: Uint32(0),
- AckNum: Uint32(0),
- Flags: Uint8(header.TCPFlagSyn),
- WindowSize: Uint16(8192),
- Checksum: Uint16(0xf51c),
- UrgentPointer: Uint16(0),
- Options: []byte{3, 3, 2, 0},
- },
- &Payload{Bytes: nil},
- },
- },
- {
- description: "with payload",
- wantBytes: []byte{
- // IPv4 header
- 0x45, 0x00, 0x00, 0x37, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06,
- 0xf9, 0x6c, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01,
- // TCP header
- 0x30, 0x39, 0xd4, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x60, 0x02, 0x20, 0x00, 0xe5, 0x21, 0x00, 0x00,
- // WindowScale Option
- 0x03, 0x03, 0x02,
- // NOP Option
- 0x00,
- // Payload: "Sample Data"
- 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
- },
- wantLayers: []Layer{
- &IPv4{
- IHL: Uint8(20),
- TOS: Uint8(0),
- TotalLength: Uint16(55),
- ID: Uint16(1),
- Flags: Uint8(0),
- FragmentOffset: Uint16(0),
- TTL: Uint8(64),
- Protocol: Uint8(uint8(header.TCPProtocolNumber)),
- Checksum: Uint16(0xf96c),
- SrcAddr: Address(tcpip.Address(net.ParseIP("192.168.0.2").To4())),
- DstAddr: Address(tcpip.Address(net.ParseIP("192.168.0.1").To4())),
- },
- &TCP{
- SrcPort: Uint16(12345),
- DstPort: Uint16(54321),
- SeqNum: Uint32(0),
- AckNum: Uint32(0),
- Flags: Uint8(header.TCPFlagSyn),
- WindowSize: Uint16(8192),
- Checksum: Uint16(0xe521),
- UrgentPointer: Uint16(0),
- Options: []byte{3, 3, 2, 0},
- },
- &Payload{Bytes: []byte("Sample Data")},
- },
- },
- } {
- t.Run(tt.description, func(t *testing.T) {
- layers := parse(parseIPv4, tt.wantBytes)
- if !layers.match(tt.wantLayers) {
- t.Fatalf("match failed with diff: %s", layers.diff(tt.wantLayers))
- }
- gotBytes, err := layers.ToBytes()
- if err != nil {
- t.Fatalf("ToBytes() failed on %s: %s", &layers, err)
- }
- if !bytes.Equal(tt.wantBytes, gotBytes) {
- t.Fatalf("mismatching bytes, gotBytes: %x, wantBytes: %x", gotBytes, tt.wantBytes)
- }
- })
- }
-}
diff --git a/test/packetimpact/testbench/rawsockets.go b/test/packetimpact/testbench/rawsockets.go
deleted file mode 100644
index 278229b7e..000000000
--- a/test/packetimpact/testbench/rawsockets.go
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2020 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 testbench
-
-import (
- "encoding/binary"
- "fmt"
- "math"
- "net"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/usermem"
-)
-
-// Sniffer can sniff raw packets on the wire.
-type Sniffer struct {
- t *testing.T
- fd int
-}
-
-func htons(x uint16) uint16 {
- buf := [2]byte{}
- binary.BigEndian.PutUint16(buf[:], x)
- return usermem.ByteOrder.Uint16(buf[:])
-}
-
-// NewSniffer creates a Sniffer connected to *device.
-func NewSniffer(t *testing.T) (Sniffer, error) {
- snifferFd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL)))
- if err != nil {
- return Sniffer{}, err
- }
- if err := unix.SetsockoptInt(snifferFd, unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, 1); err != nil {
- t.Fatalf("can't set sockopt SO_RCVBUFFORCE to 1: %s", err)
- }
- if err := unix.SetsockoptInt(snifferFd, unix.SOL_SOCKET, unix.SO_RCVBUF, 1e7); err != nil {
- t.Fatalf("can't setsockopt SO_RCVBUF to 10M: %s", err)
- }
- return Sniffer{
- t: t,
- fd: snifferFd,
- }, nil
-}
-
-// maxReadSize should be large enough for the maximum frame size in bytes. If a
-// packet too large for the buffer arrives, the test will get a fatal error.
-const maxReadSize int = 65536
-
-// Recv tries to read one frame until the timeout is up.
-func (s *Sniffer) Recv(timeout time.Duration) []byte {
- deadline := time.Now().Add(timeout)
- for {
- timeout = deadline.Sub(time.Now())
- if timeout <= 0 {
- return nil
- }
- whole, frac := math.Modf(timeout.Seconds())
- tv := unix.Timeval{
- Sec: int64(whole),
- Usec: int64(frac * float64(time.Microsecond/time.Second)),
- }
-
- if err := unix.SetsockoptTimeval(s.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv); err != nil {
- s.t.Fatalf("can't setsockopt SO_RCVTIMEO: %s", err)
- }
-
- buf := make([]byte, maxReadSize)
- nread, _, err := unix.Recvfrom(s.fd, buf, unix.MSG_TRUNC)
- if err == unix.EINTR || err == unix.EAGAIN {
- // There was a timeout.
- continue
- }
- if err != nil {
- s.t.Fatalf("can't read: %s", err)
- }
- if nread > maxReadSize {
- s.t.Fatalf("received a truncated frame of %d bytes", nread)
- }
- return buf[:nread]
- }
-}
-
-// Drain drains the Sniffer's socket receive buffer by receiving until there's
-// nothing else to receive.
-func (s *Sniffer) Drain() {
- s.t.Helper()
- flags, err := unix.FcntlInt(uintptr(s.fd), unix.F_GETFL, 0)
- if err != nil {
- s.t.Fatalf("failed to get sniffer socket fd flags: %s", err)
- }
- if _, err := unix.FcntlInt(uintptr(s.fd), unix.F_SETFL, flags|unix.O_NONBLOCK); err != nil {
- s.t.Fatalf("failed to make sniffer socket non-blocking: %s", err)
- }
- for {
- buf := make([]byte, maxReadSize)
- _, _, err := unix.Recvfrom(s.fd, buf, unix.MSG_TRUNC)
- if err == unix.EINTR || err == unix.EAGAIN || err == unix.EWOULDBLOCK {
- break
- }
- }
- if _, err := unix.FcntlInt(uintptr(s.fd), unix.F_SETFL, flags); err != nil {
- s.t.Fatalf("failed to restore sniffer socket fd flags: %s", err)
- }
-}
-
-// close the socket that Sniffer is using.
-func (s *Sniffer) close() error {
- if err := unix.Close(s.fd); err != nil {
- return fmt.Errorf("can't close sniffer socket: %w", err)
- }
- s.fd = -1
- return nil
-}
-
-// Injector can inject raw frames.
-type Injector struct {
- t *testing.T
- fd int
-}
-
-// NewInjector creates a new injector on *device.
-func NewInjector(t *testing.T) (Injector, error) {
- ifInfo, err := net.InterfaceByName(Device)
- if err != nil {
- return Injector{}, err
- }
-
- var haddr [8]byte
- copy(haddr[:], ifInfo.HardwareAddr)
- sa := unix.SockaddrLinklayer{
- Protocol: unix.ETH_P_IP,
- Ifindex: ifInfo.Index,
- Halen: uint8(len(ifInfo.HardwareAddr)),
- Addr: haddr,
- }
-
- injectFd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL)))
- if err != nil {
- return Injector{}, err
- }
- if err := unix.Bind(injectFd, &sa); err != nil {
- return Injector{}, err
- }
- return Injector{
- t: t,
- fd: injectFd,
- }, nil
-}
-
-// Send a raw frame.
-func (i *Injector) Send(b []byte) {
- if _, err := unix.Write(i.fd, b); err != nil {
- i.t.Fatalf("can't write: %s of len %d", err, len(b))
- }
-}
-
-// close the underlying socket.
-func (i *Injector) close() error {
- if err := unix.Close(i.fd); err != nil {
- return fmt.Errorf("can't close sniffer socket: %w", err)
- }
- i.fd = -1
- return nil
-}
diff --git a/test/packetimpact/testbench/testbench.go b/test/packetimpact/testbench/testbench.go
deleted file mode 100644
index d64f32a5b..000000000
--- a/test/packetimpact/testbench/testbench.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2020 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 testbench
-
-import (
- "flag"
- "fmt"
- "math/rand"
- "net"
- "os/exec"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/test/packetimpact/netdevs"
-)
-
-var (
- // DUTType is the type of device under test.
- DUTType = ""
- // Device is the local device on the test network.
- Device = ""
- // LocalIPv4 is the local IPv4 address on the test network.
- LocalIPv4 = ""
- // LocalIPv6 is the local IPv6 address on the test network.
- LocalIPv6 = ""
- // LocalMAC is the local MAC address on the test network.
- LocalMAC = ""
- // POSIXServerIP is the POSIX server's IP address on the control network.
- POSIXServerIP = ""
- // POSIXServerPort is the UDP port the POSIX server is bound to on the
- // control network.
- POSIXServerPort = 40000
- // RemoteIPv4 is the DUT's IPv4 address on the test network.
- RemoteIPv4 = ""
- // RemoteIPv6 is the DUT's IPv6 address on the test network.
- RemoteIPv6 = ""
- // RemoteMAC is the DUT's MAC address on the test network.
- RemoteMAC = ""
- // RPCKeepalive is the gRPC keepalive.
- RPCKeepalive = 10 * time.Second
- // RPCTimeout is the gRPC timeout.
- RPCTimeout = 100 * time.Millisecond
-)
-
-// RegisterFlags defines flags and associates them with the package-level
-// exported variables above. It should be called by tests in their init
-// functions.
-func RegisterFlags(fs *flag.FlagSet) {
- fs.StringVar(&POSIXServerIP, "posix_server_ip", POSIXServerIP, "ip address to listen to for UDP commands")
- fs.IntVar(&POSIXServerPort, "posix_server_port", POSIXServerPort, "port to listen to for UDP commands")
- fs.DurationVar(&RPCTimeout, "rpc_timeout", RPCTimeout, "gRPC timeout")
- fs.DurationVar(&RPCKeepalive, "rpc_keepalive", RPCKeepalive, "gRPC keepalive")
- fs.StringVar(&LocalIPv4, "local_ipv4", LocalIPv4, "local IPv4 address for test packets")
- fs.StringVar(&RemoteIPv4, "remote_ipv4", RemoteIPv4, "remote IPv4 address for test packets")
- fs.StringVar(&RemoteIPv6, "remote_ipv6", RemoteIPv6, "remote IPv6 address for test packets")
- fs.StringVar(&RemoteMAC, "remote_mac", RemoteMAC, "remote mac address for test packets")
- fs.StringVar(&Device, "device", Device, "local device for test packets")
- fs.StringVar(&DUTType, "dut_type", DUTType, "type of device under test")
-}
-
-// genPseudoFlags populates flag-like global config based on real flags.
-//
-// genPseudoFlags must only be called after flag.Parse.
-func genPseudoFlags() error {
- out, err := exec.Command("ip", "addr", "show").CombinedOutput()
- if err != nil {
- return fmt.Errorf("listing devices: %q: %w", string(out), err)
- }
- devs, err := netdevs.ParseDevices(string(out))
- if err != nil {
- return fmt.Errorf("parsing devices: %w", err)
- }
-
- _, deviceInfo, err := netdevs.FindDeviceByIP(net.ParseIP(LocalIPv4), devs)
- if err != nil {
- return fmt.Errorf("can't find deviceInfo: %w", err)
- }
-
- LocalMAC = deviceInfo.MAC.String()
- LocalIPv6 = deviceInfo.IPv6Addr.String()
-
- return nil
-}
-
-// GenerateRandomPayload generates a random byte slice of the specified length,
-// causing a fatal test failure if it is unable to do so.
-func GenerateRandomPayload(t *testing.T, n int) []byte {
- t.Helper()
- buf := make([]byte, n)
- if _, err := rand.Read(buf); err != nil {
- t.Fatalf("rand.Read(buf) failed: %s", err)
- }
- return buf
-}
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
deleted file mode 100644
index 09e91b832..000000000
--- a/test/packetimpact/tests/BUILD
+++ /dev/null
@@ -1,265 +0,0 @@
-load("//test/packetimpact/runner:defs.bzl", "packetimpact_go_test")
-
-package(
- default_visibility = ["//test/packetimpact:__subpackages__"],
- licenses = ["notice"],
-)
-
-packetimpact_go_test(
- name = "fin_wait2_timeout",
- srcs = ["fin_wait2_timeout_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "ipv4_id_uniqueness",
- srcs = ["ipv4_id_uniqueness_test.go"],
- # TODO(b/157506701) Fix netstack then remove the line below.
- expect_netstack_failure = True,
- deps = [
- "//pkg/abi/linux",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "udp_recv_multicast",
- srcs = ["udp_recv_multicast_test.go"],
- # TODO(b/152813495): Fix netstack then remove the line below.
- expect_netstack_failure = True,
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "udp_icmp_error_propagation",
- srcs = ["udp_icmp_error_propagation_test.go"],
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_reordering",
- srcs = ["tcp_reordering_test.go"],
- # TODO(b/139368047): Fix netstack then remove the line below.
- expect_netstack_failure = True,
- deps = [
- "//pkg/tcpip/header",
- "//pkg/tcpip/seqnum",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_window_shrink",
- srcs = ["tcp_window_shrink_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_zero_window_probe",
- srcs = ["tcp_zero_window_probe_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_zero_window_probe_retransmit",
- srcs = ["tcp_zero_window_probe_retransmit_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_zero_window_probe_usertimeout",
- srcs = ["tcp_zero_window_probe_usertimeout_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_retransmits",
- srcs = ["tcp_retransmits_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_outside_the_window",
- srcs = ["tcp_outside_the_window_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//pkg/tcpip/seqnum",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_noaccept_close_rst",
- srcs = ["tcp_noaccept_close_rst_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_send_window_sizes_piggyback",
- srcs = ["tcp_send_window_sizes_piggyback_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_close_wait_ack",
- srcs = ["tcp_close_wait_ack_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//pkg/tcpip/seqnum",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_paws_mechanism",
- srcs = ["tcp_paws_mechanism_test.go"],
- # TODO(b/156682000): Fix netstack then remove the line below.
- expect_netstack_failure = True,
- deps = [
- "//pkg/tcpip/header",
- "//pkg/tcpip/seqnum",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_user_timeout",
- srcs = ["tcp_user_timeout_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_queue_receive_in_syn_sent",
- srcs = ["tcp_queue_receive_in_syn_sent_test.go"],
- # TODO(b/157658105): Fix netstack then remove the line below.
- expect_netstack_failure = True,
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_synsent_reset",
- srcs = ["tcp_synsent_reset_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_synrcvd_reset",
- srcs = ["tcp_synrcvd_reset_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_splitseg_mss",
- srcs = ["tcp_splitseg_mss_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_cork_mss",
- srcs = ["tcp_cork_mss_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "tcp_handshake_window_size",
- srcs = ["tcp_handshake_window_size_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "icmpv6_param_problem",
- srcs = ["icmpv6_param_problem_test.go"],
- # TODO(b/153485026): Fix netstack then remove the line below.
- expect_netstack_failure = True,
- deps = [
- "//pkg/tcpip",
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-packetimpact_go_test(
- name = "udp_send_recv_dgram",
- srcs = ["udp_send_recv_dgram_test.go"],
- deps = [
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/test/packetimpact/tests/fin_wait2_timeout_test.go b/test/packetimpact/tests/fin_wait2_timeout_test.go
deleted file mode 100644
index 407565078..000000000
--- a/test/packetimpact/tests/fin_wait2_timeout_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2020 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 fin_wait2_timeout_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestFinWait2Timeout(t *testing.T) {
- for _, tt := range []struct {
- description string
- linger2 bool
- }{
- {"WithLinger2", true},
- {"WithoutLinger2", false},
- } {
- t.Run(tt.description, func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
- conn.Connect()
-
- acceptFd, _ := dut.Accept(listenFd)
- if tt.linger2 {
- tv := unix.Timeval{Sec: 1, Usec: 0}
- dut.SetSockOptTimeval(acceptFd, unix.SOL_TCP, unix.TCP_LINGER2, &tv)
- }
- dut.Close(acceptFd)
-
- if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected a FIN-ACK within 1 second but got none: %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-
- time.Sleep(5 * time.Second)
- conn.Drain()
-
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if tt.linger2 {
- if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
- t.Fatalf("expected a RST packet within a second but got none: %s", err)
- }
- } else {
- if got, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, 10*time.Second); got != nil || err == nil {
- t.Fatalf("expected no RST packets within ten seconds but got one: %s", got)
- }
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/icmpv6_param_problem_test.go b/test/packetimpact/tests/icmpv6_param_problem_test.go
deleted file mode 100644
index 4d1d9a7f5..000000000
--- a/test/packetimpact/tests/icmpv6_param_problem_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2020 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 icmpv6_param_problem_test
-
-import (
- "encoding/binary"
- "flag"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestICMPv6ParamProblemTest sends a packet with a bad next header. The DUT
-// should respond with an ICMPv6 Parameter Problem message.
-func TestICMPv6ParamProblemTest(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- conn := testbench.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{})
- defer conn.Close()
- ipv6 := testbench.IPv6{
- // 254 is reserved and used for experimentation and testing. This should
- // cause an error.
- NextHeader: testbench.Uint8(254),
- }
- icmpv6 := testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
- NDPPayload: []byte("hello world"),
- }
-
- toSend := (*testbench.Connection)(&conn).CreateFrame(testbench.Layers{&ipv6}, &icmpv6)
- (*testbench.Connection)(&conn).SendFrame(toSend)
-
- // Build the expected ICMPv6 payload, which includes an index to the
- // problematic byte and also the problematic packet as described in
- // https://tools.ietf.org/html/rfc4443#page-12 .
- ipv6Sent := toSend[1:]
- expectedPayload, err := ipv6Sent.ToBytes()
- if err != nil {
- t.Fatalf("can't convert %s to bytes: %s", ipv6Sent, err)
- }
-
- // The problematic field is the NextHeader.
- b := make([]byte, 4)
- binary.BigEndian.PutUint32(b, header.IPv6NextHeaderOffset)
- expectedPayload = append(b, expectedPayload...)
- expectedICMPv6 := testbench.ICMPv6{
- Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem),
- NDPPayload: expectedPayload,
- }
-
- paramProblem := testbench.Layers{
- &testbench.Ether{},
- &testbench.IPv6{},
- &expectedICMPv6,
- }
- timeout := time.Second
- if _, err := conn.ExpectFrame(paramProblem, timeout); err != nil {
- t.Errorf("expected %s within %s but got none: %s", paramProblem, timeout, err)
- }
-}
diff --git a/test/packetimpact/tests/ipv4_id_uniqueness_test.go b/test/packetimpact/tests/ipv4_id_uniqueness_test.go
deleted file mode 100644
index 70f6df5e0..000000000
--- a/test/packetimpact/tests/ipv4_id_uniqueness_test.go
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2020 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 ipv4_id_uniqueness_test
-
-import (
- "context"
- "flag"
- "fmt"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func recvTCPSegment(conn *testbench.TCPIPv4, expect *testbench.TCP, expectPayload *testbench.Payload) (uint16, error) {
- layers, err := conn.ExpectData(expect, expectPayload, time.Second)
- if err != nil {
- return 0, fmt.Errorf("failed to receive TCP segment: %s", err)
- }
- if len(layers) < 2 {
- return 0, fmt.Errorf("got packet with layers: %v, expected to have at least 2 layers (link and network)", layers)
- }
- ipv4, ok := layers[1].(*testbench.IPv4)
- if !ok {
- return 0, fmt.Errorf("got network layer: %T, expected: *IPv4", layers[1])
- }
- if *ipv4.Flags&header.IPv4FlagDontFragment != 0 {
- return 0, fmt.Errorf("got IPv4 DF=1, expected DF=0")
- }
- return *ipv4.ID, nil
-}
-
-// RFC 6864 section 4.2 states: "The IPv4 ID of non-atomic datagrams MUST NOT
-// be reused when sending a copy of an earlier non-atomic datagram."
-//
-// This test creates a TCP connection, uses the IP_MTU_DISCOVER socket option
-// to force the DF bit to be 0, and checks that a retransmitted segment has a
-// different IPv4 Identification value than the original segment.
-func TestIPv4RetransmitIdentificationUniqueness(t *testing.T) {
- for _, tc := range []struct {
- name string
- payload []byte
- }{
- {"SmallPayload", []byte("sample data")},
- // 512 bytes is chosen because sending more than this in a single segment
- // causes the retransmission to send less than the original amount.
- {"512BytePayload", testbench.GenerateRandomPayload(t, 512)},
- } {
- t.Run(tc.name, func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
-
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
-
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- remoteFD, _ := dut.Accept(listenFD)
- defer dut.Close(remoteFD)
-
- dut.SetSockOptInt(remoteFD, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- // TODO(b/129291778) The following socket option clears the DF bit on
- // IP packets sent over the socket, and is currently not supported by
- // gVisor. gVisor by default sends packets with DF=0 anyway, so the
- // socket option being not supported does not affect the operation of
- // this test. Once the socket option is supported, the following call
- // can be changed to simply assert success.
- ret, errno := dut.SetSockOptIntWithErrno(context.Background(), remoteFD, unix.IPPROTO_IP, linux.IP_MTU_DISCOVER, linux.IP_PMTUDISC_DONT)
- if ret == -1 && errno != unix.ENOTSUP {
- t.Fatalf("failed to set IP_MTU_DISCOVER socket option to IP_PMTUDISC_DONT: %s", errno)
- }
-
- samplePayload := &testbench.Payload{Bytes: tc.payload}
-
- dut.Send(remoteFD, tc.payload, 0)
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("failed to receive TCP segment sent for RTT calculation: %s", err)
- }
- // Let the DUT estimate RTO with RTT from the DATA-ACK.
- // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
- // we can skip sending this ACK.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-
- dut.Send(remoteFD, tc.payload, 0)
- expectTCP := &testbench.TCP{SeqNum: testbench.Uint32(uint32(*conn.RemoteSeqNum()))}
- originalID, err := recvTCPSegment(&conn, expectTCP, samplePayload)
- if err != nil {
- t.Fatalf("failed to receive TCP segment: %s", err)
- }
-
- retransmitID, err := recvTCPSegment(&conn, expectTCP, samplePayload)
- if err != nil {
- t.Fatalf("failed to receive retransmitted TCP segment: %s", err)
- }
- if originalID == retransmitID {
- t.Fatalf("unexpectedly got retransmitted TCP segment with same IPv4 ID field=%d", originalID)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_close_wait_ack_test.go b/test/packetimpact/tests/tcp_close_wait_ack_test.go
deleted file mode 100644
index 6e7ff41d7..000000000
--- a/test/packetimpact/tests/tcp_close_wait_ack_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2020 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 tcp_close_wait_ack_test
-
-import (
- "flag"
- "fmt"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestCloseWaitAck(t *testing.T) {
- for _, tt := range []struct {
- description string
- makeTestingTCP func(conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP
- seqNumOffset seqnum.Size
- expectAck bool
- }{
- {"OTW", GenerateOTWSeqSegment, 0, false},
- {"OTW", GenerateOTWSeqSegment, 1, true},
- {"OTW", GenerateOTWSeqSegment, 2, true},
- {"ACK", GenerateUnaccACKSegment, 0, false},
- {"ACK", GenerateUnaccACKSegment, 1, true},
- {"ACK", GenerateUnaccACKSegment, 2, true},
- } {
- t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- acceptFd, _ := dut.Accept(listenFd)
-
- // Send a FIN to DUT to intiate the active close
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)})
- gotTCP, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
- }
- windowSize := seqnum.Size(*gotTCP.WindowSize)
-
- // Send a segment with OTW Seq / unacc ACK and expect an ACK back
- conn.Send(tt.makeTestingTCP(&conn, tt.seqNumOffset, windowSize), &testbench.Payload{Bytes: []byte("Sample Data")})
- gotAck, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
- if tt.expectAck && err != nil {
- t.Fatalf("expected an ack but got none: %s", err)
- }
- if !tt.expectAck && gotAck != nil {
- t.Fatalf("expected no ack but got one: %s", gotAck)
- }
-
- // Now let's verify DUT is indeed in CLOSE_WAIT
- dut.Close(acceptFd)
- if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
- t.Fatalf("expected DUT to send a FIN: %s", err)
- }
- // Ack the FIN from DUT
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- // Send some extra data to DUT
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: []byte("Sample Data")})
- if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
- t.Fatalf("expected DUT to send an RST: %s", err)
- }
- })
- }
-}
-
-// This generates an segment with seqnum = RCV.NXT + RCV.WND + seqNumOffset, the
-// generated segment is only acceptable when seqNumOffset is 0, otherwise an ACK
-// is expected from the receiver.
-func GenerateOTWSeqSegment(conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
- lastAcceptable := conn.LocalSeqNum().Add(windowSize)
- otwSeq := uint32(lastAcceptable.Add(seqNumOffset))
- return testbench.TCP{SeqNum: testbench.Uint32(otwSeq), Flags: testbench.Uint8(header.TCPFlagAck)}
-}
-
-// This generates an segment with acknum = SND.NXT + seqNumOffset, the generated
-// segment is only acceptable when seqNumOffset is 0, otherwise an ACK is
-// expected from the receiver.
-func GenerateUnaccACKSegment(conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
- lastAcceptable := conn.RemoteSeqNum()
- unaccAck := uint32(lastAcceptable.Add(seqNumOffset))
- return testbench.TCP{AckNum: testbench.Uint32(unaccAck), Flags: testbench.Uint8(header.TCPFlagAck)}
-}
diff --git a/test/packetimpact/tests/tcp_cork_mss_test.go b/test/packetimpact/tests/tcp_cork_mss_test.go
deleted file mode 100644
index fb8f48629..000000000
--- a/test/packetimpact/tests/tcp_cork_mss_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2020 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 tcp_cork_mss_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestTCPCorkMSS tests for segment coalesce and split as per MSS.
-func TestTCPCorkMSS(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- const mss = uint32(header.TCPDefaultMSS)
- options := make([]byte, header.TCPOptionMSSLength)
- header.EncodeMSSOption(mss, options)
- conn.ConnectWithOptions(options)
-
- acceptFD, _ := dut.Accept(listenFD)
- defer dut.Close(acceptFD)
-
- dut.SetSockOptInt(acceptFD, unix.IPPROTO_TCP, unix.TCP_CORK, 1)
-
- // Let the dut application send 2 small segments to be held up and coalesced
- // until the application sends a larger segment to fill up to > MSS.
- sampleData := []byte("Sample Data")
- dut.Send(acceptFD, sampleData, 0)
- dut.Send(acceptFD, sampleData, 0)
-
- expectedData := sampleData
- expectedData = append(expectedData, sampleData...)
- largeData := make([]byte, mss+1)
- expectedData = append(expectedData, largeData...)
- dut.Send(acceptFD, largeData, 0)
-
- // Expect the segments to be coalesced and sent and capped to MSS.
- expectedPayload := testbench.Payload{Bytes: expectedData[:mss]}
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- // Expect the coalesced segment to be split and transmitted.
- expectedPayload = testbench.Payload{Bytes: expectedData[mss:]}
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Check for segments to *not* be held up because of TCP_CORK when
- // the current send window is less than MSS.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(2 * len(sampleData)))})
- dut.Send(acceptFD, sampleData, 0)
- dut.Send(acceptFD, sampleData, 0)
- expectedPayload = testbench.Payload{Bytes: append(sampleData, sampleData...)}
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-}
diff --git a/test/packetimpact/tests/tcp_handshake_window_size_test.go b/test/packetimpact/tests/tcp_handshake_window_size_test.go
deleted file mode 100644
index 652b530d0..000000000
--- a/test/packetimpact/tests/tcp_handshake_window_size_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2020 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 tcp_handshake_window_size_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestTCPHandshakeWindowSize tests if the stack is honoring the window size
-// communicated during handshake.
-func TestTCPHandshakeWindowSize(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- // Start handshake with zero window size.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn), WindowSize: testbench.Uint16(uint16(0))})
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK: %s", err)
- }
- // Update the advertised window size to a non-zero value with the ACK that
- // completes the handshake.
- //
- // Set the window size with MSB set and expect the dut to treat it as
- // an unsigned value.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(uint16(1 << 15))})
-
- acceptFd, _ := dut.Accept(listenFD)
- defer dut.Close(acceptFd)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- // Since we advertised a zero window followed by a non-zero window,
- // expect the dut to honor the recently advertised non-zero window
- // and actually send out the data instead of probing for zero window.
- dut.Send(acceptFd, sampleData, 0)
- if _, err := conn.ExpectNextData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go b/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
deleted file mode 100644
index b9b3e91d3..000000000
--- a/test/packetimpact/tests/tcp_noaccept_close_rst_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2020 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 tcp_noaccept_close_rst_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestTcpNoAcceptCloseReset(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- conn.Connect()
- defer conn.Close()
- dut.Close(listenFd)
- if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, 1*time.Second); err != nil {
- t.Fatalf("expected a RST-ACK packet but got none: %s", err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_outside_the_window_test.go b/test/packetimpact/tests/tcp_outside_the_window_test.go
deleted file mode 100644
index ad8c74234..000000000
--- a/test/packetimpact/tests/tcp_outside_the_window_test.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2020 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 tcp_outside_the_window_test
-
-import (
- "flag"
- "fmt"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestTCPOutsideTheWindows tests the behavior of the DUT when packets arrive
-// that are inside or outside the TCP window. Packets that are outside the
-// window should force an extra ACK, as described in RFC793 page 69:
-// https://tools.ietf.org/html/rfc793#page-69
-func TestTCPOutsideTheWindow(t *testing.T) {
- for _, tt := range []struct {
- description string
- tcpFlags uint8
- payload []testbench.Layer
- seqNumOffset seqnum.Size
- expectACK bool
- }{
- {"SYN", header.TCPFlagSyn, nil, 0, true},
- {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 0, true},
- {"ACK", header.TCPFlagAck, nil, 0, false},
- {"FIN", header.TCPFlagFin, nil, 0, false},
- {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 0, true},
-
- {"SYN", header.TCPFlagSyn, nil, 1, true},
- {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 1, true},
- {"ACK", header.TCPFlagAck, nil, 1, true},
- {"FIN", header.TCPFlagFin, nil, 1, false},
- {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 1, true},
-
- {"SYN", header.TCPFlagSyn, nil, 2, true},
- {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 2, true},
- {"ACK", header.TCPFlagAck, nil, 2, true},
- {"FIN", header.TCPFlagFin, nil, 2, false},
- {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 2, true},
- } {
- t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
- conn.Connect()
- acceptFD, _ := dut.Accept(listenFD)
- defer dut.Close(acceptFD)
-
- windowSize := seqnum.Size(*conn.SynAck().WindowSize) + tt.seqNumOffset
- conn.Drain()
- // Ignore whatever incrementing that this out-of-order packet might cause
- // to the AckNum.
- localSeqNum := testbench.Uint32(uint32(*conn.LocalSeqNum()))
- conn.Send(testbench.TCP{
- Flags: testbench.Uint8(tt.tcpFlags),
- SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum().Add(windowSize))),
- }, tt.payload...)
- timeout := 3 * time.Second
- gotACK, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
- if tt.expectACK && err != nil {
- t.Fatalf("expected an ACK packet within %s but got none: %s", timeout, err)
- }
- if !tt.expectACK && gotACK != nil {
- t.Fatalf("expected no ACK packet within %s but got one: %s", timeout, gotACK)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_paws_mechanism_test.go b/test/packetimpact/tests/tcp_paws_mechanism_test.go
deleted file mode 100644
index 55db4ece6..000000000
--- a/test/packetimpact/tests/tcp_paws_mechanism_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2020 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 tcp_paws_mechanism_test
-
-import (
- "encoding/hex"
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestPAWSMechanism(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- options := make([]byte, header.TCPOptionTSLength)
- header.EncodeTSOption(currentTS(), 0, options)
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn), Options: options})
- synAck, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("didn't get synack during handshake: %s", err)
- }
- parsedSynOpts := header.ParseSynOptions(synAck.Options, true)
- if !parsedSynOpts.TS {
- t.Fatalf("expected TSOpt from DUT, options we got:\n%s", hex.Dump(synAck.Options))
- }
- tsecr := parsedSynOpts.TSVal
- header.EncodeTSOption(currentTS(), tsecr, options)
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options})
- acceptFD, _ := dut.Accept(listenFD)
- defer dut.Close(acceptFD)
-
- sampleData := []byte("Sample Data")
- sentTSVal := currentTS()
- header.EncodeTSOption(sentTSVal, tsecr, options)
- // 3ms here is chosen arbitrarily to make sure we have increasing timestamps
- // every time we send one, it should not cause any flakiness because timestamps
- // only need to be non-decreasing.
- time.Sleep(3 * time.Millisecond)
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
-
- gotTCP, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected an ACK but got none: %s", err)
- }
-
- parsedOpts := header.ParseTCPOptions(gotTCP.Options)
- if !parsedOpts.TS {
- t.Fatalf("expected TS option in response, options we got:\n%s", hex.Dump(gotTCP.Options))
- }
- if parsedOpts.TSVal < tsecr {
- t.Fatalf("TSVal should be non-decreasing, but %d < %d", parsedOpts.TSVal, tsecr)
- }
- if parsedOpts.TSEcr != sentTSVal {
- t.Fatalf("TSEcr should match our sent TSVal, %d != %d", parsedOpts.TSEcr, sentTSVal)
- }
- tsecr = parsedOpts.TSVal
- lastAckNum := gotTCP.AckNum
-
- badTSVal := sentTSVal - 100
- header.EncodeTSOption(badTSVal, tsecr, options)
- // 3ms here is chosen arbitrarily and this time.Sleep() should not cause flakiness
- // due to the exact same reasoning discussed above.
- time.Sleep(3 * time.Millisecond)
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
-
- gotTCP, err = conn.Expect(testbench.TCP{AckNum: lastAckNum, Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected segment with AckNum %d but got none: %s", lastAckNum, err)
- }
- parsedOpts = header.ParseTCPOptions(gotTCP.Options)
- if !parsedOpts.TS {
- t.Fatalf("expected TS option in response, options we got:\n%s", hex.Dump(gotTCP.Options))
- }
- if parsedOpts.TSVal < tsecr {
- t.Fatalf("TSVal should be non-decreasing, but %d < %d", parsedOpts.TSVal, tsecr)
- }
- if parsedOpts.TSEcr != sentTSVal {
- t.Fatalf("TSEcr should match our sent TSVal, %d != %d", parsedOpts.TSEcr, sentTSVal)
- }
-}
-
-func currentTS() uint32 {
- return uint32(time.Now().UnixNano() / 1e6)
-}
diff --git a/test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go b/test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go
deleted file mode 100644
index b640d8673..000000000
--- a/test/packetimpact/tests/tcp_queue_receive_in_syn_sent_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2020 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 tcp_queue_receive_in_syn_sent_test
-
-import (
- "bytes"
- "context"
- "encoding/hex"
- "errors"
- "flag"
- "net"
- "sync"
- "syscall"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestQueueReceiveInSynSent(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
-
- socket, remotePort := dut.CreateBoundSocket(unix.SOCK_STREAM, unix.IPPROTO_TCP, net.ParseIP(testbench.RemoteIPv4))
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- sampleData := []byte("Sample Data")
-
- dut.SetNonBlocking(socket, true)
- if _, err := dut.ConnectWithErrno(context.Background(), socket, conn.LocalAddr()); !errors.Is(err, syscall.EINPROGRESS) {
- t.Fatalf("failed to bring DUT to SYN-SENT, got: %s, want EINPROGRESS", err)
- }
- if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)}, time.Second); err != nil {
- t.Fatalf("expected a SYN from DUT, but got none: %s", err)
- }
-
- // Issue RECEIVE call in SYN-SENT, this should be queued for process until the connection
- // is established.
- dut.SetNonBlocking(socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
- n, buff, err := dut.RecvWithErrno(ctx, socket, int32(len(sampleData)), 0)
- if n == -1 {
- t.Fatalf("failed to recv on DUT: %s", err)
- }
- if got := buff[:n]; !bytes.Equal(got, sampleData) {
- t.Fatalf("received data don't match, got:\n%s, want:\n%s", hex.Dump(got), hex.Dump(sampleData))
- }
- }()
-
- // The following sleep is used to prevent the connection from being established while the
- // RPC is in flight.
- time.Sleep(time.Second)
-
- // Bring the connection to Established.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)})
- if _, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
- t.Fatalf("expected an ACK from DUT, but got none: %s", err)
- }
-
- // Send sample data to DUT.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: sampleData})
-}
diff --git a/test/packetimpact/tests/tcp_reordering_test.go b/test/packetimpact/tests/tcp_reordering_test.go
deleted file mode 100644
index a5378a9dd..000000000
--- a/test/packetimpact/tests/tcp_reordering_test.go
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2020 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 reordering_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
- tb "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- tb.RegisterFlags(flag.CommandLine)
-}
-
-func TestReorderingWindow(t *testing.T) {
- dut := tb.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := tb.NewTCPIPv4(t, tb.TCP{DstPort: &remotePort}, tb.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- // Enable SACK.
- opts := make([]byte, 40)
- optsOff := 0
- optsOff += header.EncodeNOP(opts[optsOff:])
- optsOff += header.EncodeNOP(opts[optsOff:])
- optsOff += header.EncodeSACKPermittedOption(opts[optsOff:])
-
- // Ethernet guarantees that the MTU is at least 1500 bytes.
- const minMTU = 1500
- const mss = minMTU - header.IPv4MinimumSize - header.TCPMinimumSize
- optsOff += header.EncodeMSSOption(mss, opts[optsOff:])
-
- conn.ConnectWithOptions(opts[:optsOff])
-
- acceptFd, _ := dut.Accept(listenFd)
- defer dut.Close(acceptFd)
-
- if tb.DUTType == "linux" {
- // Linux has changed its handling of reordering, force the old behavior.
- dut.SetSockOpt(acceptFd, unix.IPPROTO_TCP, unix.TCP_CONGESTION, []byte("reno"))
- }
-
- pls := dut.GetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_MAXSEG)
- if tb.DUTType == "netstack" {
- // netstack does not impliment TCP_MAXSEG correctly. Fake it
- // here. Netstack uses the max SACK size which is 32. The MSS
- // option is 8 bytes, making the total 36 bytes.
- pls = mss - 36
- }
-
- payload := make([]byte, pls)
-
- seqNum1 := *conn.RemoteSeqNum()
- const numPkts = 10
- // Send some packets, checking that we receive each.
- for i, sn := 0, seqNum1; i < numPkts; i++ {
- dut.Send(acceptFd, payload, 0)
-
- gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second)
- sn.UpdateForward(seqnum.Size(len(payload)))
- if err != nil {
- t.Errorf("Expect #%d: %s", i+1, err)
- continue
- }
- if gotOne == nil {
- t.Errorf("#%d: expected a packet within a second but got none", i+1)
- }
- }
-
- seqNum2 := *conn.RemoteSeqNum()
-
- // SACK packets #2-4.
- sackBlock := make([]byte, 40)
- sbOff := 0
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeNOP(sackBlock[sbOff:])
- sbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- seqNum1.Add(seqnum.Size(len(payload))),
- seqNum1.Add(seqnum.Size(4 * len(payload))),
- }}, sackBlock[sbOff:])
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum1)), Options: sackBlock[:sbOff]})
-
- // ACK first packet.
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum1) + uint32(len(payload)))})
-
- // Check for retransmit.
- gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(seqNum1))}, time.Second)
- if err != nil {
- t.Error("Expect for retransmit:", err)
- }
- if gotOne == nil {
- t.Error("expected a retransmitted packet within a second but got none")
- }
-
- // ACK all send packets with a DSACK block for packet #1. This tells
- // the other end that we got both the original and retransmit for
- // packet #1.
- dsackBlock := make([]byte, 40)
- dsbOff := 0
- dsbOff += header.EncodeNOP(dsackBlock[dsbOff:])
- dsbOff += header.EncodeNOP(dsackBlock[dsbOff:])
- dsbOff += header.EncodeSACKBlocks([]header.SACKBlock{{
- seqNum1.Add(seqnum.Size(len(payload))),
- seqNum1.Add(seqnum.Size(4 * len(payload))),
- }}, dsackBlock[dsbOff:])
-
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), AckNum: tb.Uint32(uint32(seqNum2)), Options: dsackBlock[:dsbOff]})
-
- // Send half of the original window of packets, checking that we
- // received each.
- for i, sn := 0, seqNum2; i < numPkts/2; i++ {
- dut.Send(acceptFd, payload, 0)
-
- gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second)
- sn.UpdateForward(seqnum.Size(len(payload)))
- if err != nil {
- t.Errorf("Expect #%d: %s", i+1, err)
- continue
- }
- if gotOne == nil {
- t.Errorf("#%d: expected a packet within a second but got none", i+1)
- }
- }
-
- if tb.DUTType == "netstack" {
- // The window should now be halved, so we should receive any
- // more, even if we send them.
- dut.Send(acceptFd, payload, 0)
- if got, err := conn.Expect(tb.TCP{}, 100*time.Millisecond); got != nil || err == nil {
- t.Fatalf("expected no packets within 100 millisecond, but got one: %s", got)
- }
- return
- }
-
- // Linux reduces the window by three. Check that we can receive the rest.
- for i, sn := 0, seqNum2.Add(seqnum.Size(numPkts/2*len(payload))); i < 2; i++ {
- dut.Send(acceptFd, payload, 0)
-
- gotOne, err := conn.Expect(tb.TCP{SeqNum: tb.Uint32(uint32(sn))}, time.Second)
- sn.UpdateForward(seqnum.Size(len(payload)))
- if err != nil {
- t.Errorf("Expect #%d: %s", i+1, err)
- continue
- }
- if gotOne == nil {
- t.Errorf("#%d: expected a packet within a second but got none", i+1)
- }
- }
-
- // The window should now be full.
- dut.Send(acceptFd, payload, 0)
- if got, err := conn.Expect(tb.TCP{}, 100*time.Millisecond); got != nil || err == nil {
- t.Fatalf("expected no packets within 100 millisecond, but got one: %s", got)
- }
-}
diff --git a/test/packetimpact/tests/tcp_retransmits_test.go b/test/packetimpact/tests/tcp_retransmits_test.go
deleted file mode 100644
index 6940eb7fb..000000000
--- a/test/packetimpact/tests/tcp_retransmits_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2020 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 tcp_retransmits_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestRetransmits tests retransmits occur at exponentially increasing
-// time intervals.
-func TestRetransmits(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- acceptFd, _ := dut.Accept(listenFd)
- defer dut.Close(acceptFd)
-
- dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- dut.Send(acceptFd, sampleData, 0)
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- // Give a chance for the dut to estimate RTO with RTT from the DATA-ACK.
- // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which
- // we can skip sending this ACK.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-
- startRTO := time.Second
- current := startRTO
- first := time.Now()
- dut.Send(acceptFd, sampleData, 0)
- seq := testbench.Uint32(uint32(*conn.RemoteSeqNum()))
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: seq}, samplePayload, startRTO); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- // Expect retransmits of the same segment.
- for i := 0; i < 5; i++ {
- start := time.Now()
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: seq}, samplePayload, 2*current); err != nil {
- t.Fatalf("expected payload was not received: %s loop %d", err, i)
- }
- if i == 0 {
- startRTO = time.Now().Sub(first)
- current = 2 * startRTO
- continue
- }
- // Check if the probes came at exponentially increasing intervals.
- if p := time.Since(start); p < current-startRTO {
- t.Fatalf("retransmit came sooner interval %d probe %d", p, i)
- }
- current *= 2
- }
-}
diff --git a/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go b/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go
deleted file mode 100644
index 90ab85419..000000000
--- a/test/packetimpact/tests/tcp_send_window_sizes_piggyback_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2020 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 tcp_send_window_sizes_piggyback_test
-
-import (
- "flag"
- "fmt"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestSendWindowSizesPiggyback tests cases where segment sizes are close to
-// sender window size and checks for ACK piggybacking for each of those case.
-func TestSendWindowSizesPiggyback(t *testing.T) {
- sampleData := []byte("Sample Data")
- segmentSize := uint16(len(sampleData))
- // Advertise receive window sizes that are lesser, equal to or greater than
- // enqueued segment size and check for segment transmits. The test attempts
- // to enqueue a segment on the dut before acknowledging previous segment and
- // lets the dut piggyback any ACKs along with the enqueued segment.
- for _, tt := range []struct {
- description string
- windowSize uint16
- expectedPayload1 []byte
- expectedPayload2 []byte
- enqueue bool
- }{
- // Expect the first segment to be split as it cannot be accomodated in
- // the sender window. This means we need not enqueue a new segment after
- // the first segment.
- {"WindowSmallerThanSegment", segmentSize - 1, sampleData[:(segmentSize - 1)], sampleData[(segmentSize - 1):], false /* enqueue */},
-
- {"WindowEqualToSegment", segmentSize, sampleData, sampleData, true /* enqueue */},
-
- // Expect the second segment to not be split as its size is greater than
- // the available sender window size. The segments should not be split
- // when there is pending unacknowledged data and the segment-size is
- // greater than available sender window.
- {"WindowGreaterThanSegment", segmentSize + 1, sampleData, sampleData, true /* enqueue */},
- } {
- t.Run(fmt.Sprintf("%s%d", tt.description, tt.windowSize), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
-
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort, WindowSize: testbench.Uint16(tt.windowSize)}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- acceptFd, _ := dut.Accept(listenFd)
- defer dut.Close(acceptFd)
-
- dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- expectedTCP := testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}
-
- dut.Send(acceptFd, sampleData, 0)
- expectedPayload := testbench.Payload{Bytes: tt.expectedPayload1}
- if _, err := conn.ExpectData(&expectedTCP, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Expect any enqueued segment to be transmitted by the dut along with
- // piggybacked ACK for our data.
-
- if tt.enqueue {
- // Enqueue a segment for the dut to transmit.
- dut.Send(acceptFd, sampleData, 0)
- }
-
- // Send ACK for the previous segment along with data for the dut to
- // receive and ACK back. Sending this ACK would make room for the dut
- // to transmit any enqueued segment.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh), WindowSize: testbench.Uint16(tt.windowSize)}, &testbench.Payload{Bytes: sampleData})
-
- // Expect the dut to piggyback the ACK for received data along with
- // the segment enqueued for transmit.
- expectedPayload = testbench.Payload{Bytes: tt.expectedPayload2}
- if _, err := conn.ExpectData(&expectedTCP, &expectedPayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_splitseg_mss_test.go b/test/packetimpact/tests/tcp_splitseg_mss_test.go
deleted file mode 100644
index 9350d0988..000000000
--- a/test/packetimpact/tests/tcp_splitseg_mss_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2020 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 tcp_splitseg_mss_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestTCPSplitSegMSS lets the dut try to send segments larger than MSS.
-// It tests if the transmitted segments are capped at MSS and are split.
-func TestTCPSplitSegMSS(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- const mss = uint32(header.TCPDefaultMSS)
- options := make([]byte, header.TCPOptionMSSLength)
- header.EncodeMSSOption(mss, options)
- conn.ConnectWithOptions(options)
-
- acceptFD, _ := dut.Accept(listenFD)
- defer dut.Close(acceptFD)
-
- // Let the dut send a segment larger than MSS.
- largeData := make([]byte, mss+1)
- for i := 0; i < 2; i++ {
- dut.Send(acceptFD, largeData, 0)
- if i == 0 {
- // On Linux, the initial segment goes out beyond MSS and the segment
- // split occurs on retransmission. Call ExpectData to wait to
- // receive the split segment.
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: largeData[:mss]}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- } else {
- if _, err := conn.ExpectNextData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: largeData[:mss]}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectNextData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &testbench.Payload{Bytes: largeData[mss:]}, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- }
-}
diff --git a/test/packetimpact/tests/tcp_synrcvd_reset_test.go b/test/packetimpact/tests/tcp_synrcvd_reset_test.go
deleted file mode 100644
index 7d5deab01..000000000
--- a/test/packetimpact/tests/tcp_synrcvd_reset_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2020 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 tcp_syn_reset_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestTCPSynRcvdReset tests transition from SYN-RCVD to CLOSED.
-func TestTCPSynRcvdReset(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- // Expect dut connection to have transitioned to SYN-RCVD state.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)})
- // Expect the connection to have transitioned SYN-RCVD to CLOSED.
- // TODO(gvisor.dev/issue/478): Check for TCP_INFO on the dut side.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Fatalf("expected a TCP RST %s", err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_synsent_reset_test.go b/test/packetimpact/tests/tcp_synsent_reset_test.go
deleted file mode 100644
index 6898a2239..000000000
--- a/test/packetimpact/tests/tcp_synsent_reset_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2020 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 tcp_synsent_reset_test
-
-import (
- "flag"
- "net"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- tb "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- tb.RegisterFlags(flag.CommandLine)
-}
-
-// dutSynSentState sets up the dut connection in SYN-SENT state.
-func dutSynSentState(t *testing.T) (*tb.DUT, *tb.TCPIPv4, uint16, uint16) {
- dut := tb.NewDUT(t)
-
- clientFD, clientPort := dut.CreateBoundSocket(unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, net.ParseIP(tb.RemoteIPv4))
- port := uint16(9001)
- conn := tb.NewTCPIPv4(t, tb.TCP{SrcPort: &port, DstPort: &clientPort}, tb.TCP{SrcPort: &clientPort, DstPort: &port})
-
- sa := unix.SockaddrInet4{Port: int(port)}
- copy(sa.Addr[:], net.IP(net.ParseIP(tb.LocalIPv4)).To4())
- // Bring the dut to SYN-SENT state with a non-blocking connect.
- dut.Connect(clientFD, &sa)
- if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN\n")
- }
-
- return &dut, &conn, port, clientPort
-}
-
-// TestTCPSynSentReset tests RFC793, p67: SYN-SENT to CLOSED transition.
-func TestTCPSynSentReset(t *testing.T) {
- dut, conn, _, _ := dutSynSentState(t)
- defer conn.Close()
- defer dut.TearDown()
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst | header.TCPFlagAck)})
- // Expect the connection to have closed.
- // TODO(gvisor.dev/issue/478): Check for TCP_INFO on the dut side.
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Fatalf("expected a TCP RST")
- }
-}
-
-// TestTCPSynSentRcvdReset tests RFC793, p70, SYN-SENT to SYN-RCVD to CLOSED
-// transitions.
-func TestTCPSynSentRcvdReset(t *testing.T) {
- dut, c, remotePort, clientPort := dutSynSentState(t)
- defer dut.TearDown()
- defer c.Close()
-
- conn := tb.NewTCPIPv4(t, tb.TCP{SrcPort: &remotePort, DstPort: &clientPort}, tb.TCP{SrcPort: &clientPort, DstPort: &remotePort})
- defer conn.Close()
- // Initiate new SYN connection with the same port pair
- // (simultaneous open case), expect the dut connection to move to
- // SYN-RCVD state
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK %s\n", err)
- }
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)})
- // Expect the connection to have transitioned SYN-RCVD to CLOSED.
- // TODO(gvisor.dev/issue/478): Check for TCP_INFO on the dut side.
- conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(&tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Fatalf("expected a TCP RST")
- }
-}
diff --git a/test/packetimpact/tests/tcp_user_timeout_test.go b/test/packetimpact/tests/tcp_user_timeout_test.go
deleted file mode 100644
index 87e45d765..000000000
--- a/test/packetimpact/tests/tcp_user_timeout_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2020 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 tcp_user_timeout_test
-
-import (
- "flag"
- "fmt"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func sendPayload(conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) error {
- sampleData := make([]byte, 100)
- for i := range sampleData {
- sampleData[i] = uint8(i)
- }
- conn.Drain()
- dut.Send(fd, sampleData, 0)
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &testbench.Payload{Bytes: sampleData}, time.Second); err != nil {
- return fmt.Errorf("expected data but got none: %w", err)
- }
- return nil
-}
-
-func sendFIN(conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) error {
- dut.Close(fd)
- return nil
-}
-
-func TestTCPUserTimeout(t *testing.T) {
- for _, tt := range []struct {
- description string
- userTimeout time.Duration
- sendDelay time.Duration
- }{
- {"NoUserTimeout", 0, 3 * time.Second},
- {"ACKBeforeUserTimeout", 5 * time.Second, 4 * time.Second},
- {"ACKAfterUserTimeout", 5 * time.Second, 7 * time.Second},
- } {
- for _, ttf := range []struct {
- description string
- f func(conn *testbench.TCPIPv4, dut *testbench.DUT, fd int32) error
- }{
- {"AfterPayload", sendPayload},
- {"AfterFIN", sendFIN},
- } {
- t.Run(tt.description+ttf.description, func(t *testing.T) {
- // Create a socket, listen, TCP handshake, and accept.
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFD, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFD)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
- conn.Connect()
- acceptFD, _ := dut.Accept(listenFD)
-
- if tt.userTimeout != 0 {
- dut.SetSockOptInt(acceptFD, unix.SOL_TCP, unix.TCP_USER_TIMEOUT, int32(tt.userTimeout.Milliseconds()))
- }
-
- if err := ttf.f(&conn, &dut, acceptFD); err != nil {
- t.Fatal(err)
- }
-
- time.Sleep(tt.sendDelay)
- conn.Drain()
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-
- // If TCP_USER_TIMEOUT was set and the above delay was longer than the
- // TCP_USER_TIMEOUT then the DUT should send a RST in response to the
- // testbench's packet.
- expectRST := tt.userTimeout != 0 && tt.sendDelay > tt.userTimeout
- expectTimeout := 5 * time.Second
- got, err := conn.Expect(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, expectTimeout)
- if expectRST && err != nil {
- t.Errorf("expected RST packet within %s but got none: %s", expectTimeout, err)
- }
- if !expectRST && got != nil {
- t.Errorf("expected no RST packet within %s but got one: %s", expectTimeout, got)
- }
- })
- }
- }
-}
diff --git a/test/packetimpact/tests/tcp_window_shrink_test.go b/test/packetimpact/tests/tcp_window_shrink_test.go
deleted file mode 100644
index e78d04756..000000000
--- a/test/packetimpact/tests/tcp_window_shrink_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2020 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 tcp_window_shrink_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestWindowShrink(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- acceptFd, _ := dut.Accept(listenFd)
- defer dut.Close(acceptFd)
-
- dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- dut.Send(acceptFd, sampleData, 0)
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-
- dut.Send(acceptFd, sampleData, 0)
- dut.Send(acceptFd, sampleData, 0)
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- // We close our receiving window here
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
-
- dut.Send(acceptFd, []byte("Sample Data"), 0)
- // Note: There is another kind of zero-window probing which Windows uses (by sending one
- // new byte at `RemoteSeqNum`), if netstack wants to go that way, we may want to change
- // the following lines.
- expectedRemoteSeqNum := *conn.RemoteSeqNum() - 1
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: testbench.Uint32(uint32(expectedRemoteSeqNum))}, nil, time.Second); err != nil {
- t.Fatalf("expected a packet with sequence number %d: %s", expectedRemoteSeqNum, err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go b/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go
deleted file mode 100644
index 5ab193181..000000000
--- a/test/packetimpact/tests/tcp_zero_window_probe_retransmit_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2020 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 tcp_zero_window_probe_retransmit_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestZeroWindowProbeRetransmit tests retransmits of zero window probes
-// to be sent at exponentially inreasing time intervals.
-func TestZeroWindowProbeRetransmit(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- acceptFd, _ := dut.Accept(listenFd)
- defer dut.Close(acceptFd)
-
- dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- // Send and receive sample data to the dut.
- dut.Send(acceptFd, sampleData, 0)
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
-
- // Check for the dut to keep the connection alive as long as the zero window
- // probes are acknowledged. Check if the zero window probes are sent at
- // exponentially increasing intervals. The timeout intervals are function
- // of the recorded first zero probe transmission duration.
- //
- // Advertize zero receive window again.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
- probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum() - 1))
- ackProbe := testbench.Uint32(uint32(*conn.RemoteSeqNum()))
-
- startProbeDuration := time.Second
- current := startProbeDuration
- first := time.Now()
- // Ask the dut to send out data.
- dut.Send(acceptFd, sampleData, 0)
- // Expect the dut to keep the connection alive as long as the remote is
- // acknowledging the zero-window probes.
- for i := 0; i < 5; i++ {
- start := time.Now()
- // Expect zero-window probe with a timeout which is a function of the typical
- // first retransmission time. The retransmission times is supposed to
- // exponentially increase.
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: probeSeq}, nil, 2*current); err != nil {
- t.Fatalf("expected a probe with sequence number %d: loop %d", probeSeq, i)
- }
- if i == 0 {
- startProbeDuration = time.Now().Sub(first)
- current = 2 * startProbeDuration
- continue
- }
- // Check if the probes came at exponentially increasing intervals.
- if p := time.Since(start); p < current-startProbeDuration {
- t.Fatalf("zero probe came sooner interval %d probe %d\n", p, i)
- }
- // Acknowledge the zero-window probes from the dut.
- conn.Send(testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
- current *= 2
- }
- // Advertize non-zero window.
- conn.Send(testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck)})
- // Expect the dut to recover and transmit data.
- if _, err := conn.ExpectData(&testbench.
- TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_test.go b/test/packetimpact/tests/tcp_zero_window_probe_test.go
deleted file mode 100644
index 649fd5699..000000000
--- a/test/packetimpact/tests/tcp_zero_window_probe_test.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2020 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 tcp_zero_window_probe_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestZeroWindowProbe tests few cases of zero window probing over the
-// same connection.
-func TestZeroWindowProbe(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- acceptFd, _ := dut.Accept(listenFd)
- defer dut.Close(acceptFd)
-
- dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- start := time.Now()
- // Send and receive sample data to the dut.
- dut.Send(acceptFd, sampleData, 0)
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- sendTime := time.Now().Sub(start)
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
-
- // Test 1: Check for receive of a zero window probe, record the duration for
- // probe to be sent.
- //
- // Advertize zero window to the dut.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
-
- // Expected sequence number of the zero window probe.
- probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum() - 1))
- // Expected ack number of the ACK for the probe.
- ackProbe := testbench.Uint32(uint32(*conn.RemoteSeqNum()))
-
- // Expect there are no zero-window probes sent until there is data to be sent out
- // from the dut.
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: probeSeq}, nil, 2*time.Second); err == nil {
- t.Fatalf("unexpected packet with sequence number %d: %s", probeSeq, err)
- }
-
- start = time.Now()
- // Ask the dut to send out data.
- dut.Send(acceptFd, sampleData, 0)
- // Expect zero-window probe from the dut.
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: probeSeq}, nil, time.Second); err != nil {
- t.Fatalf("expected a packet with sequence number %d: %s", probeSeq, err)
- }
- // Expect the probe to be sent after some time. Compare against the previous
- // time recorded when the dut immediately sends out data on receiving the
- // send command.
- if startProbeDuration := time.Now().Sub(start); startProbeDuration <= sendTime {
- t.Fatalf("expected the first probe to be sent out after retransmission interval, got %s want > %s", startProbeDuration, sendTime)
- }
-
- // Test 2: Check if the dut recovers on advertizing non-zero receive window.
- // and sends out the sample payload after the send window opens.
- //
- // Advertize non-zero window to the dut and ack the zero window probe.
- conn.Send(testbench.TCP{AckNum: ackProbe, Flags: testbench.Uint8(header.TCPFlagAck)})
- // Expect the dut to recover and transmit data.
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: ackProbe}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
-
- // Test 3: Sanity check for dut's processing of a similar probe it sent.
- // Check if the dut responds as we do for a similar probe sent to it.
- // Basically with sequence number to one byte behind the unacknowledged
- // sequence number.
- p := testbench.Uint32(uint32(*conn.LocalSeqNum()))
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), SeqNum: testbench.Uint32(uint32(*conn.LocalSeqNum() - 1))})
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: p}, nil, time.Second); err != nil {
- t.Fatalf("expected a packet with ack number: %d: %s", p, err)
- }
-}
diff --git a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go b/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
deleted file mode 100644
index 3c467b14f..000000000
--- a/test/packetimpact/tests/tcp_zero_window_probe_usertimeout_test.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2020 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 tcp_zero_window_probe_usertimeout_test
-
-import (
- "flag"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-// TestZeroWindowProbeUserTimeout sanity tests user timeout when we are
-// retransmitting zero window probes.
-func TestZeroWindowProbeUserTimeout(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close()
-
- conn.Connect()
- acceptFd, _ := dut.Accept(listenFd)
- defer dut.Close(acceptFd)
-
- dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
-
- sampleData := []byte("Sample Data")
- samplePayload := &testbench.Payload{Bytes: sampleData}
-
- // Send and receive sample data to the dut.
- dut.Send(acceptFd, sampleData, 0)
- if _, err := conn.ExpectData(&testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected payload was not received: %s", err)
- }
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected packet was not received: %s", err)
- }
-
- // Test 1: Check for receive of a zero window probe, record the duration for
- // probe to be sent.
- //
- // Advertize zero window to the dut.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
-
- // Expected sequence number of the zero window probe.
- probeSeq := testbench.Uint32(uint32(*conn.RemoteSeqNum() - 1))
- start := time.Now()
- // Ask the dut to send out data.
- dut.Send(acceptFd, sampleData, 0)
- // Expect zero-window probe from the dut.
- if _, err := conn.ExpectData(&testbench.TCP{SeqNum: probeSeq}, nil, time.Second); err != nil {
- t.Fatalf("expected a packet with sequence number %d: %s", probeSeq, err)
- }
- // Record the duration for first probe, the dut sends the zero window probe after
- // a retransmission time interval.
- startProbeDuration := time.Now().Sub(start)
-
- // Test 2: Check if the dut times out the connection by honoring usertimeout
- // when the dut is sending zero-window probes.
- //
- // Reduce the retransmit timeout.
- dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int32(startProbeDuration.Milliseconds()))
- // Advertize zero window again.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), WindowSize: testbench.Uint16(0)})
- // Ask the dut to send out data that would trigger zero window probe retransmissions.
- dut.Send(acceptFd, sampleData, 0)
-
- // Wait for the connection to timeout after multiple zero-window probe retransmissions.
- time.Sleep(8 * startProbeDuration)
-
- // Expect the connection to have timed out and closed which would cause the dut
- // to reply with a RST to the ACK we send.
- conn.Send(testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- if _, err := conn.ExpectData(&testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, nil, time.Second); err != nil {
- t.Fatalf("expected a TCP RST")
- }
-}
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
deleted file mode 100644
index b754918f6..000000000
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright 2020 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 udp_icmp_error_propagation_test
-
-import (
- "context"
- "flag"
- "fmt"
- "net"
- "sync"
- "syscall"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-type connectionMode bool
-
-func (c connectionMode) String() string {
- if c {
- return "Connected"
- }
- return "Connectionless"
-}
-
-type icmpError int
-
-const (
- portUnreachable icmpError = iota
- timeToLiveExceeded
-)
-
-func (e icmpError) String() string {
- switch e {
- case portUnreachable:
- return "PortUnreachable"
- case timeToLiveExceeded:
- return "TimeToLiveExpired"
- }
- return "Unknown ICMP error"
-}
-
-func (e icmpError) ToICMPv4() *testbench.ICMPv4 {
- switch e {
- case portUnreachable:
- return &testbench.ICMPv4{Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable), Code: testbench.Uint8(header.ICMPv4PortUnreachable)}
- case timeToLiveExceeded:
- return &testbench.ICMPv4{Type: testbench.ICMPv4Type(header.ICMPv4TimeExceeded), Code: testbench.Uint8(header.ICMPv4TTLExceeded)}
- }
- return nil
-}
-
-type errorDetection struct {
- name string
- useValidConn bool
- f func(context.Context, testData) error
-}
-
-type testData struct {
- dut *testbench.DUT
- conn *testbench.UDPIPv4
- remoteFD int32
- remotePort uint16
- cleanFD int32
- cleanPort uint16
- wantErrno syscall.Errno
-}
-
-// wantErrno computes the errno to expect given the connection mode of a UDP
-// socket and the ICMP error it will receive.
-func wantErrno(c connectionMode, icmpErr icmpError) syscall.Errno {
- if c && icmpErr == portUnreachable {
- return syscall.Errno(unix.ECONNREFUSED)
- }
- return syscall.Errno(0)
-}
-
-// sendICMPError sends an ICMP error message in response to a UDP datagram.
-func sendICMPError(conn *testbench.UDPIPv4, icmpErr icmpError, udp *testbench.UDP) error {
- layers := (*testbench.Connection)(conn).CreateFrame(nil)
- layers = layers[:len(layers)-1]
- ip, ok := udp.Prev().(*testbench.IPv4)
- if !ok {
- return fmt.Errorf("expected %s to be IPv4", udp.Prev())
- }
- if icmpErr == timeToLiveExceeded {
- *ip.TTL = 1
- // Let serialization recalculate the checksum since we set the TTL
- // to 1.
- ip.Checksum = nil
- }
- // Note that the ICMP payload is valid in this case because the UDP
- // payload is empty. If the UDP payload were not empty, the packet
- // length during serialization may not be calculated correctly,
- // resulting in a mal-formed packet.
- layers = append(layers, icmpErr.ToICMPv4(), ip, udp)
-
- (*testbench.Connection)(conn).SendFrameStateless(layers)
- return nil
-}
-
-// testRecv tests observing the ICMP error through the recv syscall. A packet
-// is sent to the DUT, and if wantErrno is non-zero, then the first recv should
-// fail and the second should succeed. Otherwise if wantErrno is zero then the
-// first recv should succeed immediately.
-func testRecv(ctx context.Context, d testData) error {
- // Check that receiving on the clean socket works.
- d.conn.Send(testbench.UDP{DstPort: &d.cleanPort})
- d.dut.Recv(d.cleanFD, 100, 0)
-
- d.conn.Send(testbench.UDP{})
-
- if d.wantErrno != syscall.Errno(0) {
- ctx, cancel := context.WithTimeout(ctx, time.Second)
- defer cancel()
- ret, _, err := d.dut.RecvWithErrno(ctx, d.remoteFD, 100, 0)
- if ret != -1 {
- return fmt.Errorf("recv after ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", d.wantErrno)
- }
- if err != d.wantErrno {
- return fmt.Errorf("recv after ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, d.wantErrno)
- }
- }
-
- d.dut.Recv(d.remoteFD, 100, 0)
- return nil
-}
-
-// testSendTo tests observing the ICMP error through the send syscall. If
-// wantErrno is non-zero, the first send should fail and a subsequent send
-// should suceed; while if wantErrno is zero then the first send should just
-// succeed.
-func testSendTo(ctx context.Context, d testData) error {
- // Check that sending on the clean socket works.
- d.dut.SendTo(d.cleanFD, nil, 0, d.conn.LocalAddr())
- if _, err := d.conn.Expect(testbench.UDP{SrcPort: &d.cleanPort}, time.Second); err != nil {
- return fmt.Errorf("did not receive UDP packet from clean socket on DUT: %s", err)
- }
-
- if d.wantErrno != syscall.Errno(0) {
- ctx, cancel := context.WithTimeout(ctx, time.Second)
- defer cancel()
- ret, err := d.dut.SendToWithErrno(ctx, d.remoteFD, nil, 0, d.conn.LocalAddr())
-
- if ret != -1 {
- return fmt.Errorf("sendto after ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", d.wantErrno)
- }
- if err != d.wantErrno {
- return fmt.Errorf("sendto after ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, d.wantErrno)
- }
- }
-
- d.dut.SendTo(d.remoteFD, nil, 0, d.conn.LocalAddr())
- if _, err := d.conn.Expect(testbench.UDP{}, time.Second); err != nil {
- return fmt.Errorf("did not receive UDP packet as expected: %s", err)
- }
- return nil
-}
-
-func testSockOpt(_ context.Context, d testData) error {
- // Check that there's no pending error on the clean socket.
- if errno := syscall.Errno(d.dut.GetSockOptInt(d.cleanFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != syscall.Errno(0) {
- return fmt.Errorf("unexpected error (%[1]d) %[1]v on clean socket", errno)
- }
-
- if errno := syscall.Errno(d.dut.GetSockOptInt(d.remoteFD, unix.SOL_SOCKET, unix.SO_ERROR)); errno != d.wantErrno {
- return fmt.Errorf("SO_ERROR sockopt after ICMP error is (%[1]d) %[1]v, expected (%[2]d) %[2]v", errno, d.wantErrno)
- }
-
- // Check that after clearing socket error, sending doesn't fail.
- d.dut.SendTo(d.remoteFD, nil, 0, d.conn.LocalAddr())
- if _, err := d.conn.Expect(testbench.UDP{}, time.Second); err != nil {
- return fmt.Errorf("did not receive UDP packet as expected: %s", err)
- }
- return nil
-}
-
-// TestUDPICMPErrorPropagation tests that ICMP error messages in response to
-// UDP datagrams are processed correctly. RFC 1122 section 4.1.3.3 states that:
-// "UDP MUST pass to the application layer all ICMP error messages that it
-// receives from the IP layer."
-//
-// The test cases are parametrized in 3 dimensions: 1. the UDP socket is either
-// put into connection mode or left connectionless, 2. the ICMP message type
-// and code, and 3. the method by which the ICMP error is observed on the
-// socket: sendto, recv, or getsockopt(SO_ERROR).
-//
-// Linux's udp(7) man page states: "All fatal errors will be passed to the user
-// as an error return even when the socket is not connected. This includes
-// asynchronous errors received from the network." In practice, the only
-// combination of parameters to the test that causes an error to be observable
-// on the UDP socket is receiving a port unreachable message on a connected
-// socket.
-func TestUDPICMPErrorPropagation(t *testing.T) {
- for _, connect := range []connectionMode{true, false} {
- for _, icmpErr := range []icmpError{portUnreachable, timeToLiveExceeded} {
- wantErrno := wantErrno(connect, icmpErr)
-
- for _, errDetect := range []errorDetection{
- errorDetection{"SendTo", false, testSendTo},
- // Send to an address that's different from the one that caused an ICMP
- // error to be returned.
- errorDetection{"SendToValid", true, testSendTo},
- errorDetection{"Recv", false, testRecv},
- errorDetection{"SockOpt", false, testSockOpt},
- } {
- t.Run(fmt.Sprintf("%s/%s/%s", connect, icmpErr, errDetect.name), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
-
- remoteFD, remotePort := dut.CreateBoundSocket(unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP("0.0.0.0"))
- defer dut.Close(remoteFD)
-
- // Create a second, clean socket on the DUT to ensure that the ICMP
- // error messages only affect the sockets they are intended for.
- cleanFD, cleanPort := dut.CreateBoundSocket(unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP("0.0.0.0"))
- defer dut.Close(cleanFD)
-
- conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort})
- defer conn.Close()
-
- if connect {
- dut.Connect(remoteFD, conn.LocalAddr())
- dut.Connect(cleanFD, conn.LocalAddr())
- }
-
- dut.SendTo(remoteFD, nil, 0, conn.LocalAddr())
- udp, err := conn.Expect(testbench.UDP{}, time.Second)
- if err != nil {
- t.Fatalf("did not receive message from DUT: %s", err)
- }
-
- if err := sendICMPError(&conn, icmpErr, udp); err != nil {
- t.Fatal(err)
- }
-
- errDetectConn := &conn
- if errDetect.useValidConn {
- // connClean is a UDP socket on the test runner that was not
- // involved in the generation of the ICMP error. As such,
- // interactions between it and the the DUT should be independent of
- // the ICMP error at least at the port level.
- connClean := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort})
- defer connClean.Close()
-
- errDetectConn = &connClean
- }
-
- if err := errDetect.f(context.Background(), testData{&dut, errDetectConn, remoteFD, remotePort, cleanFD, cleanPort, wantErrno}); err != nil {
- t.Fatal(err)
- }
- })
- }
- }
- }
-}
-
-// TestICMPErrorDuringUDPRecv tests behavior when a UDP socket is in the middle
-// of a blocking recv and receives an ICMP error.
-func TestICMPErrorDuringUDPRecv(t *testing.T) {
- for _, connect := range []connectionMode{true, false} {
- for _, icmpErr := range []icmpError{portUnreachable, timeToLiveExceeded} {
- wantErrno := wantErrno(connect, icmpErr)
-
- t.Run(fmt.Sprintf("%s/%s", connect, icmpErr), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
-
- remoteFD, remotePort := dut.CreateBoundSocket(unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP("0.0.0.0"))
- defer dut.Close(remoteFD)
-
- // Create a second, clean socket on the DUT to ensure that the ICMP
- // error messages only affect the sockets they are intended for.
- cleanFD, cleanPort := dut.CreateBoundSocket(unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP("0.0.0.0"))
- defer dut.Close(cleanFD)
-
- conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort})
- defer conn.Close()
-
- if connect {
- dut.Connect(remoteFD, conn.LocalAddr())
- dut.Connect(cleanFD, conn.LocalAddr())
- }
-
- dut.SendTo(remoteFD, nil, 0, conn.LocalAddr())
- udp, err := conn.Expect(testbench.UDP{}, time.Second)
- if err != nil {
- t.Fatalf("did not receive message from DUT: %s", err)
- }
-
- var wg sync.WaitGroup
- wg.Add(2)
- go func() {
- defer wg.Done()
-
- if wantErrno != syscall.Errno(0) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- ret, _, err := dut.RecvWithErrno(ctx, remoteFD, 100, 0)
- if ret != -1 {
- t.Errorf("recv during ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", wantErrno)
- return
- }
- if err != wantErrno {
- t.Errorf("recv during ICMP error resulted in error (%[1]d) %[1]v, expected (%[2]d) %[2]v", err, wantErrno)
- return
- }
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- if ret, _, err := dut.RecvWithErrno(ctx, remoteFD, 100, 0); ret == -1 {
- t.Errorf("recv after ICMP error failed with (%[1]d) %[1]", err)
- }
- }()
-
- go func() {
- defer wg.Done()
-
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- if ret, _, err := dut.RecvWithErrno(ctx, cleanFD, 100, 0); ret == -1 {
- t.Errorf("recv on clean socket failed with (%[1]d) %[1]", err)
- }
- }()
-
- // TODO(b/155684889) This sleep is to allow time for the DUT to
- // actually call recv since we want the ICMP error to arrive during the
- // blocking recv, and should be replaced when a better synchronization
- // alternative is available.
- time.Sleep(2 * time.Second)
-
- if err := sendICMPError(&conn, icmpErr, udp); err != nil {
- t.Fatal(err)
- }
-
- conn.Send(testbench.UDP{DstPort: &cleanPort})
- conn.Send(testbench.UDP{})
- wg.Wait()
- })
- }
- }
-}
diff --git a/test/packetimpact/tests/udp_recv_multicast_test.go b/test/packetimpact/tests/udp_recv_multicast_test.go
deleted file mode 100644
index 77a9bfa1d..000000000
--- a/test/packetimpact/tests/udp_recv_multicast_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 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 udp_recv_multicast_test
-
-import (
- "flag"
- "net"
- "testing"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestUDPRecvMulticast(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- boundFD, remotePort := dut.CreateBoundSocket(unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP("0.0.0.0"))
- defer dut.Close(boundFD)
- conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort})
- defer conn.Close()
- conn.SendIP(testbench.IPv4{DstAddr: testbench.Address(tcpip.Address(net.ParseIP("224.0.0.1").To4()))}, testbench.UDP{})
- dut.Recv(boundFD, 100, 0)
-}
diff --git a/test/packetimpact/tests/udp_send_recv_dgram_test.go b/test/packetimpact/tests/udp_send_recv_dgram_test.go
deleted file mode 100644
index 224feef85..000000000
--- a/test/packetimpact/tests/udp_send_recv_dgram_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2020 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 udp_send_recv_dgram_test
-
-import (
- "flag"
- "net"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestUDPRecv(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- boundFD, remotePort := dut.CreateBoundSocket(unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP("0.0.0.0"))
- defer dut.Close(boundFD)
- conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort})
- defer conn.Close()
-
- testCases := []struct {
- name string
- payload []byte
- }{
- {"emptypayload", nil},
- {"small payload", []byte("hello world")},
- {"1kPayload", testbench.GenerateRandomPayload(t, 1<<10)},
- // Even though UDP allows larger dgrams we don't test it here as
- // they need to be fragmented and written out as individual
- // frames.
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- conn.Send(testbench.UDP{}, &testbench.Payload{Bytes: tc.payload})
- if got, want := string(dut.Recv(boundFD, int32(len(tc.payload)), 0)), string(tc.payload); got != want {
- t.Fatalf("received payload does not match sent payload got: %s, want: %s", got, want)
- }
- })
- }
-}
-
-func TestUDPSend(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- boundFD, remotePort := dut.CreateBoundSocket(unix.SOCK_DGRAM, unix.IPPROTO_UDP, net.ParseIP("0.0.0.0"))
- defer dut.Close(boundFD)
- conn := testbench.NewUDPIPv4(t, testbench.UDP{DstPort: &remotePort}, testbench.UDP{SrcPort: &remotePort})
- defer conn.Close()
-
- testCases := []struct {
- name string
- payload []byte
- }{
- {"emptypayload", nil},
- {"small payload", []byte("hello world")},
- {"1kPayload", testbench.GenerateRandomPayload(t, 1<<10)},
- // Even though UDP allows larger dgrams we don't test it here as
- // they need to be fragmented and written out as individual
- // frames.
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- conn.Drain()
- if got, want := int(dut.SendTo(boundFD, tc.payload, 0, conn.LocalAddr())), len(tc.payload); got != want {
- t.Fatalf("short write got: %d, want: %d", got, want)
- }
- if _, err := conn.ExpectData(testbench.UDP{SrcPort: &remotePort}, testbench.Payload{Bytes: tc.payload}, 1*time.Second); err != nil {
- t.Fatal(err)
- }
- })
- }
-}