diff options
-rw-r--r-- | test/syscalls/linux/BUILD | 5 | ||||
-rw-r--r-- | test/syscalls/linux/ip_socket_test_util.cc | 16 | ||||
-rw-r--r-- | test/syscalls/linux/ip_socket_test_util.h | 16 | ||||
-rw-r--r-- | test/syscalls/linux/ping_socket.cc | 221 | ||||
-rw-r--r-- | test/syscalls/linux/socket_test_util.cc | 109 | ||||
-rw-r--r-- | test/syscalls/linux/socket_test_util.h | 30 |
6 files changed, 334 insertions, 63 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 9a912dba8..d8b562e9d 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1572,10 +1572,13 @@ cc_binary( srcs = ["ping_socket.cc"], linkstatic = 1, deps = [ + ":ip_socket_test_util", ":socket_test_util", "//test/util:file_descriptor", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", gtest, - "//test/util:save_util", "//test/util:test_main", "//test/util:test_util", ], diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc index 95082a0f2..e90a7e411 100644 --- a/test/syscalls/linux/ip_socket_test_util.cc +++ b/test/syscalls/linux/ip_socket_test_util.cc @@ -140,6 +140,22 @@ SocketPairKind IPv4UDPUnboundSocketPair(int type) { /* dual_stack = */ false)}; } +SocketKind ICMPUnboundSocket(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "ICMP socket"); + return SocketKind{ + description, AF_INET, type | SOCK_DGRAM, IPPROTO_ICMP, + UnboundSocketCreator(AF_INET, type | SOCK_DGRAM, IPPROTO_ICMP)}; +} + +SocketKind ICMPv6UnboundSocket(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "ICMPv6 socket"); + return SocketKind{ + description, AF_INET6, type | SOCK_DGRAM, IPPROTO_ICMPV6, + UnboundSocketCreator(AF_INET6, type | SOCK_DGRAM, IPPROTO_ICMPV6)}; +} + SocketKind IPv4UDPUnboundSocket(int type) { std::string description = absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket"); diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h index 9c3859fcd..bde481f7e 100644 --- a/test/syscalls/linux/ip_socket_test_util.h +++ b/test/syscalls/linux/ip_socket_test_util.h @@ -84,20 +84,28 @@ SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type); // SocketPairs created with AF_INET and the given type. SocketPairKind IPv4UDPUnboundSocketPair(int type); +// ICMPUnboundSocket returns a SocketKind that represents a SimpleSocket created +// with AF_INET, SOCK_DGRAM, IPPROTO_ICMP, and the given type. +SocketKind ICMPUnboundSocket(int type); + +// ICMPv6UnboundSocket returns a SocketKind that represents a SimpleSocket +// created with AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6, and the given type. +SocketKind ICMPv6UnboundSocket(int type); + // IPv4UDPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET, SOCK_DGRAM, and the given type. +// created with AF_INET, SOCK_DGRAM, IPPROTO_UDP, and the given type. SocketKind IPv4UDPUnboundSocket(int type); // IPv6UDPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET6, SOCK_DGRAM, and the given type. +// created with AF_INET6, SOCK_DGRAM, IPPROTO_UDP, and the given type. SocketKind IPv6UDPUnboundSocket(int type); // IPv4TCPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET, SOCK_STREAM and the given type. +// created with AF_INET, SOCK_STREAM, IPPROTO_TCP and the given type. SocketKind IPv4TCPUnboundSocket(int type); // IPv6TCPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET6, SOCK_STREAM and the given 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 diff --git a/test/syscalls/linux/ping_socket.cc b/test/syscalls/linux/ping_socket.cc index 8b78e4b16..8268e91da 100644 --- a/test/syscalls/linux/ping_socket.cc +++ b/test/syscalls/linux/ping_socket.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <errno.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> @@ -19,14 +20,22 @@ #include <sys/types.h> #include <unistd.h> +#include <cctype> +#include <cstring> #include <vector> #include "gtest/gtest.h" +#include "absl/algorithm/container.h" +#include "absl/strings/str_join.h" +#include "absl/types/optional.h" +#include "test/syscalls/linux/ip_socket_test_util.h" #include "test/syscalls/linux/socket_test_util.h" #include "test/util/file_descriptor.h" -#include "test/util/save_util.h" #include "test/util/test_util.h" +// Note: These tests require /proc/sys/net/ipv4/ping_group_range to be +// configured to allow the tester to create ping sockets (see icmp(7)). + namespace gvisor { namespace testing { namespace { @@ -42,7 +51,8 @@ TEST(PingSocket, ICMPPortExhaustion) { auto s = Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); if (!s.ok()) { ASSERT_EQ(s.error().errno_value(), EACCES); - GTEST_SKIP(); + GTEST_SKIP() << "TODO(gvisor.dev/issue/6126): Buildkite does not allow " + "creation of ICMP or ICMPv6 sockets"; } } @@ -70,7 +80,212 @@ TEST(PingSocket, ICMPPortExhaustion) { } } -} // namespace +struct BindTestCase { + TestAddress bind_to; + int want = 0; + absl::optional<int> want_gvisor; +}; + +// Test fixture for socket binding. +class Fixture + : public ::testing::TestWithParam<std::tuple<SocketKind, BindTestCase>> {}; + +TEST_P(Fixture, Bind) { + auto [socket_factory, test_case] = GetParam(); + auto socket = socket_factory.Create(); + if (!socket.ok()) { + ASSERT_EQ(socket.error().errno_value(), EACCES); + GTEST_SKIP() << "TODO(gvisor.dev/issue/6126): Buildkite does not allow " + "creation of ICMP or ICMPv6 sockets"; + } + auto socket_fd = std::move(socket).ValueOrDie(); + + const int want = test_case.want_gvisor.has_value() && IsRunningOnGvisor() + ? *test_case.want_gvisor + : test_case.want; + if (want == 0) { + EXPECT_THAT(bind(socket_fd->get(), AsSockAddr(&test_case.bind_to.addr), + test_case.bind_to.addr_len), + SyscallSucceeds()); + } else { + EXPECT_THAT(bind(socket_fd->get(), AsSockAddr(&test_case.bind_to.addr), + test_case.bind_to.addr_len), + SyscallFailsWithErrno(want)); + } +} + +std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() { + return ApplyVec<std::tuple<SocketKind, BindTestCase>>( + [](const BindTestCase& test_case) { + return std::make_tuple(ICMPUnboundSocket(0), test_case); + }, + std::vector<BindTestCase>{ + { + .bind_to = V4Any(), + .want = 0, + .want_gvisor = 0, + }, + { + .bind_to = V4Broadcast(), + .want = EADDRNOTAVAIL, + // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP + // sockets are no longer allowed to bind to broadcast addresses. + .want_gvisor = 0, + }, + { + .bind_to = V4Loopback(), + .want = 0, + }, + { + .bind_to = V4LoopbackSubnetBroadcast(), + .want = EADDRNOTAVAIL, + // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP + // sockets are no longer allowed to bind to broadcast addresses. + .want_gvisor = 0, + }, + { + .bind_to = V4Multicast(), + .want = EADDRNOTAVAIL, + }, + { + .bind_to = V4MulticastAllHosts(), + .want = EADDRNOTAVAIL, + }, + { + .bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"), + .want = EADDRNOTAVAIL, + }, + // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the test + // cases below once ICMP sockets return EAFNOSUPPORT when binding to + // IPv6 addresses. + { + .bind_to = V6Any(), + .want = EAFNOSUPPORT, + .want_gvisor = EINVAL, + }, + { + .bind_to = V6Loopback(), + .want = EAFNOSUPPORT, + .want_gvisor = EINVAL, + }, + { + .bind_to = V6Multicast(), + .want = EAFNOSUPPORT, + .want_gvisor = EINVAL, + }, + { + .bind_to = V6MulticastInterfaceLocalAllNodes(), + .want = EAFNOSUPPORT, + .want_gvisor = EINVAL, + }, + { + .bind_to = V6MulticastLinkLocalAllNodes(), + .want = EAFNOSUPPORT, + .want_gvisor = EINVAL, + }, + { + .bind_to = V6MulticastLinkLocalAllRouters(), + .want = EAFNOSUPPORT, + .want_gvisor = EINVAL, + }, + { + .bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"), + .want = EAFNOSUPPORT, + .want_gvisor = EINVAL, + }, + }); +} +std::vector<std::tuple<SocketKind, BindTestCase>> ICMPv6TestCases() { + return ApplyVec<std::tuple<SocketKind, BindTestCase>>( + [](const BindTestCase& test_case) { + return std::make_tuple(ICMPv6UnboundSocket(0), test_case); + }, + std::vector<BindTestCase>{ + { + .bind_to = V4Any(), + .want = EINVAL, + }, + { + .bind_to = V4Broadcast(), + .want = EINVAL, + }, + { + .bind_to = V4Loopback(), + .want = EINVAL, + }, + { + .bind_to = V4LoopbackSubnetBroadcast(), + .want = EINVAL, + }, + { + .bind_to = V4Multicast(), + .want = EINVAL, + }, + { + .bind_to = V4MulticastAllHosts(), + .want = EINVAL, + }, + { + .bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"), + .want = EINVAL, + }, + { + .bind_to = V6Any(), + .want = 0, + }, + { + .bind_to = V6Loopback(), + .want = 0, + }, + // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the + // multicast test cases below once ICMPv6 sockets return EINVAL when + // binding to IPv6 multicast addresses. + { + .bind_to = V6Multicast(), + .want = EINVAL, + .want_gvisor = EADDRNOTAVAIL, + }, + { + .bind_to = V6MulticastInterfaceLocalAllNodes(), + .want = EINVAL, + .want_gvisor = EADDRNOTAVAIL, + }, + { + .bind_to = V6MulticastLinkLocalAllNodes(), + .want = EINVAL, + .want_gvisor = EADDRNOTAVAIL, + }, + { + .bind_to = V6MulticastLinkLocalAllRouters(), + .want = EINVAL, + .want_gvisor = EADDRNOTAVAIL, + }, + { + .bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"), + .want = EADDRNOTAVAIL, + }, + }); +} + +std::vector<std::tuple<SocketKind, BindTestCase>> AllTestCases() { + return VecCat<std::tuple<SocketKind, BindTestCase>>(ICMPTestCases(), + ICMPv6TestCases()); +} + +std::string TestDescription( + const ::testing::TestParamInfo<Fixture::ParamType>& info) { + auto [socket_factory, test_case] = info.param; + std::string name = absl::StrJoin( + {socket_factory.description, test_case.bind_to.description}, "_"); + absl::c_replace_if( + name, [](char c) { return !std::isalnum(c); }, '_'); + return name; +} + +INSTANTIATE_TEST_SUITE_P(PingSockets, Fixture, + ::testing::ValuesIn(AllTestCases()), TestDescription); + +} // namespace } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index 9e3a129cf..6039b2a67 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -15,6 +15,7 @@ #include "test/syscalls/linux/socket_test_util.h" #include <arpa/inet.h> +#include <netinet/in.h> #include <poll.h> #include <sys/socket.h> @@ -798,84 +799,82 @@ TestAddress TestAddress::WithPort(uint16_t port) const { return addr; } -TestAddress V4Any() { - TestAddress t("V4Any"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = htonl(INADDR_ANY); - return t; -} +namespace { -TestAddress V4Loopback() { - TestAddress t("V4Loopback"); +TestAddress V4Addr(std::string description, in_addr_t addr) { + TestAddress t(std::move(description)); t.addr.ss_family = AF_INET; t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = - htonl(INADDR_LOOPBACK); + reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = addr; return t; } -TestAddress V4MappedAny() { - TestAddress t("V4MappedAny"); +TestAddress V6Addr(std::string description, const in6_addr& addr) { + TestAddress t(std::move(description)); t.addr.ss_family = AF_INET6; t.addr_len = sizeof(sockaddr_in6); - inet_pton(AF_INET6, "::ffff:0.0.0.0", - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr); + reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = addr; return t; } +} // namespace + +TestAddress V4AddrStr(std::string description, const char* addr) { + in_addr_t s_addr; + inet_pton(AF_INET, addr, &s_addr); + return V4Addr(description, s_addr); +} + +TestAddress V6AddrStr(std::string description, const char* addr) { + struct in6_addr s_addr; + inet_pton(AF_INET6, addr, &s_addr); + return V6Addr(description, s_addr); +} + +TestAddress V4Any() { return V4Addr("V4Any", htonl(INADDR_ANY)); } + +TestAddress V4Broadcast() { + return V4Addr("V4Broadcast", htonl(INADDR_BROADCAST)); +} + +TestAddress V4Loopback() { + return V4Addr("V4Loopback", htonl(INADDR_LOOPBACK)); +} + +TestAddress V4LoopbackSubnetBroadcast() { + return V4AddrStr("V4LoopbackSubnetBroadcast", "127.255.255.255"); +} + +TestAddress V4MappedAny() { return V6AddrStr("V4MappedAny", "::ffff:0.0.0.0"); } + TestAddress V4MappedLoopback() { - TestAddress t("V4MappedLoopback"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - inet_pton(AF_INET6, "::ffff:127.0.0.1", - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr); - return t; + return V6AddrStr("V4MappedLoopback", "::ffff:127.0.0.1"); } TestAddress V4Multicast() { - TestAddress t("V4Multicast"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = - inet_addr(kMulticastAddress); - return t; + return V4Addr("V4Multicast", inet_addr(kMulticastAddress)); } -TestAddress V4Broadcast() { - TestAddress t("V4Broadcast"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = - htonl(INADDR_BROADCAST); - return t; +TestAddress V4MulticastAllHosts() { + return V4Addr("V4MulticastAllHosts", htonl(INADDR_ALLHOSTS_GROUP)); } -TestAddress V6Any() { - TestAddress t("V6Any"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = in6addr_any; - return t; +TestAddress V6Any() { return V6Addr("V6Any", in6addr_any); } + +TestAddress V6Loopback() { return V6Addr("V6Loopback", in6addr_loopback); } + +TestAddress V6Multicast() { return V6AddrStr("V6Multicast", "ff05::1234"); } + +TestAddress V6MulticastInterfaceLocalAllNodes() { + return V6AddrStr("V6MulticastInterfaceLocalAllNodes", "ff01::1"); } -TestAddress V6Loopback() { - TestAddress t("V6Loopback"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = in6addr_loopback; - return t; +TestAddress V6MulticastLinkLocalAllNodes() { + return V6AddrStr("V6MulticastLinkLocalAllNodes", "ff02::1"); } -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; +TestAddress V6MulticastLinkLocalAllRouters() { + return V6AddrStr("V6MulticastLinkLocalAllRouters", "ff02::2"); } // Checksum computes the internet checksum of a buffer. diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h index f7ba90130..76dc090e0 100644 --- a/test/syscalls/linux/socket_test_util.h +++ b/test/syscalls/linux/socket_test_util.h @@ -499,15 +499,45 @@ struct TestAddress { constexpr char kMulticastAddress[] = "224.0.2.1"; constexpr char kBroadcastAddress[] = "255.255.255.255"; +// Returns a TestAddress with `addr` parsed as an IPv4 address described by +// `description`. +TestAddress V4AddrStr(std::string description, const char* addr); +// Returns a TestAddress with `addr` parsed as an IPv6 address described by +// `description`. +TestAddress V6AddrStr(std::string description, const char* addr); + +// Returns a TestAddress for the IPv4 any address. TestAddress V4Any(); +// Returns a TestAddress for the IPv4 limited broadcast address. TestAddress V4Broadcast(); +// Returns a TestAddress for the IPv4 loopback address. TestAddress V4Loopback(); +// Returns a TestAddress for the subnet broadcast of the IPv4 loopback address. +TestAddress V4LoopbackSubnetBroadcast(); +// Returns a TestAddress for the IPv4-mapped IPv6 any address. TestAddress V4MappedAny(); +// Returns a TestAddress for the IPv4-mapped IPv6 loopback address. TestAddress V4MappedLoopback(); +// Returns a TestAddress for a IPv4 multicast address. TestAddress V4Multicast(); +// Returns a TestAddress for the IPv4 all-hosts multicast group address. +TestAddress V4MulticastAllHosts(); + +// Returns a TestAddress for the IPv6 any address. TestAddress V6Any(); +// Returns a TestAddress for the IPv6 loopback address. TestAddress V6Loopback(); +// Returns a TestAddress for a IPv6 multicast address. TestAddress V6Multicast(); +// Returns a TestAddress for the IPv6 interface-local all-nodes multicast group +// address. +TestAddress V6MulticastInterfaceLocalAllNodes(); +// Returns a TestAddress for the IPv6 link-local all-nodes multicast group +// address. +TestAddress V6MulticastLinkLocalAllNodes(); +// Returns a TestAddress for the IPv6 link-local all-routers multicast group +// address. +TestAddress V6MulticastLinkLocalAllRouters(); // Compute the internet checksum of an IP header. uint16_t IPChecksum(struct iphdr ip); |