| // 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 <sys/socket.h> |
| #include <sys/types.h> |
| |
| #include "gtest/gtest.h" |
| #include "test/util/file_descriptor.h" |
| #include "test/util/socket_util.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(), AsSockAddr(¶m.send_addr), param.send_addr_len), |
| SyscallSucceeds()); |
| } |
| |
| if (param.connect_addr_len > 0) { |
| ASSERT_THAT(connect(s1.get(), AsSockAddr(¶m.connect_addr), |
| param.connect_addr_len), |
| SyscallSucceeds()); |
| } |
| |
| ASSERT_THAT(bind(s2.get(), AsSockAddr(¶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(), AsSockAddr(&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, |
| AsSockAddr(&sendto_addr), param.sendto_addr_len), |
| SyscallFailsWithErrno(ElementOf(param.sendto_errnos))); |
| return; |
| } |
| |
| ASSERT_THAT( |
| RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0, |
| AsSockAddr(&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, |
| AsSockAddr(&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(), AsSockAddr(&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_SUITE_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(igudger): 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(igudger): 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 |