Add reuseaddr regression test

This commit is contained in:
jiangjianfeng 2025-07-22 10:05:07 +00:00 committed by Ruihan Li
parent 345cc9d055
commit 9d9633e3d2
2 changed files with 211 additions and 0 deletions

View File

@ -0,0 +1,210 @@
// SPDX-License-Identifier: MPL-2.0
#include <unistd.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include "../test.h"
int sock1;
int sock2;
int sock3;
struct sockaddr_in addr;
socklen_t addrlen;
FN_SETUP(init)
{
sock1 = CHECK(socket(AF_INET, SOCK_STREAM, 0));
sock2 = CHECK(socket(AF_INET, SOCK_STREAM, 0));
sock3 = CHECK(socket(AF_INET, SOCK_STREAM, 0));
addr.sin_family = AF_INET;
CHECK(inet_aton("127.0.0.1", &addr.sin_addr));
addrlen = sizeof(addr);
}
END_SETUP()
FN_TEST(bind_to_ephemeral)
{
int option = 1;
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
addr.sin_port = 0;
TEST_SUCC(bind(sock1, (struct sockaddr *)&addr, addrlen));
TEST_SUCC(getsockname(sock1, (struct sockaddr *)&addr, &addrlen));
TEST_ERRNO(bind(sock2, (struct sockaddr *)&addr, addrlen), EADDRINUSE);
TEST_SUCC(setsockopt(sock2, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
TEST_SUCC(bind(sock2, (struct sockaddr *)&addr, addrlen));
}
END_TEST()
void renew_socks()
{
CHECK(close(sock1));
CHECK(close(sock2));
CHECK(close(sock3));
sock1 = CHECK(socket(AF_INET, SOCK_STREAM, 0));
sock2 = CHECK(socket(AF_INET, SOCK_STREAM, 0));
sock3 = CHECK(socket(AF_INET, SOCK_STREAM, 0));
}
FN_TEST(bind_to_listening_port)
{
renew_socks();
int option = 1;
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
addr.sin_port = 0;
TEST_SUCC(bind(sock1, (struct sockaddr *)&addr, addrlen));
TEST_SUCC(listen(sock1, 1));
TEST_SUCC(getsockname(sock1, (struct sockaddr *)&addr, &addrlen));
TEST_ERRNO(bind(sock2, (struct sockaddr *)&addr, addrlen), EADDRINUSE);
TEST_SUCC(setsockopt(sock2, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
// Currently, Asterinas does not check whether the port is already in use
// by a listening socket when binding, so this test will fail.
// TEST_ERRNO(bind(sock2, (struct sockaddr *)&addr, addrlen), EADDRINUSE);
}
END_TEST()
FN_TEST(listen_on_the_same_port)
{
renew_socks();
int option = 1;
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
addr.sin_port = 0;
TEST_SUCC(bind(sock1, (struct sockaddr *)&addr, addrlen));
TEST_SUCC(getsockname(sock1, (struct sockaddr *)&addr, &addrlen));
TEST_SUCC(setsockopt(sock2, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
TEST_SUCC(bind(sock2, (struct sockaddr *)&addr, addrlen));
TEST_SUCC(listen(sock1, 1));
TEST_ERRNO(listen(sock2, 1), EADDRINUSE);
TEST_SUCC(setsockopt(sock3, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
// Currently, Asterinas does not check whether the port is already in use
// by a listening socket when binding, so this test will fail.
// TEST_ERRNO(bind(sock3, (struct sockaddr *)&addr, addrlen), EADDRINUSE);
}
END_TEST()
FN_TEST(bind_to_connected_port)
{
renew_socks();
int option = 1;
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
addr.sin_port = 0;
TEST_SUCC(bind(sock1, (struct sockaddr *)&addr, addrlen));
TEST_SUCC(listen(sock1, 3));
TEST_SUCC(getsockname(sock1, (struct sockaddr *)&addr, &addrlen));
TEST_SUCC(setsockopt(sock2, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
TEST_SUCC(connect(sock2, (struct sockaddr *)&addr, addrlen));
struct sockaddr_in sock2_addr;
TEST_SUCC(getsockname(sock2, (struct sockaddr *)&sock2_addr, &addrlen));
int sock3 = TEST_SUCC(socket(AF_INET, SOCK_STREAM, 0));
TEST_ERRNO(bind(sock3, (struct sockaddr *)&sock2_addr, addrlen),
EADDRINUSE);
TEST_SUCC(setsockopt(sock3, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
TEST_SUCC(bind(sock3, (struct sockaddr *)&sock2_addr, addrlen));
TEST_ERRNO(connect(sock2, (struct sockaddr *)&addr, addrlen), EISCONN);
TEST_SUCC(close(sock3));
}
END_TEST()
FN_TEST(enable_reuse_after_bound)
{
renew_socks();
int option = 0;
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
addr.sin_port = 0;
TEST_SUCC(bind(sock1, (struct sockaddr *)&addr, addrlen));
TEST_SUCC(getsockname(sock1, (struct sockaddr *)&addr, &addrlen));
option = 1;
TEST_SUCC(setsockopt(sock2, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
TEST_ERRNO(bind(sock2, (struct sockaddr *)&addr, addrlen), EADDRINUSE);
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
TEST_SUCC(bind(sock2, (struct sockaddr *)&addr, addrlen));
}
END_TEST()
FN_TEST(disable_reuse_after_bound)
{
renew_socks();
int option = 1;
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof(option)));
addr.sin_port = 0;
TEST_SUCC(bind(sock1, (struct sockaddr *)&addr, addrlen));
option = 0;
socklen_t option_len = sizeof(option);
TEST_SUCC(setsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
option_len));
TEST_RES(getsockopt(sock1, SOL_SOCKET, SO_REUSEADDR, &option,
&option_len),
option == 0 && option_len == 4);
TEST_SUCC(getsockname(sock1, (struct sockaddr *)&addr, &addrlen));
// The following test succeeds on Linux because Linux does not allow disabling
// SO_REUSEADDR for TCP sockets once the socket is bound. In contrast, Asterinas
// enforces a stricter rule: the port can be reused only if all sockets bound to it
// have port reuse enabled.
// See the discussion at <https://github.com/asterinas/asterinas/pull/2277#discussion_r2230139244>.
//
// option = 1;
// TEST_SUCC(setsockopt(sock2, SOL_SOCKET, SO_REUSEADDR, &option,
// sizeof(option)));
// TEST_SUCC(bind(sock2, (struct sockaddr *)&addr, addrlen));
}
END_TEST()
FN_SETUP(cleanup)
{
CHECK(close(sock1));
CHECK(close(sock2));
CHECK(close(sock3));
}
END_SETUP()

View File

@ -32,6 +32,7 @@ sleep 0.2
./send_buf_full
./tcp_err
./tcp_poll
./tcp_reuseaddr
./udp_err
./unix_stream_err
./unix_seqpacket_err