diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/syscalls/linux/raw_socket.cc | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc index 66f0e6ca4..f0eb7cc4a 100644 --- a/test/syscalls/linux/raw_socket.cc +++ b/test/syscalls/linux/raw_socket.cc @@ -939,6 +939,124 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Combine(::testing::Values(IPPROTO_TCP, IPPROTO_UDP), ::testing::Values(AF_INET, AF_INET6))); +void TestRawSocketMaybeBindReceive(bool do_bind) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); + + constexpr char payload[] = "abcdefgh"; + + const sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)}, + }; + + FileDescriptor udp_sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, SOL_UDP)); + sockaddr_in udp_sock_bind_addr = addr; + socklen_t udp_sock_bind_addr_len = sizeof(udp_sock_bind_addr); + ASSERT_THAT(bind(udp_sock.get(), + reinterpret_cast<const sockaddr*>(&udp_sock_bind_addr), + sizeof(udp_sock_bind_addr)), + SyscallSucceeds()); + ASSERT_THAT(getsockname(udp_sock.get(), + reinterpret_cast<sockaddr*>(&udp_sock_bind_addr), + &udp_sock_bind_addr_len), + SyscallSucceeds()); + ASSERT_EQ(udp_sock_bind_addr_len, sizeof(udp_sock_bind_addr)); + + FileDescriptor raw_sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, SOL_UDP)); + + auto test_recv = [&](const char* scope, uint32_t expected_destination) { + SCOPED_TRACE(scope); + + constexpr int kInfinitePollTimeout = -1; + pollfd pfd = { + .fd = raw_sock.get(), + .events = POLLIN, + }; + ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfinitePollTimeout), + SyscallSucceedsWithValue(1)); + + struct ipv4_udp_packet { + iphdr ip; + udphdr udp; + char data[sizeof(payload)]; + + // Used to make sure only the required space is used. + char unused_space; + } ABSL_ATTRIBUTE_PACKED; + constexpr size_t kExpectedIPPacketSize = + offsetof(ipv4_udp_packet, unused_space); + + // Receive the whole IPv4 packet on the raw socket. + ipv4_udp_packet read_raw_packet; + sockaddr_in peer; + socklen_t peerlen = sizeof(peer); + ASSERT_EQ( + recvfrom(raw_sock.get(), reinterpret_cast<char*>(&read_raw_packet), + sizeof(read_raw_packet), 0 /* flags */, + reinterpret_cast<sockaddr*>(&peer), &peerlen), + static_cast<ssize_t>(kExpectedIPPacketSize)) + << strerror(errno); + ASSERT_EQ(peerlen, sizeof(peer)); + EXPECT_EQ(read_raw_packet.ip.version, static_cast<unsigned int>(IPVERSION)); + // IHL holds the number of header bytes in 4 byte units. + EXPECT_EQ(read_raw_packet.ip.ihl, sizeof(read_raw_packet.ip) / 4); + EXPECT_EQ(ntohs(read_raw_packet.ip.tot_len), kExpectedIPPacketSize); + EXPECT_EQ(ntohs(read_raw_packet.ip.frag_off) & IP_OFFMASK, 0); + EXPECT_EQ(read_raw_packet.ip.protocol, SOL_UDP); + EXPECT_EQ(ntohl(read_raw_packet.ip.saddr), INADDR_LOOPBACK); + EXPECT_EQ(ntohl(read_raw_packet.ip.daddr), expected_destination); + EXPECT_EQ(read_raw_packet.udp.source, udp_sock_bind_addr.sin_port); + EXPECT_EQ(read_raw_packet.udp.dest, udp_sock_bind_addr.sin_port); + EXPECT_EQ(ntohs(read_raw_packet.udp.len), + kExpectedIPPacketSize - sizeof(read_raw_packet.ip)); + for (size_t i = 0; i < sizeof(payload); i++) { + EXPECT_EQ(read_raw_packet.data[i], payload[i]) + << "byte mismatch @ idx=" << i; + } + EXPECT_EQ(peer.sin_family, AF_INET); + EXPECT_EQ(peer.sin_port, 0); + EXPECT_EQ(ntohl(peer.sin_addr.s_addr), INADDR_LOOPBACK); + }; + + if (do_bind) { + ASSERT_THAT(bind(raw_sock.get(), reinterpret_cast<const sockaddr*>(&addr), + sizeof(addr)), + SyscallSucceeds()); + } + + constexpr int kSendToFlags = 0; + sockaddr_in different_addr = udp_sock_bind_addr; + different_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK + 1); + ASSERT_THAT(sendto(udp_sock.get(), payload, sizeof(payload), kSendToFlags, + reinterpret_cast<const sockaddr*>(&different_addr), + sizeof(different_addr)), + SyscallSucceedsWithValue(sizeof(payload))); + if (!do_bind) { + ASSERT_NO_FATAL_FAILURE( + test_recv("different_addr", ntohl(different_addr.sin_addr.s_addr))); + } + ASSERT_THAT(sendto(udp_sock.get(), payload, sizeof(payload), kSendToFlags, + reinterpret_cast<const sockaddr*>(&udp_sock_bind_addr), + sizeof(udp_sock_bind_addr)), + SyscallSucceedsWithValue(sizeof(payload))); + ASSERT_NO_FATAL_FAILURE( + test_recv("addr", ntohl(udp_sock_bind_addr.sin_addr.s_addr))); +} + +TEST(RawSocketTest, UnboundReceive) { + // Test that a raw socket receives packets destined to any address if it is + // not bound to an address. + ASSERT_NO_FATAL_FAILURE(TestRawSocketMaybeBindReceive(false /* do_bind */)); +} + +TEST(RawSocketTest, BindReceive) { + // Test that a raw socket only receives packets destined to the address it is + // bound to. + ASSERT_NO_FATAL_FAILURE(TestRawSocketMaybeBindReceive(true /* do_bind */)); +} + } // namespace } // namespace testing |