summaryrefslogtreecommitdiffhomepage
path: root/test/packetimpact/testbench
diff options
context:
space:
mode:
authorgVisor bot <gvisor-bot@google.com>2020-04-07 17:48:06 -0700
committergVisor bot <gvisor-bot@google.com>2020-04-07 17:49:21 -0700
commitdbcc59af0b834b6295589a594fe4cc1c360e66f7 (patch)
treeea5e48327004da2f371a86a0c0333e6eda9acb3d /test/packetimpact/testbench
parentacf0259255bae190759e39fbff3bac6c94122734 (diff)
Test TCP sender behavior against window shrinking
RFC 1122 Section 3.7: A sending TCP MUST be robust against window shrinking, which may cause the "useable window" to become negative. PiperOrigin-RevId: 305377072
Diffstat (limited to 'test/packetimpact/testbench')
-rw-r--r--test/packetimpact/testbench/connections.go52
-rw-r--r--test/packetimpact/testbench/dut.go58
2 files changed, 103 insertions, 7 deletions
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go
index 8d1f562ee..579da59c3 100644
--- a/test/packetimpact/testbench/connections.go
+++ b/test/packetimpact/testbench/connections.go
@@ -187,9 +187,19 @@ func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) {
conn.SendFrame(conn.CreateFrame(tcp, additionalLayers...))
}
-// Recv gets a packet from the sniffer within the timeout provided. If no packet
-// arrives before the timeout, it returns nil.
+// Recv gets a packet from the sniffer within the timeout provided.
+// If no packet arrives before the timeout, it returns nil.
func (conn *TCPIPv4) Recv(timeout time.Duration) *TCP {
+ layers := conn.RecvFrame(timeout)
+ if tcpLayerIndex < len(layers) {
+ return layers[tcpLayerIndex].(*TCP)
+ }
+ return nil
+}
+
+// RecvFrame gets a frame (of type Layers) within the timeout provided.
+// If no frame arrives before the timeout, it returns nil.
+func (conn *TCPIPv4) RecvFrame(timeout time.Duration) Layers {
deadline := time.Now().Add(timeout)
for {
timeout = time.Until(deadline)
@@ -216,14 +226,16 @@ func (conn *TCPIPv4) Recv(timeout time.Duration) *TCP {
for i := tcpLayerIndex + 1; i < len(layers); i++ {
conn.RemoteSeqNum.UpdateForward(seqnum.Size(layers[i].length()))
}
- return tcpHeader
+ return layers
}
return nil
}
// Expect a packet that matches the provided tcp within the timeout specified.
-// If it doesn't arrive in time, the test fails.
+// If it doesn't arrive in time, it returns nil.
func (conn *TCPIPv4) Expect(tcp TCP, timeout time.Duration) *TCP {
+ // We cannot implement this directly using ExpectFrame as we cannot specify
+ // the Payload part.
deadline := time.Now().Add(timeout)
for {
timeout = time.Until(deadline)
@@ -231,15 +243,41 @@ func (conn *TCPIPv4) Expect(tcp TCP, timeout time.Duration) *TCP {
return nil
}
gotTCP := conn.Recv(timeout)
- if gotTCP == nil {
- return nil
- }
if tcp.match(gotTCP) {
return gotTCP
}
}
}
+// ExpectFrame expects a frame that matches the specified layers within the
+// timeout specified. If it doesn't arrive in time, it returns nil.
+func (conn *TCPIPv4) ExpectFrame(layers Layers, timeout time.Duration) Layers {
+ deadline := time.Now().Add(timeout)
+ for {
+ timeout = time.Until(deadline)
+ if timeout <= 0 {
+ return nil
+ }
+ gotLayers := conn.RecvFrame(timeout)
+ if layers.match(gotLayers) {
+ return gotLayers
+ }
+ }
+}
+
+// ExpectData is a convenient method that expects a TCP packet along with
+// the payload to arrive within the timeout specified. If it doesn't arrive
+// in time, it causes a fatal test failure.
+func (conn *TCPIPv4) ExpectData(tcp TCP, data []byte, timeout time.Duration) {
+ expected := []Layer{&Ether{}, &IPv4{}, &tcp}
+ if len(data) > 0 {
+ expected = append(expected, &Payload{Bytes: data})
+ }
+ if conn.ExpectFrame(expected, timeout) == nil {
+ conn.t.Fatalf("expected to get a TCP frame %s with payload %x", &tcp, data)
+ }
+}
+
// Handshake performs a TCP 3-way handshake.
func (conn *TCPIPv4) Handshake() {
// Send the SYN.
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index f342aee01..9335909c0 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -291,6 +291,35 @@ func (dut *DUT) ListenWithErrno(ctx context.Context, sockfd, backlog int32) (int
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_())
+}
+
// 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
@@ -324,6 +353,35 @@ func (dut *DUT) SetSockOptWithErrno(ctx context.Context, sockfd, level, optname
return resp.GetRet(), syscall.Errno(resp.GetErrno_())
}
+// 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()
+ req := pb.SetSockOptIntRequest{
+ Sockfd: sockfd,
+ Level: level,
+ Optname: optname,
+ Intval: optval,
+ }
+ resp, err := dut.posixServer.SetSockOptInt(ctx, &req)
+ if err != nil {
+ dut.t.Fatalf("failed to call SetSockOptInt: %s", err)
+ }
+ return resp.GetRet(), syscall.Errno(resp.GetErrno_())
+}
+
// 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.