summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls')
-rw-r--r--test/syscalls/linux/raw_socket_ipv4.cc421
1 files changed, 334 insertions, 87 deletions
diff --git a/test/syscalls/linux/raw_socket_ipv4.cc b/test/syscalls/linux/raw_socket_ipv4.cc
index 19cded07f..b13806dcb 100644
--- a/test/syscalls/linux/raw_socket_ipv4.cc
+++ b/test/syscalls/linux/raw_socket_ipv4.cc
@@ -16,8 +16,11 @@
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
+#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <unistd.h>
+#include <algorithm>
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_test_util.h"
@@ -39,22 +42,26 @@ class RawSocketTest : public ::testing::Test {
// Closes the socket created by SetUp().
void TearDown() override;
- // The socket used for both reading and writing.
- int s_;
+ // Checks that both an ICMP echo request and reply are received. Calls should
+ // be wrapped in ASSERT_NO_FATAL_FAILURE.
+ void ExpectICMPSuccess(const struct icmphdr& icmp);
- // The loopback address.
- struct sockaddr_in addr_;
+ void SendEmptyICMP(const struct icmphdr& icmp);
+
+ void SendEmptyICMPTo(int sock, struct sockaddr_in* addr,
+ const struct icmphdr& icmp);
- void SendEmptyICMP(struct icmphdr *icmp);
+ void ReceiveICMP(char* recv_buf, size_t recv_buf_len, size_t expected_size,
+ struct sockaddr_in* src);
- void SendEmptyICMPTo(int sock, struct sockaddr_in *addr,
- struct icmphdr *icmp);
+ void ReceiveICMPFrom(char* recv_buf, size_t recv_buf_len,
+ size_t expected_size, struct sockaddr_in* src, int sock);
- void ReceiveICMP(char *recv_buf, size_t recv_buf_len, size_t expected_size,
- struct sockaddr_in *src);
+ // The socket used for both reading and writing.
+ int s_;
- void ReceiveICMPFrom(char *recv_buf, size_t recv_buf_len,
- size_t expected_size, struct sockaddr_in *src, int sock);
+ // The loopback address.
+ struct sockaddr_in addr_;
};
void RawSocketTest::SetUp() {
@@ -100,49 +107,9 @@ TEST_F(RawSocketTest, SendAndReceive) {
icmp.checksum = 2011;
icmp.un.echo.sequence = 2012;
icmp.un.echo.id = 2014;
- ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(&icmp));
-
- // We're going to receive both the echo request and reply, but the order is
- // indeterminate.
- char recv_buf[512];
- struct sockaddr_in src;
- bool received_request = false;
- bool received_reply = false;
-
- for (int i = 0; i < 2; i++) {
- // Receive the packet.
- ASSERT_NO_FATAL_FAILURE(ReceiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf),
- sizeof(struct icmphdr), &src));
- EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0);
- struct icmphdr *recvd_icmp =
- reinterpret_cast<struct icmphdr *>(recv_buf + sizeof(struct iphdr));
- switch (recvd_icmp->type) {
- case ICMP_ECHO:
- EXPECT_FALSE(received_request);
- received_request = true;
- // The packet should be identical to what we sent.
- EXPECT_EQ(memcmp(recv_buf + sizeof(struct iphdr), &icmp, sizeof(icmp)),
- 0);
- break;
-
- case ICMP_ECHOREPLY:
- EXPECT_FALSE(received_reply);
- received_reply = true;
- // Most fields should be the same.
- EXPECT_EQ(recvd_icmp->code, icmp.code);
- EXPECT_EQ(recvd_icmp->un.echo.sequence, icmp.un.echo.sequence);
- EXPECT_EQ(recvd_icmp->un.echo.id, icmp.un.echo.id);
- // A couple are different.
- EXPECT_EQ(recvd_icmp->type, ICMP_ECHOREPLY);
- // The checksum is computed in such a way that it is guaranteed to have
- // changed.
- EXPECT_NE(recvd_icmp->checksum, icmp.checksum);
- break;
- }
- }
+ ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp));
- ASSERT_TRUE(received_request);
- ASSERT_TRUE(received_reply);
+ ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp));
}
// We should be able to create multiple raw sockets for the same protocol and
@@ -162,7 +129,7 @@ TEST_F(RawSocketTest, MultipleSocketReceive) {
icmp.checksum = 2014;
icmp.un.echo.sequence = 2016;
icmp.un.echo.id = 2018;
- ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(&icmp));
+ ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp));
// Both sockets will receive the echo request and reply in indeterminate
// order, so we'll need to read 2 packets from each.
@@ -191,12 +158,14 @@ TEST_F(RawSocketTest, MultipleSocketReceive) {
int types[] = {ICMP_ECHO, ICMP_ECHOREPLY};
for (int type : types) {
auto match_type = [=](char buf[kBufSize]) {
- struct icmphdr *icmp =
- reinterpret_cast<struct icmphdr *>(buf + sizeof(struct iphdr));
+ struct icmphdr* icmp =
+ reinterpret_cast<struct icmphdr*>(buf + sizeof(struct iphdr));
return icmp->type == type;
};
- char *icmp1 = *std::find_if(recv_buf1.begin(), recv_buf1.end(), match_type);
- char *icmp2 = *std::find_if(recv_buf2.begin(), recv_buf2.end(), match_type);
+ const char* icmp1 =
+ *std::find_if(recv_buf1.begin(), recv_buf1.end(), match_type);
+ const char* icmp2 =
+ *std::find_if(recv_buf2.begin(), recv_buf2.end(), match_type);
ASSERT_NE(icmp1, *recv_buf1.end());
ASSERT_NE(icmp2, *recv_buf2.end());
EXPECT_EQ(memcmp(icmp1 + sizeof(struct iphdr), icmp2 + sizeof(struct iphdr),
@@ -217,10 +186,10 @@ TEST_F(RawSocketTest, RawAndPingSockets) {
struct icmphdr icmp;
icmp.type = ICMP_ECHO;
icmp.code = 0;
- icmp.un.echo.sequence =
- *static_cast<unsigned short *>(&icmp.un.echo.sequence);
+ icmp.un.echo.sequence = *static_cast<unsigned short*>(&icmp.un.echo.sequence);
ASSERT_THAT(RetryEINTR(sendto)(ping_sock.get(), &icmp, sizeof(icmp), 0,
- (struct sockaddr *)&addr_, sizeof(addr_)),
+ reinterpret_cast<struct sockaddr*>(&addr_),
+ sizeof(addr_)),
SyscallSucceedsWithValue(sizeof(icmp)));
// Both sockets will receive the echo request and reply in indeterminate
@@ -247,12 +216,12 @@ TEST_F(RawSocketTest, RawAndPingSockets) {
int types[] = {ICMP_ECHO, ICMP_ECHOREPLY};
for (int type : types) {
auto match_type_ping = [=](char buf[kBufSize]) {
- struct icmphdr *icmp = reinterpret_cast<struct icmphdr *>(buf);
+ struct icmphdr* icmp = reinterpret_cast<struct icmphdr*>(buf);
return icmp->type == type;
};
auto match_type_raw = [=](char buf[kBufSize]) {
- struct icmphdr *icmp =
- reinterpret_cast<struct icmphdr *>(buf + sizeof(struct iphdr));
+ struct icmphdr* icmp =
+ reinterpret_cast<struct icmphdr*>(buf + sizeof(struct iphdr));
return icmp->type == type;
};
@@ -266,39 +235,317 @@ TEST_F(RawSocketTest, RawAndPingSockets) {
}
}
-void RawSocketTest::SendEmptyICMP(struct icmphdr *icmp) {
+// Test that shutting down an unconnected socket fails.
+TEST_F(RawSocketTest, FailShutdownWithoutConnect) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(shutdown(s_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN));
+ ASSERT_THAT(shutdown(s_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN));
+}
+
+// Test that writing to a shutdown write socket fails.
+TEST_F(RawSocketTest, FailWritingToShutdown) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+ ASSERT_THAT(shutdown(s_, SHUT_WR), SyscallSucceeds());
+
+ char c;
+ ASSERT_THAT(RetryEINTR(write)(s_, &c, sizeof(c)),
+ SyscallFailsWithErrno(EPIPE));
+}
+
+// Test that reading from a shutdown read socket gets nothing.
+TEST_F(RawSocketTest, FailReadingFromShutdown) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+ ASSERT_THAT(shutdown(s_, SHUT_RD), SyscallSucceeds());
+
+ char c;
+ ASSERT_THAT(read(s_, &c, sizeof(c)), SyscallSucceedsWithValue(0));
+}
+
+// Test that listen() fails.
+TEST_F(RawSocketTest, FailListen) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(listen(s_, 1), SyscallFailsWithErrno(ENOTSUP));
+}
+
+// Test that accept() fails.
+TEST_F(RawSocketTest, FailAccept) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ struct sockaddr saddr;
+ socklen_t addrlen;
+ ASSERT_THAT(accept(s_, &saddr, &addrlen), SyscallFailsWithErrno(ENOTSUP));
+}
+
+// Test that getpeername() returns nothing before connect().
+TEST_F(RawSocketTest, FailGetPeerNameBeforeConnect) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ struct sockaddr saddr;
+ socklen_t addrlen;
+ ASSERT_THAT(getpeername(s_, &saddr, &addrlen),
+ SyscallFailsWithErrno(ENOTCONN));
+}
+
+// Test that getpeername() returns something after connect().
+TEST_F(RawSocketTest, GetPeerName) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+ struct sockaddr saddr;
+ socklen_t addrlen;
+ ASSERT_THAT(getpeername(s_, &saddr, &addrlen), SyscallSucceeds());
+ ASSERT_GT(addrlen, 0);
+}
+
+// Test that the socket is writable immediately.
+TEST_F(RawSocketTest, PollWritableImmediately) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ struct pollfd pfd = {};
+ pfd.fd = s_;
+ pfd.events = POLLOUT;
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 10000), SyscallSucceedsWithValue(1));
+}
+
+// Test that the socket isn't readable before receiving anything.
+TEST_F(RawSocketTest, PollNotReadableInitially) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ // Try to receive data with MSG_DONTWAIT, which returns immediately if there's
+ // nothing to be read.
+ char buf[117];
+ ASSERT_THAT(RetryEINTR(recv)(s_, buf, sizeof(buf), MSG_DONTWAIT),
+ SyscallFailsWithErrno(EAGAIN));
+}
+
+// Test that the socket becomes readable once something is written to it.
+TEST_F(RawSocketTest, PollTriggeredOnWrite) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ // Write something so that there's data to be read.
+ struct icmphdr icmp = {};
+ ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp));
+
+ struct pollfd pfd = {};
+ pfd.fd = s_;
+ pfd.events = POLLIN;
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 10000), SyscallSucceedsWithValue(1));
+}
+
+// Test that we can connect() to a valid IP (loopback).
+TEST_F(RawSocketTest, ConnectToLoopback) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+}
+
+// Test that connect() sends packets to the right place.
+TEST_F(RawSocketTest, SendAndReceiveViaConnect) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+
+ // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence,
+ // and ID. None of that should matter for raw sockets - the kernel should
+ // still give us the packet.
+ struct icmphdr icmp;
+ icmp.type = ICMP_ECHO;
+ icmp.code = 0;
+ icmp.checksum = 2001;
+ icmp.un.echo.sequence = 2003;
+ icmp.un.echo.id = 2004;
+ ASSERT_THAT(send(s_, &icmp, sizeof(icmp), 0),
+ SyscallSucceedsWithValue(sizeof(icmp)));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp));
+}
+
+// Test that calling send() without connect() fails.
+TEST_F(RawSocketTest, SendWithoutConnectFails) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence,
+ // and ID. None of that should matter for raw sockets - the kernel should
+ // still give us the packet.
+ struct icmphdr icmp;
+ icmp.type = ICMP_ECHO;
+ icmp.code = 0;
+ icmp.checksum = 2015;
+ icmp.un.echo.sequence = 2017;
+ icmp.un.echo.id = 2019;
+ ASSERT_THAT(send(s_, &icmp, sizeof(icmp), 0),
+ SyscallFailsWithErrno(ENOTCONN));
+}
+
+// Bind to localhost.
+TEST_F(RawSocketTest, BindToLocalhost) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+}
+
+// Bind to a different address.
+TEST_F(RawSocketTest, BindToInvalid) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ struct sockaddr_in bind_addr = {};
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_addr = {1}; // 1.0.0.0 - An address that we can't bind to.
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
+}
+
+// Bind to localhost, then send and receive packets.
+TEST_F(RawSocketTest, BindSendAndReceive) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+
+ // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence,
+ // and ID. None of that should matter for raw sockets - the kernel should
+ // still give us the packet.
+ struct icmphdr icmp;
+ icmp.type = ICMP_ECHO;
+ icmp.code = 0;
+ icmp.checksum = 2001;
+ icmp.un.echo.sequence = 2004;
+ icmp.un.echo.id = 2007;
+ ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp));
+}
+
+// Bind and connect to localhost and send/receive packets.
+TEST_F(RawSocketTest, BindConnectSendAndReceive) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ ASSERT_THAT(
+ bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+ ASSERT_THAT(
+ connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
+ SyscallSucceeds());
+
+ // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence,
+ // and ID. None of that should matter for raw sockets - the kernel should
+ // still give us the packet.
+ struct icmphdr icmp;
+ icmp.type = ICMP_ECHO;
+ icmp.code = 0;
+ icmp.checksum = 2009;
+ icmp.un.echo.sequence = 2010;
+ icmp.un.echo.id = 7;
+ ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp));
+}
+
+void RawSocketTest::ExpectICMPSuccess(const struct icmphdr& icmp) {
+ // We're going to receive both the echo request and reply, but the order is
+ // indeterminate.
+ char recv_buf[512];
+ struct sockaddr_in src;
+ bool received_request = false;
+ bool received_reply = false;
+
+ for (int i = 0; i < 2; i++) {
+ // Receive the packet.
+ ASSERT_NO_FATAL_FAILURE(ReceiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf),
+ sizeof(struct icmphdr), &src));
+ EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0);
+ struct icmphdr* recvd_icmp =
+ reinterpret_cast<struct icmphdr*>(recv_buf + sizeof(struct iphdr));
+ switch (recvd_icmp->type) {
+ case ICMP_ECHO:
+ EXPECT_FALSE(received_request);
+ received_request = true;
+ // The packet should be identical to what we sent.
+ EXPECT_EQ(memcmp(recv_buf + sizeof(struct iphdr), &icmp, sizeof(icmp)),
+ 0);
+ break;
+
+ case ICMP_ECHOREPLY:
+ EXPECT_FALSE(received_reply);
+ received_reply = true;
+ // Most fields should be the same.
+ EXPECT_EQ(recvd_icmp->code, icmp.code);
+ EXPECT_EQ(recvd_icmp->un.echo.sequence, icmp.un.echo.sequence);
+ EXPECT_EQ(recvd_icmp->un.echo.id, icmp.un.echo.id);
+ // A couple are different.
+ EXPECT_EQ(recvd_icmp->type, ICMP_ECHOREPLY);
+ // The checksum is computed in such a way that it is guaranteed to have
+ // changed.
+ EXPECT_NE(recvd_icmp->checksum, icmp.checksum);
+ break;
+ }
+ }
+
+ ASSERT_TRUE(received_request);
+ ASSERT_TRUE(received_reply);
+}
+
+void RawSocketTest::SendEmptyICMP(const struct icmphdr& icmp) {
ASSERT_NO_FATAL_FAILURE(SendEmptyICMPTo(s_, &addr_, icmp));
}
-void RawSocketTest::SendEmptyICMPTo(int sock, struct sockaddr_in *addr,
- struct icmphdr *icmp) {
- struct iovec iov = {.iov_base = icmp, .iov_len = sizeof(*icmp)};
- struct msghdr msg {
- .msg_name = addr, .msg_namelen = sizeof(*addr), .msg_iov = &iov,
- .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0,
- };
- ASSERT_THAT(sendmsg(sock, &msg, 0), SyscallSucceedsWithValue(sizeof(*icmp)));
+void RawSocketTest::SendEmptyICMPTo(int sock, struct sockaddr_in* addr,
+ const struct icmphdr& icmp) {
+ // It's safe to use const_cast here because sendmsg won't modify the iovec.
+ struct iovec iov = {};
+ iov.iov_base = static_cast<void*>(const_cast<struct icmphdr*>(&icmp));
+ iov.iov_len = sizeof(icmp);
+ struct msghdr msg = {};
+ msg.msg_name = addr;
+ msg.msg_namelen = sizeof(*addr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ ASSERT_THAT(sendmsg(sock, &msg, 0), SyscallSucceedsWithValue(sizeof(icmp)));
}
-void RawSocketTest::ReceiveICMP(char *recv_buf, size_t recv_buf_len,
- size_t expected_size, struct sockaddr_in *src) {
+void RawSocketTest::ReceiveICMP(char* recv_buf, size_t recv_buf_len,
+ size_t expected_size, struct sockaddr_in* src) {
ASSERT_NO_FATAL_FAILURE(
ReceiveICMPFrom(recv_buf, recv_buf_len, expected_size, src, s_));
}
-void RawSocketTest::ReceiveICMPFrom(char *recv_buf, size_t recv_buf_len,
+void RawSocketTest::ReceiveICMPFrom(char* recv_buf, size_t recv_buf_len,
size_t expected_size,
- struct sockaddr_in *src, int sock) {
- struct iovec iov = {.iov_base = recv_buf, .iov_len = recv_buf_len};
- struct msghdr msg = {
- .msg_name = src,
- .msg_namelen = sizeof(*src),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = NULL,
- .msg_controllen = 0,
- .msg_flags = 0,
- };
+ struct sockaddr_in* src, int sock) {
+ struct iovec iov = {};
+ iov.iov_base = recv_buf;
+ iov.iov_len = recv_buf_len;
+ struct msghdr msg = {};
+ msg.msg_name = src;
+ msg.msg_namelen = sizeof(*src);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
// We should receive the ICMP packet plus 20 bytes of IP header.
ASSERT_THAT(recvmsg(sock, &msg, 0),
SyscallSucceedsWithValue(expected_size + sizeof(struct iphdr)));