Receive RST packets as ECONNRESET errors

This commit is contained in:
Ruihan Li
2025-02-24 23:07:58 +08:00
committed by Tate, Hongliang Tian
parent aa29640ed7
commit d40d452e9d
6 changed files with 256 additions and 31 deletions

View File

@ -616,6 +616,7 @@ END_TEST()
sk_addr.sin_port = S_PORT; \
\
sk_connect = TEST_SUCC(socket(PF_INET, SOCK_STREAM, 0)); \
pfd.fd = sk_connect; \
TEST_SUCC(connect(sk_connect, (struct sockaddr *)&sk_addr, \
sizeof(sk_addr))); \
\
@ -628,6 +629,7 @@ FN_TEST(shutdown_shutdown)
int sk_accept;
int sk_connect;
socklen_t len;
struct pollfd pfd __attribute__((unused));
SETUP_CONN;
@ -647,4 +649,108 @@ FN_TEST(shutdown_shutdown)
}
END_TEST()
FN_TEST(connreset)
{
int sk_accept;
int sk_connect;
struct linger lin = { .l_onoff = 1, .l_linger = 0 };
struct pollfd pfd = { .events = POLLIN | POLLOUT };
char buf[6] = "hello";
int err;
socklen_t len;
#define RESET_CONN \
TEST_SUCC(setsockopt(sk_accept, SOL_SOCKET, SO_LINGER, &lin, \
sizeof(lin))); \
TEST_SUCC(close(sk_accept));
#define EV_ERR (POLLIN | POLLOUT | POLLHUP | POLLERR)
#define EV_NO_ERR (POLLIN | POLLOUT | POLLHUP)
// Test 1: `recv` should fail with `ECONNRESET`
SETUP_CONN;
RESET_CONN;
TEST_RES(poll(&pfd, 1, 0), pfd.revents == EV_ERR);
TEST_ERRNO(recv(sk_connect, buf, 0, 0), ECONNRESET);
TEST_RES(poll(&pfd, 1, 0), pfd.revents == EV_NO_ERR);
TEST_RES(recv(sk_connect, buf, 0, 0), _ret == 0);
TEST_SUCC(close(sk_connect));
// Test 2: `send` should fail with `ECONNRESET`
SETUP_CONN;
RESET_CONN;
TEST_RES(poll(&pfd, 1, 0), pfd.revents == EV_ERR);
TEST_ERRNO(send(sk_connect, buf, 0, 0), ECONNRESET);
TEST_RES(poll(&pfd, 1, 0), pfd.revents == EV_NO_ERR);
TEST_ERRNO(send(sk_connect, buf, 0, 0), EPIPE);
TEST_SUCC(close(sk_connect));
// Test 3: `recv` should drain the buffer, then fail with `ECONNRESET`
SETUP_CONN;
TEST_RES(send(sk_accept, buf, sizeof(buf), 0), _ret == sizeof(buf));
RESET_CONN;
TEST_RES(recv(sk_connect, buf, 4, 0),
_ret == 4 && memcmp(buf, "hell", 4) == 0);
TEST_RES(recv(sk_connect, buf, sizeof(buf), 0),
_ret == 2 && memcmp(buf, "o", 2) == 0);
TEST_ERRNO(recv(sk_connect, buf, sizeof(buf), 0), ECONNRESET);
TEST_RES(recv(sk_connect, buf, 0, 0), _ret == 0);
TEST_SUCC(close(sk_connect));
// Test 3: `getsockopt(SO_ERROR)` should report `ECONNRESET`
SETUP_CONN;
RESET_CONN;
TEST_RES(poll(&pfd, 1, 0), pfd.revents == EV_ERR);
len = sizeof(err);
TEST_RES(getsockopt(sk_connect, SOL_SOCKET, SO_ERROR, &err, &len),
len == sizeof(err) && err == ECONNRESET);
TEST_RES(poll(&pfd, 1, 0), pfd.revents == EV_NO_ERR);
TEST_RES(getsockopt(sk_connect, SOL_SOCKET, SO_ERROR, &err, &len),
len == sizeof(err) && err == 0);
TEST_SUCC(close(sk_connect));
#undef EV_ERR
#undef EV_NO_ERR
#undef RESET_CONN
}
END_TEST()
#undef SETUP_CONN
FN_TEST(listen_close)
{
int sk_listen;
int sk_connect;
sk_addr.sin_port = htons(0x4321);
sk_listen = TEST_SUCC(socket(PF_INET, SOCK_STREAM, 0));
TEST_SUCC(
bind(sk_listen, (struct sockaddr *)&sk_addr, sizeof(sk_addr)));
TEST_SUCC(listen(sk_listen, 10));
sk_connect = TEST_SUCC(socket(PF_INET, SOCK_STREAM, 0));
TEST_SUCC(connect(sk_connect, (struct sockaddr *)&sk_addr,
sizeof(sk_addr)));
// Test: `close(sk_listen)` will reset all connections in the backlog
TEST_SUCC(close(sk_listen));
TEST_ERRNO(send(sk_connect, &sk_connect, sizeof(sk_connect), 0),
ECONNRESET);
TEST_SUCC(close(sk_connect));
}
END_TEST()

View File

@ -230,14 +230,27 @@ FN_TEST(poll_shutdown_readwrite)
CHECK(write(sk_connect, buf, 4096));
// TODO: The following test cannot be passed on Asterinas due to the following reasons:
// 1. On Linux, an RST packet is generated when attempting to write to a closed socket.
// However, Asterinas currently does not generate this packet.
// 2. RST packets cause a POLLERR on Linux, but Asterinas currently lack support for this.
// 1. An RST packet is generated when attempting to write to a closed socket.
// 2. The RST packet will cause a POLLERR.
pfd.fd = sk_connect;
TEST_RES(poll(&pfd, 1, 0),
pfd.revents ==
(POLLIN | POLLOUT | POLLRDHUP | POLLHUP | POLLERR));
pfd.fd = sk_accept;
TEST_RES(poll(&pfd, 1, 0),
pfd.revents ==
(POLLIN | POLLOUT | POLLRDHUP | POLLHUP | POLLERR));
// TEST_RES(poll(&pfd, 1, 0),
// pfd.revents ==
// (POLLIN | POLLOUT | POLLRDHUP | POLLHUP | POLLERR));
int err = 0;
socklen_t errlen = sizeof(err);
// FIXME: This socket error should be `EPIPE`, but in Asterinas it is
// `ECONNRESET`. See the Linux implementation for details:
// <https://github.com/torvalds/linux/blob/848e076317446f9c663771ddec142d7c2eb4cb43/net/ipv4/tcp_input.c#L4553-L4555>.
//
// TEST_RES(getsockopt(sk_connect, SOL_SOCKET, SO_ERROR, &err, &errlen),
// errlen == sizeof(err) && err == EPIPE);
TEST_RES(getsockopt(sk_accept, SOL_SOCKET, SO_ERROR, &err, &errlen),
errlen == sizeof(err) && err == ECONNRESET);
}
END_TEST()