|
14 | 14 |
|
15 | 15 | #include "test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h"
|
16 | 16 |
|
| 17 | +#include <net/if.h> |
| 18 | +#include <sys/socket.h> |
| 19 | + |
| 20 | +#include <cerrno> |
| 21 | +#include <cstring> |
| 22 | +#include <iostream> |
| 23 | +#include <optional> |
| 24 | +#include <utility> |
| 25 | + |
| 26 | +#include "gmock/gmock.h" |
| 27 | +#include "gtest/gtest.h" |
| 28 | +#include "absl/cleanup/cleanup.h" |
| 29 | +#include "test/syscalls/linux/ip_socket_test_util.h" |
| 30 | +#include "test/util/file_descriptor.h" |
| 31 | +#include "test/util/posix_error.h" |
| 32 | +#include "test/util/socket_util.h" |
| 33 | +#include "test/util/test_util.h" |
| 34 | + |
17 | 35 | namespace gvisor {
|
18 | 36 | namespace testing {
|
19 | 37 |
|
| 38 | +void IPv6UDPUnboundExternalNetworkingSocketTest::SetUp() { |
| 39 | +#ifdef ANDROID |
| 40 | + GTEST_SKIP() << "Android does not support getifaddrs in r22"; |
| 41 | +#endif |
| 42 | + |
| 43 | + ifaddrs* ifaddr; |
| 44 | + ASSERT_THAT(getifaddrs(&ifaddr), SyscallSucceeds()); |
| 45 | + auto cleanup = absl::MakeCleanup([ifaddr] { freeifaddrs(ifaddr); }); |
| 46 | + |
| 47 | + for (const ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { |
| 48 | + ASSERT_NE(ifa->ifa_name, nullptr); |
| 49 | + ASSERT_NE(ifa->ifa_addr, nullptr); |
| 50 | + |
| 51 | + if (ifa->ifa_addr->sa_family != AF_INET6) { |
| 52 | + continue; |
| 53 | + } |
| 54 | + |
| 55 | + std::optional<std::pair<int, sockaddr_in6>>& if_pair = *[this, ifa]() { |
| 56 | + if (strcmp(ifa->ifa_name, "lo") == 0) { |
| 57 | + return &lo_if_; |
| 58 | + } |
| 59 | + return ð_if_; |
| 60 | + }(); |
| 61 | + |
| 62 | + const int if_index = |
| 63 | + ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex(ifa->ifa_name)); |
| 64 | + |
| 65 | + std::cout << " name=" << ifa->ifa_name |
| 66 | + << " addr=" << GetAddrStr(ifa->ifa_addr) << " index=" << if_index |
| 67 | + << " has_value=" << if_pair.has_value() << std::endl; |
| 68 | + |
| 69 | + if (if_pair.has_value()) { |
| 70 | + continue; |
| 71 | + } |
| 72 | + |
| 73 | + if_pair = std::make_pair( |
| 74 | + if_index, *reinterpret_cast<const sockaddr_in6*>(ifa->ifa_addr)); |
| 75 | + } |
| 76 | + |
| 77 | + if (!(eth_if_.has_value() && lo_if_.has_value())) { |
| 78 | + GTEST_SKIP() << " eth_if_.has_value()=" << eth_if_.has_value() |
| 79 | + << " lo_if_.has_value()=" << lo_if_.has_value(); |
| 80 | + } |
| 81 | +} |
| 82 | + |
20 | 83 | TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestJoinLeaveMulticast) {
|
21 | 84 | auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
22 | 85 | auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
@@ -82,5 +145,58 @@ TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestJoinLeaveMulticast) {
|
82 | 145 | SyscallFailsWithErrno(EAGAIN));
|
83 | 146 | }
|
84 | 147 |
|
| 148 | +// Test that an AF_INET6 socket can set the IP_ADD_MEMBERSHIP socket option. |
| 149 | +TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, AddV4MembershipToV6Socket) { |
| 150 | + TestAddress send_addr = V4Multicast(); |
| 151 | + sockaddr_in* send_addr_in = reinterpret_cast<sockaddr_in*>(&send_addr.addr); |
| 152 | + |
| 153 | + // recv is an AF_INET6 socket while send is an AF_INET socket. |
| 154 | + auto recv = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); |
| 155 | + FileDescriptor send = |
| 156 | + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); |
| 157 | + |
| 158 | + // Make recv join the multicast group with address `send_addr`. |
| 159 | + // Note that IP_ADD_MEMBERSHIP is used instead of IPV6_ADD_MEMBERSHIP, and |
| 160 | + // the group address is an IPv4 address. |
| 161 | + struct ip_mreq mreq; |
| 162 | + mreq.imr_multiaddr.s_addr = send_addr_in->sin_addr.s_addr; |
| 163 | + mreq.imr_interface.s_addr = htonl(INADDR_ANY); |
| 164 | + ASSERT_THAT( |
| 165 | + setsockopt(recv->get(), SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)), |
| 166 | + SyscallSucceeds()); |
| 167 | + |
| 168 | + // Bind recv to ::. |
| 169 | + auto recv_addr = V6Any(); |
| 170 | + ASSERT_THAT( |
| 171 | + bind(recv->get(), AsSockAddr(&recv_addr.addr), recv_addr.addr_len), |
| 172 | + SyscallSucceeds()); |
| 173 | + socklen_t recv_addr_len = recv_addr.addr_len; |
| 174 | + ASSERT_THAT( |
| 175 | + getsockname(recv->get(), AsSockAddr(&recv_addr.addr), &recv_addr_len), |
| 176 | + SyscallSucceeds()); |
| 177 | + EXPECT_EQ(recv_addr_len, recv_addr.addr_len); |
| 178 | + |
| 179 | + // Send a multicast packet... |
| 180 | + send_addr_in->sin_port = |
| 181 | + reinterpret_cast<sockaddr_in*>(&recv_addr.addr)->sin_port; |
| 182 | + char send_buf[200]; |
| 183 | + RandomizeBuffer(send_buf, sizeof(send_buf)); |
| 184 | + ASSERT_THAT( |
| 185 | + RetryEINTR(sendto)(send.get(), send_buf, sizeof(send_buf), 0, |
| 186 | + AsSockAddr(&send_addr.addr), send_addr.addr_len), |
| 187 | + SyscallSucceedsWithValue(sizeof(send_buf))); |
| 188 | + |
| 189 | + // ...and check that it was received. |
| 190 | + char recv_buf[sizeof(send_buf)] = {}; |
| 191 | + ASSERT_THAT( |
| 192 | + RecvTimeout(recv->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), |
| 193 | + IsPosixErrorOkAndHolds(sizeof(recv_buf))); |
| 194 | + EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); |
| 195 | + |
| 196 | + ASSERT_THAT( |
| 197 | + setsockopt(recv->get(), SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)), |
| 198 | + SyscallSucceeds()); |
| 199 | +} |
| 200 | + |
85 | 201 | } // namespace testing
|
86 | 202 | } // namespace gvisor
|
0 commit comments