[CRIU] [PATCH 1/2] zdtm: check a case when one port is shared between two sockets

Andrei Vagin avagin at openvz.org
Tue Oct 31 08:53:42 MSK 2017


From: Andrei Vagin <avagin at virtuozzo.com>

SO_REUSEPORT allows multiple sockets on the same host to bind to the
same port.

Signed-off-by: Andrei Vagin <avagin at virtuozzo.com>
---
 test/zdtm/lib/tcp.c                        |  21 +++-
 test/zdtm/lib/zdtmtst.h                    |   9 ++
 test/zdtm/static/Makefile                  |   1 +
 test/zdtm/static/socket-tcp-reuseport.c    | 171 +++++++++++++++++++++++++++++
 test/zdtm/static/socket-tcp-reuseport.desc |   1 +
 5 files changed, 201 insertions(+), 2 deletions(-)
 create mode 100644 test/zdtm/static/socket-tcp-reuseport.c
 create mode 100644 test/zdtm/static/socket-tcp-reuseport.desc

diff --git a/test/zdtm/lib/tcp.c b/test/zdtm/lib/tcp.c
index 4fb190266..e753e3c8a 100644
--- a/test/zdtm/lib/tcp.c
+++ b/test/zdtm/lib/tcp.c
@@ -11,6 +11,16 @@ union sockaddr_inet {
 
 int tcp_init_server(int family, int *port)
 {
+	struct zdtm_tcp_opts opts = {
+		.reuseaddr = true,
+		.reuseport = false,
+	};
+
+	return tcp_init_server_with_opts(family, port, &opts);
+}
+
+int tcp_init_server_with_opts(int family, int *port, struct zdtm_tcp_opts *opts)
+{
 	union sockaddr_inet addr;
 	int sock;
 	int yes = 1, ret;
@@ -25,13 +35,20 @@ int tcp_init_server(int family, int *port)
 	} else
 		return -1;
 
-	sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
+	sock = socket(family, SOCK_STREAM | opts->flags, IPPROTO_TCP);
 	if (sock == -1) {
 		pr_perror("socket() failed");
 		return -1;
 	}
 
-	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1 ) {
+	if (opts->reuseport &&
+	    setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(int)) == -1) {
+		pr_perror("");
+		return -1;
+	}
+
+	if (opts->reuseaddr &&
+	    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1 ) {
 		pr_perror("setsockopt() error");
 		return -1;
 	}
diff --git a/test/zdtm/lib/zdtmtst.h b/test/zdtm/lib/zdtmtst.h
index de01cc2be..cd513b9c9 100644
--- a/test/zdtm/lib/zdtmtst.h
+++ b/test/zdtm/lib/zdtmtst.h
@@ -3,6 +3,7 @@
 
 #include <sys/types.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #define INPROGRESS ".inprogress"
 
@@ -141,4 +142,12 @@ extern int tcp_init_server(int family, int *port);
 extern int tcp_accept_server(int sock);
 extern int tcp_init_client(int family, char *servIP, unsigned short servPort);
 
+struct zdtm_tcp_opts {
+	bool reuseaddr;
+	bool reuseport;
+	int flags;
+};
+
+extern int tcp_init_server_with_opts(int family, int *port, struct zdtm_tcp_opts *opts);
+
 #endif /* _VIMITESU_H_ */
diff --git a/test/zdtm/static/Makefile b/test/zdtm/static/Makefile
index 9d0cb24aa..9789d624d 100644
--- a/test/zdtm/static/Makefile
+++ b/test/zdtm/static/Makefile
@@ -66,6 +66,7 @@ TST_NOFILE	:=				\
 		socket-tcp-reseted		\
 		socket-tcp6			\
 		socket-tcp-local		\
+		socket-tcp-reuseport		\
 		socket-tcp-nfconntrack		\
 		socket-tcp6-local		\
 		socket-tcpbuf			\
