summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorBhasker Hariharan <bhaskerh@google.com>2021-02-02 11:03:37 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-02 11:05:28 -0800
commit8c7c5abafbd8a72a43105cc352b42e48c12a99e8 (patch)
tree99949c5fb992f8af16e686241840ed13683df5a8 /test
parent3817c7349de2dde950fd65dcab1f4859c095eeaf (diff)
Add support for rate limiting out of window ACKs.
Netstack today will send dupACK's with no rate limit for incoming out of window segments. This can result in ACK loops for example if a TCP socket connects to itself (actually permitted by TCP). Where the ACK sent in response to packets being out of order itself gets considered as an out of window segment resulting in another ACK being generated. PiperOrigin-RevId: 355206877
Diffstat (limited to 'test')
-rw-r--r--test/packetimpact/tests/tcp_outside_the_window_test.go18
-rw-r--r--test/syscalls/linux/tcp_socket.cc36
2 files changed, 54 insertions, 0 deletions
diff --git a/test/packetimpact/tests/tcp_outside_the_window_test.go b/test/packetimpact/tests/tcp_outside_the_window_test.go
index 1b041932a..8909a348e 100644
--- a/test/packetimpact/tests/tcp_outside_the_window_test.go
+++ b/test/packetimpact/tests/tcp_outside_the_window_test.go
@@ -84,6 +84,24 @@ func TestTCPOutsideTheWindow(t *testing.T) {
if tt.expectACK && err != nil {
t.Fatalf("expected an ACK packet within %s but got none: %s", timeout, err)
}
+ // Data packets w/o SYN bits are always acked by Linux. Netstack ACK's data packets
+ // always right now. So only send a second segment and test for no ACK for packets
+ // with no data.
+ if tt.expectACK && tt.payload == nil {
+ // Sending another out-of-window segment immediately should not trigger
+ // an ACK if less than 500ms(default rate limit for out-of-window ACKs)
+ // has passed since the last ACK was sent.
+ t.Logf("sending another segment")
+ conn.Send(t, testbench.TCP{
+ Flags: testbench.Uint8(tt.tcpFlags),
+ SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
+ }, tt.payload...)
+ timeout := 3 * time.Second
+ gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck), AckNum: localSeqNum}, timeout)
+ if err == nil {
+ t.Fatalf("expected no ACK packet but got one: %s", gotACK)
+ }
+ }
if !tt.expectACK && gotACK != nil {
t.Fatalf("expected no ACK packet within %s but got one: %s", timeout, gotACK)
}
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index 9028ab024..f56c50e61 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -1168,6 +1168,42 @@ TEST_P(SimpleTcpSocketTest, SelfConnectSendRecv_NoRandomSave) {
EXPECT_EQ(read_bytes, kBufSz);
}
+TEST_P(SimpleTcpSocketTest, SelfConnectSend_NoRandomSave) {
+ // Initialize address to the loopback one.
+ sockaddr_storage addr =
+ ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
+ socklen_t addrlen = sizeof(addr);
+
+ const FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ constexpr int max_seg = 256;
+ ASSERT_THAT(
+ setsockopt(s.get(), SOL_TCP, TCP_MAXSEG, &max_seg, sizeof(max_seg)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(bind(s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen),
+ SyscallSucceeds());
+ // Get the bound port.
+ ASSERT_THAT(
+ getsockname(s.get(), reinterpret_cast<struct sockaddr*>(&addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(
+ s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen),
+ SyscallSucceeds());
+
+ std::vector<char> writebuf(512 << 10); // 512 KiB.
+
+ // Try to send the whole thing.
+ int n;
+ ASSERT_THAT(n = SendFd(s.get(), writebuf.data(), writebuf.size(), 0),
+ SyscallSucceeds());
+
+ // We should have written the whole thing.
+ EXPECT_EQ(n, writebuf.size());
+ EXPECT_THAT(shutdown(s.get(), SHUT_WR), SyscallSucceedsWithValue(0));
+}
+
TEST_P(SimpleTcpSocketTest, NonBlockingConnect) {
const FileDescriptor listener =
ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));