Fix flaky raw socket test.

The specific issue was:

- Test creates a raw ICMP socket
- Test sends an ICMP echo request (aka ping request) to itself via loopback
- Now two events race:
  - The raw socket recieves the ICMP echo request
  - Netstack receives the request and generates a reply (aka ping reply),
    which it sends back over loopback, where it is eventually received by the
    raw socket
- The test was written to expect packets in a specific order, but they can
  come in any order.

PiperOrigin-RevId: 236179066
Change-Id: I02c07c919d3d28093add3d18dd9196fbbc870813
This commit is contained in:
Kevin Krakauer 2019-02-28 12:52:06 -08:00 committed by Shentubot
parent 121db29a93
commit 420a89acd3
1 changed files with 61 additions and 42 deletions

View File

@ -45,15 +45,15 @@ class RawSocketTest : public ::testing::Test {
// The loopback address. // The loopback address.
struct sockaddr_in addr_; struct sockaddr_in addr_;
void sendEmptyICMP(struct icmphdr *icmp); void SendEmptyICMP(struct icmphdr *icmp);
void sendEmptyICMPTo(int sock, struct sockaddr_in *addr, void SendEmptyICMPTo(int sock, struct sockaddr_in *addr,
struct icmphdr *icmp); struct icmphdr *icmp);
void receiveICMP(char *recv_buf, size_t recv_buf_len, size_t expected_size, void ReceiveICMP(char *recv_buf, size_t recv_buf_len, size_t expected_size,
struct sockaddr_in *src); struct sockaddr_in *src);
void receiveICMPFrom(char *recv_buf, size_t recv_buf_len, void ReceiveICMPFrom(char *recv_buf, size_t recv_buf_len,
size_t expected_size, struct sockaddr_in *src, int sock); size_t expected_size, struct sockaddr_in *src, int sock);
}; };
@ -97,34 +97,52 @@ TEST_F(RawSocketTest, SendAndReceive) {
struct icmphdr icmp; struct icmphdr icmp;
icmp.type = ICMP_ECHO; icmp.type = ICMP_ECHO;
icmp.code = 0; icmp.code = 0;
icmp.checksum = *(unsigned short *)&icmp.checksum; icmp.checksum = 2011;
icmp.un.echo.sequence = *(unsigned short *)&icmp.un.echo.sequence; icmp.un.echo.sequence = 2012;
icmp.un.echo.id = *(unsigned short *)&icmp.un.echo.id; icmp.un.echo.id = 2014;
ASSERT_NO_FATAL_FAILURE(sendEmptyICMP(&icmp)); ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(&icmp));
// Receive the packet and make sure it's identical. // We're going to receive both the echo request and reply, but the order is
// indeterminate.
char recv_buf[512]; char recv_buf[512];
struct sockaddr_in src; struct sockaddr_in src;
ASSERT_NO_FATAL_FAILURE(receiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf), bool received_request = false;
sizeof(struct icmphdr), &src)); bool received_reply = false;
EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0);
EXPECT_EQ(memcmp(recv_buf + sizeof(struct iphdr), &icmp, sizeof(icmp)), 0);
// We should also receive the automatically generated echo reply. for (int i = 0; i < 2; i++) {
ASSERT_NO_FATAL_FAILURE(receiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf), // Receive the packet.
sizeof(struct icmphdr), &src)); ASSERT_NO_FATAL_FAILURE(ReceiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf),
EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0); sizeof(struct icmphdr), &src));
struct icmphdr *reply_icmp = EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0);
(struct icmphdr *)(recv_buf + sizeof(struct iphdr)); struct icmphdr *recvd_icmp =
// Most fields should be the same. reinterpret_cast<struct icmphdr *>(recv_buf + sizeof(struct iphdr));
EXPECT_EQ(reply_icmp->code, icmp.code); switch (recvd_icmp->type) {
EXPECT_EQ(reply_icmp->un.echo.sequence, icmp.un.echo.sequence); case ICMP_ECHO:
EXPECT_EQ(reply_icmp->un.echo.id, icmp.un.echo.id); EXPECT_FALSE(received_request);
// A couple are different. received_request = true;
EXPECT_EQ(reply_icmp->type, ICMP_ECHOREPLY); // The packet should be identical to what we sent.
// The checksum is computed in such a way that it is guaranteed to have EXPECT_EQ(memcmp(recv_buf + sizeof(struct iphdr), &icmp, sizeof(icmp)),
// changed. 0);
EXPECT_NE(reply_icmp->checksum, icmp.checksum); 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);
} }
// We should be able to create multiple raw sockets for the same protocol and // We should be able to create multiple raw sockets for the same protocol and
@ -141,21 +159,21 @@ TEST_F(RawSocketTest, MultipleSocketReceive) {
struct icmphdr icmp; struct icmphdr icmp;
icmp.type = ICMP_ECHO; icmp.type = ICMP_ECHO;
icmp.code = 0; icmp.code = 0;
icmp.checksum = *(unsigned short *)&icmp.checksum; icmp.checksum = 2014;
icmp.un.echo.sequence = *(unsigned short *)&icmp.un.echo.sequence; icmp.un.echo.sequence = 2016;
icmp.un.echo.id = *(unsigned short *)&icmp.un.echo.id; icmp.un.echo.id = 2018;
ASSERT_NO_FATAL_FAILURE(sendEmptyICMP(&icmp)); ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(&icmp));
// Receive it on socket 1. // Receive it on socket 1.
char recv_buf1[512]; char recv_buf1[512];
struct sockaddr_in src; struct sockaddr_in src;
ASSERT_NO_FATAL_FAILURE(receiveICMP(recv_buf1, ABSL_ARRAYSIZE(recv_buf1), ASSERT_NO_FATAL_FAILURE(ReceiveICMP(recv_buf1, ABSL_ARRAYSIZE(recv_buf1),
sizeof(struct icmphdr), &src)); sizeof(struct icmphdr), &src));
EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0); EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0);
// Receive it on socket 2. // Receive it on socket 2.
char recv_buf2[512]; char recv_buf2[512];
ASSERT_NO_FATAL_FAILURE(receiveICMPFrom(recv_buf2, ABSL_ARRAYSIZE(recv_buf2), ASSERT_NO_FATAL_FAILURE(ReceiveICMPFrom(recv_buf2, ABSL_ARRAYSIZE(recv_buf2),
sizeof(struct icmphdr), &src, sizeof(struct icmphdr), &src,
s2.get())); s2.get()));
EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0); EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0);
@ -177,7 +195,8 @@ TEST_F(RawSocketTest, RawAndPingSockets) {
struct icmphdr icmp; struct icmphdr icmp;
icmp.type = ICMP_ECHO; icmp.type = ICMP_ECHO;
icmp.code = 0; icmp.code = 0;
icmp.un.echo.sequence = *(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, ASSERT_THAT(RetryEINTR(sendto)(ping_sock.get(), &icmp, sizeof(icmp), 0,
(struct sockaddr *)&addr_, sizeof(addr_)), (struct sockaddr *)&addr_, sizeof(addr_)),
SyscallSucceedsWithValue(sizeof(icmp))); SyscallSucceedsWithValue(sizeof(icmp)));
@ -185,7 +204,7 @@ TEST_F(RawSocketTest, RawAndPingSockets) {
// Receive the packet via raw socket. // Receive the packet via raw socket.
char recv_buf[512]; char recv_buf[512];
struct sockaddr_in src; struct sockaddr_in src;
ASSERT_NO_FATAL_FAILURE(receiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf), ASSERT_NO_FATAL_FAILURE(ReceiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf),
sizeof(struct icmphdr), &src)); sizeof(struct icmphdr), &src));
EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0); EXPECT_EQ(memcmp(&src, &addr_, sizeof(sockaddr_in)), 0);
@ -201,11 +220,11 @@ TEST_F(RawSocketTest, RawAndPingSockets) {
0); 0);
} }
void RawSocketTest::sendEmptyICMP(struct icmphdr *icmp) { void RawSocketTest::SendEmptyICMP(struct icmphdr *icmp) {
ASSERT_NO_FATAL_FAILURE(sendEmptyICMPTo(s_, &addr_, icmp)); ASSERT_NO_FATAL_FAILURE(SendEmptyICMPTo(s_, &addr_, icmp));
} }
void RawSocketTest::sendEmptyICMPTo(int sock, struct sockaddr_in *addr, void RawSocketTest::SendEmptyICMPTo(int sock, struct sockaddr_in *addr,
struct icmphdr *icmp) { struct icmphdr *icmp) {
struct iovec iov = {.iov_base = icmp, .iov_len = sizeof(*icmp)}; struct iovec iov = {.iov_base = icmp, .iov_len = sizeof(*icmp)};
struct msghdr msg { struct msghdr msg {
@ -215,13 +234,13 @@ void RawSocketTest::sendEmptyICMPTo(int sock, struct sockaddr_in *addr,
ASSERT_THAT(sendmsg(sock, &msg, 0), SyscallSucceedsWithValue(sizeof(*icmp))); ASSERT_THAT(sendmsg(sock, &msg, 0), SyscallSucceedsWithValue(sizeof(*icmp)));
} }
void RawSocketTest::receiveICMP(char *recv_buf, size_t recv_buf_len, void RawSocketTest::ReceiveICMP(char *recv_buf, size_t recv_buf_len,
size_t expected_size, struct sockaddr_in *src) { size_t expected_size, struct sockaddr_in *src) {
ASSERT_NO_FATAL_FAILURE( ASSERT_NO_FATAL_FAILURE(
receiveICMPFrom(recv_buf, recv_buf_len, expected_size, src, s_)); 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, size_t expected_size,
struct sockaddr_in *src, int sock) { struct sockaddr_in *src, int sock) {
struct iovec iov = {.iov_base = recv_buf, .iov_len = recv_buf_len}; struct iovec iov = {.iov_base = recv_buf, .iov_len = recv_buf_len};