diff options
Diffstat (limited to 'test/syscalls')
-rw-r--r-- | test/syscalls/linux/BUILD | 10 | ||||
-rw-r--r-- | test/syscalls/linux/epoll.cc | 36 | ||||
-rw-r--r-- | test/syscalls/linux/ip_socket_test_util.cc | 97 | ||||
-rw-r--r-- | test/syscalls/linux/ip_socket_test_util.h | 19 | ||||
-rw-r--r-- | test/syscalls/linux/proc_isolated.cc | 22 | ||||
-rw-r--r-- | test/syscalls/linux/raw_socket.cc | 129 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc | 59 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_udp_unbound_external_networking.h | 18 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc | 220 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h | 18 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc | 2 |
11 files changed, 389 insertions, 241 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 5b882875f..b96005d7d 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -590,6 +590,7 @@ cc_binary( "//test/util:posix_error", "//test/util:test_main", "//test/util:test_util", + "//test/util:thread_util", ], ) @@ -1978,6 +1979,7 @@ cc_binary( defines = select_system(), linkstatic = 1, deps = [ + ":ip_socket_test_util", ":unix_domain_socket_test_util", "//test/util:capability_util", "//test/util:file_descriptor", @@ -2660,16 +2662,11 @@ 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", - "//test/util:socket_util", - "//test/util:test_util", ], alwayslink = 1, ) @@ -2685,6 +2682,9 @@ cc_library( ], deps = [ ":socket_ip_udp_unbound_external_networking", + "//test/util:socket_util", + "//test/util:test_util", + "@com_google_absl//absl/cleanup", gtest, ], alwayslink = 1, diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc index 3ef8b0327..c2dc8174c 100644 --- a/test/syscalls/linux/epoll.cc +++ b/test/syscalls/linux/epoll.cc @@ -30,6 +30,7 @@ #include "test/util/file_descriptor.h" #include "test/util/posix_error.h" #include "test/util/test_util.h" +#include "test/util/thread_util.h" namespace gvisor { namespace testing { @@ -496,6 +497,41 @@ TEST(EpollTest, PipeReaderHupAfterWriterClosed) { EXPECT_EQ(result[0].data.u64, kMagicConstant); } +TEST(EpollTest, DoubleLayerEpoll) { + int pipefds[2]; + ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds()); + FileDescriptor rfd(pipefds[0]); + FileDescriptor wfd(pipefds[1]); + + auto epfd1 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + ASSERT_NO_ERRNO( + RegisterEpollFD(epfd1.get(), rfd.get(), EPOLLIN | EPOLLHUP, rfd.get())); + + auto epfd2 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + ASSERT_NO_ERRNO(RegisterEpollFD(epfd2.get(), epfd1.get(), EPOLLIN | EPOLLHUP, + epfd1.get())); + + // Write to wfd and then check if epoll events were generated correctly. + // Run this loop a couple of times to check if event in epfd1 is cleaned. + constexpr char data[] = "data"; + for (int i = 0; i < 2; ++i) { + ScopedThread thread1([&wfd, &data]() { + sleep(1); + ASSERT_EQ(WriteFd(wfd.get(), data, sizeof(data)), sizeof(data)); + }); + + struct epoll_event ret_events[2]; + ASSERT_THAT(RetryEINTR(epoll_wait)(epfd2.get(), ret_events, 2, 5000), + SyscallSucceedsWithValue(1)); + ASSERT_EQ(ret_events[0].data.fd, epfd1.get()); + ASSERT_THAT(RetryEINTR(epoll_wait)(epfd1.get(), ret_events, 2, 5000), + SyscallSucceedsWithValue(1)); + ASSERT_EQ(ret_events[0].data.fd, rfd.get()); + char readBuf[sizeof(data)]; + ASSERT_EQ(ReadFd(rfd.get(), readBuf, sizeof(data)), sizeof(data)); + } +} + } // namespace } // namespace testing diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc index a1216d23f..d18e616d0 100644 --- a/test/syscalls/linux/ip_socket_test_util.cc +++ b/test/syscalls/linux/ip_socket_test_util.cc @@ -16,6 +16,7 @@ #include <net/if.h> #include <netinet/in.h> +#include <netpacket/packet.h> #include <sys/socket.h> #include <cstring> @@ -196,75 +197,53 @@ SocketKind IPv6TCPUnboundSocket(int type) { UnboundSocketCreator(AF_INET6, type | SOCK_STREAM, IPPROTO_TCP)}; } -PosixError IfAddrHelper::Load() { - Release(); -#ifndef ANDROID - RETURN_ERROR_IF_SYSCALL_FAIL(getifaddrs(&ifaddr_)); -#else - // Android does not support getifaddrs in r22. - return PosixError(ENOSYS, "getifaddrs"); -#endif - return NoError(); -} - -void IfAddrHelper::Release() { - if (ifaddr_) { -#ifndef ANDROID - // Android does not support freeifaddrs in r22. - freeifaddrs(ifaddr_); -#endif - ifaddr_ = nullptr; - } -} - -std::vector<std::string> IfAddrHelper::InterfaceList(int family) const { - std::vector<std::string> names; - for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) { - continue; - } - names.emplace(names.end(), ifa->ifa_name); - } - return names; -} - -const sockaddr* IfAddrHelper::GetAddr(int family, std::string name) const { - for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) { - continue; - } - if (name == ifa->ifa_name) { - return ifa->ifa_addr; - } - } - return nullptr; -} - -PosixErrorOr<int> IfAddrHelper::GetIndex(std::string name) const { - return InterfaceIndex(name); -} - std::string GetAddr4Str(const in_addr* a) { char str[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, a, str, sizeof(str)); - return std::string(str); + return inet_ntop(AF_INET, a, str, sizeof(str)); } std::string GetAddr6Str(const in6_addr* a) { char str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, a, str, sizeof(str)); - return std::string(str); + return inet_ntop(AF_INET6, a, str, sizeof(str)); } std::string GetAddrStr(const sockaddr* a) { - if (a->sa_family == AF_INET) { - auto src = &(reinterpret_cast<const sockaddr_in*>(a)->sin_addr); - return GetAddr4Str(src); - } else if (a->sa_family == AF_INET6) { - auto src = &(reinterpret_cast<const sockaddr_in6*>(a)->sin6_addr); - return GetAddr6Str(src); + switch (a->sa_family) { + case AF_INET: { + return GetAddr4Str(&(reinterpret_cast<const sockaddr_in*>(a)->sin_addr)); + } + case AF_INET6: { + return GetAddr6Str( + &(reinterpret_cast<const sockaddr_in6*>(a)->sin6_addr)); + } + case AF_PACKET: { + const sockaddr_ll& ll = *reinterpret_cast<const sockaddr_ll*>(a); + std::ostringstream ss; + ss << std::hex; + ss << std::showbase; + ss << '{'; + ss << " protocol=" << ntohs(ll.sll_protocol); + ss << " ifindex=" << ll.sll_ifindex; + ss << " hatype=" << ll.sll_hatype; + ss << " pkttype=" << static_cast<unsigned short>(ll.sll_pkttype); + if (ll.sll_halen != 0) { + ss << " addr="; + for (unsigned char i = 0; i < ll.sll_halen; ++i) { + if (i != 0) { + ss << ':'; + } + ss << static_cast<unsigned short>(ll.sll_addr[i]); + } + } + ss << " }"; + return ss.str(); + } + default: { + std::ostringstream ss; + ss << "invalid(sa_family=" << a->sa_family << ")"; + return ss.str(); + } } - return std::string("<invalid>"); } } // namespace testing diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h index 556838356..957006e25 100644 --- a/test/syscalls/linux/ip_socket_test_util.h +++ b/test/syscalls/linux/ip_socket_test_util.h @@ -115,25 +115,6 @@ SocketKind IPv4TCPUnboundSocket(int type); // created with AF_INET6, SOCK_STREAM, IPPROTO_TCP and the given type. SocketKind IPv6TCPUnboundSocket(int type); -// IfAddrHelper is a helper class that determines the local interfaces present -// and provides functions to obtain their names, index numbers, and IP address. -class IfAddrHelper { - public: - IfAddrHelper() : ifaddr_(nullptr) {} - ~IfAddrHelper() { Release(); } - - PosixError Load(); - void Release(); - - std::vector<std::string> InterfaceList(int family) const; - - const sockaddr* GetAddr(int family, std::string name) const; - PosixErrorOr<int> GetIndex(std::string name) const; - - private: - struct ifaddrs* ifaddr_; -}; - // GetAddr4Str returns the given IPv4 network address structure as a string. std::string GetAddr4Str(const in_addr* a); diff --git a/test/syscalls/linux/proc_isolated.cc b/test/syscalls/linux/proc_isolated.cc index a38689667..38d079d2b 100644 --- a/test/syscalls/linux/proc_isolated.cc +++ b/test/syscalls/linux/proc_isolated.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <linux/msg.h> #include <linux/sem.h> #include <linux/shm.h> @@ -73,6 +74,27 @@ TEST(ProcDefaults, PresenceOfSem) { ASSERT_EQ(semmni, SEMMNI); } +TEST(ProcDefaults, PresenceOfMsgMniMaxMnb) { + uint64_t msgmni = 0; + uint64_t msgmax = 0; + uint64_t msgmnb = 0; + + std::string proc_file; + proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/msgmni")); + ASSERT_FALSE(proc_file.empty()); + ASSERT_TRUE(absl::SimpleAtoi(proc_file, &msgmni)); + proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/msgmax")); + ASSERT_FALSE(proc_file.empty()); + ASSERT_TRUE(absl::SimpleAtoi(proc_file, &msgmax)); + proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/msgmnb")); + ASSERT_FALSE(proc_file.empty()); + ASSERT_TRUE(absl::SimpleAtoi(proc_file, &msgmnb)); + + ASSERT_EQ(msgmni, MSGMNI); + ASSERT_EQ(msgmax, MSGMAX); + ASSERT_EQ(msgmnb, MSGMNB); +} + } // namespace } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc index ef1db47ee..ef176cbee 100644 --- a/test/syscalls/linux/raw_socket.cc +++ b/test/syscalls/linux/raw_socket.cc @@ -25,6 +25,7 @@ #include <algorithm> #include "gtest/gtest.h" +#include "test/syscalls/linux/ip_socket_test_util.h" #include "test/syscalls/linux/unix_domain_socket_test_util.h" #include "test/util/capability_util.h" #include "test/util/file_descriptor.h" @@ -39,6 +40,9 @@ namespace testing { namespace { +using ::testing::IsNull; +using ::testing::NotNull; + // Fixture for tests parameterized by protocol. class RawSocketTest : public ::testing::TestWithParam<std::tuple<int, int>> { protected: @@ -1057,6 +1061,131 @@ TEST(RawSocketTest, BindReceive) { ASSERT_NO_FATAL_FAILURE(TestRawSocketMaybeBindReceive(true /* do_bind */)); } +TEST(RawSocketTest, ReceiveIPPacketInfo) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); + + FileDescriptor raw = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_UDP)); + + const sockaddr_in addr_ = { + .sin_family = AF_INET, + .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)}, + }; + ASSERT_THAT( + bind(raw.get(), reinterpret_cast<const sockaddr*>(&addr_), sizeof(addr_)), + SyscallSucceeds()); + + // Register to receive IP packet info. + constexpr int one = 1; + ASSERT_THAT(setsockopt(raw.get(), IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)), + SyscallSucceeds()); + + constexpr char send_buf[] = "malformed UDP"; + ASSERT_THAT(sendto(raw.get(), send_buf, sizeof(send_buf), 0 /* flags */, + reinterpret_cast<const sockaddr*>(&addr_), sizeof(addr_)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + struct { + iphdr ip; + char data[sizeof(send_buf)]; + + // Extra space in the receive buffer should be unused. + char unused_space; + } ABSL_ATTRIBUTE_PACKED recv_buf; + iovec recv_iov = { + .iov_base = &recv_buf, + .iov_len = sizeof(recv_buf), + }; + in_pktinfo received_pktinfo; + char recv_cmsg_buf[CMSG_SPACE(sizeof(received_pktinfo))]; + msghdr recv_msg = { + .msg_iov = &recv_iov, + .msg_iovlen = 1, + .msg_control = recv_cmsg_buf, + .msg_controllen = CMSG_LEN(sizeof(received_pktinfo)), + }; + ASSERT_THAT(RetryEINTR(recvmsg)(raw.get(), &recv_msg, 0), + SyscallSucceedsWithValue(sizeof(iphdr) + sizeof(send_buf))); + EXPECT_EQ(memcmp(send_buf, &recv_buf.data, sizeof(send_buf)), 0); + EXPECT_EQ(recv_buf.ip.version, static_cast<unsigned int>(IPVERSION)); + // IHL holds the number of header bytes in 4 byte units. + EXPECT_EQ(recv_buf.ip.ihl, sizeof(iphdr) / 4); + EXPECT_EQ(ntohs(recv_buf.ip.tot_len), sizeof(iphdr) + sizeof(send_buf)); + EXPECT_EQ(recv_buf.ip.protocol, IPPROTO_UDP); + EXPECT_EQ(ntohl(recv_buf.ip.saddr), INADDR_LOOPBACK); + EXPECT_EQ(ntohl(recv_buf.ip.daddr), INADDR_LOOPBACK); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&recv_msg); + ASSERT_THAT(cmsg, NotNull()); + EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(received_pktinfo))); + EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IP); + EXPECT_EQ(cmsg->cmsg_type, IP_PKTINFO); + memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(received_pktinfo)); + EXPECT_EQ(received_pktinfo.ipi_ifindex, + ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex())); + EXPECT_EQ(ntohl(received_pktinfo.ipi_spec_dst.s_addr), INADDR_LOOPBACK); + EXPECT_EQ(ntohl(received_pktinfo.ipi_addr.s_addr), INADDR_LOOPBACK); + + EXPECT_THAT(CMSG_NXTHDR(&recv_msg, cmsg), IsNull()); +} + +TEST(RawSocketTest, ReceiveIPv6PacketInfo) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); + + FileDescriptor raw = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_RAW, IPPROTO_UDP)); + + const sockaddr_in6 addr_ = { + .sin6_family = AF_INET6, + .sin6_addr = in6addr_loopback, + }; + ASSERT_THAT( + bind(raw.get(), reinterpret_cast<const sockaddr*>(&addr_), sizeof(addr_)), + SyscallSucceeds()); + + // Register to receive IPv6 packet info. + constexpr int one = 1; + ASSERT_THAT( + setsockopt(raw.get(), IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)), + SyscallSucceeds()); + + constexpr char send_buf[] = "malformed UDP"; + ASSERT_THAT(sendto(raw.get(), send_buf, sizeof(send_buf), 0 /* flags */, + reinterpret_cast<const sockaddr*>(&addr_), sizeof(addr_)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + char recv_buf[sizeof(send_buf) + 1]; + iovec recv_iov = { + .iov_base = recv_buf, + .iov_len = sizeof(recv_buf), + }; + in6_pktinfo received_pktinfo; + char recv_cmsg_buf[CMSG_SPACE(sizeof(received_pktinfo))]; + msghdr recv_msg = { + .msg_iov = &recv_iov, + .msg_iovlen = 1, + .msg_control = recv_cmsg_buf, + .msg_controllen = CMSG_LEN(sizeof(received_pktinfo)), + }; + ASSERT_THAT(RetryEINTR(recvmsg)(raw.get(), &recv_msg, 0), + SyscallSucceedsWithValue(sizeof(send_buf))); + EXPECT_EQ(memcmp(send_buf, recv_buf, sizeof(send_buf)), 0); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&recv_msg); + ASSERT_THAT(cmsg, NotNull()); + EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(received_pktinfo))); + EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IPV6); + EXPECT_EQ(cmsg->cmsg_type, IPV6_PKTINFO); + memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(received_pktinfo)); + EXPECT_EQ(received_pktinfo.ipi6_ifindex, + ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex())); + ASSERT_EQ(memcmp(&received_pktinfo.ipi6_addr, &in6addr_loopback, + sizeof(in6addr_loopback)), + 0); + + EXPECT_THAT(CMSG_NXTHDR(&recv_msg, cmsg), IsNull()); +} + } // namespace } // namespace testing diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc deleted file mode 100644 index af2459a2f..000000000 --- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc +++ /dev/null @@ -1,59 +0,0 @@ -// 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/util/socket_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 index 2e8aab129..92c20eba9 100644 --- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h +++ b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h @@ -16,29 +16,13 @@ #define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ #include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/util/socket_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_; -}; +class IPUDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest {}; } // 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 index c6e775b2a..6c67ec51e 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc @@ -14,9 +14,62 @@ #include "test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h" +#include <net/if.h> + +#include "absl/cleanup/cleanup.h" +#include "test/util/socket_util.h" +#include "test/util/test_util.h" + namespace gvisor { namespace testing { +void IPv4UDPUnboundExternalNetworkingSocketTest::SetUp() { +#ifdef ANDROID + GTEST_SKIP() << "Android does not support getifaddrs in r22"; +#endif + + ifaddrs* ifaddr; + ASSERT_THAT(getifaddrs(&ifaddr), SyscallSucceeds()); + auto cleanup = absl::MakeCleanup([ifaddr] { freeifaddrs(ifaddr); }); + + for (const ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + ASSERT_NE(ifa->ifa_name, nullptr); + ASSERT_NE(ifa->ifa_addr, nullptr); + + if (ifa->ifa_addr->sa_family != AF_INET) { + continue; + } + + std::optional<std::pair<int, sockaddr_in>>& if_pair = *[this, ifa]() { + if (strcmp(ifa->ifa_name, "lo") == 0) { + return &lo_if_; + } + return ð_if_; + }(); + + const int if_index = + ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex(ifa->ifa_name)); + + std::cout << " name=" << ifa->ifa_name + << " addr=" << GetAddrStr(ifa->ifa_addr) << " index=" << if_index + << " has_value=" << if_pair.has_value() << std::endl; + + if (if_pair.has_value()) { + continue; + } + + if_pair = std::make_pair( + if_index, *reinterpret_cast<const sockaddr_in*>(ifa->ifa_addr)); + } + + if (!(eth_if_.has_value() && lo_if_.has_value())) { + // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its + // IPv4 address on eth0. + GTEST_SKIP() << " eth_if_.has_value()=" << eth_if_.has_value() + << " lo_if_.has_value()=" << lo_if_.has_value(); + } +} + TestAddress V4EmptyAddress() { TestAddress t("V4Empty"); t.addr.ss_family = AF_INET; @@ -28,7 +81,6 @@ TestAddress V4EmptyAddress() { // the destination port number. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, UDPBroadcastReceivedOnExpectedPort) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -101,8 +153,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // not a unicast address. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, UDPBroadcastReceivedOnExpectedAddresses) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -149,7 +199,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // Bind the non-receiving socket to the unicast ethernet address. auto norecv_addr = rcv1_addr; reinterpret_cast<sockaddr_in*>(&norecv_addr.addr)->sin_addr = - eth_if_addr_.sin_addr; + eth_if_addr().sin_addr; ASSERT_THAT( bind(norcv->get(), AsSockAddr(&norecv_addr.addr), norecv_addr.addr_len), SyscallSucceedsWithValue(0)); @@ -184,7 +234,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // (UDPBroadcastSendRecvOnSocketBoundToAny). TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, UDPBroadcastSendRecvOnSocketBoundToBroadcast) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); // Enable SO_BROADCAST. @@ -224,7 +273,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // (UDPBroadcastSendRecvOnSocketBoundToBroadcast). TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, UDPBroadcastSendRecvOnSocketBoundToAny) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); // Enable SO_BROADCAST. @@ -261,7 +309,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // Verifies that a UDP broadcast fails to send on a socket with SO_BROADCAST // disabled. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendBroadcast) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); // Broadcast a test message without having enabled SO_BROADCAST on the sending @@ -306,12 +353,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendUnicastOnUnbound) { // set interface or group membership. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelfNoGroup) { - // FIXME(b/125485338): A group membership is not required for external - // multicast on gVisor. - SKIP_IF(IsRunningOnGvisor()); - - SKIP_IF(!found_net_interfaces_); - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto bind_addr = V4Any(); @@ -345,7 +386,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // Check that multicast packets will be delivered to the sending socket without // setting an interface. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelf) { - SKIP_IF(!found_net_interfaces_); auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto bind_addr = V4Any(); @@ -388,7 +428,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelf) { // set interface and IP_MULTICAST_LOOP disabled. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelfLoopOff) { - SKIP_IF(!found_net_interfaces_); auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto bind_addr = V4Any(); @@ -434,12 +473,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // Check that multicast packets won't be delivered to another socket with no // set interface or group membership. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) { - // FIXME(b/125485338): A group membership is not required for external - // multicast on gVisor. - SKIP_IF(IsRunningOnGvisor()); - - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -476,7 +509,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) { // Check that multicast packets will be delivered to another socket without // setting an interface. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticast) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -522,7 +554,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticast) { // set interface and IP_MULTICAST_LOOP disabled on the sending socket. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSenderNoLoop) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -572,8 +603,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // setting an interface and IP_MULTICAST_LOOP disabled on the receiving socket. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastReceiverNoLoop) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -624,7 +653,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // and both will receive data on it when bound to the ANY address. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastToTwoBoundToAny) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); std::unique_ptr<FileDescriptor> receivers[2] = { ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), @@ -689,7 +717,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // and both will receive data on it when bound to the multicast address. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastToTwoBoundToMulticastAddress) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); std::unique_ptr<FileDescriptor> receivers[2] = { ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), @@ -757,7 +784,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // multicast address, both will receive data. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastToTwoBoundToAnyAndMulticastAddress) { - SKIP_IF(!found_net_interfaces_); auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); std::unique_ptr<FileDescriptor> receivers[2] = { ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), @@ -829,8 +855,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // is not a multicast address. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, IpMulticastLoopbackFromAddr) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -893,8 +917,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // interface, a multicast packet sent out uses the latter as its source address. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, IpMulticastLoopbackIfNicAndAddr) { - SKIP_IF(!found_net_interfaces_); - // Create receiver, bind to ANY and join the multicast group. auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto receiver_addr = V4Any(); @@ -906,11 +928,15 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, &receiver_addr_len), SyscallSucceeds()); EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - int receiver_port = + const in_port_t receiver_port = reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = lo_if_idx_; + const ip_mreqn group = { + .imr_multiaddr = + { + .s_addr = inet_addr(kMulticastAddress), + }, + .imr_ifindex = lo_if_idx(), + }; ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)), SyscallSucceeds()); @@ -918,9 +944,10 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // Set outgoing multicast interface config, with NIC and addr pointing to // different interfaces. auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - ip_mreqn iface = {}; - iface.imr_ifindex = lo_if_idx_; - iface.imr_address = eth_if_addr_.sin_addr; + const ip_mreqn iface = { + .imr_address = eth_if_addr().sin_addr, + .imr_ifindex = lo_if_idx(), + }; ASSERT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, sizeof(iface)), SyscallSucceeds()); @@ -928,67 +955,104 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, // Send a multicast packet. auto sendto_addr = V4Multicast(); reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = receiver_port; - char send_buf[4] = {}; + char send_buf[4]; ASSERT_THAT( RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, AsSockAddr(&sendto_addr.addr), sendto_addr.addr_len), SyscallSucceedsWithValue(sizeof(send_buf))); // Receive a multicast packet. - char recv_buf[sizeof(send_buf)] = {}; + char recv_buf[sizeof(send_buf) + 1]; auto src_addr = V4EmptyAddress(); ASSERT_THAT( RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0, AsSockAddr(&src_addr.addr), &src_addr.addr_len), - SyscallSucceedsWithValue(sizeof(recv_buf))); - ASSERT_EQ(sizeof(struct sockaddr_in), src_addr.addr_len); - sockaddr_in* src_addr_in = reinterpret_cast<sockaddr_in*>(&src_addr.addr); - - // FIXME (b/137781162): When sending a multicast packet use the proper logic - // to determine the packet's src-IP. - SKIP_IF(IsRunningOnGvisor()); + SyscallSucceedsWithValue(sizeof(send_buf))); + ASSERT_EQ(src_addr.addr_len, sizeof(struct sockaddr_in)); // Verify the received source address. - EXPECT_EQ(eth_if_addr_.sin_addr.s_addr, src_addr_in->sin_addr.s_addr); + // + // TODO(https://gvisor.dev/issue/6686): gVisor is a strong host, preventing + // the packet from being sent from the loopback device using the ethernet + // device's address. + if (IsRunningOnGvisor()) { + EXPECT_EQ(GetAddrStr(AsSockAddr(&src_addr.addr)), + GetAddr4Str(&lo_if_addr().sin_addr)); + } else { + EXPECT_EQ(GetAddrStr(AsSockAddr(&src_addr.addr)), + GetAddr4Str(ð_if_addr().sin_addr)); + } } // Check that when we are bound to one interface we can set IP_MULTICAST_IF to // another interface. TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, IpMulticastLoopbackBindToOneIfSetMcastIfToAnother) { - SKIP_IF(!found_net_interfaces_); - - // FIXME (b/137790511): When bound to one interface it is not possible to set - // IP_MULTICAST_IF to a different interface. - SKIP_IF(IsRunningOnGvisor()); - - // Create sender and bind to eth interface. - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - ASSERT_THAT( - bind(sender->get(), AsSockAddr(ð_if_addr_), sizeof(eth_if_addr_)), - SyscallSucceeds()); - // Run through all possible combinations of index and address for // IP_MULTICAST_IF that selects the loopback interface. - struct { - int imr_ifindex; - struct in_addr imr_address; - } test_data[] = { - {lo_if_idx_, {}}, - {0, lo_if_addr_.sin_addr}, - {lo_if_idx_, lo_if_addr_.sin_addr}, - {lo_if_idx_, eth_if_addr_.sin_addr}, + ip_mreqn ifaces[] = { + { + .imr_address = {}, + .imr_ifindex = lo_if_idx(), + }, + { + .imr_address = lo_if_addr().sin_addr, + .imr_ifindex = 0, + }, + { + .imr_address = lo_if_addr().sin_addr, + .imr_ifindex = lo_if_idx(), + }, + { + .imr_address = eth_if_addr().sin_addr, + .imr_ifindex = lo_if_idx(), + }, }; - for (auto t : test_data) { - ip_mreqn iface = {}; - iface.imr_ifindex = t.imr_ifindex; - iface.imr_address = t.imr_address; - EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()) - << "imr_index=" << iface.imr_ifindex - << " imr_address=" << GetAddr4Str(&iface.imr_address); + + { + auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + ASSERT_THAT( + bind(sender->get(), AsSockAddr(ð_if_addr()), sizeof(eth_if_addr())), + SyscallSucceeds()); + + for (const ip_mreqn& iface : ifaces) { + EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, + sizeof(iface)), + SyscallSucceeds()) + << " imr_index=" << iface.imr_ifindex + << " imr_address=" << GetAddr4Str(&iface.imr_address); + } + } + + { + char eth_if_name[IF_NAMESIZE]; + memset(eth_if_name, 0xAA, sizeof(eth_if_name)); + ASSERT_NE(if_indextoname(eth_if_idx(), eth_if_name), nullptr) + << strerror(errno); + auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BINDTODEVICE, + eth_if_name, sizeof(eth_if_name)), + SyscallSucceeds()); + + for (const ip_mreqn& iface : ifaces) { + // FIXME(b/137790511): Disallow mismatching IP_MULTICAST_IF and + // SO_BINDTODEVICE. + if (IsRunningOnGvisor()) { + EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, + &iface, sizeof(iface)), + SyscallSucceeds()) + << " imr_index=" << iface.imr_ifindex + << " imr_address=" << GetAddr4Str(&iface.imr_address); + } else { + EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, + &iface, sizeof(iface)), + SyscallFailsWithErrno(EINVAL)) + << " imr_index=" << iface.imr_ifindex + << " imr_address=" << GetAddr4Str(&iface.imr_address); + } + } } } + } // 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 index 20922ac1f..ac917b32c 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h @@ -22,8 +22,22 @@ namespace testing { // Test fixture for tests that apply to unbound IPv4 UDP sockets in a sandbox // with external networking support. -using IPv4UDPUnboundExternalNetworkingSocketTest = - IPUDPUnboundExternalNetworkingSocketTest; +class IPv4UDPUnboundExternalNetworkingSocketTest + : public IPUDPUnboundExternalNetworkingSocketTest { + protected: + void SetUp() override; + + int lo_if_idx() const { return std::get<0>(lo_if_.value()); } + int eth_if_idx() const { return std::get<0>(eth_if_.value()); } + + const sockaddr_in& lo_if_addr() const { return std::get<1>(lo_if_.value()); } + const sockaddr_in& eth_if_addr() const { + return std::get<1>(eth_if_.value()); + } + + private: + std::optional<std::pair<int, sockaddr_in>> lo_if_, eth_if_; +}; } // 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 index d72b6b53f..c6e563cd3 100644 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc +++ b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc @@ -18,8 +18,6 @@ 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()); |