: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> 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. 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 (9): selftests: mptcp: sockopt: replace /dev/urandom with getrandom selftests: mptcp: sockopt: add protocol arguments selftests: mptcp: sockopt: add inq argument selftests: mptcp: sockopt: rename pipefd to ipcfd selftests: mptcp: sockopt: set TCP_INQ sockopt selftests: mptcp: sockopt: handle INQ on server side selftests: mptcp: sockopt: handle INQ on client side 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 | 613 ------------------ .../selftests/net/mptcp/mptcp_sockopt.c | 368 +++++++++-- .../selftests/net/mptcp/mptcp_sockopt.sh | 6 +- 5 files changed, 335 insertions(+), 655 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> 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 | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 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 ipcfd) { 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(ipcfd, buf2, 4); assert(ret == 4); - close(pipefd); + close(ipcfd); 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 ipcfd) { ssize_t ret, ret2, ret3; 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(ipcfd, "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 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); 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 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) { - close(pipefds[0]); - ret = server(pipefds[1]); - close(pipefds[1]); + close(ipcfds[0]); + ret = server(ipcfds[1]); + close(ipcfds[1]); return ret; } - 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> Add a helper 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> --- tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 11 +++++++++++ 1 file changed, 11 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 int xaccept(int s) return 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 int server(int ipcfd) { int fd = -1, r; @@ -XXX,XX +XXX,XX @@ static int server(int ipcfd) alarm(15); r = xaccept(fd); + if (inq) + do_setsockopt_inq(r); + process_one_client(r, ipcfd); close(fd); -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Integrate process_one_client() and get_tcp_inq() from mptcp_inq.c into mptcp_sockopt.c, and rename process_one_client() to process_inq_client() to handle TCP_INQ-specific operations on server side. Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- .../selftests/net/mptcp/mptcp_sockopt.c | 155 +++++++++++++++++- 1 file changed, 153 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 @@ #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 ipcfd) 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_inq_client(int fd, int ipcfd) +{ + 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(ipcfd, "xmit", 4); + assert(ret == 4); + + ret = read(ipcfd, &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(ipcfd, "huge", 4); + assert(ret == 4); + + ret = read(ipcfd, &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(ipcfd, "shut", 4); + assert(ret == 4); + + /* wait for hangup. Should have received one more byte of data. */ + ret = read(ipcfd, 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); @@ -XXX,XX +XXX,XX @@ static int server(int ipcfd) alarm(15); r = xaccept(fd); - if (inq) + if (inq) { do_setsockopt_inq(r); - process_one_client(r, ipcfd); + process_inq_client(r, ipcfd); + } else { + process_one_client(r, ipcfd); + } close(fd); return 0; -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> Integrate connect_one_server() and wait_for_ack() from mptcp_inq.c into mptcp_sockopt.c, and rename connect_one_server() to connect_inq_server() to handle TCP_INQ-specific operations on client side. 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 ipcfd) 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_inq_server(int fd, int ipcfd) +{ + 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(ipcfd, buf2, 4); + assert(ret == 4); + + assert(strncmp(buf2, "xmit", 4) == 0); + + ret = write(ipcfd, &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(ipcfd, buf2, 4); + assert(strncmp(buf2, "huge", 4) == 0); + + total = rand() % (16 * 1024 * 1024); + total += (1 * 1024 * 1024); + sent = total; + + ret = write(ipcfd, &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(ipcfd, 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(ipcfd, "closed", 6); + assert(ret == 6); + + close(ipcfd); +} + static void process_one_client(int fd, int ipcfd) { 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_inq_server(fd, ipcfd); + else + connect_one_server(fd, ipcfd); return 0; } -- 2.48.1
From: Geliang Tang <tanggeliang@kylinos.cn> This patch updates 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 | 613 ------------------ 3 files changed, 1 insertion(+), 615 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); - - 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; -} -- 2.48.1