summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/socket_inet_loopback.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/socket_inet_loopback.cc')
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc2264
1 files changed, 0 insertions, 2264 deletions
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
deleted file mode 100644
index b24618a88..000000000
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ /dev/null
@@ -1,2264 +0,0 @@
-// Copyright 2018 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 <netinet/in.h>
-#include <netinet/tcp.h>
-#include <poll.h>
-#include <string.h>
-
-#include <atomic>
-#include <iostream>
-#include <memory>
-#include <string>
-#include <tuple>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
-#include "test/util/file_descriptor.h"
-#include "test/util/posix_error.h"
-#include "test/util/save_util.h"
-#include "test/util/test_util.h"
-#include "test/util/thread_util.h"
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-using ::testing::Gt;
-
-PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
- switch (family) {
- case AF_INET:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
- case AF_INET6:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
- switch (family) {
- case AF_INET:
- reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
- return NoError();
- case AF_INET6:
- reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
- return NoError();
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-struct TestParam {
- TestAddress listener;
- TestAddress connector;
-};
-
-std::string DescribeTestParam(::testing::TestParamInfo<TestParam> const& info) {
- return absl::StrCat("Listen", info.param.listener.description, "_Connect",
- info.param.connector.description);
-}
-
-using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>;
-
-TEST(BadSocketPairArgs, ValidateErrForBadCallsToSocketPair) {
- int fd[2] = {};
-
- // Valid AF but invalid for socketpair(2) return ESOCKTNOSUPPORT.
- ASSERT_THAT(socketpair(AF_INET, 0, 0, fd),
- SyscallFailsWithErrno(ESOCKTNOSUPPORT));
- ASSERT_THAT(socketpair(AF_INET6, 0, 0, fd),
- SyscallFailsWithErrno(ESOCKTNOSUPPORT));
-
- // Invalid AF will return ENOAFSUPPORT.
- ASSERT_THAT(socketpair(AF_MAX, 0, 0, fd),
- SyscallFailsWithErrno(EAFNOSUPPORT));
- ASSERT_THAT(socketpair(8675309, 0, 0, fd),
- SyscallFailsWithErrno(EAFNOSUPPORT));
-}
-
-enum class Operation {
- Bind,
- Connect,
- SendTo,
-};
-
-std::string OperationToString(Operation operation) {
- switch (operation) {
- case Operation::Bind:
- return "Bind";
- case Operation::Connect:
- return "Connect";
- case Operation::SendTo:
- return "SendTo";
- }
-}
-
-using OperationSequence = std::vector<Operation>;
-
-using DualStackSocketTest =
- ::testing::TestWithParam<std::tuple<TestAddress, OperationSequence>>;
-
-TEST_P(DualStackSocketTest, AddressOperations) {
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_DGRAM, 0));
-
- const TestAddress& addr = std::get<0>(GetParam());
- const OperationSequence& operations = std::get<1>(GetParam());
-
- auto addr_in = reinterpret_cast<const sockaddr*>(&addr.addr);
-
- // sockets may only be bound once. Both `connect` and `sendto` cause a socket
- // to be bound.
- bool bound = false;
- for (const Operation& operation : operations) {
- bool sockname = false;
- bool peername = false;
- switch (operation) {
- case Operation::Bind: {
- ASSERT_NO_ERRNO(SetAddrPort(
- addr.family(), const_cast<sockaddr_storage*>(&addr.addr), 0));
-
- int bind_ret = bind(fd.get(), addr_in, addr.addr_len);
-
- // Dual stack sockets may only be bound to AF_INET6.
- if (!bound && addr.family() == AF_INET6) {
- EXPECT_THAT(bind_ret, SyscallSucceeds());
- bound = true;
-
- sockname = true;
- } else {
- EXPECT_THAT(bind_ret, SyscallFailsWithErrno(EINVAL));
- }
- break;
- }
- case Operation::Connect: {
- ASSERT_NO_ERRNO(SetAddrPort(
- addr.family(), const_cast<sockaddr_storage*>(&addr.addr), 1337));
-
- EXPECT_THAT(connect(fd.get(), addr_in, addr.addr_len),
- SyscallSucceeds())
- << GetAddrStr(addr_in);
- bound = true;
-
- sockname = true;
- peername = true;
-
- break;
- }
- case Operation::SendTo: {
- const char payload[] = "hello";
- ASSERT_NO_ERRNO(SetAddrPort(
- addr.family(), const_cast<sockaddr_storage*>(&addr.addr), 1337));
-
- ssize_t sendto_ret = sendto(fd.get(), &payload, sizeof(payload), 0,
- addr_in, addr.addr_len);
-
- EXPECT_THAT(sendto_ret, SyscallSucceedsWithValue(sizeof(payload)));
- sockname = !bound;
- bound = true;
- break;
- }
- }
-
- if (sockname) {
- sockaddr_storage sock_addr;
- socklen_t addrlen = sizeof(sock_addr);
- ASSERT_THAT(getsockname(fd.get(), reinterpret_cast<sockaddr*>(&sock_addr),
- &addrlen),
- SyscallSucceeds());
- ASSERT_EQ(addrlen, sizeof(struct sockaddr_in6));
-
- auto sock_addr_in6 = reinterpret_cast<const sockaddr_in6*>(&sock_addr);
-
- if (operation == Operation::SendTo) {
- EXPECT_EQ(sock_addr_in6->sin6_family, AF_INET6);
- EXPECT_TRUE(IN6_IS_ADDR_UNSPECIFIED(sock_addr_in6->sin6_addr.s6_addr32))
- << OperationToString(operation) << " getsocknam="
- << GetAddrStr(reinterpret_cast<sockaddr*>(&sock_addr));
-
- EXPECT_NE(sock_addr_in6->sin6_port, 0);
- } else if (IN6_IS_ADDR_V4MAPPED(
- reinterpret_cast<const sockaddr_in6*>(addr_in)
- ->sin6_addr.s6_addr32)) {
- EXPECT_TRUE(IN6_IS_ADDR_V4MAPPED(sock_addr_in6->sin6_addr.s6_addr32))
- << OperationToString(operation) << " getsocknam="
- << GetAddrStr(reinterpret_cast<sockaddr*>(&sock_addr));
- }
- }
-
- if (peername) {
- sockaddr_storage peer_addr;
- socklen_t addrlen = sizeof(peer_addr);
- ASSERT_THAT(getpeername(fd.get(), reinterpret_cast<sockaddr*>(&peer_addr),
- &addrlen),
- SyscallSucceeds());
- ASSERT_EQ(addrlen, sizeof(struct sockaddr_in6));
-
- if (addr.family() == AF_INET ||
- IN6_IS_ADDR_V4MAPPED(reinterpret_cast<const sockaddr_in6*>(addr_in)
- ->sin6_addr.s6_addr32)) {
- EXPECT_TRUE(IN6_IS_ADDR_V4MAPPED(
- reinterpret_cast<const sockaddr_in6*>(&peer_addr)
- ->sin6_addr.s6_addr32))
- << OperationToString(operation) << " getpeername="
- << GetAddrStr(reinterpret_cast<sockaddr*>(&peer_addr));
- }
- }
- }
-}
-
-// TODO(gvisor.dev/issues/1556): uncomment V4MappedAny.
-INSTANTIATE_TEST_SUITE_P(
- All, DualStackSocketTest,
- ::testing::Combine(
- ::testing::Values(V4Any(), V4Loopback(), /*V4MappedAny(),*/
- V4MappedLoopback(), V6Any(), V6Loopback()),
- ::testing::ValuesIn<OperationSequence>(
- {{Operation::Bind, Operation::Connect, Operation::SendTo},
- {Operation::Bind, Operation::SendTo, Operation::Connect},
- {Operation::Connect, Operation::Bind, Operation::SendTo},
- {Operation::Connect, Operation::SendTo, Operation::Bind},
- {Operation::SendTo, Operation::Bind, Operation::Connect},
- {Operation::SendTo, Operation::Connect, Operation::Bind}})),
- [](::testing::TestParamInfo<
- std::tuple<TestAddress, OperationSequence>> const& info) {
- const TestAddress& addr = std::get<0>(info.param);
- const OperationSequence& operations = std::get<1>(info.param);
- std::string s = addr.description;
- for (const Operation& operation : operations) {
- absl::StrAppend(&s, OperationToString(operation));
- }
- return s;
- });
-
-void tcpSimpleConnectTest(TestAddress const& listener,
- TestAddress const& connector, bool unbound) {
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- if (!unbound) {
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- }
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- //
- // We have to assign a name to the accepted socket, as unamed temporary
- // objects are destructed upon full evaluation of the expression it is in,
- // potentially causing the connecting socket to fail to shutdown properly.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- ASSERT_THAT(shutdown(listen_fd.get(), SHUT_RDWR), SyscallSucceeds());
-
- ASSERT_THAT(shutdown(conn_fd.get(), SHUT_RDWR), SyscallSucceeds());
-}
-
-TEST_P(SocketInetLoopbackTest, TCP) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- tcpSimpleConnectTest(listener, connector, true);
-}
-
-TEST_P(SocketInetLoopbackTest, TCPListenUnbound) {
- auto const& param = GetParam();
-
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- tcpSimpleConnectTest(listener, connector, false);
-}
-
-TEST_P(SocketInetLoopbackTest, TCPListenClose) {
- auto const& param = GetParam();
-
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- constexpr int kAcceptCount = 32;
- constexpr int kBacklog = kAcceptCount * 2;
- constexpr int kFDs = 128;
- constexpr int kThreadCount = 4;
- constexpr int kFDsPerThread = kFDs / kThreadCount;
-
- // Create the listening socket.
- FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), kBacklog), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- DisableSave ds; // Too many system calls.
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- FileDescriptor clients[kFDs];
- std::unique_ptr<ScopedThread> threads[kThreadCount];
- for (int i = 0; i < kFDs; i++) {
- clients[i] = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
- }
- for (int i = 0; i < kThreadCount; i++) {
- threads[i] = absl::make_unique<ScopedThread>([&connector, &conn_addr,
- &clients, i]() {
- for (int j = 0; j < kFDsPerThread; j++) {
- int k = i * kFDsPerThread + j;
- int ret =
- connect(clients[k].get(), reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len);
- if (ret != 0) {
- EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS));
- }
- }
- });
- }
- for (int i = 0; i < kThreadCount; i++) {
- threads[i]->Join();
- }
- for (int i = 0; i < kAcceptCount; i++) {
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
- }
- // TODO(b/138400178): Fix cooperative S/R failure when ds.reset() is invoked
- // before function end.
- // ds.reset();
-}
-
-TEST_P(SocketInetLoopbackTest, TCPbacklog) {
- auto const& param = GetParam();
-
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), 2), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
- int i = 0;
- while (1) {
- int ret;
-
- // Connect to the listening socket.
- const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ret = connect(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len);
- if (ret != 0) {
- EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS));
- struct pollfd pfd = {
- .fd = conn_fd.get(),
- .events = POLLOUT,
- };
- ret = poll(&pfd, 1, 3000);
- if (ret == 0) break;
- EXPECT_THAT(ret, SyscallSucceedsWithValue(1));
- }
- EXPECT_THAT(RetryEINTR(send)(conn_fd.get(), &i, sizeof(i), 0),
- SyscallSucceedsWithValue(sizeof(i)));
- ASSERT_THAT(shutdown(conn_fd.get(), SHUT_RDWR), SyscallSucceeds());
- i++;
- }
-
- for (; i != 0; i--) {
- // Accept the connection.
- //
- // We have to assign a name to the accepted socket, as unamed temporary
- // objects are destructed upon full evaluation of the expression it is in,
- // potentially causing the connecting socket to fail to shutdown properly.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
- }
-}
-
-// TCPFinWait2Test creates a pair of connected sockets then closes one end to
-// trigger FIN_WAIT2 state for the closed endpoint. Then it binds the same local
-// IP/port on a new socket and tries to connect. The connect should fail w/
-// an EADDRINUSE. Then we wait till the FIN_WAIT2 timeout is over and try the
-// connect again with a new socket and this time it should succeed.
-//
-// TCP timers are not S/R today, this can cause this test to be flaky when run
-// under random S/R due to timer being reset on a restore.
-TEST_P(SocketInetLoopbackTest, TCPFinWait2Test_NoRandomSave) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- // Lower FIN_WAIT2 state to 5 seconds for test.
- constexpr int kTCPLingerTimeout = 5;
- EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
- &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
- SyscallSucceedsWithValue(0));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- sockaddr_storage conn_bound_addr;
- socklen_t conn_addrlen = connector.addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
- &conn_addrlen),
- SyscallSucceeds());
-
- // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
- conn_fd.reset();
-
- // Now bind and connect a new socket.
- const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- // Disable cooperative saves after this point. As a save between the first
- // bind/connect and the second one can cause the linger timeout timer to
- // be restarted causing the final bind/connect to fail.
- DisableSave ds;
-
- // TODO(gvisor.dev/issue/1030): Portmanager does not track all 5 tuple
- // reservations which causes the bind() to succeed on gVisor but connect
- // correctly fails.
- if (IsRunningOnGvisor()) {
- ASSERT_THAT(
- bind(conn_fd2.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
- conn_addrlen),
- SyscallSucceeds());
- ASSERT_THAT(RetryEINTR(connect)(conn_fd2.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- conn_addrlen),
- SyscallFailsWithErrno(EADDRINUSE));
- } else {
- ASSERT_THAT(
- bind(conn_fd2.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
- conn_addrlen),
- SyscallFailsWithErrno(EADDRINUSE));
- }
-
- // Sleep for a little over the linger timeout to reduce flakiness in
- // save/restore tests.
- absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 2));
-
- ds.reset();
-
- if (!IsRunningOnGvisor()) {
- ASSERT_THAT(
- bind(conn_fd2.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
- conn_addrlen),
- SyscallSucceeds());
- }
- ASSERT_THAT(RetryEINTR(connect)(conn_fd2.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- conn_addrlen),
- SyscallSucceeds());
-}
-
-// TCPLinger2TimeoutAfterClose creates a pair of connected sockets
-// then closes one end to trigger FIN_WAIT2 state for the closed endpont.
-// It then sleeps for the TCP_LINGER2 timeout and verifies that bind/
-// connecting the same address succeeds.
-//
-// TCP timers are not S/R today, this can cause this test to be flaky when run
-// under random S/R due to timer being reset on a restore.
-TEST_P(SocketInetLoopbackTest, TCPLinger2TimeoutAfterClose_NoRandomSave) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- sockaddr_storage conn_bound_addr;
- socklen_t conn_addrlen = connector.addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
- &conn_addrlen),
- SyscallSucceeds());
-
- constexpr int kTCPLingerTimeout = 5;
- EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
- &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
- SyscallSucceedsWithValue(0));
-
- // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
- conn_fd.reset();
-
- absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 1));
-
- // Now bind and connect a new socket and verify that we can immediately
- // rebind the address bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- ASSERT_THAT(bind(conn_fd2.get(),
- reinterpret_cast<sockaddr*>(&conn_bound_addr), conn_addrlen),
- SyscallSucceeds());
- ASSERT_THAT(RetryEINTR(connect)(conn_fd2.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- conn_addrlen),
- SyscallSucceeds());
-}
-
-// TCPResetAfterClose creates a pair of connected sockets then closes
-// one end to trigger FIN_WAIT2 state for the closed endpoint verifies
-// that we generate RSTs for any new data after the socket is fully
-// closed.
-TEST_P(SocketInetLoopbackTest, TCPResetAfterClose) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
- conn_fd.reset();
-
- int data = 1234;
-
- // Now send data which should trigger a RST as the other end should
- // have timed out and closed the socket.
- EXPECT_THAT(RetryEINTR(send)(accepted.get(), &data, sizeof(data), 0),
- SyscallSucceeds());
- // Sleep for a shortwhile to get a RST back.
- absl::SleepFor(absl::Seconds(1));
-
- // Try writing again and we should get an EPIPE back.
- EXPECT_THAT(RetryEINTR(send)(accepted.get(), &data, sizeof(data), 0),
- SyscallFailsWithErrno(EPIPE));
-
- // Trying to read should return zero as the other end did send
- // us a FIN. We do it twice to verify that the RST does not cause an
- // ECONNRESET on the read after EOF has been read by applicaiton.
- EXPECT_THAT(RetryEINTR(recv)(accepted.get(), &data, sizeof(data), 0),
- SyscallSucceedsWithValue(0));
- EXPECT_THAT(RetryEINTR(recv)(accepted.get(), &data, sizeof(data), 0),
- SyscallSucceedsWithValue(0));
-}
-
-// This test is disabled under random save as the the restore run
-// results in the stack.Seed() being different which can cause
-// sequence number of final connect to be one that is considered
-// old and can cause the test to be flaky.
-TEST_P(SocketInetLoopbackTest, TCPTimeWaitTest_NoRandomSave) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- // We disable saves after this point as a S/R causes the netstack seed
- // to be regenerated which changes what ports/ISN is picked for a given
- // tuple (src ip,src port, dst ip, dst port). This can cause the final
- // SYN to use a sequence number that looks like one from the current
- // connection in TIME_WAIT and will not be accepted causing the test
- // to timeout.
- //
- // TODO(gvisor.dev/issue/940): S/R portSeed/portHint
- DisableSave ds;
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- sockaddr_storage conn_bound_addr;
- socklen_t conn_addrlen = connector.addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
- &conn_addrlen),
- SyscallSucceeds());
-
- // close the accept FD to trigger TIME_WAIT on the accepted socket which
- // should cause the conn_fd to follow CLOSE_WAIT->LAST_ACK->CLOSED instead of
- // TIME_WAIT.
- accepted.reset();
- absl::SleepFor(absl::Seconds(1));
- conn_fd.reset();
- absl::SleepFor(absl::Seconds(1));
-
- // Now bind and connect a new socket and verify that we can immediately
- // rebind the address bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- ASSERT_THAT(bind(conn_fd2.get(),
- reinterpret_cast<sockaddr*>(&conn_bound_addr), conn_addrlen),
- SyscallSucceeds());
- ASSERT_THAT(RetryEINTR(connect)(conn_fd2.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- conn_addrlen),
- SyscallSucceeds());
-}
-
-TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- const uint16_t port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Set the userTimeout on the listening socket.
- constexpr int kUserTimeout = 10;
- ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_USER_TIMEOUT,
- &kUserTimeout, sizeof(kUserTimeout)),
- SyscallSucceeds());
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
- // Verify that the accepted socket inherited the user timeout set on
- // listening socket.
- int get = -1;
- socklen_t get_len = sizeof(get);
- ASSERT_THAT(
- getsockopt(accepted.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len),
- SyscallSucceeds());
- EXPECT_EQ(get_len, sizeof(get));
- EXPECT_EQ(get, kUserTimeout);
-}
-
-// TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
-// saved. Enable S/R once issue is fixed.
-TEST_P(SocketInetLoopbackTest, TCPDeferAccept_NoRandomSave) {
- // TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
- // saved. Enable S/R issue is fixed.
- DisableSave ds;
-
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- const uint16_t port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Set the TCP_DEFER_ACCEPT on the listening socket.
- constexpr int kTCPDeferAccept = 3;
- ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT,
- &kTCPDeferAccept, sizeof(kTCPDeferAccept)),
- SyscallSucceeds());
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Set the listening socket to nonblock so that we can verify that there is no
- // connection in queue despite the connect above succeeding since the peer has
- // sent no data and TCP_DEFER_ACCEPT is set on the listening socket. Set the
- // FD to O_NONBLOCK.
- int opts;
- ASSERT_THAT(opts = fcntl(listen_fd.get(), F_GETFL), SyscallSucceeds());
- opts |= O_NONBLOCK;
- ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
-
- ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr),
- SyscallFailsWithErrno(EWOULDBLOCK));
-
- // Set FD back to blocking.
- opts &= ~O_NONBLOCK;
- ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
-
- // Now write some data to the socket.
- int data = 0;
- ASSERT_THAT(RetryEINTR(write)(conn_fd.get(), &data, sizeof(data)),
- SyscallSucceedsWithValue(sizeof(data)));
-
- // This should now cause the connection to complete and be delivered to the
- // accept socket.
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Verify that the accepted socket returns the data written.
- int get = -1;
- ASSERT_THAT(RetryEINTR(recv)(accepted.get(), &get, sizeof(get), 0),
- SyscallSucceedsWithValue(sizeof(get)));
-
- EXPECT_EQ(get, data);
-}
-
-// TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
-// saved. Enable S/R once issue is fixed.
-TEST_P(SocketInetLoopbackTest, TCPDeferAcceptTimeout_NoRandomSave) {
- // TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
- // saved. Enable S/R once issue is fixed.
- DisableSave ds;
-
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
- listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- const uint16_t port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Set the TCP_DEFER_ACCEPT on the listening socket.
- constexpr int kTCPDeferAccept = 3;
- ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT,
- &kTCPDeferAccept, sizeof(kTCPDeferAccept)),
- SyscallSucceeds());
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Set the listening socket to nonblock so that we can verify that there is no
- // connection in queue despite the connect above succeeding since the peer has
- // sent no data and TCP_DEFER_ACCEPT is set on the listening socket. Set the
- // FD to O_NONBLOCK.
- int opts;
- ASSERT_THAT(opts = fcntl(listen_fd.get(), F_GETFL), SyscallSucceeds());
- opts |= O_NONBLOCK;
- ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
-
- // Verify that there is no acceptable connection before TCP_DEFER_ACCEPT
- // timeout is hit.
- absl::SleepFor(absl::Seconds(kTCPDeferAccept - 1));
- ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr),
- SyscallFailsWithErrno(EWOULDBLOCK));
-
- // Set FD back to blocking.
- opts &= ~O_NONBLOCK;
- ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
-
- // Now sleep for a little over the TCP_DEFER_ACCEPT duration. When the timeout
- // is hit a SYN-ACK should be retransmitted by the listener as a last ditch
- // attempt to complete the connection with or without data.
- absl::SleepFor(absl::Seconds(2));
-
- // Verify that we have a connection that can be accepted even though no
- // data was written.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- All, SocketInetLoopbackTest,
- ::testing::Values(
- // Listeners bound to IPv4 addresses refuse connections using IPv6
- // addresses.
- TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()},
- TestParam{V4Any(), V4MappedAny()},
- TestParam{V4Any(), V4MappedLoopback()},
- TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
- TestParam{V4MappedAny(), V4Any()},
- TestParam{V4MappedAny(), V4Loopback()},
- TestParam{V4MappedAny(), V4MappedAny()},
- TestParam{V4MappedAny(), V4MappedLoopback()},
- TestParam{V4MappedLoopback(), V4Any()},
- TestParam{V4MappedLoopback(), V4Loopback()},
- TestParam{V4MappedLoopback(), V4MappedLoopback()},
-
- // Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()},
- TestParam{V6Any(), V4MappedAny()},
- TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()},
- TestParam{V6Any(), V6Loopback()},
-
- // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
- // addresses.
- TestParam{V6Loopback(), V6Any()},
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
-
-using SocketInetReusePortTest = ::testing::TestWithParam<TestParam>;
-
-// TODO(gvisor.dev/issue/940): Remove _NoRandomSave when portHint/stack.Seed is
-// saved/restored.
-TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread_NoRandomSave) {
- auto const& param = GetParam();
-
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
- sockaddr_storage listen_addr = listener.addr;
- sockaddr_storage conn_addr = connector.addr;
- constexpr int kThreadCount = 3;
- constexpr int kConnectAttempts = 10000;
-
- // Create the listening socket.
- FileDescriptor listener_fds[kThreadCount];
- for (int i = 0; i < kThreadCount; i++) {
- listener_fds[i] = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- int fd = listener_fds[i].get();
-
- ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(
- bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(fd, 40), SyscallSucceeds());
-
- // On the first bind we need to determine which port was bound.
- if (i != 0) {
- continue;
- }
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(
- getsockname(listener_fds[0].get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
- ASSERT_NO_ERRNO(SetAddrPort(listener.family(), &listen_addr, port));
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- }
-
- std::atomic<int> connects_received = ATOMIC_VAR_INIT(0);
- std::unique_ptr<ScopedThread> listen_thread[kThreadCount];
- int accept_counts[kThreadCount] = {};
- // TODO(avagin): figure how to not disable S/R for the whole test.
- // We need to take into account that this test executes a lot of system
- // calls from many threads.
- DisableSave ds;
-
- for (int i = 0; i < kThreadCount; i++) {
- listen_thread[i] = absl::make_unique<ScopedThread>(
- [&listener_fds, &accept_counts, i, &connects_received]() {
- do {
- auto fd = Accept(listener_fds[i].get(), nullptr, nullptr);
- if (!fd.ok()) {
- if (connects_received >= kConnectAttempts) {
- // Another thread have shutdown our read side causing the
- // accept to fail.
- break;
- }
- ASSERT_NO_ERRNO(fd);
- break;
- }
- // Receive some data from a socket to be sure that the connect()
- // system call has been completed on another side.
- // Do a short read and then close the socket to trigger a RST. This
- // ensures that both ends of the connection are cleaned up and no
- // goroutines hang around in TIME-WAIT. We do this so that this test
- // does not timeout under gotsan runs where lots of goroutines can
- // cause the test to use absurd amounts of memory.
- //
- // See: https://tools.ietf.org/html/rfc2525#page-50 section 2.17
- uint16_t data;
- EXPECT_THAT(
- RetryEINTR(recv)(fd.ValueOrDie().get(), &data, sizeof(data), 0),
- SyscallSucceedsWithValue(sizeof(data)));
- accept_counts[i]++;
- } while (++connects_received < kConnectAttempts);
-
- // Shutdown all sockets to wake up other threads.
- for (int j = 0; j < kThreadCount; j++) {
- shutdown(listener_fds[j].get(), SHUT_RDWR);
- }
- });
- }
-
- ScopedThread connecting_thread([&connector, &conn_addr]() {
- for (int i = 0; i < kConnectAttempts; i++) {
- const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(
- RetryEINTR(connect)(fd.get(), reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Do two separate sends to ensure two segments are received. This is
- // required for netstack where read is incorrectly assuming a whole
- // segment is read when endpoint.Read() is called which is technically
- // incorrect as the syscall that invoked endpoint.Read() may only
- // consume it partially. This results in a case where a close() of
- // such a socket does not trigger a RST in netstack due to the
- // endpoint assuming that the endpoint has no unread data.
- EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0),
- SyscallSucceedsWithValue(sizeof(i)));
-
- // TODO(gvisor.dev/issue/1449): Remove this block once netstack correctly
- // generates a RST.
- if (IsRunningOnGvisor()) {
- EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0),
- SyscallSucceedsWithValue(sizeof(i)));
- }
- }
- });
-
- // Join threads to be sure that all connections have been counted
- connecting_thread.Join();
- for (int i = 0; i < kThreadCount; i++) {
- listen_thread[i]->Join();
- }
- // Check that connections are distributed fairly between listening sockets
- for (int i = 0; i < kThreadCount; i++)
- EXPECT_THAT(accept_counts[i],
- EquivalentWithin((kConnectAttempts / kThreadCount), 0.10));
-}
-
-TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThread) {
- auto const& param = GetParam();
-
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
- sockaddr_storage listen_addr = listener.addr;
- sockaddr_storage conn_addr = connector.addr;
- constexpr int kThreadCount = 3;
-
- // Create the listening socket.
- FileDescriptor listener_fds[kThreadCount];
- for (int i = 0; i < kThreadCount; i++) {
- listener_fds[i] =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(listener.family(), SOCK_DGRAM, 0));
- int fd = listener_fds[i].get();
-
- ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(
- bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len),
- SyscallSucceeds());
-
- // On the first bind we need to determine which port was bound.
- if (i != 0) {
- continue;
- }
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(
- getsockname(listener_fds[0].get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
- ASSERT_NO_ERRNO(SetAddrPort(listener.family(), &listen_addr, port));
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- }
-
- constexpr int kConnectAttempts = 10000;
- std::atomic<int> packets_received = ATOMIC_VAR_INIT(0);
- std::unique_ptr<ScopedThread> receiver_thread[kThreadCount];
- int packets_per_socket[kThreadCount] = {};
- // TODO(avagin): figure how to not disable S/R for the whole test.
- DisableSave ds; // Too expensive.
-
- for (int i = 0; i < kThreadCount; i++) {
- receiver_thread[i] = absl::make_unique<ScopedThread>(
- [&listener_fds, &packets_per_socket, i, &packets_received]() {
- do {
- struct sockaddr_storage addr = {};
- socklen_t addrlen = sizeof(addr);
- int data;
-
- auto ret = RetryEINTR(recvfrom)(
- listener_fds[i].get(), &data, sizeof(data), 0,
- reinterpret_cast<struct sockaddr*>(&addr), &addrlen);
-
- if (packets_received < kConnectAttempts) {
- ASSERT_THAT(ret, SyscallSucceedsWithValue(sizeof(data)));
- }
-
- if (ret != sizeof(data)) {
- // Another thread may have shutdown our read side causing the
- // recvfrom to fail.
- break;
- }
-
- packets_received++;
- packets_per_socket[i]++;
-
- // A response is required to synchronize with the main thread,
- // otherwise the main thread can send more than can fit into receive
- // queues.
- EXPECT_THAT(RetryEINTR(sendto)(
- listener_fds[i].get(), &data, sizeof(data), 0,
- reinterpret_cast<sockaddr*>(&addr), addrlen),
- SyscallSucceedsWithValue(sizeof(data)));
- } while (packets_received < kConnectAttempts);
-
- // Shutdown all sockets to wake up other threads.
- for (int j = 0; j < kThreadCount; j++)
- shutdown(listener_fds[j].get(), SHUT_RDWR);
- });
- }
-
- ScopedThread main_thread([&connector, &conn_addr]() {
- for (int i = 0; i < kConnectAttempts; i++) {
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(connector.family(), SOCK_DGRAM, 0));
- EXPECT_THAT(RetryEINTR(sendto)(fd.get(), &i, sizeof(i), 0,
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceedsWithValue(sizeof(i)));
- int data;
- EXPECT_THAT(RetryEINTR(recv)(fd.get(), &data, sizeof(data), 0),
- SyscallSucceedsWithValue(sizeof(data)));
- }
- });
-
- main_thread.Join();
-
- // Join threads to be sure that all connections have been counted
- for (int i = 0; i < kThreadCount; i++) {
- receiver_thread[i]->Join();
- }
- // Check that packets are distributed fairly between listening sockets.
- for (int i = 0; i < kThreadCount; i++)
- EXPECT_THAT(packets_per_socket[i],
- EquivalentWithin((kConnectAttempts / kThreadCount), 0.10));
-}
-
-TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThreadShort) {
- auto const& param = GetParam();
-
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
- sockaddr_storage listen_addr = listener.addr;
- sockaddr_storage conn_addr = connector.addr;
- constexpr int kThreadCount = 3;
-
- // TODO(b/141211329): endpointsByNic.seed has to be saved/restored.
- const DisableSave ds141211329;
-
- // Create listening sockets.
- FileDescriptor listener_fds[kThreadCount];
- for (int i = 0; i < kThreadCount; i++) {
- listener_fds[i] =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(listener.family(), SOCK_DGRAM, 0));
- int fd = listener_fds[i].get();
-
- ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(
- bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len),
- SyscallSucceeds());
-
- // On the first bind we need to determine which port was bound.
- if (i != 0) {
- continue;
- }
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(
- getsockname(listener_fds[0].get(),
- reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
- ASSERT_NO_ERRNO(SetAddrPort(listener.family(), &listen_addr, port));
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- }
-
- constexpr int kConnectAttempts = 10;
- FileDescriptor client_fds[kConnectAttempts];
-
- // Do the first run without save/restore.
- DisableSave ds;
- for (int i = 0; i < kConnectAttempts; i++) {
- client_fds[i] =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(connector.family(), SOCK_DGRAM, 0));
- EXPECT_THAT(RetryEINTR(sendto)(client_fds[i].get(), &i, sizeof(i), 0,
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceedsWithValue(sizeof(i)));
- }
- ds.reset();
-
- // Check that a mapping of client and server sockets has
- // not been change after save/restore.
- for (int i = 0; i < kConnectAttempts; i++) {
- EXPECT_THAT(RetryEINTR(sendto)(client_fds[i].get(), &i, sizeof(i), 0,
- reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
- SyscallSucceedsWithValue(sizeof(i)));
- }
-
- struct pollfd pollfds[kThreadCount];
- for (int i = 0; i < kThreadCount; i++) {
- pollfds[i].fd = listener_fds[i].get();
- pollfds[i].events = POLLIN;
- }
-
- std::map<uint16_t, int> portToFD;
-
- int received = 0;
- while (received < kConnectAttempts * 2) {
- ASSERT_THAT(poll(pollfds, kThreadCount, -1),
- SyscallSucceedsWithValue(Gt(0)));
-
- for (int i = 0; i < kThreadCount; i++) {
- if ((pollfds[i].revents & POLLIN) == 0) {
- continue;
- }
-
- received++;
-
- const int fd = pollfds[i].fd;
- struct sockaddr_storage addr = {};
- socklen_t addrlen = sizeof(addr);
- int data;
- EXPECT_THAT(RetryEINTR(recvfrom)(
- fd, &data, sizeof(data), 0,
- reinterpret_cast<struct sockaddr*>(&addr), &addrlen),
- SyscallSucceedsWithValue(sizeof(data)));
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(connector.family(), addr));
- auto prev_port = portToFD.find(port);
- // Check that all packets from one client have been delivered to the
- // same server socket.
- if (prev_port == portToFD.end()) {
- portToFD[port] = fd;
- } else {
- EXPECT_EQ(portToFD[port], fd);
- }
- }
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- All, SocketInetReusePortTest,
- ::testing::Values(
- // Listeners bound to IPv4 addresses refuse connections using IPv6
- // addresses.
- TestParam{V4Any(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
-
- // Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Loopback()}, TestParam{V6Any(), V6Loopback()},
-
- // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
- // addresses.
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
-
-struct ProtocolTestParam {
- std::string description;
- int type;
-};
-
-std::string DescribeProtocolTestParam(
- ::testing::TestParamInfo<ProtocolTestParam> const& info) {
- return info.param.description;
-}
-
-using SocketMultiProtocolInetLoopbackTest =
- ::testing::TestWithParam<ProtocolTestParam>;
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedLoopbackOnlyReservesV4) {
- auto const& param = GetParam();
-
- for (int i = 0; true; i++) {
- // Bind the v4 loopback on a dual stack socket.
- TestAddress const& test_addr_dual = V4MappedLoopback();
- sockaddr_storage addr_dual = test_addr_dual.addr;
- const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_dual.family(), param.type, 0));
- ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual),
- test_addr_dual.addr_len),
- SyscallSucceeds());
-
- // Get the port that we bound.
- socklen_t addrlen = test_addr_dual.addr_len;
- ASSERT_THAT(getsockname(fd_dual.get(),
- reinterpret_cast<sockaddr*>(&addr_dual), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual));
-
- // Verify that we can still bind the v6 loopback on the same port.
- TestAddress const& test_addr_v6 = V6Loopback();
- sockaddr_storage addr_v6 = test_addr_v6.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port));
- const FileDescriptor fd_v6 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0));
- int ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6),
- test_addr_v6.addr_len);
- if (ret == -1 && errno == EADDRINUSE) {
- // Port may have been in use.
- ASSERT_LT(i, 100); // Give up after 100 tries.
- continue;
- }
- ASSERT_THAT(ret, SyscallSucceeds());
-
- // Verify that binding the v4 loopback with the same port on a v4 socket
- // fails.
- TestAddress const& test_addr_v4 = V4Loopback();
- sockaddr_storage addr_v4 = test_addr_v4.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port));
- const FileDescriptor fd_v4 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4),
- test_addr_v4.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // No need to try again.
- break;
- }
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedAnyOnlyReservesV4) {
- auto const& param = GetParam();
-
- for (int i = 0; true; i++) {
- // Bind the v4 any on a dual stack socket.
- TestAddress const& test_addr_dual = V4MappedAny();
- sockaddr_storage addr_dual = test_addr_dual.addr;
- const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_dual.family(), param.type, 0));
- ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual),
- test_addr_dual.addr_len),
- SyscallSucceeds());
-
- // Get the port that we bound.
- socklen_t addrlen = test_addr_dual.addr_len;
- ASSERT_THAT(getsockname(fd_dual.get(),
- reinterpret_cast<sockaddr*>(&addr_dual), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual));
-
- // Verify that we can still bind the v6 loopback on the same port.
- TestAddress const& test_addr_v6 = V6Loopback();
- sockaddr_storage addr_v6 = test_addr_v6.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port));
- const FileDescriptor fd_v6 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0));
- int ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6),
- test_addr_v6.addr_len);
- if (ret == -1 && errno == EADDRINUSE) {
- // Port may have been in use.
- ASSERT_LT(i, 100); // Give up after 100 tries.
- continue;
- }
- ASSERT_THAT(ret, SyscallSucceeds());
-
- // Verify that binding the v4 loopback with the same port on a v4 socket
- // fails.
- TestAddress const& test_addr_v4 = V4Loopback();
- sockaddr_storage addr_v4 = test_addr_v4.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port));
- const FileDescriptor fd_v4 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4),
- test_addr_v4.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // No need to try again.
- break;
- }
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, DualStackV6AnyReservesEverything) {
- auto const& param = GetParam();
-
- // Bind the v6 any on a dual stack socket.
- TestAddress const& test_addr_dual = V6Any();
- sockaddr_storage addr_dual = test_addr_dual.addr;
- const FileDescriptor fd_dual =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_dual.family(), param.type, 0));
- ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual),
- test_addr_dual.addr_len),
- SyscallSucceeds());
-
- // Get the port that we bound.
- socklen_t addrlen = test_addr_dual.addr_len;
- ASSERT_THAT(getsockname(fd_dual.get(),
- reinterpret_cast<sockaddr*>(&addr_dual), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual));
-
- // Verify that binding the v6 loopback with the same port fails.
- TestAddress const& test_addr_v6 = V6Loopback();
- sockaddr_storage addr_v6 = test_addr_v6.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port));
- const FileDescriptor fd_v6 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6),
- test_addr_v6.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v4 loopback on the same port with a v6 socket
- // fails.
- TestAddress const& test_addr_v4_mapped = V4MappedLoopback();
- sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, port));
- const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v4_mapped.family(), param.type, 0));
- ASSERT_THAT(
- bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped),
- test_addr_v4_mapped.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v4 loopback on the same port with a v4 socket
- // fails.
- TestAddress const& test_addr_v4 = V4Loopback();
- sockaddr_storage addr_v4 = test_addr_v4.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port));
- const FileDescriptor fd_v4 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4),
- test_addr_v4.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) {
- auto const& param = GetParam();
-
- for (int i = 0; true; i++) {
- // Bind the v6 any on a v6-only socket.
- TestAddress const& test_addr_dual = V6Any();
- sockaddr_storage addr_dual = test_addr_dual.addr;
- const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_dual.family(), param.type, 0));
- EXPECT_THAT(setsockopt(fd_dual.get(), IPPROTO_IPV6, IPV6_V6ONLY,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual),
- test_addr_dual.addr_len),
- SyscallSucceeds());
-
- // Get the port that we bound.
- socklen_t addrlen = test_addr_dual.addr_len;
- ASSERT_THAT(getsockname(fd_dual.get(),
- reinterpret_cast<sockaddr*>(&addr_dual), &addrlen),
- SyscallSucceeds());
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual));
-
- // Verify that binding the v6 loopback with the same port fails.
- TestAddress const& test_addr_v6 = V6Loopback();
- sockaddr_storage addr_v6 = test_addr_v6.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port));
- const FileDescriptor fd_v6 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6),
- test_addr_v6.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that we can still bind the v4 loopback on the same port.
- TestAddress const& test_addr_v4_mapped = V4MappedLoopback();
- sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, port));
- const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v4_mapped.family(), param.type, 0));
- int ret =
- bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped),
- test_addr_v4_mapped.addr_len);
- if (ret == -1 && errno == EADDRINUSE) {
- // Port may have been in use.
- ASSERT_LT(i, 100); // Give up after 100 tries.
- continue;
- }
- ASSERT_THAT(ret, SyscallSucceeds());
-
- // No need to try again.
- break;
- }
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) {
- auto const& param = GetParam();
-
- // FIXME(b/76031995): Support disabling SO_REUSEADDR for TCP sockets and make
- // it disabled by default.
- SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM);
-
- for (int i = 0; true; i++) {
- // Bind the v6 loopback on a dual stack socket.
- TestAddress const& test_addr = V6Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- test_addr.addr_len),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(
- connect(connected_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- EXPECT_THAT(
- bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr),
- connected_addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v6 loopback with the same port fails.
- TestAddress const& test_addr_v6 = V6Loopback();
- sockaddr_storage addr_v6 = test_addr_v6.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port));
- const FileDescriptor fd_v6 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6),
- test_addr_v6.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v4 any with the same port fails.
- TestAddress const& test_addr_v4_any = V4Any();
- sockaddr_storage addr_v4_any = test_addr_v4_any.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v4_any.family(), &addr_v4_any, ephemeral_port));
- const FileDescriptor fd_v4_any = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v4_any.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v4_any.get(), reinterpret_cast<sockaddr*>(&addr_v4_any),
- test_addr_v4_any.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that we can still bind the v4 loopback on the same port.
- TestAddress const& test_addr_v4_mapped = V4MappedLoopback();
- sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped,
- ephemeral_port));
- const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v4_mapped.family(), param.type, 0));
- int ret =
- bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped),
- test_addr_v4_mapped.addr_len);
- if (ret == -1 && errno == EADDRINUSE) {
- // Port may have been in use.
- ASSERT_LT(i, 100); // Give up after 100 tries.
- continue;
- }
- EXPECT_THAT(ret, SyscallSucceeds());
-
- // No need to try again.
- break;
- }
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReservedReuseAddr) {
- auto const& param = GetParam();
-
- // FIXME(b/129164367): Support SO_REUSEADDR on UDP sockets.
- SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_DGRAM);
-
- // Bind the v6 loopback on a dual stack socket.
- TestAddress const& test_addr = V6Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- test_addr.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(connect(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&bound_addr), bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr),
- connected_addr_len),
- SyscallSucceeds());
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) {
- auto const& param = GetParam();
-
- // FIXME(b/76031995): Support disabling SO_REUSEADDR for TCP sockets and make
- // it disabled by default.
- SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM);
-
- for (int i = 0; true; i++) {
- // Bind the v4 loopback on a dual stack socket.
- TestAddress const& test_addr = V4MappedLoopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- test_addr.addr_len),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(
- connect(connected_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- EXPECT_THAT(
- bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr),
- connected_addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v4 loopback on the same port with a v4 socket
- // fails.
- TestAddress const& test_addr_v4 = V4Loopback();
- sockaddr_storage addr_v4 = test_addr_v4.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v4.family(), &addr_v4, ephemeral_port));
- const FileDescriptor fd_v4 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0));
- EXPECT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4),
- test_addr_v4.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v6 any on the same port with a dual-stack socket
- // fails.
- TestAddress const& test_addr_v6_any = V6Any();
- sockaddr_storage addr_v6_any = test_addr_v6_any.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v6_any.family(), &addr_v6_any, ephemeral_port));
- const FileDescriptor fd_v6_any = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v6_any.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v6_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any),
- test_addr_v6_any.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // For some reason, binding the TCP v6-only any is flaky on Linux. Maybe we
- // tend to run out of ephemeral ports? Regardless, binding the v6 loopback
- // seems pretty reliable. Only try to bind the v6-only any on UDP and
- // gVisor.
-
- int ret = -1;
-
- if (!IsRunningOnGvisor() && param.type == SOCK_STREAM) {
- // Verify that we can still bind the v6 loopback on the same port.
- TestAddress const& test_addr_v6 = V6Loopback();
- sockaddr_storage addr_v6 = test_addr_v6.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port));
- const FileDescriptor fd_v6 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v6.family(), param.type, 0));
- ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6),
- test_addr_v6.addr_len);
- } else {
- // Verify that we can still bind the v6 any on the same port with a
- // v6-only socket.
- const FileDescriptor fd_v6_only_any = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v6_any.family(), param.type, 0));
- EXPECT_THAT(setsockopt(fd_v6_only_any.get(), IPPROTO_IPV6, IPV6_V6ONLY,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ret =
- bind(fd_v6_only_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any),
- test_addr_v6_any.addr_len);
- }
-
- if (ret == -1 && errno == EADDRINUSE) {
- // Port may have been in use.
- ASSERT_LT(i, 100); // Give up after 100 tries.
- continue;
- }
- EXPECT_THAT(ret, SyscallSucceeds());
-
- // No need to try again.
- break;
- }
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest,
- V4MappedEphemeralPortReservedResueAddr) {
- auto const& param = GetParam();
-
- // FIXME(b/129164367): Support SO_REUSEADDR on UDP sockets.
- SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_DGRAM);
-
- // Bind the v4 loopback on a dual stack socket.
- TestAddress const& test_addr = V4MappedLoopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- test_addr.addr_len),
- SyscallSucceeds());
-
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(connect(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&bound_addr), bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr),
- connected_addr_len),
- SyscallSucceeds());
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) {
- auto const& param = GetParam();
-
- // FIXME(b/76031995): Support disabling SO_REUSEADDR for TCP sockets and make
- // it disabled by default.
- SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_STREAM);
-
- for (int i = 0; true; i++) {
- // Bind the v4 loopback on a v4 socket.
- TestAddress const& test_addr = V4Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- test_addr.addr_len),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(
- connect(connected_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- EXPECT_THAT(
- bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr),
- connected_addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v4 loopback on the same port with a v6 socket
- // fails.
- TestAddress const& test_addr_v4_mapped = V4MappedLoopback();
- sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr;
- ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped,
- ephemeral_port));
- const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v4_mapped.family(), param.type, 0));
- EXPECT_THAT(
- bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped),
- test_addr_v4_mapped.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Verify that binding the v6 any on the same port with a dual-stack socket
- // fails.
- TestAddress const& test_addr_v6_any = V6Any();
- sockaddr_storage addr_v6_any = test_addr_v6_any.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v6_any.family(), &addr_v6_any, ephemeral_port));
- const FileDescriptor fd_v6_any = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v6_any.family(), param.type, 0));
- ASSERT_THAT(bind(fd_v6_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any),
- test_addr_v6_any.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // For some reason, binding the TCP v6-only any is flaky on Linux. Maybe we
- // tend to run out of ephemeral ports? Regardless, binding the v6 loopback
- // seems pretty reliable. Only try to bind the v6-only any on UDP and
- // gVisor.
-
- int ret = -1;
-
- if (!IsRunningOnGvisor() && param.type == SOCK_STREAM) {
- // Verify that we can still bind the v6 loopback on the same port.
- TestAddress const& test_addr_v6 = V6Loopback();
- sockaddr_storage addr_v6 = test_addr_v6.addr;
- ASSERT_NO_ERRNO(
- SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port));
- const FileDescriptor fd_v6 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v6.family(), param.type, 0));
- ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6),
- test_addr_v6.addr_len);
- } else {
- // Verify that we can still bind the v6 any on the same port with a
- // v6-only socket.
- const FileDescriptor fd_v6_only_any = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(test_addr_v6_any.family(), param.type, 0));
- EXPECT_THAT(setsockopt(fd_v6_only_any.get(), IPPROTO_IPV6, IPV6_V6ONLY,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ret =
- bind(fd_v6_only_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any),
- test_addr_v6_any.addr_len);
- }
-
- if (ret == -1 && errno == EADDRINUSE) {
- // Port may have been in use.
- ASSERT_LT(i, 100); // Give up after 100 tries.
- continue;
- }
- EXPECT_THAT(ret, SyscallSucceeds());
-
- // No need to try again.
- break;
- }
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReservedReuseAddr) {
- auto const& param = GetParam();
-
- // FIXME(b/129164367): Support SO_REUSEADDR on UDP sockets.
- SKIP_IF(IsRunningOnGvisor() && param.type == SOCK_DGRAM);
-
- // Bind the v4 loopback on a v4 socket.
- TestAddress const& test_addr = V4Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
-
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- test_addr.addr_len),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
-
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- ASSERT_THAT(connect(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&bound_addr), bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr),
- connected_addr_len),
- SyscallSucceeds());
-}
-
-TEST_P(SocketMultiProtocolInetLoopbackTest, PortReuseTwoSockets) {
- auto const& param = GetParam();
- TestAddress const& test_addr = V4Loopback();
- sockaddr_storage addr = test_addr.addr;
-
- for (int i = 0; i < 2; i++) {
- const int portreuse1 = i % 2;
- auto s1 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- int fd1 = s1.get();
- socklen_t addrlen = test_addr.addr_len;
-
- EXPECT_THAT(
- setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &portreuse1, sizeof(int)),
- SyscallSucceeds());
-
- ASSERT_THAT(bind(fd1, reinterpret_cast<sockaddr*>(&addr), addrlen),
- SyscallSucceeds());
-
- ASSERT_THAT(getsockname(fd1, reinterpret_cast<sockaddr*>(&addr), &addrlen),
- SyscallSucceeds());
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(fd1, 1), SyscallSucceeds());
- }
-
- // j is less than 4 to check that the port reuse logic works correctly after
- // closing bound sockets.
- for (int j = 0; j < 4; j++) {
- const int portreuse2 = j % 2;
- auto s2 =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- int fd2 = s2.get();
-
- EXPECT_THAT(
- setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &portreuse2, sizeof(int)),
- SyscallSucceeds());
-
- std::cout << portreuse1 << " " << portreuse2;
- int ret = bind(fd2, reinterpret_cast<sockaddr*>(&addr), addrlen);
-
- // Verify that two sockets can be bound to the same port only if
- // SO_REUSEPORT is set for both of them.
- if (!portreuse1 || !portreuse2) {
- ASSERT_THAT(ret, SyscallFailsWithErrno(EADDRINUSE));
- } else {
- ASSERT_THAT(ret, SyscallSucceeds());
- }
- }
- }
-}
-
-// Check that when a socket was bound to an address with REUSEPORT and then
-// closed, we can bind a different socket to the same address without needing
-// REUSEPORT.
-TEST_P(SocketMultiProtocolInetLoopbackTest, NoReusePortFollowingReusePort) {
- auto const& param = GetParam();
- TestAddress const& test_addr = V4Loopback();
- sockaddr_storage addr = test_addr.addr;
-
- auto s = ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- int fd = s.get();
- socklen_t addrlen = test_addr.addr_len;
- int portreuse = 1;
- ASSERT_THAT(
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &portreuse, sizeof(portreuse)),
- SyscallSucceeds());
- ASSERT_THAT(bind(fd, reinterpret_cast<sockaddr*>(&addr), addrlen),
- SyscallSucceeds());
- ASSERT_THAT(getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen),
- SyscallSucceeds());
- ASSERT_EQ(addrlen, test_addr.addr_len);
-
- s.reset();
-
- // Open a new socket and bind to the same address, but w/o REUSEPORT.
- s = ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- fd = s.get();
- portreuse = 0;
- ASSERT_THAT(
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &portreuse, sizeof(portreuse)),
- SyscallSucceeds());
- ASSERT_THAT(bind(fd, reinterpret_cast<sockaddr*>(&addr), addrlen),
- SyscallSucceeds());
-}
-
-INSTANTIATE_TEST_SUITE_P(
- AllFamilies, SocketMultiProtocolInetLoopbackTest,
- ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
- ProtocolTestParam{"UDP", SOCK_DGRAM}),
- DescribeProtocolTestParam);
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor