diff options
author | Ghanan Gowripalan <ghanan@google.com> | 2021-01-06 11:39:14 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-01-06 11:41:42 -0800 |
commit | abe9d9f67f2c2c696ef26690fa8518dfc4e28728 (patch) | |
tree | eabba6c15b22345ab322f7380548c4c8eb5e50fe /test | |
parent | 0c4118d5b8428648c252df55a7867ac6f287f146 (diff) |
Support add/remove IPv6 multicast group sock opt
IPv4 was always supported but UDP never supported joining/leaving IPv6
multicast groups via socket options.
Add: IPPROTO_IPV6, IPV6_JOIN_GROUP/IPV6_ADD_MEMBERSHIP
Remove: IPPROTO_IPV6, IPV6_LEAVE_GROUP/IPV6_DROP_MEMBERSHIP
Test: integration_test.TestUDPAddRemoveMembershipSocketOption
PiperOrigin-RevId: 350396072
Diffstat (limited to 'test')
11 files changed, 336 insertions, 73 deletions
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index a5b9233f7..7fe7db5cb 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -683,6 +683,11 @@ syscall_test( syscall_test( size = "medium", + test = "//test/syscalls/linux:socket_ipv6_udp_unbound_loopback_test", +) + +syscall_test( + size = "medium", add_hostinet = True, shard_count = more_shards, # Takes too long under gotsan to run. diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 4e0c8a574..017f997de 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -20,6 +20,7 @@ exports_files( "socket_ip_udp_loopback_nonblock.cc", "socket_ip_unbound.cc", "socket_ipv4_udp_unbound_external_networking_test.cc", + "socket_ipv6_udp_unbound_external_networking_test.cc", "socket_ipv4_udp_unbound_loopback.cc", "socket_ipv6_udp_unbound_loopback.cc", "socket_ipv4_udp_unbound_loopback_nogotsan.cc", @@ -2511,6 +2512,23 @@ cc_library( ) cc_library( + name = "socket_ip_udp_unbound_external_networking", + testonly = 1, + srcs = [ + "socket_ip_udp_unbound_external_networking.cc", + ], + hdrs = [ + "socket_ip_udp_unbound_external_networking.h", + ], + deps = [ + ":ip_socket_test_util", + ":socket_test_util", + "//test/util:test_util", + ], + alwayslink = 1, +) + +cc_library( name = "socket_ipv4_udp_unbound_external_networking_test_cases", testonly = 1, srcs = [ @@ -2520,10 +2538,24 @@ cc_library( "socket_ipv4_udp_unbound_external_networking.h", ], deps = [ - ":ip_socket_test_util", - ":socket_test_util", + ":socket_ip_udp_unbound_external_networking", + gtest, + ], + alwayslink = 1, +) + +cc_library( + name = "socket_ipv6_udp_unbound_external_networking_test_cases", + testonly = 1, + srcs = [ + "socket_ipv6_udp_unbound_external_networking.cc", + ], + hdrs = [ + "socket_ipv6_udp_unbound_external_networking.h", + ], + deps = [ + ":socket_ip_udp_unbound_external_networking", gtest, - "//test/util:test_util", ], alwayslink = 1, ) @@ -2723,6 +2755,22 @@ cc_binary( ) cc_binary( + name = "socket_ipv6_udp_unbound_external_networking_test", + testonly = 1, + srcs = [ + "socket_ipv6_udp_unbound_external_networking_test.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_ipv6_udp_unbound_external_networking_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( name = "socket_bind_to_device_test", testonly = 1, srcs = [ diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc new file mode 100644 index 000000000..fdbb2216b --- /dev/null +++ b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc @@ -0,0 +1,59 @@ +// Copyright 2020 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 "test/syscalls/linux/socket_ip_udp_unbound_external_networking.h" + +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +void IPUDPUnboundExternalNetworkingSocketTest::SetUp() { + // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its + // IPv4 address on eth0. + found_net_interfaces_ = false; + + // Get interface list. + ASSERT_NO_ERRNO(if_helper_.Load()); + std::vector<std::string> if_names = if_helper_.InterfaceList(AF_INET); + if (if_names.size() != 2) { + return; + } + + // Figure out which interface is where. + std::string lo = if_names[0]; + std::string eth = if_names[1]; + if (lo != "lo") std::swap(lo, eth); + if (lo != "lo") return; + + lo_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(lo)); + auto lo_if_addr = if_helper_.GetAddr(AF_INET, lo); + if (lo_if_addr == nullptr) { + return; + } + lo_if_addr_ = *reinterpret_cast<const sockaddr_in*>(lo_if_addr); + + eth_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(eth)); + auto eth_if_addr = if_helper_.GetAddr(AF_INET, eth); + if (eth_if_addr == nullptr) { + return; + } + eth_if_addr_ = *reinterpret_cast<const sockaddr_in*>(eth_if_addr); + + found_net_interfaces_ = true; +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h new file mode 100644 index 000000000..e5287addb --- /dev/null +++ b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h @@ -0,0 +1,46 @@ +// Copyright 2020 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to unbound IP UDP sockets in a sandbox +// with external networking support. +class IPUDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest { + protected: + void SetUp() override; + + IfAddrHelper if_helper_; + + // found_net_interfaces_ is set to false if SetUp() could not obtain + // all interface infos that we need. + bool found_net_interfaces_; + + // Interface infos. + int lo_if_idx_; + int eth_if_idx_; + sockaddr_in lo_if_addr_; + sockaddr_in eth_if_addr_; +}; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc index 2eecb0866..940289d15 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc @@ -14,23 +14,6 @@ #include "test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h" -#include <arpa/inet.h> -#include <ifaddrs.h> -#include <netinet/in.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdint> -#include <cstdio> -#include <cstring> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - namespace gvisor { namespace testing { @@ -41,41 +24,6 @@ TestAddress V4EmptyAddress() { return t; } -void IPv4UDPUnboundExternalNetworkingSocketTest::SetUp() { - // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its - // IPv4 address on eth0. - found_net_interfaces_ = false; - - // Get interface list. - ASSERT_NO_ERRNO(if_helper_.Load()); - std::vector<std::string> if_names = if_helper_.InterfaceList(AF_INET); - if (if_names.size() != 2) { - return; - } - - // Figure out which interface is where. - std::string lo = if_names[0]; - std::string eth = if_names[1]; - if (lo != "lo") std::swap(lo, eth); - if (lo != "lo") return; - - lo_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(lo)); - auto lo_if_addr = if_helper_.GetAddr(AF_INET, lo); - if (lo_if_addr == nullptr) { - return; - } - lo_if_addr_ = *reinterpret_cast<const sockaddr_in*>(lo_if_addr); - - eth_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(eth)); - auto eth_if_addr = if_helper_.GetAddr(AF_INET, eth); - if (eth_if_addr == nullptr) { - return; - } - eth_if_addr_ = *reinterpret_cast<const sockaddr_in*>(eth_if_addr); - - found_net_interfaces_ = true; -} - // Verifies that a broadcast UDP packet will arrive at all UDP sockets with // the destination port number. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h index 0e9e70e8e..20922ac1f 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h @@ -15,30 +15,15 @@ #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/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" +#include "test/syscalls/linux/socket_ip_udp_unbound_external_networking.h" namespace gvisor { namespace testing { // Test fixture for tests that apply to unbound IPv4 UDP sockets in a sandbox // with external networking support. -class IPv4UDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest { - protected: - void SetUp(); - - IfAddrHelper if_helper_; - - // found_net_interfaces_ is set to false if SetUp() could not obtain - // all interface infos that we need. - bool found_net_interfaces_; - - // Interface infos. - int lo_if_idx_; - int eth_if_idx_; - sockaddr_in lo_if_addr_; - sockaddr_in eth_if_addr_; -}; +using IPv4UDPUnboundExternalNetworkingSocketTest = + IPUDPUnboundExternalNetworkingSocketTest; } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc new file mode 100644 index 000000000..7364a1ea5 --- /dev/null +++ b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc @@ -0,0 +1,90 @@ +// Copyright 2020 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 "test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h" + +namespace gvisor { +namespace testing { + +TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestJoinLeaveMulticast) { + SKIP_IF(!found_net_interfaces_); + + auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + + auto receiver_addr = V6Any(); + ASSERT_THAT( + bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), + receiver_addr.addr_len), + SyscallSucceeds()); + socklen_t receiver_addr_len = receiver_addr.addr_len; + ASSERT_THAT(getsockname(receiver->get(), + reinterpret_cast<sockaddr*>(&receiver_addr.addr), + &receiver_addr_len), + SyscallSucceeds()); + EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); + + // Register to receive multicast packets. + auto multicast_addr = V6Multicast(); + ipv6_mreq group_req = { + .ipv6mr_multiaddr = + reinterpret_cast<sockaddr_in6*>(&multicast_addr.addr)->sin6_addr, + .ipv6mr_interface = + (unsigned int)ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")), + }; + ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + &group_req, sizeof(group_req)), + SyscallSucceeds()); + + // Set the sender to the loopback interface. + auto sender_addr = V6Loopback(); + ASSERT_THAT( + bind(sender->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), + sender_addr.addr_len), + SyscallSucceeds()); + + // Send a multicast packet. + auto send_addr = multicast_addr; + reinterpret_cast<sockaddr_in6*>(&send_addr.addr)->sin6_port = + reinterpret_cast<sockaddr_in6*>(&receiver_addr.addr)->sin6_port; + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&send_addr.addr), + send_addr.addr_len), + SyscallSucceedsWithValue(sizeof(send_buf))); + + // Check that we received the multicast packet. + char recv_buf[sizeof(send_buf)] = {}; + ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), 0), + SyscallSucceedsWithValue(sizeof(recv_buf))); + + EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); + + // Leave the group and make sure we don't receive its multicast traffic. + ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, + &group_req, sizeof(group_req)), + SyscallSucceeds()); + RandomizeBuffer(send_buf, sizeof(send_buf)); + ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&send_addr.addr), + send_addr.addr_len), + SyscallSucceedsWithValue(sizeof(send_buf))); + ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), + MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h new file mode 100644 index 000000000..731ae0a1f --- /dev/null +++ b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h @@ -0,0 +1,31 @@ +// Copyright 2020 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. + +#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ + +#include "test/syscalls/linux/socket_ip_udp_unbound_external_networking.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to unbound IPv6 UDP sockets in a sandbox +// with external networking support. +using IPv6UDPUnboundExternalNetworkingSocketTest = + IPUDPUnboundExternalNetworkingSocketTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6yy_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc new file mode 100644 index 000000000..5c764b8fd --- /dev/null +++ b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc @@ -0,0 +1,39 @@ +// Copyright 2020 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 "test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h" + +#include <vector> + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { +namespace { + +std::vector<SocketKind> GetSockets() { + return ApplyVec<SocketKind>( + IPv6UDPUnboundSocket, + AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})); +} + +INSTANTIATE_TEST_SUITE_P(IPv6UDPUnboundSockets, + IPv6UDPUnboundExternalNetworkingSocketTest, + ::testing::ValuesIn(GetSockets())); + +} // namespace +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index a760581b5..26dacc95e 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -860,6 +860,17 @@ TestAddress V6Loopback() { return t; } +TestAddress V6Multicast() { + TestAddress t("V6Multicast"); + t.addr.ss_family = AF_INET6; + t.addr_len = sizeof(sockaddr_in6); + EXPECT_EQ( + 1, + inet_pton(AF_INET6, "ff05::1234", + reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr)); + return t; +} + // Checksum computes the internet checksum of a buffer. uint16_t Checksum(uint16_t* buf, ssize_t buf_size) { // Add up the 16-bit values in the buffer. diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h index 5e205339f..75c0d4735 100644 --- a/test/syscalls/linux/socket_test_util.h +++ b/test/syscalls/linux/socket_test_util.h @@ -502,6 +502,7 @@ TestAddress V4MappedLoopback(); TestAddress V4Multicast(); TestAddress V6Any(); TestAddress V6Loopback(); +TestAddress V6Multicast(); // Compute the internet checksum of an IP header. uint16_t IPChecksum(struct iphdr ip); |