diff options
Diffstat (limited to 'test/syscalls/linux/socket_inet_loopback.cc')
-rw-r--r-- | test/syscalls/linux/socket_inet_loopback.cc | 2264 |
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 |