543 lines
21 KiB
C++
543 lines
21 KiB
C++
// Copyright 2018 Google LLC
|
|
//
|
|
// 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 "test/syscalls/linux/socket_ip_tcp_generic.h"
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <stdio.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "gtest/gtest.h"
|
|
#include "test/syscalls/linux/socket_test_util.h"
|
|
#include "test/util/test_util.h"
|
|
|
|
namespace gvisor {
|
|
namespace testing {
|
|
|
|
TEST_P(TCPSocketPairTest, TcpInfoSucceedes) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
struct tcp_info opt = {};
|
|
socklen_t optLen = sizeof(opt);
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen),
|
|
SyscallSucceeds());
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, ShortTcpInfoSucceedes) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
struct tcp_info opt = {};
|
|
socklen_t optLen = 1;
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen),
|
|
SyscallSucceeds());
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, ZeroTcpInfoSucceedes) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
struct tcp_info opt = {};
|
|
socklen_t optLen = 0;
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen),
|
|
SyscallSucceeds());
|
|
}
|
|
|
|
// This test validates that an RST is sent instead of a FIN when data is
|
|
// unread on calls to close(2).
|
|
TEST_P(TCPSocketPairTest, RSTSentOnCloseWithUnreadData) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
char buf[10] = {};
|
|
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Wait until t_ sees the data on its side but don't read it.
|
|
struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0};
|
|
constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data.
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Now close the connected without reading the data.
|
|
ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
|
|
|
|
// Wait for the other end to receive the RST (up to 20 seconds).
|
|
struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0};
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// A shutdown with unread data will cause a RST to be sent instead
|
|
// of a FIN, per RFC 2525 section 2.17; this is also what Linux does.
|
|
ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallFailsWithErrno(ECONNRESET));
|
|
}
|
|
|
|
// This test will validate that a RST will cause POLLHUP to trigger.
|
|
TEST_P(TCPSocketPairTest, RSTCausesPollHUP) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
char buf[10] = {};
|
|
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Wait until second sees the data on its side but don't read it.
|
|
struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0};
|
|
constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data.
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN);
|
|
|
|
// Confirm we at least have one unread byte.
|
|
int bytes_available = 0;
|
|
ASSERT_THAT(
|
|
RetryEINTR(ioctl)(sockets->second_fd(), FIONREAD, &bytes_available),
|
|
SyscallSucceeds());
|
|
EXPECT_GT(bytes_available, 0);
|
|
|
|
// Now close the connected socket without reading the data from the second,
|
|
// this will cause a RST and we should see that with POLLHUP.
|
|
ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
|
|
|
|
// Wait for the other end to receive the RST (up to 20 seconds).
|
|
struct pollfd poll_fd3 = {sockets->first_fd(), POLLHUP, 0};
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd3, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
ASSERT_NE(poll_fd.revents & (POLLHUP | POLLIN), 0);
|
|
}
|
|
|
|
// This test validates that even if a RST is sent the other end will not
|
|
// get an ECONNRESET until it's read all data.
|
|
TEST_P(TCPSocketPairTest, RSTSentOnCloseWithUnreadDataAllowsReadBuffered) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
char buf[10] = {};
|
|
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
ASSERT_THAT(RetryEINTR(write)(sockets->second_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Wait until second sees the data on its side but don't read it.
|
|
struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0};
|
|
constexpr int kPollTimeoutMs = 30000; // Wait up to 30 seconds for the data.
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Wait until first sees the data on its side but don't read it.
|
|
struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN, 0};
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Now close the connected socket without reading the data from the second.
|
|
ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
|
|
|
|
// Wait for the other end to receive the RST (up to 30 seconds).
|
|
struct pollfd poll_fd3 = {sockets->first_fd(), POLLHUP, 0};
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd3, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Since we also have data buffered we should be able to read it before
|
|
// the syscall will fail with ECONNRESET.
|
|
ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// A shutdown with unread data will cause a RST to be sent instead
|
|
// of a FIN, per RFC 2525 section 2.17; this is also what Linux does.
|
|
ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallFailsWithErrno(ECONNRESET));
|
|
}
|
|
|
|
// This test will verify that a clean shutdown (FIN) is preformed when there
|
|
// is unread data but only the write side is closed.
|
|
TEST_P(TCPSocketPairTest, FINSentOnShutdownWrWithUnreadData) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
char buf[10] = {};
|
|
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Wait until t_ sees the data on its side but don't read it.
|
|
struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0};
|
|
constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data.
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Now shutdown the write end leaving the read end open.
|
|
ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_WR), SyscallSucceeds());
|
|
|
|
// Wait for the other end to receive the FIN (up to 20 seconds).
|
|
struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0};
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Since we didn't shutdown the read end this will be a clean close.
|
|
ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(0));
|
|
}
|
|
|
|
// This test will verify that when data is received by a socket, even if it's
|
|
// not read SHUT_RD will not cause any packets to be generated and data will
|
|
// remain in the buffer and can be read later.
|
|
TEST_P(TCPSocketPairTest, ShutdownRdShouldCauseNoPacketsWithUnreadData) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
char buf[10] = {};
|
|
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Wait until t_ sees the data on its side but don't read it.
|
|
struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0};
|
|
constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data.
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Now shutdown the read end, this will generate no packets to the other end.
|
|
ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RD), SyscallSucceeds());
|
|
|
|
// We should not receive any events on the other side of the socket.
|
|
struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0};
|
|
constexpr int kPollNoResponseTimeoutMs = 3000;
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollNoResponseTimeoutMs),
|
|
SyscallSucceedsWithValue(0)); // Timeout.
|
|
|
|
// Even though we did a SHUT_RD on the read end we can still read the data.
|
|
ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, ClosedReadNonBlockingSocket) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
// Set the read end to O_NONBLOCK.
|
|
int opts = 0;
|
|
ASSERT_THAT(opts = fcntl(sockets->second_fd(), F_GETFL), SyscallSucceeds());
|
|
ASSERT_THAT(fcntl(sockets->second_fd(), F_SETFL, opts | O_NONBLOCK),
|
|
SyscallSucceeds());
|
|
|
|
char buf[10] = {};
|
|
ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Wait until second_fd sees the data and then recv it.
|
|
struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0};
|
|
constexpr int kPollTimeoutMs = 2000; // Wait up to 2 seconds for the data.
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Now shutdown the write end leaving the read end open.
|
|
ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds());
|
|
|
|
// Wait for close notification and recv again.
|
|
struct pollfd poll_fd2 = {sockets->second_fd(), POLLIN, 0};
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0),
|
|
SyscallSucceedsWithValue(0));
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest,
|
|
ShutdownRdUnreadDataShouldCauseNoPacketsUnlessClosed) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
char buf[10] = {};
|
|
ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
// Wait until t_ sees the data on its side but don't read it.
|
|
struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0};
|
|
constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data.
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1));
|
|
|
|
// Now shutdown the read end, this will generate no packets to the other end.
|
|
ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RD), SyscallSucceeds());
|
|
|
|
// We should not receive any events on the other side of the socket.
|
|
struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0};
|
|
constexpr int kPollNoResponseTimeoutMs = 3000;
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollNoResponseTimeoutMs),
|
|
SyscallSucceedsWithValue(0)); // Timeout.
|
|
|
|
// Now since we've fully closed the connection it will generate a RST.
|
|
ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
|
|
ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs),
|
|
SyscallSucceedsWithValue(1)); // The other end has closed.
|
|
|
|
// A shutdown with unread data will cause a RST to be sent instead
|
|
// of a FIN, per RFC 2525 section 2.17; this is also what Linux does.
|
|
ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)),
|
|
SyscallFailsWithErrno(ECONNRESET));
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, TCPCorkDefault) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(
|
|
getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOff);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPCork) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK,
|
|
&kSockOptOn, sizeof(kSockOptOn)),
|
|
SyscallSucceeds());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(
|
|
getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOn);
|
|
|
|
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK,
|
|
&kSockOptOff, sizeof(kSockOptOff)),
|
|
SyscallSucceeds());
|
|
|
|
EXPECT_THAT(
|
|
getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOff);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, TCPCork) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK,
|
|
&kSockOptOn, sizeof(kSockOptOn)),
|
|
SyscallSucceeds());
|
|
|
|
constexpr char kData[] = "abc";
|
|
ASSERT_THAT(WriteFd(sockets->first_fd(), kData, sizeof(kData)),
|
|
SyscallSucceedsWithValue(sizeof(kData)));
|
|
|
|
ASSERT_NO_FATAL_FAILURE(RecvNoData(sockets->second_fd()));
|
|
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK,
|
|
&kSockOptOff, sizeof(kSockOptOff)),
|
|
SyscallSucceeds());
|
|
|
|
// Create a receive buffer larger than kData.
|
|
char buf[(sizeof(kData) + 1) * 2] = {};
|
|
ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0),
|
|
SyscallSucceedsWithValue(sizeof(kData)));
|
|
EXPECT_EQ(absl::string_view(kData, sizeof(kData)),
|
|
absl::string_view(buf, sizeof(kData)));
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, TCPQuickAckDefault) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get,
|
|
&get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOn);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPQuickAck) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK,
|
|
&kSockOptOff, sizeof(kSockOptOff)),
|
|
SyscallSucceeds());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get,
|
|
&get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOff);
|
|
|
|
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK,
|
|
&kSockOptOn, sizeof(kSockOptOn)),
|
|
SyscallSucceeds());
|
|
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get,
|
|
&get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOn);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SoKeepaliveDefault) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(
|
|
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, &get, &get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOff);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetSoKeepalive) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE,
|
|
&kSockOptOn, sizeof(kSockOptOn)),
|
|
SyscallSucceeds());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(
|
|
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, &get, &get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOn);
|
|
|
|
ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE,
|
|
&kSockOptOff, sizeof(kSockOptOff)),
|
|
SyscallSucceeds());
|
|
|
|
EXPECT_THAT(
|
|
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, &get, &get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOff);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, TCPKeepidleDefault) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, &get,
|
|
&get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, 2 * 60 * 60); // 2 hours.
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, TCPKeepintvlDefault) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL, &get,
|
|
&get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, 75); // 75 seconds.
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPKeepidleZero) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
constexpr int kZero = 0;
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, &kZero,
|
|
sizeof(kZero)),
|
|
SyscallFailsWithErrno(EINVAL));
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPKeepintvlZero) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
constexpr int kZero = 0;
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL,
|
|
&kZero, sizeof(kZero)),
|
|
SyscallFailsWithErrno(EINVAL));
|
|
}
|
|
|
|
// Copied from include/net/tcp.h.
|
|
constexpr int MAX_TCP_KEEPIDLE = 32767;
|
|
constexpr int MAX_TCP_KEEPINTVL = 32767;
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPKeepidleAboveMax) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
constexpr int kAboveMax = MAX_TCP_KEEPIDLE + 1;
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE,
|
|
&kAboveMax, sizeof(kAboveMax)),
|
|
SyscallFailsWithErrno(EINVAL));
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPKeepintvlAboveMax) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
constexpr int kAboveMax = MAX_TCP_KEEPINTVL + 1;
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL,
|
|
&kAboveMax, sizeof(kAboveMax)),
|
|
SyscallFailsWithErrno(EINVAL));
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPKeepidleToMax) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE,
|
|
&MAX_TCP_KEEPIDLE, sizeof(MAX_TCP_KEEPIDLE)),
|
|
SyscallSucceedsWithValue(0));
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, &get,
|
|
&get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, MAX_TCP_KEEPIDLE);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetTCPKeepintvlToMax) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL,
|
|
&MAX_TCP_KEEPINTVL, sizeof(MAX_TCP_KEEPINTVL)),
|
|
SyscallSucceedsWithValue(0));
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL, &get,
|
|
&get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, MAX_TCP_KEEPINTVL);
|
|
}
|
|
|
|
TEST_P(TCPSocketPairTest, SetOOBInline) {
|
|
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
|
|
|
EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_OOBINLINE,
|
|
&kSockOptOn, sizeof(kSockOptOn)),
|
|
SyscallSucceeds());
|
|
|
|
int get = -1;
|
|
socklen_t get_len = sizeof(get);
|
|
EXPECT_THAT(
|
|
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_OOBINLINE, &get, &get_len),
|
|
SyscallSucceedsWithValue(0));
|
|
EXPECT_EQ(get_len, sizeof(get));
|
|
EXPECT_EQ(get, kSockOptOn);
|
|
}
|
|
|
|
} // namespace testing
|
|
} // namespace gvisor
|