diff options
author | Ian Gudger <igudger@google.com> | 2019-02-07 23:14:06 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2019-02-07 23:15:23 -0800 |
commit | 80f901b16b8bb8fe397cc44578035173f5155b24 (patch) | |
tree | 91707e2f2b424f71f7bac661c05a830b56244255 /test/syscalls/linux | |
parent | fda4d1f4f11201c34bd15d41ba4c94279d135d95 (diff) |
Plumb IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP to netstack.
Also includes a few fixes for IPv4 multicast support. IPv6 support is coming in
a followup CL.
PiperOrigin-RevId: 233008638
Change-Id: If7dae6222fef43fda48033f0292af77832d95e82
Diffstat (limited to 'test/syscalls/linux')
-rw-r--r-- | test/syscalls/linux/BUILD | 34 | ||||
-rw-r--r-- | test/syscalls/linux/ip_socket_test_util.cc | 33 | ||||
-rw-r--r-- | test/syscalls/linux/ip_socket_test_util.h | 7 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_udp_generic.cc | 14 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ipv4_udp_unbound.cc | 424 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ipv4_udp_unbound.h | 29 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc | 35 | ||||
-rw-r--r-- | test/syscalls/linux/socket_test_util.cc | 38 | ||||
-rw-r--r-- | test/syscalls/linux/socket_test_util.h | 5 |
9 files changed, 606 insertions, 13 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 590ee1659..75fa52a57 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1931,6 +1931,24 @@ cc_library( alwayslink = 1, ) +cc_library( + name = "socket_ipv4_udp_unbound_test_cases", + testonly = 1, + srcs = [ + "socket_ipv4_udp_unbound.cc", + ], + hdrs = [ + "socket_ipv4_udp_unbound.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, @@ -2125,6 +2143,22 @@ cc_binary( ) cc_binary( + name = "socket_ipv4_udp_unbound_loopback_test", + testonly = 1, + srcs = [ + "socket_ipv4_udp_unbound_loopback.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_ipv4_udp_unbound_test_cases", + ":socket_test_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( name = "socket_domain_test", testonly = 1, srcs = [ diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc index 1659d3d83..f8232fc24 100644 --- a/test/syscalls/linux/ip_socket_test_util.cc +++ b/test/syscalls/linux/ip_socket_test_util.cc @@ -12,11 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <net/if.h> +#include <sys/ioctl.h> +#include <cstring> + #include "test/syscalls/linux/ip_socket_test_util.h" namespace gvisor { namespace testing { +PosixErrorOr<int> InterfaceIndex(std::string name) { + // TODO: Consider using netlink. + ifreq req = {}; + memcpy(req.ifr_name, name.c_str(), name.size()); + ASSIGN_OR_RETURN_ERRNO(auto sock, Socket(AF_INET, SOCK_DGRAM, 0)); + RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(sock.get(), SIOCGIFINDEX, &req)); + return req.ifr_ifindex; +} + namespace { std::string DescribeSocketType(int type) { @@ -28,7 +41,7 @@ std::string DescribeSocketType(int type) { SocketPairKind IPv6TCPAcceptBindSocketPair(int type) { std::string description = - absl::StrCat(DescribeSocketType(type), "IPv6 TCP socket"); + absl::StrCat(DescribeSocketType(type), "connected IPv6 TCP socket"); return SocketPairKind{ description, TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM, 0, /* dual_stack = */ false)}; @@ -36,7 +49,7 @@ SocketPairKind IPv6TCPAcceptBindSocketPair(int type) { SocketPairKind IPv4TCPAcceptBindSocketPair(int type) { std::string description = - absl::StrCat(DescribeSocketType(type), "IPv4 TCP socket"); + absl::StrCat(DescribeSocketType(type), "connected IPv4 TCP socket"); return SocketPairKind{ description, TCPAcceptBindSocketPairCreator(AF_INET, type | SOCK_STREAM, 0, /* dual_stack = */ false)}; @@ -44,7 +57,7 @@ SocketPairKind IPv4TCPAcceptBindSocketPair(int type) { SocketPairKind DualStackTCPAcceptBindSocketPair(int type) { std::string description = - absl::StrCat(DescribeSocketType(type), "dual stack TCP socket"); + absl::StrCat(DescribeSocketType(type), "connected dual stack TCP socket"); return SocketPairKind{ description, TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM, 0, /* dual_stack = */ true)}; @@ -52,7 +65,7 @@ SocketPairKind DualStackTCPAcceptBindSocketPair(int type) { SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type) { std::string description = - absl::StrCat(DescribeSocketType(type), "IPv6 UDP socket"); + absl::StrCat(DescribeSocketType(type), "connected IPv6 UDP socket"); return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator( AF_INET6, type | SOCK_DGRAM, 0, /* dual_stack = */ false)}; @@ -60,7 +73,7 @@ SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type) { SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type) { std::string description = - absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket"); + absl::StrCat(DescribeSocketType(type), "connected IPv4 UDP socket"); return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator( AF_INET, type | SOCK_DGRAM, 0, /* dual_stack = */ false)}; @@ -68,11 +81,19 @@ SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type) { SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type) { std::string description = - absl::StrCat(DescribeSocketType(type), "dual stack UDP socket"); + absl::StrCat(DescribeSocketType(type), "connected dual stack UDP socket"); return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator( AF_INET6, type | SOCK_DGRAM, 0, /* dual_stack = */ true)}; } +SocketPairKind IPv4UDPUnboundSocketPair(int type) { + std::string description = + absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket"); + return SocketPairKind{ + description, UDPUnboundSocketPairCreator(AF_INET, type | SOCK_DGRAM, 0, + /* dual_stack = */ false)}; +} + } // 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 1e1400ecd..a6721091a 100644 --- a/test/syscalls/linux/ip_socket_test_util.h +++ b/test/syscalls/linux/ip_socket_test_util.h @@ -21,6 +21,9 @@ namespace gvisor { namespace testing { +// InterfaceIndex returns the index of the named interface. +PosixErrorOr<int> InterfaceIndex(std::string name); + // IPv6TCPAcceptBindSocketPair returns a SocketPairKind that represents // SocketPairs created with bind() and accept() syscalls with AF_INET6 and the // given type bound to the IPv6 loopback. @@ -51,6 +54,10 @@ SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type); // AF_INET6 and the given type bound to the IPv4 loopback. SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type); +// IPv4UDPUnboundSocketPair returns a SocketPairKind that represents +// SocketPairs created with AF_INET and the given type. +SocketPairKind IPv4UDPUnboundSocketPair(int type); + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc index 789154fb3..58d1c846d 100644 --- a/test/syscalls/linux/socket_ip_udp_generic.cc +++ b/test/syscalls/linux/socket_ip_udp_generic.cc @@ -117,5 +117,19 @@ TEST_P(UDPSocketPairTest, SetUDPMulticastTTLAboveMax) { SyscallFailsWithErrno(EINVAL)); } +TEST_P(UDPSocketPairTest, SetEmptyIPAddMembership) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + struct ip_mreqn req = {}; + int ret = setsockopt(sockets->first_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, + sizeof(req)); + // FIXME: gVisor returns the incorrect errno. + if (IsRunningOnGvisor()) { + EXPECT_THAT(ret, SyscallFails()); + } else { + EXPECT_THAT(ret, SyscallFailsWithErrno(EINVAL)); + } +} + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc new file mode 100644 index 000000000..1b47139e4 --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc @@ -0,0 +1,424 @@ +// 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.h" + +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <cstdio> + +#include "gtest/gtest.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 { + +// Check that packets are not received without a group memebership. Default send +// interface configured by bind. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNoGroup) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Bind the first FD to the loopback. This is an alternative to + // IP_MULTICAST_IF for setting the default send interface. + sockaddr_in senderAddr = {}; + senderAddr.sin_family = AF_INET; + senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT( + bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr), + sizeof(senderAddr)), + SyscallSucceeds()); + + // Bind the second FD to the v4 any address. If multicast worked like unicast, + // this would ensure that we get the packet. + sockaddr_in receiverAddr = {}; + receiverAddr.sin_family = AF_INET; + receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + EXPECT_THAT( + bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr), + sizeof(receiverAddr)), + SyscallSucceeds()); + socklen_t receiverAddrLen = sizeof(receiverAddr); + EXPECT_THAT( + getsockname(sockets->second_fd(), + reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen), + SyscallSucceeds()); + EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr)); + + // Send the multicast packet. + sockaddr_in sendAddr = {}; + sendAddr.sin_family = AF_INET; + sendAddr.sin_port = receiverAddr.sin_port; + sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1"); + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + EXPECT_THAT(RetryEINTR(sendto)( + sockets->first_fd(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + // Check that we did not receive the multicast packet. + char recv_buf[sizeof(send_buf)] = {}; + EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), + MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +// Check that not setting a default send interface prevents multicast packets +// from being sent. Group membership interface configured by address. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackAddrNoDefaultSendIf) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Bind the second FD to the v4 any address to ensure that we can receive any + // unicast packet. + sockaddr_in receiverAddr = {}; + receiverAddr.sin_family = AF_INET; + receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + EXPECT_THAT( + bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr), + sizeof(receiverAddr)), + SyscallSucceeds()); + socklen_t receiverAddrLen = sizeof(receiverAddr); + EXPECT_THAT( + getsockname(sockets->second_fd(), + reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen), + SyscallSucceeds()); + EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr)); + + // Register to receive multicast packets. + ip_mreq group = {}; + group.imr_multiaddr.s_addr = inet_addr("224.0.2.1"); + group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + + // Send a multicast packet. + sockaddr_in sendAddr = {}; + sendAddr.sin_family = AF_INET; + sendAddr.sin_port = receiverAddr.sin_port; + sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1"); + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + EXPECT_THAT(RetryEINTR(sendto)( + sockets->first_fd(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)), + SyscallFailsWithErrno(ENETUNREACH)); +} + +// Check that not setting a default send interface prevents multicast packets +// from being sent. Group membership interface configured by NIC ID. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNicNoDefaultSendIf) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Bind the second FD to the v4 any address to ensure that we can receive any + // unicast packet. + sockaddr_in receiverAddr = {}; + receiverAddr.sin_family = AF_INET; + receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + EXPECT_THAT( + bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr), + sizeof(receiverAddr)), + SyscallSucceeds()); + socklen_t receiverAddrLen = sizeof(receiverAddr); + EXPECT_THAT( + getsockname(sockets->second_fd(), + reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen), + SyscallSucceeds()); + EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr)); + + // Register to receive multicast packets. + ip_mreqn group = {}; + group.imr_multiaddr.s_addr = inet_addr("224.0.2.1"); + group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + + // Send a multicast packet. + sockaddr_in sendAddr = {}; + sendAddr.sin_family = AF_INET; + sendAddr.sin_port = receiverAddr.sin_port; + sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1"); + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + EXPECT_THAT(RetryEINTR(sendto)( + sockets->first_fd(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)), + SyscallFailsWithErrno(ENETUNREACH)); +} + +// Check that multicast works when the default send interface is configured by +// bind and the group membership is configured by address. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackAddr) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Bind the first FD to the loopback. This is an alternative to + // IP_MULTICAST_IF for setting the default send interface. + sockaddr_in senderAddr = {}; + senderAddr.sin_family = AF_INET; + senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT( + bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr), + sizeof(senderAddr)), + SyscallSucceeds()); + + // Bind the second FD to the v4 any address to ensure that we can receive the + // multicast packet. + sockaddr_in receiverAddr = {}; + receiverAddr.sin_family = AF_INET; + receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + EXPECT_THAT( + bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr), + sizeof(receiverAddr)), + SyscallSucceeds()); + socklen_t receiverAddrLen = sizeof(receiverAddr); + EXPECT_THAT( + getsockname(sockets->second_fd(), + reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen), + SyscallSucceeds()); + EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr)); + + // Register to receive multicast packets. + ip_mreq group = {}; + group.imr_multiaddr.s_addr = inet_addr("224.0.2.1"); + group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + + // Send a multicast packet. + sockaddr_in sendAddr = {}; + sendAddr.sin_family = AF_INET; + sendAddr.sin_port = receiverAddr.sin_port; + sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1"); + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + EXPECT_THAT(RetryEINTR(sendto)( + sockets->first_fd(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + // Check that we received the multicast packet. + char recv_buf[sizeof(send_buf)] = {}; + ASSERT_THAT( + RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), 0), + SyscallSucceedsWithValue(sizeof(recv_buf))); + + EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); +} + +// Check that multicast works when the default send interface is confgured by +// bind and the group membership is configured by NIC ID. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNic) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Bind the first FD to the loopback. This is an alternative to + // IP_MULTICAST_IF for setting the default send interface. + sockaddr_in senderAddr = {}; + senderAddr.sin_family = AF_INET; + senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT( + bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr), + sizeof(senderAddr)), + SyscallSucceeds()); + + // Bind the second FD to the v4 any address to ensure that we can receive the + // multicast packet. + sockaddr_in receiverAddr = {}; + receiverAddr.sin_family = AF_INET; + receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + EXPECT_THAT( + bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr), + sizeof(receiverAddr)), + SyscallSucceeds()); + socklen_t receiverAddrLen = sizeof(receiverAddr); + EXPECT_THAT( + getsockname(sockets->second_fd(), + reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen), + SyscallSucceeds()); + EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr)); + + // Register to receive multicast packets. + ip_mreqn group = {}; + group.imr_multiaddr.s_addr = inet_addr("224.0.2.1"); + group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + + // Send a multicast packet. + sockaddr_in sendAddr = {}; + sendAddr.sin_family = AF_INET; + sendAddr.sin_port = receiverAddr.sin_port; + sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1"); + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + EXPECT_THAT(RetryEINTR(sendto)( + sockets->first_fd(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + // Check that we received the multicast packet. + char recv_buf[sizeof(send_buf)] = {}; + ASSERT_THAT( + RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), 0), + SyscallSucceedsWithValue(sizeof(recv_buf))); + + EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); +} + +// Check that dropping a group membership that does not exist fails. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastInvalidDrop) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Unregister from a membership that we didn't have. + ip_mreq group = {}; + group.imr_multiaddr.s_addr = inet_addr("224.0.2.1"); + group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_DROP_MEMBERSHIP, + &group, sizeof(group)), + SyscallFailsWithErrno(EADDRNOTAVAIL)); +} + +// Check that dropping a group membership prevents multicast packets from being +// delivered. Default send address configured by bind and group membership +// interface configured by address. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastDropAddr) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Bind the first FD to the loopback. This is an alternative to + // IP_MULTICAST_IF for setting the default send interface. + sockaddr_in senderAddr = {}; + senderAddr.sin_family = AF_INET; + senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT( + bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr), + sizeof(senderAddr)), + SyscallSucceeds()); + + // Bind the second FD to the v4 any address to ensure that we can receive the + // multicast packet. + sockaddr_in receiverAddr = {}; + receiverAddr.sin_family = AF_INET; + receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + EXPECT_THAT( + bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr), + sizeof(receiverAddr)), + SyscallSucceeds()); + socklen_t receiverAddrLen = sizeof(receiverAddr); + EXPECT_THAT( + getsockname(sockets->second_fd(), + reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen), + SyscallSucceeds()); + EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr)); + + // Register and unregister to receive multicast packets. + ip_mreq group = {}; + group.imr_multiaddr.s_addr = inet_addr("224.0.2.1"); + group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_DROP_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + + // Send a multicast packet. + sockaddr_in sendAddr = {}; + sendAddr.sin_family = AF_INET; + sendAddr.sin_port = receiverAddr.sin_port; + sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1"); + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + EXPECT_THAT(RetryEINTR(sendto)( + sockets->first_fd(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + // Check that we did not receive the multicast packet. + char recv_buf[sizeof(send_buf)] = {}; + EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), + MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +// Check that dropping a group membership prevents multicast packets from being +// delivered. Default send address configured by bind and group membership +// interface configured by NIC ID. +TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastDropNic) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + // Bind the first FD to the loopback. This is an alternative to + // IP_MULTICAST_IF for setting the default send interface. + sockaddr_in senderAddr = {}; + senderAddr.sin_family = AF_INET; + senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_THAT( + bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr), + sizeof(senderAddr)), + SyscallSucceeds()); + + // Bind the second FD to the v4 any address to ensure that we can receive the + // multicast packet. + sockaddr_in receiverAddr = {}; + receiverAddr.sin_family = AF_INET; + receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + EXPECT_THAT( + bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr), + sizeof(receiverAddr)), + SyscallSucceeds()); + socklen_t receiverAddrLen = sizeof(receiverAddr); + EXPECT_THAT( + getsockname(sockets->second_fd(), + reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen), + SyscallSucceeds()); + EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr)); + + // Register and unregister to receive multicast packets. + ip_mreqn group = {}; + group.imr_multiaddr.s_addr = inet_addr("224.0.2.1"); + group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_DROP_MEMBERSHIP, + &group, sizeof(group)), + SyscallSucceeds()); + + // Send a multicast packet. + sockaddr_in sendAddr = {}; + sendAddr.sin_family = AF_INET; + sendAddr.sin_port = receiverAddr.sin_port; + sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1"); + char send_buf[200]; + RandomizeBuffer(send_buf, sizeof(send_buf)); + EXPECT_THAT(RetryEINTR(sendto)( + sockets->first_fd(), send_buf, sizeof(send_buf), 0, + reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + // Check that we did not receive the multicast packet. + char recv_buf[sizeof(send_buf)] = {}; + EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), + MSG_DONTWAIT), + SyscallFailsWithErrno(EAGAIN)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.h b/test/syscalls/linux/socket_ipv4_udp_unbound.h new file mode 100644 index 000000000..a780c0144 --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_udp_unbound.h @@ -0,0 +1,29 @@ +// 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_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of IPv4 UDP sockets. +using IPv4UDPUnboundSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_ diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc new file mode 100644 index 000000000..b70faa33d --- /dev/null +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc @@ -0,0 +1,35 @@ +// Copyright 2018 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 <vector> + +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_ipv4_udp_unbound.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +std::vector<SocketPairKind> GetSocketPairs() { + return ApplyVec<SocketPairKind>( + IPv4UDPUnboundSocketPair, + AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})); +} + +INSTANTIATE_TEST_CASE_P(IPv4UDPSockets, IPv4UDPUnboundSocketPairTest, + ::testing::ValuesIn(GetSocketPairs())); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index 80a59df7e..49b8c583f 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -389,23 +389,32 @@ Creator<SocketPair> TCPAcceptBindSocketPairCreator(int domain, int type, } template <typename T> +PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> CreateUDPBoundSocketPair( + int sock1, int sock2, int type, bool dual_stack) { + ASSIGN_OR_RETURN_ERRNO(T addr1, BindIP<T>(sock1, dual_stack)); + ASSIGN_OR_RETURN_ERRNO(T addr2, BindIP<T>(sock2, dual_stack)); + + return absl::make_unique<AddrFDSocketPair>(sock1, sock2, addr1, addr2); +} + +template <typename T> PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> CreateUDPBidirectionalBindSocketPair(int sock1, int sock2, int type, bool dual_stack) { - ASSIGN_OR_RETURN_ERRNO(T addr1, BindIP<T>(sock1, dual_stack)); - ASSIGN_OR_RETURN_ERRNO(T addr2, BindIP<T>(sock2, dual_stack)); + ASSIGN_OR_RETURN_ERRNO( + auto socks, CreateUDPBoundSocketPair<T>(sock1, sock2, type, dual_stack)); // Connect sock1 to sock2. - RETURN_ERROR_IF_SYSCALL_FAIL(connect( - sock1, reinterpret_cast<struct sockaddr*>(&addr2), sizeof(addr2))); + RETURN_ERROR_IF_SYSCALL_FAIL(connect(socks->first_fd(), socks->second_addr(), + socks->second_addr_size())); MaybeSave(); // Successful connection. // Connect sock2 to sock1. - RETURN_ERROR_IF_SYSCALL_FAIL(connect( - sock2, reinterpret_cast<struct sockaddr*>(&addr1), sizeof(addr1))); + RETURN_ERROR_IF_SYSCALL_FAIL(connect(socks->second_fd(), socks->first_addr(), + socks->first_addr_size())); MaybeSave(); // Successful connection. - return absl::make_unique<AddrFDSocketPair>(sock1, sock2, addr1, addr2); + return socks; } Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type, @@ -429,6 +438,21 @@ Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type, }; } +Creator<SocketPair> UDPUnboundSocketPairCreator(int domain, int type, + int protocol, bool dual_stack) { + return [=]() -> PosixErrorOr<std::unique_ptr<FDSocketPair>> { + int sock1; + RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + + int sock2; + RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); + MaybeSave(); // Successful socket creation. + + return absl::make_unique<FDSocketPair>(sock1, sock2); + }; +} + SocketPairKind Reversed(SocketPairKind const& base) { auto const& creator = base.creator; return SocketPairKind{ diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h index 6d84b3fa8..826374dc6 100644 --- a/test/syscalls/linux/socket_test_util.h +++ b/test/syscalls/linux/socket_test_util.h @@ -273,6 +273,11 @@ Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type, int protocol, bool dual_stack); +// UDPUnboundSocketPairCreator returns a Creator<SocketPair> that obtains file +// descriptors by creating UDP sockets. +Creator<SocketPair> UDPUnboundSocketPairCreator(int domain, int type, + int protocol, bool dual_stack); + // A SocketPairKind couples a human-readable description of a socket pair with // a function that creates such a socket pair. struct SocketPairKind { |