diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 3a7a4229b..2137a864e 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1569,6 +1569,7 @@ cc_binary( "//test/util:file_descriptor", "//test/util:socket_util", "//test/util:test_main", + "//test/util:test_util", ], ) diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc index 5e542d1b7..05be0ba9b 100644 --- a/test/syscalls/linux/packet_socket.cc +++ b/test/syscalls/linux/packet_socket.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include "gmock/gmock.h" @@ -27,6 +28,7 @@ #include "test/util/capability_util.h" #include "test/util/file_descriptor.h" #include "test/util/socket_util.h" +#include "test/util/test_util.h" namespace gvisor { namespace testing { @@ -396,6 +398,62 @@ TEST_P(PacketSocketTest, ReceiveSentBoundToProtocolAll) { ASSERT_EQ(src_addr.sll_pkttype, PACKET_HOST); } +// Verify that the ifindex field of sockaddr_ll must be provided when calling +// sendto on a packet socket (e.g. the interface cannot be inferred from the +// bound address). +TEST_P(PacketSocketTest, SendWithoutTargetDevice) { + if (IsRunningOnGvisor()) { + GTEST_SKIP() << "gVisor does not support sending on packet sockets."; + } + + const uint16_t kArbitraryProtocol = 0xFFFF; + + // Bind the packet socket to the loopback interface. Despite this, subsequent + // sends will require the interface be specified. + const struct sockaddr_ll bind_addr = { + .sll_family = AF_PACKET, + .sll_protocol = htons(kArbitraryProtocol), + .sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()), + .sll_halen = ETH_ALEN, + }; + ASSERT_THAT(bind(socket_.get(), reinterpret_cast(&bind_addr), + sizeof(bind_addr)), + SyscallSucceeds()); + + // Prepare a send buffer with a valid Ethernet header. + // This enables sending with SOCK_RAW, and is inconsequential for SOCK_DGRAM. + const uint64_t kContents = 0xAAAAAAAAAAAAAAAA; + struct ethhdr eth = {}; + eth.h_proto = htons(kArbitraryProtocol); + char send_buf[sizeof(eth) + sizeof(kContents)]; + memcpy(send_buf, ð, sizeof(eth)); + memcpy(send_buf + sizeof(ethhdr), &kContents, sizeof(kContents)); + + // Send to a valid address. + const struct sockaddr_ll valid_addr = { + .sll_family = AF_PACKET, + .sll_protocol = htons(kArbitraryProtocol), + .sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()), + .sll_halen = ETH_ALEN, + }; + ASSERT_THAT(sendto(socket_.get(), send_buf, sizeof(send_buf), 0, + reinterpret_cast(&valid_addr), + sizeof(valid_addr)), + SyscallSucceedsWithValue(sizeof(send_buf))); + + // Attempt to send without specifying the interface. + const struct sockaddr_ll no_dev_addr = { + .sll_family = AF_PACKET, + .sll_protocol = htons(kArbitraryProtocol), + .sll_ifindex = 0, + .sll_halen = ETH_ALEN, + }; + ASSERT_THAT(sendto(socket_.get(), send_buf, sizeof(send_buf), 0, + reinterpret_cast(&no_dev_addr), + sizeof(no_dev_addr)), + SyscallFailsWithErrno(ENXIO)); +} + INSTANTIATE_TEST_SUITE_P(AllPacketSocketTests, PacketSocketTest, Values(SOCK_DGRAM, SOCK_RAW));