diff options
author | Brian Geffon <bgeffon@google.com> | 2018-12-10 14:41:40 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-12-10 14:42:34 -0800 |
commit | d3bc79bc8438206ac6a14fde4eaa288fc07eee82 (patch) | |
tree | e820398591bfd1503456e877fa0c2bdd0f994959 /test/syscalls/linux/udp_bind.cc | |
parent | 833edbd10b49db1f934dcb2495dcb41c1310eea4 (diff) |
Open source system call tests.
PiperOrigin-RevId: 224886231
Change-Id: I0fccb4d994601739d8b16b1d4e6b31f40297fb22
Diffstat (limited to 'test/syscalls/linux/udp_bind.cc')
-rw-r--r-- | test/syscalls/linux/udp_bind.cc | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/test/syscalls/linux/udp_bind.cc b/test/syscalls/linux/udp_bind.cc new file mode 100644 index 000000000..419aaac76 --- /dev/null +++ b/test/syscalls/linux/udp_bind.cc @@ -0,0 +1,316 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +struct sockaddr_in_common { + sa_family_t sin_family; + in_port_t sin_port; +}; + +struct SendtoTestParam { + // Human readable description of test parameter. + std::string description; + + // Test is broken in gVisor, skip. + bool skip_on_gvisor; + + // Domain for the socket that will do the sending. + int send_domain; + + // Address to bind for the socket that will do the sending. + struct sockaddr_storage send_addr; + socklen_t send_addr_len; // 0 for unbound. + + // Address to connect to for the socket that will do the sending. + struct sockaddr_storage connect_addr; + socklen_t connect_addr_len; // 0 for no connection. + + // Domain for the socket that will do the receiving. + int recv_domain; + + // Address to bind for the socket that will do the receiving. + struct sockaddr_storage recv_addr; + socklen_t recv_addr_len; + + // Address to send to. + struct sockaddr_storage sendto_addr; + socklen_t sendto_addr_len; + + // Expected errno for the sendto call. + std::vector<int> sendto_errnos; // empty on success. +}; + +class SendtoTest : public ::testing::TestWithParam<SendtoTestParam> { + protected: + SendtoTest() { + // gUnit uses printf, so so will we. + printf("Testing with %s\n", GetParam().description.c_str()); + } +}; + +TEST_P(SendtoTest, Sendto) { + auto param = GetParam(); + + SKIP_IF(param.skip_on_gvisor && IsRunningOnGvisor()); + + const FileDescriptor s1 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(param.send_domain, SOCK_DGRAM, 0)); + const FileDescriptor s2 = + ASSERT_NO_ERRNO_AND_VALUE(Socket(param.recv_domain, SOCK_DGRAM, 0)); + + if (param.send_addr_len > 0) { + ASSERT_THAT(bind(s1.get(), reinterpret_cast<sockaddr*>(¶m.send_addr), + param.send_addr_len), + SyscallSucceeds()); + } + + if (param.connect_addr_len > 0) { + ASSERT_THAT( + connect(s1.get(), reinterpret_cast<sockaddr*>(¶m.connect_addr), + param.connect_addr_len), + SyscallSucceeds()); + } + + ASSERT_THAT(bind(s2.get(), reinterpret_cast<sockaddr*>(¶m.recv_addr), + param.recv_addr_len), + SyscallSucceeds()); + + struct sockaddr_storage real_recv_addr = {}; + socklen_t real_recv_addr_len = param.recv_addr_len; + ASSERT_THAT( + getsockname(s2.get(), reinterpret_cast<sockaddr*>(&real_recv_addr), + &real_recv_addr_len), + SyscallSucceeds()); + + ASSERT_EQ(real_recv_addr_len, param.recv_addr_len); + + int recv_port = + reinterpret_cast<sockaddr_in_common*>(&real_recv_addr)->sin_port; + + struct sockaddr_storage sendto_addr = param.sendto_addr; + reinterpret_cast<sockaddr_in_common*>(&sendto_addr)->sin_port = recv_port; + + char buf[20] = {}; + if (!param.sendto_errnos.empty()) { + ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0, + reinterpret_cast<sockaddr*>(&sendto_addr), + param.sendto_addr_len), + SyscallFailsWithErrno(ElementOf(param.sendto_errnos))); + return; + } + + ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0, + reinterpret_cast<sockaddr*>(&sendto_addr), + param.sendto_addr_len), + SyscallSucceedsWithValue(sizeof(buf))); + + struct sockaddr_storage got_addr = {}; + socklen_t got_addr_len = sizeof(sockaddr_storage); + ASSERT_THAT(RetryEINTR(recvfrom)(s2.get(), buf, sizeof(buf), 0, + reinterpret_cast<sockaddr*>(&got_addr), + &got_addr_len), + SyscallSucceedsWithValue(sizeof(buf))); + + ASSERT_GT(got_addr_len, sizeof(sockaddr_in_common)); + int got_port = reinterpret_cast<sockaddr_in_common*>(&got_addr)->sin_port; + + struct sockaddr_storage sender_addr = {}; + socklen_t sender_addr_len = sizeof(sockaddr_storage); + ASSERT_THAT(getsockname(s1.get(), reinterpret_cast<sockaddr*>(&sender_addr), + &sender_addr_len), + SyscallSucceeds()); + + ASSERT_GT(sender_addr_len, sizeof(sockaddr_in_common)); + int sender_port = + reinterpret_cast<sockaddr_in_common*>(&sender_addr)->sin_port; + + EXPECT_EQ(got_port, sender_port); +} + +socklen_t Ipv4Addr(sockaddr_storage* addr, int port = 0) { + auto addr4 = reinterpret_cast<sockaddr_in*>(addr); + addr4->sin_family = AF_INET; + addr4->sin_port = port; + inet_pton(AF_INET, "127.0.0.1", &addr4->sin_addr.s_addr); + return sizeof(struct sockaddr_in); +} + +socklen_t Ipv6Addr(sockaddr_storage* addr, int port = 0) { + auto addr6 = reinterpret_cast<sockaddr_in6*>(addr); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = port; + inet_pton(AF_INET6, "::1", &addr6->sin6_addr.s6_addr); + return sizeof(struct sockaddr_in6); +} + +socklen_t Ipv4MappedIpv6Addr(sockaddr_storage* addr, int port = 0) { + auto addr6 = reinterpret_cast<sockaddr_in6*>(addr); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = port; + inet_pton(AF_INET6, "::ffff:127.0.0.1", &addr6->sin6_addr.s6_addr); + return sizeof(struct sockaddr_in6); +} + +INSTANTIATE_TEST_CASE_P( + UdpBindTest, SendtoTest, + ::testing::Values( + []() { + SendtoTestParam param = {}; + param.description = "IPv4 mapped IPv6 sendto IPv4 mapped IPv6"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv6 sendto IPv6"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv6Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 sendto IPv4"; + param.send_domain = AF_INET; + param.send_addr_len = Ipv4Addr(¶m.send_addr); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 mapped IPv6 sendto IPv4"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 sendto IPv4 mapped IPv6"; + param.send_domain = AF_INET; + param.send_addr_len = Ipv4Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "unbound IPv6 sendto IPv4 mapped IPv6"; + param.send_domain = AF_INET6; + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "unbound IPv6 sendto IPv4"; + param.send_domain = AF_INET6; + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv6 sendto IPv4"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv6Addr(¶m.send_addr); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + param.sendto_errnos = {ENETUNREACH}; + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "IPv4 mapped IPv6 sendto IPv6"; + param.send_domain = AF_INET6; + param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); + param.sendto_errnos = {EAFNOSUPPORT}; + // The errno returned changed in Linux commit c8e6ad0829a723. + param.sendto_errnos = {EINVAL, EAFNOSUPPORT}; + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "connected IPv4 mapped IPv6 sendto IPv6"; + param.send_domain = AF_INET6; + param.connect_addr_len = + Ipv4MappedIpv6Addr(¶m.connect_addr, 5000); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); + // The errno returned changed in Linux commit c8e6ad0829a723. + param.sendto_errnos = {EINVAL, EAFNOSUPPORT}; + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "connected IPv6 sendto IPv4 mapped IPv6"; + // TODO: Determine if this inconsistent behavior is worth + // implementing. + param.skip_on_gvisor = true; + param.send_domain = AF_INET6; + param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000); + param.recv_domain = AF_INET6; + param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }(), + []() { + SendtoTestParam param = {}; + param.description = "connected IPv6 sendto IPv4"; + // TODO: Determine if this inconsistent behavior is worth + // implementing. + param.skip_on_gvisor = true; + param.send_domain = AF_INET6; + param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000); + param.recv_domain = AF_INET; + param.recv_addr_len = Ipv4Addr(¶m.recv_addr); + param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); + return param; + }())); + +} // namespace + +} // namespace testing +} // namespace gvisor |