:p
atchew
Login
From: Geliang Tang <tanggeliang@kylinos.cn> v2: - patch 4, a new patch, add do_setsockopt_inq helper. - cleanups. v1: - https://patchwork.kernel.org/project/mptcp/cover/cover.1754620968.git.tanggeliang@kylinos.cn/ This series consolidates the TCP_INQ test functionality from mptcp_inq into mptcp_sockopt, simplifying the test suite while maintaining full test coverage. The key changes include: 1. Adding necessary protocol arguments and IPC improvements to sockopt 2. Implementing TCP_INQ test logic within the sockopt test binary 3. Migrating test execution to use the enhanced sockopt binary 4. Removing the now-obsolete mptcp_inq test binary The consolidation provides several benefits: - Reduces code duplication by sharing common infrastructure - Simplifies maintenance with a single test binary - Maintains identical test coverage for TCP_INQ functionality - Improves code clarity with better IPC naming - Enhances flexibility through new protocol selection options The changes maintain backward compatibility while extending functionality, and all tests pass with the same coverage as before. Geliang Tang (9): selftests: mptcp: sockopt: use getrandom for initialization selftests: mptcp: sockopt: add protocol arguments selftests: mptcp: sockopt: add inq argument selftests: mptcp: sockopt: set TCP_INQ on accepted sockets selftests: mptcp: sockopt: rename pipefd to ipcfd selftests: mptcp: sockopt: add TCP_INQ client processing selftests: mptcp: sockopt: add TCP_INQ server connection selftests: mptcp: sockopt: replace mptcp_inq with sockopt selftests: mptcp: sockopt: remove obsolete mptcp_inq tools/testing/selftests/net/mptcp/.gitignore | 1 - tools/testing/selftests/net/mptcp/Makefile | 2 +- tools/testing/selftests/net/mptcp/mptcp_inq.c | 608 ------------------ .../selftests/net/mptcp/mptcp_sockopt.c | 359 ++++++++++- .../selftests/net/mptcp/mptcp_sockopt.sh | 6 +- 5 files changed, 333 insertions(+), 643 deletions(-) delete mode 100644 tools/testing/selftests/net/mptcp/mptcp_inq.c -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Replace /dev/urandom with getrandom() for initializing the RNG. This simplifies the code and avoids potential failures from opening device files while maintaining cryptographic quality randomness. These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ #include <sys/socket.h> #include <sys/types.h> #include <sys/wait.h> +#include <sys/random.h> #include <netdb.h> #include <netinet/in.h> @@ -XXX,XX +XXX,XX @@ static int rcheck(int wstatus, const char *what) static void init_rng(void) { - int fd = open("/dev/urandom", O_RDONLY); + unsigned int foo; - if (fd >= 0) { - unsigned int foo; - ssize_t ret; - - /* can't fail */ - ret = read(fd, &foo, sizeof(foo)); - assert(ret == sizeof(foo)); - - close(fd); - srand(foo); - } else { - srand(time(NULL)); + if (getrandom(&foo, sizeof(foo), 0) == -1) { + perror("getrandom"); + exit(1); } + + srand(foo); } int main(int argc, char *argv[]) -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Add -t and -r options to specify tx/rx protocols (TCP/MPTCP). This increases testing flexibility by allowing explicit protocol selection for both transmission and reception paths. These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ #include <linux/tcp.h> static int pf = AF_INET; +static int proto_tx = IPPROTO_MPTCP; +static int proto_rx = IPPROTO_MPTCP; #ifndef IPPROTO_MPTCP #define IPPROTO_MPTCP 262 @@ -XXX,XX +XXX,XX @@ static void die_perror(const char *msg) static void die_usage(int r) { - fprintf(stderr, "Usage: mptcp_sockopt [-6]\n"); + fprintf(stderr, "Usage: mptcp_sockopt [-6] [-t tcp|mptcp] [-r tcp|mptcp]\n"); exit(r); } @@ -XXX,XX +XXX,XX @@ static int sock_listen_mptcp(const char * const listenaddr, hints.ai_family = pf; for (a = addr; a; a = a->ai_next) { - sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP); + sock = socket(a->ai_family, a->ai_socktype, proto_rx); if (sock < 0) continue; @@ -XXX,XX +XXX,XX @@ static int sock_connect_mptcp(const char * const remoteaddr, return sock; } +static int protostr_to_num(const char *s) +{ + if (strcasecmp(s, "tcp") == 0) + return IPPROTO_TCP; + if (strcasecmp(s, "mptcp") == 0) + return IPPROTO_MPTCP; + + die_usage(1); + return 0; +} + static void parse_opts(int argc, char **argv) { int c; - while ((c = getopt(argc, argv, "h6")) != -1) { + while ((c = getopt(argc, argv, "h6t:r:")) != -1) { switch (c) { case 'h': die_usage(0); @@ -XXX,XX +XXX,XX @@ static void parse_opts(int argc, char **argv) case '6': pf = AF_INET6; break; + case 't': + proto_tx = protostr_to_num(optarg); + break; + case 'r': + proto_rx = protostr_to_num(optarg); + break; default: die_usage(1); break; @@ -XXX,XX +XXX,XX @@ static int client(int pipefd) switch (pf) { case AF_INET: - fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP); + fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx); break; case AF_INET6: - fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP); + fd = sock_connect_mptcp("::1", "15432", proto_tx); break; default: xerror("Unknown pf %d\n", pf); -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Introduce -i option to enable TCP_INQ testing. This prepares for consolidating TCP_INQ tests into a single binary by adding the necessary command-line interface. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static int pf = AF_INET; static int proto_tx = IPPROTO_MPTCP; static int proto_rx = IPPROTO_MPTCP; +static bool inq; #ifndef IPPROTO_MPTCP #define IPPROTO_MPTCP 262 @@ -XXX,XX +XXX,XX @@ static void die_perror(const char *msg) static void die_usage(int r) { - fprintf(stderr, "Usage: mptcp_sockopt [-6] [-t tcp|mptcp] [-r tcp|mptcp]\n"); + fprintf(stderr, "Usage: mptcp_sockopt [-6] [-t tcp|mptcp] [-r tcp|mptcp] [-i]\n"); exit(r); } @@ -XXX,XX +XXX,XX @@ static void parse_opts(int argc, char **argv) { int c; - while ((c = getopt(argc, argv, "h6t:r:")) != -1) { + while ((c = getopt(argc, argv, "h6t:r:i")) != -1) { switch (c) { case 'h': die_usage(0); @@ -XXX,XX +XXX,XX @@ static void parse_opts(int argc, char **argv) case 'r': proto_rx = protostr_to_num(optarg); break; + case 'i': + inq = true; + break; default: die_usage(1); break; -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Add helper functions to conditionally set TCP_INQ sockopt on accepted sockets when running in inq mode. This ensures proper testing of TCP_CM_INQ functionality by enabling the required socket option on both ends of the connection. This setup is necessary for validating TCP_CM_INQ behavior during TCP_INQ-specific tests. These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../testing/selftests/net/mptcp/mptcp_sockopt.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int pipefd) close(fd); } +static void do_setsockopt_inq(int fd) +{ + int on = 1; + + if (-1 == setsockopt(fd, IPPROTO_TCP, TCP_INQ, &on, sizeof(on))) + die_perror("setsockopt(TCP_INQ)"); +} + +static void do_setsockopts_accept(int fd) +{ + if (inq) + do_setsockopt_inq(fd); +} + static int xaccept(int s) { int fd = accept(s, NULL, 0); @@ -XXX,XX +XXX,XX @@ static int xaccept(int s) if (fd < 0) die_perror("accept"); + do_setsockopts_accept(fd); + return fd; } -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Rename pipefd to ipcfd to better reflect its generalized purpose since it now handles both pipes and Unix domain sockets. This naming improves code clarity for IPC channel handling. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static int xaccept(int s) return fd; } -static int server(int pipefd) +static int server(int ipcfd) { int fd = -1, r; @@ -XXX,XX +XXX,XX @@ static int server(int pipefd) break; } - r = write(pipefd, "conn", 4); + r = write(ipcfd, "conn", 4); assert(r == 4); alarm(15); r = xaccept(fd); - process_one_client(r, pipefd); + process_one_client(r, ipcfd); return 0; } @@ -XXX,XX +XXX,XX @@ static void test_ip_tos_sockopt(int fd) xerror("expect socklen_t == -1"); } -static int client(int pipefd) +static int client(int ipcfd) { int fd = -1; @@ -XXX,XX +XXX,XX @@ static int client(int pipefd) test_ip_tos_sockopt(fd); - connect_one_server(fd, pipefd); + connect_one_server(fd, ipcfd); return 0; } @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) { int e1, e2, wstatus; pid_t s, c, ret; - int pipefds[2]; + int ipcfds[2]; parse_opts(argc, argv); init_rng(); - e1 = pipe(pipefds); + e1 = inq ? socketpair(AF_UNIX, SOCK_DGRAM, 0, ipcfds) : + pipe(ipcfds); if (e1 < 0) die_perror("pipe"); s = xfork(); if (s == 0) - return server(pipefds[1]); + return server(ipcfds[1]); - close(pipefds[1]); + close(ipcfds[1]); /* wait until server bound a socket */ - e1 = read(pipefds[0], &e1, 4); + e1 = read(ipcfds[0], &e1, 4); assert(e1 == 4); c = xfork(); if (c == 0) - return client(pipefds[0]); + return client(ipcfds[0]); - close(pipefds[0]); + close(ipcfds[0]); ret = waitpid(s, &wstatus, 0); if (ret == -1) -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Implement process_one_client_inq() to handle TCP_INQ-specific client logic including: - Setting TCP_INQ sockopt - Validating INQ values via cmsg - Handling large data transfers with INQ checks - Verifying FIN sequence INQ semantics These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 153 +++++++++++++++++- 1 file changed, 152 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ #include <sys/types.h> #include <sys/wait.h> #include <sys/random.h> +#include <sys/ioctl.h> #include <netdb.h> #include <netinet/in.h> @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int pipefd) close(fd); } +static void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv) +{ + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_TCP && cmsg->cmsg_type == TCP_CM_INQ) { + memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv)); + return; + } + } + + xerror("could not find TCP_CM_INQ cmsg type"); +} + +static void process_one_client_inq(int fd, int unixfd) +{ + unsigned int tcp_inq; + size_t expect_len; + char msg_buf[4096]; + char buf[4096]; + char tmp[16]; + struct iovec iov = { + .iov_base = buf, + .iov_len = 1, + }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = msg_buf, + .msg_controllen = sizeof(msg_buf), + }; + ssize_t ret, tot; + + ret = write(unixfd, "xmit", 4); + assert(ret == 4); + + ret = read(unixfd, &expect_len, sizeof(expect_len)); + assert(ret == (ssize_t)sizeof(expect_len)); + + if (expect_len > sizeof(buf)) + xerror("expect len %zu exceeds buffer size", expect_len); + + for (;;) { + struct timespec req; + unsigned int queued; + + ret = ioctl(fd, FIONREAD, &queued); + if (ret < 0) + die_perror("FIONREAD"); + if (queued > expect_len) + xerror("FIONREAD returned %u, but only %zu expected\n", + queued, expect_len); + if (queued == expect_len) + break; + + req.tv_sec = 0; + req.tv_nsec = 1000 * 1000ul; + nanosleep(&req, NULL); + } + + /* read one byte, expect cmsg to return expected - 1 */ + ret = recvmsg(fd, &msg, 0); + if (ret < 0) + die_perror("recvmsg"); + + if (msg.msg_controllen == 0) + xerror("msg_controllen is 0"); + + get_tcp_inq(&msg, &tcp_inq); + + assert((size_t)tcp_inq == (expect_len - 1)); + + iov.iov_len = sizeof(buf); + ret = recvmsg(fd, &msg, 0); + if (ret < 0) + die_perror("recvmsg"); + + /* should have gotten exact remainder of all pending data */ + assert(ret == (ssize_t)tcp_inq); + + /* should be 0, all drained */ + get_tcp_inq(&msg, &tcp_inq); + assert(tcp_inq == 0); + + /* request a large swath of data. */ + ret = write(unixfd, "huge", 4); + assert(ret == 4); + + ret = read(unixfd, &expect_len, sizeof(expect_len)); + assert(ret == (ssize_t)sizeof(expect_len)); + + /* peer should send us a few mb of data */ + if (expect_len <= sizeof(buf)) + xerror("expect len %zu too small\n", expect_len); + + tot = 0; + do { + iov.iov_len = sizeof(buf); + ret = recvmsg(fd, &msg, 0); + if (ret < 0) + die_perror("recvmsg"); + + tot += ret; + + get_tcp_inq(&msg, &tcp_inq); + + if (tcp_inq > expect_len - tot) + xerror("inq %d, remaining %d total_len %d\n", + tcp_inq, expect_len - tot, (int)expect_len); + + assert(tcp_inq <= expect_len - tot); + } while ((size_t)tot < expect_len); + + ret = write(unixfd, "shut", 4); + assert(ret == 4); + + /* wait for hangup. Should have received one more byte of data. */ + ret = read(unixfd, tmp, sizeof(tmp)); + assert(ret == 6); + assert(strncmp(tmp, "closed", 6) == 0); + + sleep(1); + + iov.iov_len = 1; + ret = recvmsg(fd, &msg, 0); + if (ret < 0) + die_perror("recvmsg"); + assert(ret == 1); + + get_tcp_inq(&msg, &tcp_inq); + + /* tcp_inq should be 1 due to received fin. */ + assert(tcp_inq == 1); + + iov.iov_len = 1; + ret = recvmsg(fd, &msg, 0); + if (ret < 0) + die_perror("recvmsg"); + + /* expect EOF */ + assert(ret == 0); + get_tcp_inq(&msg, &tcp_inq); + assert(tcp_inq == 1); + + close(fd); +} + static void do_setsockopt_inq(int fd) { int on = 1; @@ -XXX,XX +XXX,XX @@ static int server(int ipcfd) alarm(15); r = xaccept(fd); - process_one_client(r, ipcfd); + if (inq) + process_one_client_inq(r, ipcfd); + else + process_one_client(r, ipcfd); return 0; } -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Implement connect_one_server_inq() featuring: - Wait mechanisms for ACK verification - Large data transmission with flow control - Proper shutdown sequence handling - Integration with IPC signaling These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 116 +++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ #include <netinet/in.h> #include <linux/tcp.h> +#include <linux/sockios.h> static int pf = AF_INET; static int proto_tx = IPPROTO_MPTCP; @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int pipefd) close(fd); } +/* wait up to timeout milliseconds */ +static void wait_for_ack(int fd, int timeout, size_t total) +{ + int i; + + for (i = 0; i < timeout; i++) { + int nsd, ret, queued = -1; + struct timespec req; + + ret = ioctl(fd, TIOCOUTQ, &queued); + if (ret < 0) + die_perror("TIOCOUTQ"); + + ret = ioctl(fd, SIOCOUTQNSD, &nsd); + if (ret < 0) + die_perror("SIOCOUTQNSD"); + + if ((size_t)queued > total) + xerror("TIOCOUTQ %u, but only %zu expected\n", queued, total); + assert(nsd <= queued); + + if (queued == 0) + return; + + /* wait for peer to ack rx of all data */ + req.tv_sec = 0; + req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */ + nanosleep(&req, NULL); + } + + xerror("still tx data queued after %u ms\n", timeout); +} + +static void connect_one_server_inq(int fd, int unixfd) +{ + size_t len, i, total, sent; + char buf[4096], buf2[4096]; + ssize_t ret; + + len = rand() % (sizeof(buf) - 1); + + if (len < 128) + len = 128; + + for (i = 0; i < len ; i++) { + buf[i] = rand() % 26; + buf[i] += 'A'; + } + + buf[i] = '\n'; + + /* un-block server */ + ret = read(unixfd, buf2, 4); + assert(ret == 4); + + assert(strncmp(buf2, "xmit", 4) == 0); + + ret = write(unixfd, &len, sizeof(len)); + assert(ret == (ssize_t)sizeof(len)); + + ret = write(fd, buf, len); + if (ret < 0) + die_perror("write"); + + if (ret != (ssize_t)len) + xerror("short write"); + + ret = read(unixfd, buf2, 4); + assert(strncmp(buf2, "huge", 4) == 0); + + total = rand() % (16 * 1024 * 1024); + total += (1 * 1024 * 1024); + sent = total; + + ret = write(unixfd, &total, sizeof(total)); + assert(ret == (ssize_t)sizeof(total)); + + wait_for_ack(fd, 5000, len); + + while (total > 0) { + if (total > sizeof(buf)) + len = sizeof(buf); + else + len = total; + + ret = write(fd, buf, len); + if (ret < 0) + die_perror("write"); + total -= ret; + + /* we don't have to care about buf content, only + * number of total bytes sent + */ + } + + ret = read(unixfd, buf2, 4); + assert(ret == 4); + assert(strncmp(buf2, "shut", 4) == 0); + + wait_for_ack(fd, 5000, sent); + + ret = write(fd, buf, 1); + assert(ret == 1); + close(fd); + ret = write(unixfd, "closed", 6); + assert(ret == 6); + + close(unixfd); +} + static void process_one_client(int fd, int pipefd) { ssize_t ret, ret2, ret3; @@ -XXX,XX +XXX,XX @@ static int client(int ipcfd) test_ip_tos_sockopt(fd); - connect_one_server(fd, ipcfd); + if (inq) + connect_one_server_inq(fd, ipcfd); + else + connect_one_server(fd, ipcfd); return 0; } -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Update mptcp_sockopt.sh to use mptcp_sockopt binary instead of mptcp_inq. This consolidates TCP_INQ testing into a single test binary while maintaining equivalent test coverage. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/mptcp_sockopt.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index XXXXXXX..XXXXXXX 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -XXX,XX +XXX,XX @@ run_tests() do_tcpinq_test() { print_title "TCP_INQ cmsg/ioctl $*" - ip netns exec "$ns_sbox" ./mptcp_inq "$@" + ip netns exec "$ns_sbox" ./mptcp_sockopt "$@" local lret=$? if [ $lret -ne 0 ];then ret=$lret @@ -XXX,XX +XXX,XX @@ do_tcpinq_tests() fi local args - for args in "-t tcp" "-r tcp"; do + for args in "-t tcp -i" "-r tcp -i"; do do_tcpinq_test $args lret=$? if [ $lret -ne 0 ] ; then @@ -XXX,XX +XXX,XX @@ do_tcpinq_tests() fi done - do_tcpinq_test -r tcp -t tcp + do_tcpinq_test -r tcp -t tcp -i return $? } -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Delete mptcp_inq binary and references since its functionality has been fully integrated into mptcp_sockopt. This simplifies the test suite structure and reduces maintenance overhead. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/.gitignore | 1 - tools/testing/selftests/net/mptcp/Makefile | 2 +- tools/testing/selftests/net/mptcp/mptcp_inq.c | 608 ------------------ 3 files changed, 1 insertion(+), 610 deletions(-) delete mode 100644 tools/testing/selftests/net/mptcp/mptcp_inq.c diff --git a/tools/testing/selftests/net/mptcp/.gitignore b/tools/testing/selftests/net/mptcp/.gitignore index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/.gitignore +++ b/tools/testing/selftests/net/mptcp/.gitignore @@ -XXX,XX +XXX,XX @@ # SPDX-License-Identifier: GPL-2.0-only mptcp_connect mptcp_diag -mptcp_inq mptcp_sockopt pm_nl_ctl *.pcap diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/Makefile +++ b/tools/testing/selftests/net/mptcp/Makefile @@ -XXX,XX +XXX,XX @@ TEST_PROGS := mptcp_connect.sh mptcp_connect_mmap.sh mptcp_connect_sendfile.sh \ mptcp_connect_checksum.sh pm_netlink.sh mptcp_join.sh diag.sh \ simult_flows.sh mptcp_sockopt.sh userspace_pm.sh -TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq mptcp_diag +TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_diag TEST_FILES := mptcp_lib.sh settings diff --git a/tools/testing/selftests/net/mptcp/mptcp_inq.c b/tools/testing/selftests/net/mptcp/mptcp_inq.c deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/tools/testing/selftests/net/mptcp/mptcp_inq.c +++ /dev/null @@ -XXX,XX +XXX,XX @@ -// SPDX-License-Identifier: GPL-2.0 - -#define _GNU_SOURCE - -#include <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <string.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <strings.h> -#include <unistd.h> -#include <time.h> - -#include <sys/ioctl.h> -#include <sys/random.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <netdb.h> -#include <netinet/in.h> - -#include <linux/tcp.h> -#include <linux/sockios.h> - -#ifndef IPPROTO_MPTCP -#define IPPROTO_MPTCP 262 -#endif -#ifndef SOL_MPTCP -#define SOL_MPTCP 284 -#endif - -static int pf = AF_INET; -static int proto_tx = IPPROTO_MPTCP; -static int proto_rx = IPPROTO_MPTCP; - -static void die_perror(const char *msg) -{ - perror(msg); - exit(1); -} - -static void die_usage(int r) -{ - fprintf(stderr, "Usage: mptcp_inq [-6] [ -t tcp|mptcp ] [ -r tcp|mptcp]\n"); - exit(r); -} - -static void xerror(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputc('\n', stderr); - exit(1); -} - -static const char *getxinfo_strerr(int err) -{ - if (err == EAI_SYSTEM) - return strerror(errno); - - return gai_strerror(err); -} - -static void xgetaddrinfo(const char *node, const char *service, - struct addrinfo *hints, - struct addrinfo **res) -{ - int err; - -again: - err = getaddrinfo(node, service, hints, res); - if (err) { - const char *errstr; - - if (err == EAI_SOCKTYPE) { - hints->ai_protocol = IPPROTO_TCP; - goto again; - } - - errstr = getxinfo_strerr(err); - - fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n", - node ? node : "", service ? service : "", errstr); - exit(1); - } -} - -static int sock_listen_mptcp(const char * const listenaddr, - const char * const port) -{ - int sock = -1; - struct addrinfo hints = { - .ai_protocol = IPPROTO_MPTCP, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_PASSIVE | AI_NUMERICHOST - }; - - hints.ai_family = pf; - - struct addrinfo *a, *addr; - int one = 1; - - xgetaddrinfo(listenaddr, port, &hints, &addr); - hints.ai_family = pf; - - for (a = addr; a; a = a->ai_next) { - sock = socket(a->ai_family, a->ai_socktype, proto_rx); - if (sock < 0) - continue; - - if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, - sizeof(one))) - perror("setsockopt"); - - if (bind(sock, a->ai_addr, a->ai_addrlen) == 0) - break; /* success */ - - perror("bind"); - close(sock); - sock = -1; - } - - freeaddrinfo(addr); - - if (sock < 0) - xerror("could not create listen socket"); - - if (listen(sock, 20)) - die_perror("listen"); - - return sock; -} - -static int sock_connect_mptcp(const char * const remoteaddr, - const char * const port, int proto) -{ - struct addrinfo hints = { - .ai_protocol = IPPROTO_MPTCP, - .ai_socktype = SOCK_STREAM, - }; - struct addrinfo *a, *addr; - int sock = -1; - - hints.ai_family = pf; - - xgetaddrinfo(remoteaddr, port, &hints, &addr); - for (a = addr; a; a = a->ai_next) { - sock = socket(a->ai_family, a->ai_socktype, proto); - if (sock < 0) - continue; - - if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) - break; /* success */ - - die_perror("connect"); - } - - if (sock < 0) - xerror("could not create connect socket"); - - freeaddrinfo(addr); - return sock; -} - -static int protostr_to_num(const char *s) -{ - if (strcasecmp(s, "tcp") == 0) - return IPPROTO_TCP; - if (strcasecmp(s, "mptcp") == 0) - return IPPROTO_MPTCP; - - die_usage(1); - return 0; -} - -static void parse_opts(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, "h6t:r:")) != -1) { - switch (c) { - case 'h': - die_usage(0); - break; - case '6': - pf = AF_INET6; - break; - case 't': - proto_tx = protostr_to_num(optarg); - break; - case 'r': - proto_rx = protostr_to_num(optarg); - break; - default: - die_usage(1); - break; - } - } -} - -/* wait up to timeout milliseconds */ -static void wait_for_ack(int fd, int timeout, size_t total) -{ - int i; - - for (i = 0; i < timeout; i++) { - int nsd, ret, queued = -1; - struct timespec req; - - ret = ioctl(fd, TIOCOUTQ, &queued); - if (ret < 0) - die_perror("TIOCOUTQ"); - - ret = ioctl(fd, SIOCOUTQNSD, &nsd); - if (ret < 0) - die_perror("SIOCOUTQNSD"); - - if ((size_t)queued > total) - xerror("TIOCOUTQ %u, but only %zu expected\n", queued, total); - assert(nsd <= queued); - - if (queued == 0) - return; - - /* wait for peer to ack rx of all data */ - req.tv_sec = 0; - req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */ - nanosleep(&req, NULL); - } - - xerror("still tx data queued after %u ms\n", timeout); -} - -static void connect_one_server(int fd, int unixfd) -{ - size_t len, i, total, sent; - char buf[4096], buf2[4096]; - ssize_t ret; - - len = rand() % (sizeof(buf) - 1); - - if (len < 128) - len = 128; - - for (i = 0; i < len ; i++) { - buf[i] = rand() % 26; - buf[i] += 'A'; - } - - buf[i] = '\n'; - - /* un-block server */ - ret = read(unixfd, buf2, 4); - assert(ret == 4); - - assert(strncmp(buf2, "xmit", 4) == 0); - - ret = write(unixfd, &len, sizeof(len)); - assert(ret == (ssize_t)sizeof(len)); - - ret = write(fd, buf, len); - if (ret < 0) - die_perror("write"); - - if (ret != (ssize_t)len) - xerror("short write"); - - ret = read(unixfd, buf2, 4); - assert(strncmp(buf2, "huge", 4) == 0); - - total = rand() % (16 * 1024 * 1024); - total += (1 * 1024 * 1024); - sent = total; - - ret = write(unixfd, &total, sizeof(total)); - assert(ret == (ssize_t)sizeof(total)); - - wait_for_ack(fd, 5000, len); - - while (total > 0) { - if (total > sizeof(buf)) - len = sizeof(buf); - else - len = total; - - ret = write(fd, buf, len); - if (ret < 0) - die_perror("write"); - total -= ret; - - /* we don't have to care about buf content, only - * number of total bytes sent - */ - } - - ret = read(unixfd, buf2, 4); - assert(ret == 4); - assert(strncmp(buf2, "shut", 4) == 0); - - wait_for_ack(fd, 5000, sent); - - ret = write(fd, buf, 1); - assert(ret == 1); - close(fd); - ret = write(unixfd, "closed", 6); - assert(ret == 6); - - close(unixfd); -} - -static void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv) -{ - struct cmsghdr *cmsg; - - for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) { - if (cmsg->cmsg_level == IPPROTO_TCP && cmsg->cmsg_type == TCP_CM_INQ) { - memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv)); - return; - } - } - - xerror("could not find TCP_CM_INQ cmsg type"); -} - -static void process_one_client(int fd, int unixfd) -{ - unsigned int tcp_inq; - size_t expect_len; - char msg_buf[4096]; - char buf[4096]; - char tmp[16]; - struct iovec iov = { - .iov_base = buf, - .iov_len = 1, - }; - struct msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = msg_buf, - .msg_controllen = sizeof(msg_buf), - }; - ssize_t ret, tot; - - ret = write(unixfd, "xmit", 4); - assert(ret == 4); - - ret = read(unixfd, &expect_len, sizeof(expect_len)); - assert(ret == (ssize_t)sizeof(expect_len)); - - if (expect_len > sizeof(buf)) - xerror("expect len %zu exceeds buffer size", expect_len); - - for (;;) { - struct timespec req; - unsigned int queued; - - ret = ioctl(fd, FIONREAD, &queued); - if (ret < 0) - die_perror("FIONREAD"); - if (queued > expect_len) - xerror("FIONREAD returned %u, but only %zu expected\n", - queued, expect_len); - if (queued == expect_len) - break; - - req.tv_sec = 0; - req.tv_nsec = 1000 * 1000ul; - nanosleep(&req, NULL); - } - - /* read one byte, expect cmsg to return expected - 1 */ - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - if (msg.msg_controllen == 0) - xerror("msg_controllen is 0"); - - get_tcp_inq(&msg, &tcp_inq); - - assert((size_t)tcp_inq == (expect_len - 1)); - - iov.iov_len = sizeof(buf); - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - /* should have gotten exact remainder of all pending data */ - assert(ret == (ssize_t)tcp_inq); - - /* should be 0, all drained */ - get_tcp_inq(&msg, &tcp_inq); - assert(tcp_inq == 0); - - /* request a large swath of data. */ - ret = write(unixfd, "huge", 4); - assert(ret == 4); - - ret = read(unixfd, &expect_len, sizeof(expect_len)); - assert(ret == (ssize_t)sizeof(expect_len)); - - /* peer should send us a few mb of data */ - if (expect_len <= sizeof(buf)) - xerror("expect len %zu too small\n", expect_len); - - tot = 0; - do { - iov.iov_len = sizeof(buf); - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - tot += ret; - - get_tcp_inq(&msg, &tcp_inq); - - if (tcp_inq > expect_len - tot) - xerror("inq %d, remaining %d total_len %d\n", - tcp_inq, expect_len - tot, (int)expect_len); - - assert(tcp_inq <= expect_len - tot); - } while ((size_t)tot < expect_len); - - ret = write(unixfd, "shut", 4); - assert(ret == 4); - - /* wait for hangup. Should have received one more byte of data. */ - ret = read(unixfd, tmp, sizeof(tmp)); - assert(ret == 6); - assert(strncmp(tmp, "closed", 6) == 0); - - sleep(1); - - iov.iov_len = 1; - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - assert(ret == 1); - - get_tcp_inq(&msg, &tcp_inq); - - /* tcp_inq should be 1 due to received fin. */ - assert(tcp_inq == 1); - - iov.iov_len = 1; - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - /* expect EOF */ - assert(ret == 0); - get_tcp_inq(&msg, &tcp_inq); - assert(tcp_inq == 1); - - close(fd); -} - -static int xaccept(int s) -{ - int fd = accept(s, NULL, 0); - - if (fd < 0) - die_perror("accept"); - - return fd; -} - -static int server(int unixfd) -{ - int fd = -1, r, on = 1; - - switch (pf) { - case AF_INET: - fd = sock_listen_mptcp("127.0.0.1", "15432"); - break; - case AF_INET6: - fd = sock_listen_mptcp("::1", "15432"); - break; - default: - xerror("Unknown pf %d\n", pf); - break; - } - - r = write(unixfd, "conn", 4); - assert(r == 4); - - alarm(15); - r = xaccept(fd); - - if (-1 == setsockopt(r, IPPROTO_TCP, TCP_INQ, &on, sizeof(on))) - die_perror("setsockopt"); - - process_one_client(r, unixfd); - - return 0; -} - -static int client(int unixfd) -{ - int fd = -1; - - alarm(15); - - switch (pf) { - case AF_INET: - fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx); - break; - case AF_INET6: - fd = sock_connect_mptcp("::1", "15432", proto_tx); - break; - default: - xerror("Unknown pf %d\n", pf); - } - - connect_one_server(fd, unixfd); - - return 0; -} - -static void init_rng(void) -{ - unsigned int foo; - - if (getrandom(&foo, sizeof(foo), 0) == -1) { - perror("getrandom"); - exit(1); - } - - srand(foo); -} - -static pid_t xfork(void) -{ - pid_t p = fork(); - - if (p < 0) - die_perror("fork"); - else if (p == 0) - init_rng(); - - return p; -} - -static int rcheck(int wstatus, const char *what) -{ - if (WIFEXITED(wstatus)) { - if (WEXITSTATUS(wstatus) == 0) - return 0; - fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus)); - return WEXITSTATUS(wstatus); - } else if (WIFSIGNALED(wstatus)) { - xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus)); - } else if (WIFSTOPPED(wstatus)) { - xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus)); - } - - return 111; -} - -int main(int argc, char *argv[]) -{ - int e1, e2, wstatus; - pid_t s, c, ret; - int unixfds[2]; - - parse_opts(argc, argv); - - e1 = socketpair(AF_UNIX, SOCK_DGRAM, 0, unixfds); - if (e1 < 0) - die_perror("pipe"); - - s = xfork(); - if (s == 0) - return server(unixfds[1]); - - close(unixfds[1]); - - /* wait until server bound a socket */ - e1 = read(unixfds[0], &e1, 4); - assert(e1 == 4); - - c = xfork(); - if (c == 0) - return client(unixfds[0]); - - close(unixfds[0]); - - ret = waitpid(s, &wstatus, 0); - if (ret == -1) - die_perror("waitpid"); - e1 = rcheck(wstatus, "server"); - ret = waitpid(c, &wstatus, 0); - if (ret == -1) - die_perror("waitpid"); - e2 = rcheck(wstatus, "client"); - - return e1 ? e1 : e2; -} -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> v7: - Add a new patch to reseed RNG in child after fork. - Add a new patch to fix bytes_acked validation against sent bytes. - Add a new patch to use r/w for byte counts in process_one_client(). - Split v6-0004 into two separate patches: read exact payload length, and pass len over unixfd. - Change socketpair type from SOCK_DGRAM to SOCK_STREAM to avoid indefinite blocking when peer exits. - Fully initialize buf in server_huge_transfer() to silence MSAN/Valgrind warnings. - Use a union containing struct cmsghdr for control message buffers to prevent unaligned access on strict-alignment architectures. - Reorder and renumber patches accordingly. v6: - A new patch to use recvmsg instead of read. - Squash the four original patches into two. - Reset msg.msg_controllen. - Check cmsg_len in get_tcp_inq. - Check return value of client_huge_transfer. - Address other comments from Sashiko. - https://patchwork.kernel.org/project/mptcp/cover/cover.1783673158.git.tanggeliang@kylinos.cn/ v5: - Continue using the approach from v3 and fix the instability issues. This version has undergone extensive loop testing. - The new two separate functions, server_huge_transfer() and client_huge_transfer(), improve code structure. - https://patchwork.kernel.org/project/mptcp/cover/cover.1783579976.git.tanggeliang@kylinos.cn/ v4: - v3 changed the behavior of process_one_client and connect_one_server, causing intermittent failures during loop testing. Revert to using the v2 approach, along with some cleanups. - https://patchwork.kernel.org/project/mptcp/cover/cover.1757686538.git.tanggeliang@kylinos.cn/ v3: - No longer using process_one_client_inq() and connect_one_server_inq() for switching; instead, the inq-related code has been merged into process_one_client() and connect_one_server(). - https://patchwork.kernel.org/project/mptcp/cover/cover.1756200029.git.tanggeliang@kylinos.cn/ v2: - patch 4, a new patch, add do_setsockopt_inq helper. - cleanups. - https://patchwork.kernel.org/project/mptcp/cover/cover.1754664106.git.tanggeliang@kylinos.cn/ v1: - https://patchwork.kernel.org/project/mptcp/cover/cover.1754620968.git.tanggeliang@kylinos.cn/ This series consolidates the TCP_INQ test functionality from mptcp_inq into mptcp_sockopt, simplifying the test suite while maintaining full test coverage. Geliang Tang (14): selftests: mptcp: sockopt: reseed RNG after fork selftests: mptcp: sockopt: validate bytes_acked against sent bytes selftests: mptcp: sockopt: use r and w for byte counts selftests: mptcp: sockopt: add tx/rx protocol options selftests: mptcp: sockopt: add TCP_INQ test option selftests: mptcp: sockopt: use unix socket instead of pipe selftests: mptcp: sockopt: read exact payload length selftests: mptcp: sockopt: pass echo length over unixfd selftests: mptcp: sockopt: set FIONREAD ioctl selftests: mptcp: sockopt: add huge data transfer tests selftests: mptcp: sockopt: wait ack for huge transfer selftests: mptcp: sockopt: use recvmsg instead of read selftests: mptcp: sockopt: get and verify TCP_INQ selftests: mptcp: sockopt: replace mptcp_inq with sockopt tools/testing/selftests/net/mptcp/.gitignore | 1 - tools/testing/selftests/net/mptcp/Makefile | 1 - tools/testing/selftests/net/mptcp/mptcp_inq.c | 614 ------------------ .../selftests/net/mptcp/mptcp_sockopt.c | 415 ++++++++++-- .../selftests/net/mptcp/mptcp_sockopt.sh | 8 +- 5 files changed, 372 insertions(+), 667 deletions(-) delete mode 100644 tools/testing/selftests/net/mptcp/mptcp_inq.c -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> xfork() leaves the child sharing its parent's srand() state, so server and client processes drive rand() off the same seed. After the recent consolidation of mptcp_inq.c into mptcp_sockopt.c, mptcp_inq's per-fork reseed was lost. Restore it: have the child call init_rng() again so each fork gets an independent seed from /dev/urandom. This mirrors what the standalone mptcp_inq binary used to do, and keeps buffer-filling and huge-transfer sizes uncorrelated across the two processes. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ struct so_state { #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif +static void init_rng(void); + static void __noreturn die_perror(const char *msg) { perror(msg); @@ -XXX,XX +XXX,XX @@ static pid_t xfork(void) if (p < 0) die_perror("fork"); + else if (p == 0) + init_rng(); return p; } -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> mptcpi_bytes_acked tracks the number of bytes sent and acknowledged, so it should be compared with the amount of data written (ret2), not the amount read (ret). This corrects a logical error in the test validation. Fixes: b86418beade1 ("selftests: mptcp: sockopt: fix error messages") Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int pipefd) ", diff %" PRId64, s.last_sample.mptcpi_bytes_received, ret, s.last_sample.mptcpi_bytes_received - ret); - if (s.last_sample.mptcpi_bytes_acked != ret) + if (s.last_sample.mptcpi_bytes_acked != ret2) xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64, - s.last_sample.mptcpi_bytes_acked, ret, - s.last_sample.mptcpi_bytes_acked - ret); + s.last_sample.mptcpi_bytes_acked, ret2, + s.last_sample.mptcpi_bytes_acked - ret2); } close(fd); -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Replace ret/ret2/ret3 with r (received) and w (sent) for readability. This prepares for future updates where r will be modified via '+='. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int pipefd) static void process_one_client(int fd, int pipefd) { - ssize_t ret, ret2, ret3; + ssize_t ret, r, w; struct so_state s; char buf[4096]; @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int pipefd) ret = read(fd, buf, sizeof(buf)); if (ret < 0) die_perror("read"); + r = ret; assert(s.mptcpi_rcv_delta <= (uint64_t)ret); if (s.tcpi_rcv_delta) assert(s.tcpi_rcv_delta == (uint64_t)ret); - ret2 = write(fd, buf, ret); - if (ret2 < 0) + ret = write(fd, buf, r); + if (ret < 0) die_perror("write"); + w = ret; /* wait for hangup */ - ret3 = read(fd, buf, 1); - if (ret3 != 0) - xerror("expected EOF, got %lu", ret3); + ret = read(fd, buf, 1); + if (ret != 0) + xerror("expected EOF, got %lu", ret); + r += ret; - do_getsockopts(&s, fd, ret, ret2); - if (s.mptcpi_rcv_delta != (uint64_t)ret + 1) + do_getsockopts(&s, fd, r, w); + if (s.mptcpi_rcv_delta != (uint64_t)r + 1) xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64, - s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - (ret + 1)); + s.mptcpi_rcv_delta, r + 1, s.mptcpi_rcv_delta - (r + 1)); /* be nice when running on top of older kernel */ if (s.pkt_stats_avail) { - if (s.last_sample.mptcpi_bytes_sent != ret2) + if (s.last_sample.mptcpi_bytes_sent != w) xerror("mptcpi_bytes_sent %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64, - s.last_sample.mptcpi_bytes_sent, ret2, - s.last_sample.mptcpi_bytes_sent - ret2); - if (s.last_sample.mptcpi_bytes_received != ret) + s.last_sample.mptcpi_bytes_sent, w, + s.last_sample.mptcpi_bytes_sent - w); + if (s.last_sample.mptcpi_bytes_received != r) xerror("mptcpi_bytes_received %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64, - s.last_sample.mptcpi_bytes_received, ret, - s.last_sample.mptcpi_bytes_received - ret); - if (s.last_sample.mptcpi_bytes_acked != ret2) + s.last_sample.mptcpi_bytes_received, r, + s.last_sample.mptcpi_bytes_received - r); + if (s.last_sample.mptcpi_bytes_acked != w) xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64, - s.last_sample.mptcpi_bytes_acked, ret2, - s.last_sample.mptcpi_bytes_acked - ret2); + s.last_sample.mptcpi_bytes_acked, w, + s.last_sample.mptcpi_bytes_acked - w); } close(fd); -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Add -t and -r options to specify tx/rx protocols (TCP/MPTCP). This increases testing flexibility by allowing explicit protocol selection for both transmission and reception paths. These codes are from mptcp_inq.c. Enhance compatibility with TCP tests by skipping MPTCP getsockopt checks during TCP tests since MPTCP socket options are not available. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ #include <linux/compiler.h> static int pf = AF_INET; +static int proto_tx = IPPROTO_MPTCP; +static int proto_rx = IPPROTO_MPTCP; #ifndef IPPROTO_MPTCP #define IPPROTO_MPTCP 262 @@ -XXX,XX +XXX,XX @@ static void __noreturn die_perror(const char *msg) static void die_usage(int r) { fprintf(stderr, "Usage: mptcp_sockopt [-6]\n"); + fprintf(stderr, " [-t tcp|mptcp] [-r tcp|mptcp]\n"); exit(r); } @@ -XXX,XX +XXX,XX @@ static int sock_listen_mptcp(const char * const listenaddr, hints.ai_family = pf; for (a = addr; a; a = a->ai_next) { - sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP); + sock = socket(a->ai_family, a->ai_socktype, proto_rx); if (sock < 0) continue; @@ -XXX,XX +XXX,XX @@ static int sock_connect_mptcp(const char * const remoteaddr, return sock; } +static int protostr_to_num(const char *s) +{ + if (strcasecmp(s, "tcp") == 0) + return IPPROTO_TCP; + if (strcasecmp(s, "mptcp") == 0) + return IPPROTO_MPTCP; + + die_usage(1); + return 0; +} + static void parse_opts(int argc, char **argv) { int c; - while ((c = getopt(argc, argv, "h6")) != -1) { + while ((c = getopt(argc, argv, "h6t:r:")) != -1) { switch (c) { case 'h': die_usage(0); @@ -XXX,XX +XXX,XX @@ static void parse_opts(int argc, char **argv) case '6': pf = AF_INET6; break; + case 't': + proto_tx = protostr_to_num(optarg); + break; + case 'r': + proto_rx = protostr_to_num(optarg); + break; default: die_usage(1); break; @@ -XXX,XX +XXX,XX @@ static void do_getsockopt_mptcp_full_info(struct so_state *s, int fd) static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w) { + if (proto_tx != IPPROTO_MPTCP || proto_rx != IPPROTO_MPTCP) + return; + do_getsockopt_mptcp_info(s, fd, w); do_getsockopt_tcp_info(s, fd, r, w); @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int pipefd) if (eof) total += 1; /* sequence advances due to FIN */ - assert(s.mptcpi_rcv_delta == (uint64_t)total); + if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP) + assert(s.mptcpi_rcv_delta == (uint64_t)total); close(fd); } @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int pipefd) r += ret; do_getsockopts(&s, fd, r, w); - if (s.mptcpi_rcv_delta != (uint64_t)r + 1) + if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP && + s.mptcpi_rcv_delta != (uint64_t)r + 1) xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64, s.mptcpi_rcv_delta, r + 1, s.mptcpi_rcv_delta - (r + 1)); @@ -XXX,XX +XXX,XX @@ static int client(int pipefd) switch (pf) { case AF_INET: - fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP); + fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx); break; case AF_INET6: - fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP); + fd = sock_connect_mptcp("::1", "15432", proto_tx); break; default: xerror("Unknown pf %d\n", pf); -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Add -i option to enable TCP_INQ testing. This prepares for consolidating TCP_INQ tests into a single binary by adding the necessary command-line interface. Add helper functions to conditionally set TCP_INQ sockopt on accepted sockets when running in inq mode. This ensures proper testing of TCP_CM_INQ functionality by enabling the required socket option on the incoming server connection. These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static int pf = AF_INET; static int proto_tx = IPPROTO_MPTCP; static int proto_rx = IPPROTO_MPTCP; +static bool inq; #ifndef IPPROTO_MPTCP #define IPPROTO_MPTCP 262 @@ -XXX,XX +XXX,XX @@ static void die_usage(int r) { fprintf(stderr, "Usage: mptcp_sockopt [-6]\n"); fprintf(stderr, " [-t tcp|mptcp] [-r tcp|mptcp]\n"); + fprintf(stderr, " [-i]\n"); exit(r); } @@ -XXX,XX +XXX,XX @@ static void parse_opts(int argc, char **argv) { int c; - while ((c = getopt(argc, argv, "h6t:r:")) != -1) { + while ((c = getopt(argc, argv, "h6t:r:i")) != -1) { switch (c) { case 'h': die_usage(0); @@ -XXX,XX +XXX,XX @@ static void parse_opts(int argc, char **argv) case 'r': proto_rx = protostr_to_num(optarg); break; + case 'i': + inq = true; + break; default: die_usage(1); break; @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int pipefd) close(fd); } +static void do_setsockopt_inq(int fd) +{ + int on = 1; + + if (setsockopt(fd, IPPROTO_TCP, TCP_INQ, &on, sizeof(on))) + die_perror("setsockopt(TCP_INQ)"); +} + static int xaccept(int s) { int fd = accept(s, NULL, 0); @@ -XXX,XX +XXX,XX @@ static int xaccept(int s) if (fd < 0) die_perror("accept"); + if (inq) + do_setsockopt_inq(fd); + return fd; } -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Replace pipe-based synchronization with Unix domain socket pairs (SOCK_STREAM) for bidirectional communication between the server and client processes. Pipes only support one-way flow, which required the code to open and close the pipe file descriptor at awkward points; a SOCK_STREAM socketpair gives a clean two-way channel. These IPC helpers are reused from mptcp_inq.c (which used SOCK_DGRAM); switching to SOCK_STREAM is correct here because the existing protocol exchanges fixed-size, byte-aligned control records rather than discrete datagrams. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w) do_getsockopt_mptcp_full_info(s, fd); } -static void connect_one_server(int fd, int pipefd) +static void connect_one_server(int fd, int unixfd) { char buf[4096], buf2[4096]; size_t len, i, total; @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int pipefd) do_getsockopts(&s, fd, 0, 0); /* un-block server */ - ret = read(pipefd, buf2, 4); + ret = read(unixfd, buf2, 4); assert(ret == 4); - close(pipefd); + close(unixfd); assert(strncmp(buf2, "xmit", 4) == 0); @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int pipefd) close(fd); } -static void process_one_client(int fd, int pipefd) +static void process_one_client(int fd, int unixfd) { ssize_t ret, r, w; struct so_state s; @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int pipefd) memset(&s, 0, sizeof(s)); do_getsockopts(&s, fd, 0, 0); - ret = write(pipefd, "xmit", 4); + ret = write(unixfd, "xmit", 4); assert(ret == 4); ret = read(fd, buf, sizeof(buf)); @@ -XXX,XX +XXX,XX @@ static int xaccept(int s) return fd; } -static int server(int pipefd) +static int server(int unixfd) { int fd = -1, r; @@ -XXX,XX +XXX,XX @@ static int server(int pipefd) break; } - r = write(pipefd, "conn", 4); + r = write(unixfd, "conn", 4); assert(r == 4); alarm(15); r = xaccept(fd); - process_one_client(r, pipefd); + process_one_client(r, unixfd); close(fd); return 0; @@ -XXX,XX +XXX,XX @@ static void test_ip_tos_sockopt(int fd) xerror("expect socklen_t == -1"); } -static int client(int pipefd) +static int client(int unixfd) { int fd = -1; @@ -XXX,XX +XXX,XX @@ static int client(int pipefd) test_ip_tos_sockopt(fd); - connect_one_server(fd, pipefd); + connect_one_server(fd, unixfd); return 0; } @@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[]) { int e1, e2, wstatus; pid_t s, c, ret; - int pipefds[2]; + int unixfds[2]; parse_opts(argc, argv); init_rng(); - e1 = pipe(pipefds); + e1 = socketpair(AF_UNIX, SOCK_STREAM, 0, unixfds); if (e1 < 0) - die_perror("pipe"); + die_perror("socketpair"); s = xfork(); if (s == 0) { - close(pipefds[0]); - ret = server(pipefds[1]); - close(pipefds[1]); + close(unixfds[0]); + ret = server(unixfds[1]); + close(unixfds[1]); return ret; } - close(pipefds[1]); + close(unixfds[1]); /* wait until server bound a socket */ - e1 = read(pipefds[0], &e1, 4); + e1 = read(unixfds[0], &e1, 4); assert(e1 == 4); c = xfork(); if (c == 0) - return client(pipefds[0]); + return client(unixfds[0]); - close(pipefds[0]); + close(unixfds[0]); ret = waitpid(s, &wstatus, 0); if (ret == -1) -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Use the exact remaining length ('len - total') when reading the echoed data from the socket, instead of the full buffer size. This is possible now that the payload length is passed from the client via the unixfd control channel, making the read size explicit and avoiding any potential over-read. This also silences a false positive -Wstringop-overflow warning from GCC, as the compiler can now prove that the destination buffer is large enough for the specified read size. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int unixfd) total = 0; do { - ret = read(fd, buf2 + total, sizeof(buf2) - total); + ret = read(fd, buf2 + total, len - total); if (ret < 0) die_perror("read"); if (ret == 0) { -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Pass 'len' from the client to the server over the unixfd control channel so that the client knows the server's payload size in advance for the FIONREAD loop and TCP_CM_INQ validation in subsequent patches. Also keep the unixfd open for the second write by moving 'close(unixfd)' to the end of connect_one_server(). Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int unixfd) /* un-block server */ ret = read(unixfd, buf2, 4); assert(ret == 4); - close(unixfd); assert(strncmp(buf2, "xmit", 4) == 0); + ret = write(unixfd, &len, sizeof(len)); + assert(ret == (ssize_t)sizeof(len)); + ret = write(fd, buf, len); if (ret < 0) die_perror("write"); @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int unixfd) if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP) assert(s.mptcpi_rcv_delta == (uint64_t)total); close(fd); + close(unixfd); } static void process_one_client(int fd, int unixfd) { ssize_t ret, r, w; struct so_state s; + size_t expect_len; char buf[4096]; memset(&s, 0, sizeof(s)); @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) ret = write(unixfd, "xmit", 4); assert(ret == 4); + ret = read(unixfd, &expect_len, sizeof(expect_len)); + assert(ret == (ssize_t)sizeof(expect_len)); + + if (expect_len > sizeof(buf)) + xerror("expect len %zu exceeds buffer size", expect_len); + ret = read(fd, buf, sizeof(buf)); if (ret < 0) die_perror("read"); -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Implement FIONREAD ioctl checks to validate available data size, ensuring synchronization between write and read operations with non-blocking waits. These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ #include <sys/socket.h> #include <sys/types.h> #include <sys/wait.h> +#include <sys/ioctl.h> #include <netdb.h> #include <netinet/in.h> @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) if (expect_len > sizeof(buf)) xerror("expect len %zu exceeds buffer size", expect_len); + for (;;) { + struct timespec req; + unsigned int queued; + + ret = ioctl(fd, FIONREAD, &queued); + if (ret < 0) + die_perror("FIONREAD"); + if (queued > expect_len) + xerror("FIONREAD returned %u, but only %zu expected\n", + queued, expect_len); + if (queued == expect_len) + break; + + req.tv_sec = 0; + req.tv_nsec = 1000 * 1000ul; + nanosleep(&req, NULL); + } + ret = read(fd, buf, sizeof(buf)); if (ret < 0) die_perror("read"); -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Introduce huge data transfer tests (1-17 MB) to validate MPTCP socket options under high-volume conditions, including proper EOF handling, preparing for TCP_INQ support in subsequent patches. These codes are from mptcp_inq.c, but here they have been placed into two separate functions. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w) do_getsockopt_mptcp_full_info(s, fd); } +static void server_huge_transfer(int fd, int unixfd, size_t len) +{ + char buf[4096], buf2[4]; + size_t i, total; + ssize_t ret; + + for (i = 0; i < sizeof(buf) - 1; i++) { + buf[i] = rand() % 26; + buf[i] += 'A'; + } + + buf[i] = '\n'; + + ret = read(unixfd, buf2, 4); + assert(ret == 4); + assert(strncmp(buf2, "huge", 4) == 0); + + total = rand() % (16 * 1024 * 1024); + total += (1 * 1024 * 1024); + + ret = write(unixfd, &total, sizeof(total)); + assert(ret == (ssize_t)sizeof(total)); + + while (total > 0) { + if (total > sizeof(buf)) + len = sizeof(buf); + else + len = total; + + ret = write(fd, buf, len); + if (ret < 0) + die_perror("write"); + total -= ret; + + /* we don't have to care about buf content, only + * number of total bytes sent + */ + } + + ret = read(unixfd, buf2, 4); + assert(ret == 4); + assert(strncmp(buf2, "shut", 4) == 0); + + ret = write(fd, buf, 1); + assert(ret == 1); + ret = write(unixfd, "closed", 6); + assert(ret == 6); +} + static void connect_one_server(int fd, int unixfd) { char buf[4096], buf2[4096]; @@ -XXX,XX +XXX,XX @@ static void connect_one_server(int fd, int unixfd) if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP) assert(s.mptcpi_rcv_delta == (uint64_t)total); + + if (inq) + server_huge_transfer(fd, unixfd, len); + close(fd); close(unixfd); } +struct inq_msg { + char buf[4096]; + union { + struct cmsghdr cmsg; + char msg_buf[4096]; + } control; + struct iovec iov; + struct msghdr hdr; +}; + +static void inq_msg_init(struct inq_msg *m, size_t iov_len) +{ + memset(m, 0, sizeof(*m)); + m->iov.iov_base = m->buf; + m->iov.iov_len = iov_len; + m->hdr.msg_iov = &m->iov; + m->hdr.msg_iovlen = 1; + m->hdr.msg_control = m->control.msg_buf; + m->hdr.msg_controllen = sizeof(m->control.msg_buf); +} + +static void inq_msg_reset(struct inq_msg *m, size_t iov_len, void *base) +{ + m->iov.iov_base = base; + m->iov.iov_len = iov_len; + m->hdr.msg_controllen = sizeof(m->control.msg_buf); +} + +static size_t client_huge_transfer(int fd, int unixfd) +{ + size_t expect_len; + ssize_t ret, tot; + struct inq_msg m; + char tmp[16]; + + inq_msg_init(&m, 1); + + /* request a large swath of data. */ + ret = write(unixfd, "huge", 4); + assert(ret == 4); + + ret = read(unixfd, &expect_len, sizeof(expect_len)); + assert(ret == (ssize_t)sizeof(expect_len)); + + /* peer should send us a few mb of data */ + if (expect_len <= sizeof(m.buf)) + xerror("expect len %zu too small\n", expect_len); + + tot = 0; + do { + inq_msg_reset(&m, sizeof(m.buf), m.buf); + ret = recvmsg(fd, &m.hdr, 0); + if (ret < 0) + die_perror("recvmsg"); + if (ret == 0) + xerror("unexpected EOF in huge recv"); + + tot += ret; + } while ((size_t)tot < expect_len); + + ret = write(unixfd, "shut", 4); + assert(ret == 4); + + /* wait for hangup. Should have received one more byte of data. */ + ret = read(unixfd, tmp, sizeof(tmp)); + assert(ret == 6); + assert(strncmp(tmp, "closed", 6) == 0); + + sleep(1); + + inq_msg_reset(&m, 1, m.buf); + ret = recvmsg(fd, &m.hdr, 0); + if (ret < 0) + die_perror("recvmsg"); + assert(ret == 1); + + return tot + 1; +} + static void process_one_client(int fd, int unixfd) { ssize_t ret, r, w; @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) die_perror("write"); w = ret; + if (inq) + r += client_huge_transfer(fd, unixfd); + /* wait for hangup */ ret = read(fd, buf, 1); if (ret != 0) -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Add wait_for_ack() helper to synchronize test steps by waiting until the send queue is drained (all data ACKed). Used in server_huge_transfer() to ensure reliable TCP_INQ validation. These codes are from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ #include <netinet/in.h> #include <linux/tcp.h> +#include <linux/sockios.h> #include <linux/compiler.h> static int pf = AF_INET; @@ -XXX,XX +XXX,XX @@ static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w) do_getsockopt_mptcp_full_info(s, fd); } +/* wait up to timeout milliseconds */ +static void wait_for_ack(int fd, int timeout, size_t total) +{ + int i; + + for (i = 0; i < timeout; i++) { + int nsd, ret, queued = -1; + struct timespec req; + + ret = ioctl(fd, TIOCOUTQ, &queued); + if (ret < 0) + die_perror("TIOCOUTQ"); + + ret = ioctl(fd, SIOCOUTQNSD, &nsd); + if (ret < 0) + die_perror("SIOCOUTQNSD"); + + if ((size_t)queued > total) + xerror("TIOCOUTQ %u, but only %zu expected\n", + queued, total); + assert(nsd <= queued); + + if (queued == 0) + return; + + /* wait for peer to ack rx of all data */ + req.tv_sec = 0; + req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */ + nanosleep(&req, NULL); + } + + xerror("still tx data queued after %u ms\n", timeout); +} + static void server_huge_transfer(int fd, int unixfd, size_t len) { char buf[4096], buf2[4]; size_t i, total; + size_t sent; ssize_t ret; for (i = 0; i < sizeof(buf) - 1; i++) { @@ -XXX,XX +XXX,XX @@ static void server_huge_transfer(int fd, int unixfd, size_t len) total = rand() % (16 * 1024 * 1024); total += (1 * 1024 * 1024); + sent = total; ret = write(unixfd, &total, sizeof(total)); assert(ret == (ssize_t)sizeof(total)); + wait_for_ack(fd, 5000, len); + while (total > 0) { if (total > sizeof(buf)) len = sizeof(buf); @@ -XXX,XX +XXX,XX @@ static void server_huge_transfer(int fd, int unixfd, size_t len) assert(ret == 4); assert(strncmp(buf2, "shut", 4) == 0); + wait_for_ack(fd, 5000, sent); + ret = write(fd, buf, 1); assert(ret == 1); ret = write(unixfd, "closed", 6); -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Replace read() with recvmsg() in process_one_client() to enable control message (CMSG) handling. The original read(fd, buf, sizeof(buf)) is split into two recvmsg() calls: the first reads exactly one byte, and the second reads the remaining bytes. This allows the code to access CMSG data for each received segment, facilitating the upcoming TCP_INQ validation. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) ssize_t ret, r, w; struct so_state s; size_t expect_len; - char buf[4096]; + struct inq_msg m; + + inq_msg_init(&m, 1); memset(&s, 0, sizeof(s)); do_getsockopts(&s, fd, 0, 0); @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) ret = read(unixfd, &expect_len, sizeof(expect_len)); assert(ret == (ssize_t)sizeof(expect_len)); - if (expect_len > sizeof(buf)) + if (expect_len > sizeof(m.buf)) xerror("expect len %zu exceeds buffer size", expect_len); for (;;) { @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) nanosleep(&req, NULL); } - ret = read(fd, buf, sizeof(buf)); + /* read one byte */ + ret = recvmsg(fd, &m.hdr, 0); if (ret < 0) - die_perror("read"); + die_perror("recvmsg"); r = ret; + inq_msg_reset(&m, sizeof(m.buf) - 1, m.buf + 1); + ret = recvmsg(fd, &m.hdr, 0); + if (ret < 0) + die_perror("recvmsg"); + r += ret; + assert(s.mptcpi_rcv_delta <= (uint64_t)ret); if (s.tcpi_rcv_delta) assert(s.tcpi_rcv_delta == (uint64_t)ret); - ret = write(fd, buf, r); + ret = write(fd, m.buf, r); if (ret < 0) die_perror("write"); w = ret; @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) r += client_huge_transfer(fd, unixfd); /* wait for hangup */ - ret = read(fd, buf, 1); + inq_msg_reset(&m, 1, m.buf); + ret = recvmsg(fd, &m.hdr, 0); if (ret != 0) xerror("expected EOF, got %lu", ret); r += ret; -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Add get_tcp_inq() helper to extract and verify TCP_CM_INQ values from received control messages. Integrate it into the echo test and huge data transfer to validate in-queue byte counts at key stages, ensuring correct TCP_INQ behavior with MPTCP. Code adapted from mptcp_inq.c. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -XXX,XX +XXX,XX @@ static void inq_msg_reset(struct inq_msg *m, size_t iov_len, void *base) m->hdr.msg_controllen = sizeof(m->control.msg_buf); } +static void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv) +{ + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msgh); cmsg; + cmsg = CMSG_NXTHDR(msgh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_TCP && + cmsg->cmsg_type == TCP_CM_INQ) { + if (cmsg->cmsg_len < CMSG_LEN(sizeof(*inqv))) + xerror("TCP_CM_INQ cmsg_len too small: %u", + cmsg->cmsg_len); + memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv)); + return; + } + } + + xerror("could not find TCP_CM_INQ cmsg type"); +} + static size_t client_huge_transfer(int fd, int unixfd) { + unsigned int tcp_inq; size_t expect_len; ssize_t ret, tot; struct inq_msg m; @@ -XXX,XX +XXX,XX @@ static size_t client_huge_transfer(int fd, int unixfd) xerror("unexpected EOF in huge recv"); tot += ret; + + get_tcp_inq(&m.hdr, &tcp_inq); + + if (tcp_inq > expect_len - tot) + xerror("inq %d, remaining %zu total_len %d\n", + tcp_inq, expect_len - tot, (int)expect_len); + + assert(tcp_inq <= expect_len - tot); } while ((size_t)tot < expect_len); ret = write(unixfd, "shut", 4); @@ -XXX,XX +XXX,XX @@ static size_t client_huge_transfer(int fd, int unixfd) die_perror("recvmsg"); assert(ret == 1); + get_tcp_inq(&m.hdr, &tcp_inq); + + /* tcp_inq should be 1 due to received fin. */ + assert(tcp_inq == 1); + return tot + 1; } static void process_one_client(int fd, int unixfd) { + unsigned int tcp_inq; ssize_t ret, r, w; struct so_state s; size_t expect_len; @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) die_perror("recvmsg"); r = ret; + if (inq) { + if (m.hdr.msg_controllen == 0) + xerror("msg_controllen is 0"); + + get_tcp_inq(&m.hdr, &tcp_inq); + + /* expect cmsg to return expected - 1 */ + assert((size_t)tcp_inq == (expect_len - 1)); + } + inq_msg_reset(&m, sizeof(m.buf) - 1, m.buf + 1); ret = recvmsg(fd, &m.hdr, 0); if (ret < 0) die_perror("recvmsg"); r += ret; + if (inq) { + /* should have gotten exact remainder of all pending data */ + assert(ret == (ssize_t)tcp_inq); + + /* should be 0, all drained */ + get_tcp_inq(&m.hdr, &tcp_inq); + assert(tcp_inq == 0); + } + assert(s.mptcpi_rcv_delta <= (uint64_t)ret); if (s.tcpi_rcv_delta) @@ -XXX,XX +XXX,XX @@ static void process_one_client(int fd, int unixfd) xerror("expected EOF, got %lu", ret); r += ret; + if (inq) { + get_tcp_inq(&m.hdr, &tcp_inq); + assert(tcp_inq == 1); + } + do_getsockopts(&s, fd, r, w); if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP && s.mptcpi_rcv_delta != (uint64_t)r + 1) -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> Update mptcp_sockopt.sh to use mptcp_sockopt binary instead of mptcp_inq. This consolidates TCP_INQ testing into a single test binary while maintaining equivalent test coverage. Delete mptcp_inq binary and references since its functionality has been fully integrated into mptcp_sockopt. This simplifies the test suite structure and reduces maintenance overhead. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- tools/testing/selftests/net/mptcp/.gitignore | 1 - tools/testing/selftests/net/mptcp/Makefile | 1 - tools/testing/selftests/net/mptcp/mptcp_inq.c | 614 ------------------ .../selftests/net/mptcp/mptcp_sockopt.sh | 8 +- 4 files changed, 4 insertions(+), 620 deletions(-) delete mode 100644 tools/testing/selftests/net/mptcp/mptcp_inq.c diff --git a/tools/testing/selftests/net/mptcp/.gitignore b/tools/testing/selftests/net/mptcp/.gitignore index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/.gitignore +++ b/tools/testing/selftests/net/mptcp/.gitignore @@ -XXX,XX +XXX,XX @@ # SPDX-License-Identifier: GPL-2.0-only mptcp_connect mptcp_diag -mptcp_inq mptcp_sockopt pm_nl_ctl *.pcap diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile index XXXXXXX..XXXXXXX 100644 --- a/tools/testing/selftests/net/mptcp/Makefile +++ b/tools/testing/selftests/net/mptcp/Makefile @@ -XXX,XX +XXX,XX @@ TEST_PROGS := \ TEST_GEN_FILES := \ mptcp_connect \ mptcp_diag \ - mptcp_inq \ mptcp_sockopt \ pm_nl_ctl \ # end of TEST_GEN_FILES diff --git a/tools/testing/selftests/net/mptcp/mptcp_inq.c b/tools/testing/selftests/net/mptcp/mptcp_inq.c deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/tools/testing/selftests/net/mptcp/mptcp_inq.c +++ /dev/null @@ -XXX,XX +XXX,XX @@ -// SPDX-License-Identifier: GPL-2.0 - -#define _GNU_SOURCE - -#include <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <string.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <strings.h> -#include <unistd.h> -#include <time.h> - -#include <sys/ioctl.h> -#include <sys/random.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <netdb.h> -#include <netinet/in.h> - -#include <linux/tcp.h> -#include <linux/sockios.h> -#include <linux/compiler.h> - -#ifndef IPPROTO_MPTCP -#define IPPROTO_MPTCP 262 -#endif -#ifndef SOL_MPTCP -#define SOL_MPTCP 284 -#endif - -static int pf = AF_INET; -static int proto_tx = IPPROTO_MPTCP; -static int proto_rx = IPPROTO_MPTCP; - -static void __noreturn die_perror(const char *msg) -{ - perror(msg); - exit(1); -} - -static void die_usage(int r) -{ - fprintf(stderr, "Usage: mptcp_inq [-6] [ -t tcp|mptcp ] [ -r tcp|mptcp]\n"); - exit(r); -} - -static void __noreturn xerror(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputc('\n', stderr); - exit(1); -} - -static const char *getxinfo_strerr(int err) -{ - if (err == EAI_SYSTEM) - return strerror(errno); - - return gai_strerror(err); -} - -static void xgetaddrinfo(const char *node, const char *service, - struct addrinfo *hints, - struct addrinfo **res) -{ - int err; - -again: - err = getaddrinfo(node, service, hints, res); - if (err) { - const char *errstr; - - if (err == EAI_SOCKTYPE) { - hints->ai_protocol = IPPROTO_TCP; - goto again; - } - - errstr = getxinfo_strerr(err); - - fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n", - node ? node : "", service ? service : "", errstr); - exit(1); - } -} - -static int sock_listen_mptcp(const char * const listenaddr, - const char * const port) -{ - int sock = -1; - struct addrinfo hints = { - .ai_protocol = IPPROTO_MPTCP, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_PASSIVE | AI_NUMERICHOST - }; - - hints.ai_family = pf; - - struct addrinfo *a, *addr; - int one = 1; - - xgetaddrinfo(listenaddr, port, &hints, &addr); - hints.ai_family = pf; - - for (a = addr; a; a = a->ai_next) { - sock = socket(a->ai_family, a->ai_socktype, proto_rx); - if (sock < 0) - continue; - - if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, - sizeof(one))) - perror("setsockopt"); - - if (bind(sock, a->ai_addr, a->ai_addrlen) == 0) - break; /* success */ - - perror("bind"); - close(sock); - sock = -1; - } - - freeaddrinfo(addr); - - if (sock < 0) - xerror("could not create listen socket"); - - if (listen(sock, 20)) - die_perror("listen"); - - return sock; -} - -static int sock_connect_mptcp(const char * const remoteaddr, - const char * const port, int proto) -{ - struct addrinfo hints = { - .ai_protocol = IPPROTO_MPTCP, - .ai_socktype = SOCK_STREAM, - }; - struct addrinfo *a, *addr; - int sock = -1; - - hints.ai_family = pf; - - xgetaddrinfo(remoteaddr, port, &hints, &addr); - for (a = addr; a; a = a->ai_next) { - sock = socket(a->ai_family, a->ai_socktype, proto); - if (sock < 0) - continue; - - if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) - break; /* success */ - - die_perror("connect"); - } - - if (sock < 0) - xerror("could not create connect socket"); - - freeaddrinfo(addr); - return sock; -} - -static int protostr_to_num(const char *s) -{ - if (strcasecmp(s, "tcp") == 0) - return IPPROTO_TCP; - if (strcasecmp(s, "mptcp") == 0) - return IPPROTO_MPTCP; - - die_usage(1); - return 0; -} - -static void parse_opts(int argc, char **argv) -{ - int c; - - while ((c = getopt(argc, argv, "h6t:r:")) != -1) { - switch (c) { - case 'h': - die_usage(0); - break; - case '6': - pf = AF_INET6; - break; - case 't': - proto_tx = protostr_to_num(optarg); - break; - case 'r': - proto_rx = protostr_to_num(optarg); - break; - default: - die_usage(1); - break; - } - } -} - -/* wait up to timeout milliseconds */ -static void wait_for_ack(int fd, int timeout, size_t total) -{ - int i; - - for (i = 0; i < timeout; i++) { - int nsd, ret, queued = -1; - struct timespec req; - - ret = ioctl(fd, TIOCOUTQ, &queued); - if (ret < 0) - die_perror("TIOCOUTQ"); - - ret = ioctl(fd, SIOCOUTQNSD, &nsd); - if (ret < 0) - die_perror("SIOCOUTQNSD"); - - if ((size_t)queued > total) - xerror("TIOCOUTQ %u, but only %zu expected\n", queued, total); - assert(nsd <= queued); - - if (queued == 0) - return; - - /* wait for peer to ack rx of all data */ - req.tv_sec = 0; - req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */ - nanosleep(&req, NULL); - } - - xerror("still tx data queued after %u ms\n", timeout); -} - -static void connect_one_server(int fd, int unixfd) -{ - size_t len, i, total, sent; - char buf[4096], buf2[4096]; - ssize_t ret; - - len = rand() % (sizeof(buf) - 1); - - if (len < 128) - len = 128; - - for (i = 0; i < len ; i++) { - buf[i] = rand() % 26; - buf[i] += 'A'; - } - - buf[i] = '\n'; - - /* un-block server */ - ret = read(unixfd, buf2, 4); - assert(ret == 4); - - assert(strncmp(buf2, "xmit", 4) == 0); - - ret = write(unixfd, &len, sizeof(len)); - assert(ret == (ssize_t)sizeof(len)); - - ret = write(fd, buf, len); - if (ret < 0) - die_perror("write"); - - if (ret != (ssize_t)len) - xerror("short write"); - - ret = read(unixfd, buf2, 4); - assert(strncmp(buf2, "huge", 4) == 0); - - total = rand() % (16 * 1024 * 1024); - total += (1 * 1024 * 1024); - sent = total; - - ret = write(unixfd, &total, sizeof(total)); - assert(ret == (ssize_t)sizeof(total)); - - wait_for_ack(fd, 5000, len); - - while (total > 0) { - if (total > sizeof(buf)) - len = sizeof(buf); - else - len = total; - - ret = write(fd, buf, len); - if (ret < 0) - die_perror("write"); - total -= ret; - - /* we don't have to care about buf content, only - * number of total bytes sent - */ - } - - ret = read(unixfd, buf2, 4); - assert(ret == 4); - assert(strncmp(buf2, "shut", 4) == 0); - - wait_for_ack(fd, 5000, sent); - - ret = write(fd, buf, 1); - assert(ret == 1); - close(fd); - ret = write(unixfd, "closed", 6); - assert(ret == 6); - - close(unixfd); -} - -static void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv) -{ - struct cmsghdr *cmsg; - - for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) { - if (cmsg->cmsg_level == IPPROTO_TCP && cmsg->cmsg_type == TCP_CM_INQ) { - memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv)); - return; - } - } - - xerror("could not find TCP_CM_INQ cmsg type"); -} - -static void process_one_client(int fd, int unixfd) -{ - unsigned int tcp_inq; - size_t expect_len; - char msg_buf[4096]; - char buf[4096]; - char tmp[16]; - struct iovec iov = { - .iov_base = buf, - .iov_len = 1, - }; - struct msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = msg_buf, - .msg_controllen = sizeof(msg_buf), - }; - ssize_t ret, tot; - - ret = write(unixfd, "xmit", 4); - assert(ret == 4); - - ret = read(unixfd, &expect_len, sizeof(expect_len)); - assert(ret == (ssize_t)sizeof(expect_len)); - - if (expect_len > sizeof(buf)) - xerror("expect len %zu exceeds buffer size", expect_len); - - for (;;) { - struct timespec req; - unsigned int queued; - - ret = ioctl(fd, FIONREAD, &queued); - if (ret < 0) - die_perror("FIONREAD"); - if (queued > expect_len) - xerror("FIONREAD returned %u, but only %zu expected\n", - queued, expect_len); - if (queued == expect_len) - break; - - req.tv_sec = 0; - req.tv_nsec = 1000 * 1000ul; - nanosleep(&req, NULL); - } - - /* read one byte, expect cmsg to return expected - 1 */ - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - if (msg.msg_controllen == 0) - xerror("msg_controllen is 0"); - - get_tcp_inq(&msg, &tcp_inq); - - assert((size_t)tcp_inq == (expect_len - 1)); - - iov.iov_len = sizeof(buf); - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - /* should have gotten exact remainder of all pending data */ - assert(ret == (ssize_t)tcp_inq); - - /* should be 0, all drained */ - get_tcp_inq(&msg, &tcp_inq); - assert(tcp_inq == 0); - - /* request a large swath of data. */ - ret = write(unixfd, "huge", 4); - assert(ret == 4); - - ret = read(unixfd, &expect_len, sizeof(expect_len)); - assert(ret == (ssize_t)sizeof(expect_len)); - - /* peer should send us a few mb of data */ - if (expect_len <= sizeof(buf)) - xerror("expect len %zu too small\n", expect_len); - - tot = 0; - do { - iov.iov_len = sizeof(buf); - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - tot += ret; - - get_tcp_inq(&msg, &tcp_inq); - - if (tcp_inq > expect_len - tot) - xerror("inq %d, remaining %d total_len %d\n", - tcp_inq, expect_len - tot, (int)expect_len); - - assert(tcp_inq <= expect_len - tot); - } while ((size_t)tot < expect_len); - - ret = write(unixfd, "shut", 4); - assert(ret == 4); - - /* wait for hangup. Should have received one more byte of data. */ - ret = read(unixfd, tmp, sizeof(tmp)); - assert(ret == 6); - assert(strncmp(tmp, "closed", 6) == 0); - - sleep(1); - - iov.iov_len = 1; - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - assert(ret == 1); - - get_tcp_inq(&msg, &tcp_inq); - - /* tcp_inq should be 1 due to received fin. */ - assert(tcp_inq == 1); - - iov.iov_len = 1; - ret = recvmsg(fd, &msg, 0); - if (ret < 0) - die_perror("recvmsg"); - - /* expect EOF */ - assert(ret == 0); - get_tcp_inq(&msg, &tcp_inq); - assert(tcp_inq == 1); - - close(fd); -} - -static int xaccept(int s) -{ - int fd = accept(s, NULL, 0); - - if (fd < 0) - die_perror("accept"); - - return fd; -} - -static int server(int unixfd) -{ - int fd = -1, r, on = 1; - - switch (pf) { - case AF_INET: - fd = sock_listen_mptcp("127.0.0.1", "15432"); - break; - case AF_INET6: - fd = sock_listen_mptcp("::1", "15432"); - break; - default: - xerror("Unknown pf %d\n", pf); - break; - } - - r = write(unixfd, "conn", 4); - assert(r == 4); - - alarm(15); - r = xaccept(fd); - - if (-1 == setsockopt(r, IPPROTO_TCP, TCP_INQ, &on, sizeof(on))) - die_perror("setsockopt"); - - process_one_client(r, unixfd); - - close(fd); - return 0; -} - -static int client(int unixfd) -{ - int fd = -1; - - alarm(15); - - switch (pf) { - case AF_INET: - fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx); - break; - case AF_INET6: - fd = sock_connect_mptcp("::1", "15432", proto_tx); - break; - default: - xerror("Unknown pf %d\n", pf); - } - - connect_one_server(fd, unixfd); - - return 0; -} - -static void init_rng(void) -{ - unsigned int foo; - - if (getrandom(&foo, sizeof(foo), 0) == -1) { - perror("getrandom"); - exit(1); - } - - srand(foo); -} - -static pid_t xfork(void) -{ - pid_t p = fork(); - - if (p < 0) - die_perror("fork"); - else if (p == 0) - init_rng(); - - return p; -} - -static int rcheck(int wstatus, const char *what) -{ - if (WIFEXITED(wstatus)) { - if (WEXITSTATUS(wstatus) == 0) - return 0; - fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus)); - return WEXITSTATUS(wstatus); - } else if (WIFSIGNALED(wstatus)) { - xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus)); - } else if (WIFSTOPPED(wstatus)) { - xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus)); - } - - return 111; -} - -int main(int argc, char *argv[]) -{ - int e1, e2, wstatus; - pid_t s, c, ret; - int unixfds[2]; - - parse_opts(argc, argv); - - e1 = socketpair(AF_UNIX, SOCK_DGRAM, 0, unixfds); - if (e1 < 0) - die_perror("pipe"); - - s = xfork(); - if (s == 0) { - close(unixfds[0]); - ret = server(unixfds[1]); - close(unixfds[1]); - return ret; - } - - close(unixfds[1]); - - /* wait until server bound a socket */ - e1 = read(unixfds[0], &e1, 4); - assert(e1 == 4); - - c = xfork(); - if (c == 0) - return client(unixfds[0]); - - close(unixfds[0]); - - ret = waitpid(s, &wstatus, 0); - if (ret == -1) - die_perror("waitpid"); - e1 = rcheck(wstatus, "server"); - ret = waitpid(c, &wstatus, 0); - if (ret == -1) - die_perror("waitpid"); - e2 = rcheck(wstatus, "client"); - - return e1 ? e1 : e2; -} diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index XXXXXXX..XXXXXXX 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -XXX,XX +XXX,XX @@ run_tests() do_tcpinq_test() { print_title "TCP_INQ cmsg/ioctl $*" - ip netns exec "$ns_sbox" ./mptcp_inq "$@" + ip netns exec "$ns_sbox" ./mptcp_sockopt "$@" local lret=$? if [ $lret -ne 0 ];then ret=$lret @@ -XXX,XX +XXX,XX @@ do_tcpinq_tests() local args for args in "-t tcp" "-r tcp"; do - do_tcpinq_test $args + do_tcpinq_test $args -i lret=$? if [ $lret -ne 0 ] ; then return $lret fi - do_tcpinq_test -6 $args + do_tcpinq_test -6 $args -i lret=$? if [ $lret -ne 0 ] ; then return $lret fi done - do_tcpinq_test -r tcp -t tcp + do_tcpinq_test -r tcp -t tcp -i return $? } -- 2.53.0