diff --git a/test/zdtm/static/socket-tcp-reuseport.c b/test/zdtm/static/socket-tcp-reuseport.c
new file mode 100644
index 000000000..4cd1802f9
--- /dev/null
+++ b/test/zdtm/static/socket-tcp-reuseport.c
@@ -0,0 +1,171 @@
+#include "zdtmtst.h"
+
+#ifdef ZDTM_IPV6
+#define ZDTM_FAMILY AF_INET6
+#else
+#define ZDTM_FAMILY AF_INET
+#endif
+
+const char *test_doc = "Check a case when one port is shared between two listening sockets\n";
+const char *test_author = "Andrey Vagin <avagin at parallels.com";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sched.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>  /* for sockaddr_in and inet_ntoa() */
+
+#define BUF_SIZE 4096
+
+int read_data(int fd, unsigned char *buf, int size)
+{
+	int cur = 0;
+	int ret;
+	while (cur != size) {
+		ret = read(fd, buf + cur, size - cur);
+		if (ret <= 0)
+			return -1;
+		cur += ret;
+	}
+
+	return 0;
+}
+
+int write_data(int fd, const unsigned char *buf, int size)
+{
+	int cur = 0;
+	int ret;
+
+	while (cur != size) {
+		ret = write(fd, buf + cur, size - cur);
+		if (ret <= 0)
+			return -1;
+		cur += ret;
+	}
+
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	struct zdtm_tcp_opts opts = { .reuseaddr = false,
+					.reuseport = true,
+					.flags = SOCK_NONBLOCK};
+	unsigned char buf[BUF_SIZE];
+	int port = 8880, port2;
+	int fd, fd_s, fd_s2, clt, i;
+	socklen_t optlen;
+	int no = 0, val;
+	uint32_t crc;
+
+	test_init(argc, argv);
+
+	if ((fd_s = tcp_init_server_with_opts(ZDTM_FAMILY, &port, &opts)) < 0) {
+		pr_err("initializing server failed\n");
+		return 1;
+	}
+
+	port2 = port;
+	if ((fd_s2 = tcp_init_server_with_opts(ZDTM_FAMILY, &port2, &opts)) < 0) {
+		pr_err("initializing server failed\n");
+		return 1;
+	}
+	if (port != port2)
+		return 1;
+
+	if (setsockopt(fd_s, SOL_SOCKET, SO_REUSEPORT, &no, sizeof(int)) == -1 ) {
+		pr_perror("Unable to set SO_REUSEPORT");
+		return -1;
+	}
+
+
+	clt = tcp_init_client(ZDTM_FAMILY, "localhost", port);
+	if (clt < 0)
+		return 1;
+
+	/*
+	 * parent is server of TCP connection
+	 */
+	fd = tcp_accept_server(fd_s);
+	if (fd < 0)
+		fd = tcp_accept_server(fd_s2);
+	if (fd < 0) {
+		pr_err("can't accept client connection\n");
+		return 1;
+	}
+
+	test_daemon();
+	test_waitsig();
+
+
+	optlen = sizeof(val);
+	if (getsockopt(fd_s, SOL_SOCKET, SO_REUSEPORT, &val, &optlen)) {
+		pr_perror("getsockopt");
+		return 1;
+	}
+	if (val == 1) {
+		fail("SO_REUSEPORT is set for %d\n", fd);
+		return 1;
+	}
+	optlen = sizeof(val);
+	if (getsockopt(fd_s2, SOL_SOCKET, SO_REUSEPORT, &val, &optlen)) {
+		pr_perror("getsockopt");
+		return 1;
+	}
+	if (val != 1) {
+		fail("SO_REUSEPORT is not set for %d\n", fd);
+		return 1;
+	}
+
+	for (i = 0; ; i++) {
+		crc = 0;
+		datagen(buf, BUF_SIZE, &crc);
+		if (write_data(fd, buf, BUF_SIZE)) {
+			pr_perror("can't write");
+			return 1;
+		}
+
+		memset(buf, 0, BUF_SIZE);
+		if (read_data(clt, buf, BUF_SIZE)) {
+			pr_perror("read less then have to");
+			return 1;
+		}
+		crc = 0;
+		if (datachk(buf, BUF_SIZE, &crc))
+			return 2;
+
+		close(clt);
+		close(fd);
+
+		if (i == 2)
+			break;
+
+		clt = tcp_init_client(ZDTM_FAMILY, "localhost", port);
+		if (clt < 0)
+			return 1;
+
+		/*
+		 * parent is server of TCP connection
+		 */
+		fd = tcp_accept_server(fd_s2);
+		if (fd < 0) {
+			fd = tcp_accept_server(fd_s);
+			close(fd_s);
+		} else {
+			close(fd_s2);
+		}
+		if (fd < 0) {
+			pr_err("can't accept client connection %d\n", i);
+			return 1;
+		}
+	}
+
+	pass();
+	return 0;
+}
diff --git a/test/zdtm/static/socket-tcp-reuseport.desc b/test/zdtm/static/socket-tcp-reuseport.desc
new file mode 100644
index 000000000..6a406f6ec
--- /dev/null
+++ b/test/zdtm/static/socket-tcp-reuseport.desc
@@ -0,0 +1 @@
+{'flavor': 'h ns uns', 'opts': '--tcp-established', 'flags': 'nouser samens'}
-- 
2.13.6



More information about the CRIU mailing list