From ea070b9d5f4be0b25b028e90ab4518ef2e4df16b Mon Sep 17 00:00:00 2001 From: Amanda Tait Date: Wed, 20 Feb 2019 12:53:07 -0800 Subject: Implement Broadcast support This change adds support for the SO_BROADCAST socket option in gVisor Netstack. This support includes getsockopt()/setsockopt() functionality for both UDP and TCP endpoints (the latter being a NOOP), dispatching broadcast messages up and down the stack, and route finding/creation for broadcast packets. Finally, a suite of tests have been implemented, exercising this functionality through the Linux syscall API. PiperOrigin-RevId: 234850781 Change-Id: If3e666666917d39f55083741c78314a06defb26c --- test/syscalls/linux/BUILD | 69 ++++++ test/syscalls/linux/ip_socket_test_util.cc | 16 ++ test/syscalls/linux/ip_socket_test_util.h | 8 + .../socket_ipv4_tcp_unbound_external_networking.cc | 66 ++++++ .../socket_ipv4_tcp_unbound_external_networking.h | 30 +++ ...et_ipv4_tcp_unbound_external_networking_test.cc | 35 ++++ .../socket_ipv4_udp_unbound_external_networking.cc | 231 +++++++++++++++++++++ .../socket_ipv4_udp_unbound_external_networking.h | 30 +++ ...et_ipv4_udp_unbound_external_networking_test.cc | 35 ++++ test/syscalls/linux/socket_test_util.cc | 12 ++ test/syscalls/linux/socket_test_util.h | 5 + 11 files changed, 537 insertions(+) create mode 100644 test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc create mode 100644 test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h create mode 100644 test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc create mode 100644 test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc create mode 100644 test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h create mode 100644 test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc (limited to 'test/syscalls') diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 9da5204c1..beece8930 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -148,6 +148,7 @@ cc_library( hdrs = ["ip_socket_test_util.h"], deps = [ ":socket_test_util", + "@com_google_absl//absl/strings", ], ) @@ -1970,6 +1971,42 @@ cc_library( alwayslink = 1, ) +cc_library( + name = "socket_ipv4_udp_unbound_external_networking_test_cases", + testonly = 1, + srcs = [ + "socket_ipv4_udp_unbound_external_networking.cc", + ], + hdrs = [ + "socket_ipv4_udp_unbound_external_networking.h", + ], + deps = [ + ":ip_socket_test_util", + ":socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + +cc_library( + name = "socket_ipv4_tcp_unbound_external_networking_test_cases", + testonly = 1, + srcs = [ + "socket_ipv4_tcp_unbound_external_networking.cc", + ], + hdrs = [ + "socket_ipv4_tcp_unbound_external_networking.h", + ], + deps = [ + ":ip_socket_test_util", + ":socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + cc_binary( name = "socket_abstract_test", testonly = 1, @@ -2147,6 +2184,38 @@ cc_binary( ], ) +cc_binary( + name = "socket_ipv4_udp_unbound_external_networking_test", + testonly = 1, + srcs = [ + "socket_ipv4_udp_unbound_external_networking_test.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_ipv4_udp_unbound_external_networking_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( + name = "socket_ipv4_tcp_unbound_external_networking_test", + testonly = 1, + srcs = [ + "socket_ipv4_tcp_unbound_external_networking_test.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_ipv4_tcp_unbound_external_networking_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + cc_binary( name = "socket_ip_udp_loopback_non_blocking_test", testonly = 1, diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc index f8232fc24..4ad787cc0 100644 --- a/test/syscalls/linux/ip_socket_test_util.cc +++ b/test/syscalls/linux/ip_socket_test_util.cc @@ -13,7 +13,9 @@ // limitations under the License. #include +#include #include +#include #include #include "test/syscalls/linux/ip_socket_test_util.h" @@ -95,5 +97,19 @@ SocketPairKind IPv4UDPUnboundSocketPair(int type) { /* dual_stack = */ false)}; } +SocketKind IPv4UDPUnboundSocket(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket"); + return SocketKind{description, UnboundSocketCreator( + AF_INET, type | SOCK_DGRAM, IPPROTO_UDP)}; +} + +SocketKind IPv4TCPUnboundSocket(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "IPv4 TCP socket"); + return SocketKind{description, UnboundSocketCreator( + AF_INET, type | SOCK_STREAM, IPPROTO_TCP)}; +} + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h index a6721091a..cac790e64 100644 --- a/test/syscalls/linux/ip_socket_test_util.h +++ b/test/syscalls/linux/ip_socket_test_util.h @@ -58,6 +58,14 @@ SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type); // SocketPairs created with AF_INET and the given type. SocketPairKind IPv4UDPUnboundSocketPair(int type); +// IPv4UDPUnboundSocketPair returns a SocketKind that represents +// a SimpleSocket created with AF_INET, SOCK_DGRAM, and the given type. +SocketKind IPv4UDPUnboundSocket(int type); + +// IPv4TCPUnboundSocketPair returns a SocketKind that represents +// a SimpleSocket created with AF_INET, SOCK_STREAM and the given type. +SocketKind IPv4TCPUnboundSocket(int type); + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc new file mode 100644 index 000000000..8e1c13ff4 --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.cc @@ -0,0 +1,66 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h" + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// Verifies that a newly instantiated TCP socket does not have the +// broadcast socket option enabled. +TEST_P(IPv4TCPUnboundExternalNetworkingSocketTest, TCPBroadcastDefault) { + auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + int get = -1; + socklen_t get_sz = sizeof(get); + EXPECT_THAT( + getsockopt(socket->get(), SOL_SOCKET, SO_BROADCAST, &get, &get_sz), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get, kSockOptOff); + EXPECT_EQ(get_sz, sizeof(get)); +} + +// Verifies that a newly instantiated TCP socket returns true after enabling +// the broadcast socket option. +TEST_P(IPv4TCPUnboundExternalNetworkingSocketTest, SetTCPBroadcast) { + auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + EXPECT_THAT(setsockopt(socket->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, + sizeof(kSockOptOn)), + SyscallSucceedsWithValue(0)); + + int get = -1; + socklen_t get_sz = sizeof(get); + EXPECT_THAT( + getsockopt(socket->get(), SOL_SOCKET, SO_BROADCAST, &get, &get_sz), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get, kSockOptOn); + EXPECT_EQ(get_sz, sizeof(get)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h new file mode 100644 index 000000000..b23de08d1 --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h @@ -0,0 +1,30 @@ +// Copyright 2019 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_TCP_UNBOUND_EXTERNAL_NETWORKING_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_TCP_UNBOUND_EXTERNAL_NETWORKING_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to unbound IPv4 TCP sockets in a sandbox +// with external networking support. +using IPv4TCPUnboundExternalNetworkingSocketTest = SimpleSocketTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_TCP_UNBOUND_EXTERNAL_NETWORKING_H_ diff --git a/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc new file mode 100644 index 000000000..c6fb42641 --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking_test.cc @@ -0,0 +1,35 @@ +// Copyright 2019 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 + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_ipv4_tcp_unbound_external_networking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSockets() { + return ApplyVec( + IPv4TCPUnboundSocket, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK})); +} + +INSTANTIATE_TEST_CASE_P(IPv4TCPSockets, + IPv4TCPUnboundExternalNetworkingSocketTest, + ::testing::ValuesIn(GetSockets())); +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc new file mode 100644 index 000000000..7d561b991 --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc @@ -0,0 +1,231 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h" + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// Verifies that a newly instantiated UDP socket does not have the +// broadcast socket option enabled. +TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, UDPBroadcastDefault) { + auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + int get = -1; + socklen_t get_sz = sizeof(get); + EXPECT_THAT( + getsockopt(socket->get(), SOL_SOCKET, SO_BROADCAST, &get, &get_sz), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get, kSockOptOff); + EXPECT_EQ(get_sz, sizeof(get)); +} + +// Verifies that a newly instantiated UDP socket returns true after enabling +// the broadcast socket option. +TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, SetUDPBroadcast) { + auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + EXPECT_THAT(setsockopt(socket->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, + sizeof(kSockOptOn)), + SyscallSucceedsWithValue(0)); + + int get = -1; + socklen_t get_sz = sizeof(get); + EXPECT_THAT( + getsockopt(socket->get(), SOL_SOCKET, SO_BROADCAST, &get, &get_sz), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get, kSockOptOn); + EXPECT_EQ(get_sz, sizeof(get)); +} + +// Verifies that a broadcast UDP packet will arrive at all UDP sockets with +// the destination port number. +TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, + UDPBroadcastReceivedOnAllExpectedEndpoints) { + auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + auto norcv = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + // Enable SO_BROADCAST on the sending socket. + ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, + sizeof(kSockOptOn)), + SyscallSucceedsWithValue(0)); + + // Enable SO_REUSEPORT on the receiving sockets so that they may both be bound + // to the broadcast messages destination port. + ASSERT_THAT(setsockopt(rcvr1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, + sizeof(kSockOptOn)), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(setsockopt(rcvr2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, + sizeof(kSockOptOn)), + SyscallSucceedsWithValue(0)); + + sockaddr_in rcv_addr = {}; + socklen_t rcv_addr_sz = sizeof(rcv_addr); + rcv_addr.sin_family = AF_INET; + rcv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + ASSERT_THAT(bind(rcvr1->get(), reinterpret_cast(&rcv_addr), + rcv_addr_sz), + SyscallSucceedsWithValue(0)); + // Retrieve port number from first socket so that it can be bound to the + // second socket. + rcv_addr = {}; + ASSERT_THAT( + getsockname(rcvr1->get(), reinterpret_cast(&rcv_addr), + &rcv_addr_sz), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(bind(rcvr2->get(), reinterpret_cast(&rcv_addr), + rcv_addr_sz), + SyscallSucceedsWithValue(0)); + + // Bind the non-receiving socket to an ephemeral port. + sockaddr_in norcv_addr = {}; + norcv_addr.sin_family = AF_INET; + norcv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + ASSERT_THAT( + bind(norcv->get(), reinterpret_cast(&norcv_addr), + sizeof(norcv_addr)), + SyscallSucceedsWithValue(0)); + + // Broadcast a test message. + sockaddr_in dst_addr = {}; + dst_addr.sin_family = AF_INET; + dst_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + dst_addr.sin_port = rcv_addr.sin_port; + constexpr char kTestMsg[] = "hello, world"; + EXPECT_THAT( + sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, + reinterpret_cast(&dst_addr), sizeof(dst_addr)), + SyscallSucceedsWithValue(sizeof(kTestMsg))); + + // Verify that the receiving sockets received the test message. + char buf[sizeof(kTestMsg)] = {}; + EXPECT_THAT(read(rcvr1->get(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(kTestMsg))); + EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); + memset(buf, 0, sizeof(buf)); + EXPECT_THAT(read(rcvr2->get(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(kTestMsg))); + EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); + + // Verify that the non-receiving socket did not receive the test message. + memset(buf, 0, sizeof(buf)); + EXPECT_THAT(RetryEINTR(recv)(norcv->get(), buf, sizeof(buf), MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +// Verifies that a UDP broadcast sent via the loopback interface is not received +// by the sender. +TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, + UDPBroadcastViaLoopbackFails) { + auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + // Enable SO_BROADCAST. + ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, + sizeof(kSockOptOn)), + SyscallSucceedsWithValue(0)); + + // Bind the sender to the loopback interface. + sockaddr_in src = {}; + socklen_t src_sz = sizeof(src); + src.sin_family = AF_INET; + src.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ASSERT_THAT( + bind(sender->get(), reinterpret_cast(&src), src_sz), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(getsockname(sender->get(), + reinterpret_cast(&src), &src_sz), + SyscallSucceedsWithValue(0)); + ASSERT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK)); + + // Send the message. + sockaddr_in dst = {}; + dst.sin_family = AF_INET; + dst.sin_addr.s_addr = htonl(INADDR_BROADCAST); + dst.sin_port = src.sin_port; + constexpr char kTestMsg[] = "hello, world"; + EXPECT_THAT(sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, + reinterpret_cast(&dst), sizeof(dst)), + SyscallSucceedsWithValue(sizeof(kTestMsg))); + + // Verify that the message was not received by the sender (loopback). + char buf[sizeof(kTestMsg)] = {}; + EXPECT_THAT(RetryEINTR(recv)(sender->get(), buf, sizeof(buf), MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +// Verifies that a UDP broadcast fails to send on a socket with SO_BROADCAST +// disabled. +TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendBroadcast) { + auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + // Broadcast a test message without having enabled SO_BROADCAST on the sending + // socket. + sockaddr_in addr = {}; + socklen_t addr_sz = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(12345); + addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + constexpr char kTestMsg[] = "hello, world"; + + EXPECT_THAT(sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, + reinterpret_cast(&addr), addr_sz), + SyscallFailsWithErrno(EACCES)); +} + +// Verifies that a UDP unicast on an unbound socket reaches its destination. +TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendUnicastOnUnbound) { + auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + auto rcvr = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + // Bind the receiver and retrieve its address and port number. + sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(0); + ASSERT_THAT(bind(rcvr->get(), reinterpret_cast(&addr), + sizeof(addr)), + SyscallSucceedsWithValue(0)); + memset(&addr, 0, sizeof(addr)); + socklen_t addr_sz = sizeof(addr); + ASSERT_THAT(getsockname(rcvr->get(), + reinterpret_cast(&addr), &addr_sz), + SyscallSucceedsWithValue(0)); + + // Send a test message to the receiver. + constexpr char kTestMsg[] = "hello, world"; + ASSERT_THAT(sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, + reinterpret_cast(&addr), addr_sz), + SyscallSucceedsWithValue(sizeof(kTestMsg))); + char buf[sizeof(kTestMsg)] = {}; + ASSERT_THAT(read(rcvr->get(), buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(kTestMsg))); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h new file mode 100644 index 000000000..5cf9fa8eb --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h @@ -0,0 +1,30 @@ +// Copyright 2019 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to unbound IPv4 UDP sockets in a sandbox +// with external networking support. +using IPv4UDPUnboundExternalNetworkingSocketTest = SimpleSocketTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc new file mode 100644 index 000000000..e07385134 --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc @@ -0,0 +1,35 @@ +// Copyright 2019 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 + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector GetSockets() { + return ApplyVec( + IPv4UDPUnboundSocket, + AllBitwiseCombinations(List{0, SOCK_NONBLOCK})); +} + +INSTANTIATE_TEST_CASE_P(IPv4UDPSockets, + IPv4UDPUnboundExternalNetworkingSocketTest, + ::testing::ValuesIn(GetSockets())); +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index 8d19f79ac..035087566 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -22,6 +22,7 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/time/clock.h" +#include "test/util/file_descriptor.h" #include "test/util/posix_error.h" #include "test/util/temp_path.h" #include "test/util/thread_util.h" @@ -463,6 +464,17 @@ SocketPairKind Reversed(SocketPairKind const& base) { }}; } +Creator UnboundSocketCreator(int domain, int type, + int protocol) { + return [=]() -> PosixErrorOr> { + int sock; + RETURN_ERROR_IF_SYSCALL_FAIL(sock = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + + return absl::make_unique(sock); + }; +} + std::vector IncludeReversals(std::vector vec) { return ApplyVecToVec(std::vector{NoOp, Reversed}, vec); diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h index 906b3e929..dfabdf179 100644 --- a/test/syscalls/linux/socket_test_util.h +++ b/test/syscalls/linux/socket_test_util.h @@ -278,6 +278,11 @@ Creator UDPBidirectionalBindSocketPairCreator(int domain, int type, Creator UDPUnboundSocketPairCreator(int domain, int type, int protocol, bool dual_stack); +// UnboundSocketCreator returns a Creator that obtains a file +// descriptor by creating a socket. +Creator UnboundSocketCreator(int domain, int type, + int protocol); + // A SocketPairKind couples a human-readable description of a socket pair with // a function that creates such a socket pair. struct SocketPairKind { -- cgit v1.2.3