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:
parent
121db29a93
commit
420a89acd3
|
@ -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};
|
||||||
|
|
Loading…
Reference in New Issue