[LTP] [PATCH 1/3] network: rename tcp_fastopen.c to netstress.c

Alexey Kodanev alexey.kodanev@oracle.com
Tue Sep 27 12:06:41 CEST 2016


tcp_fastopen test used in various parts of network tests to
run TCP/UDP network load. The more appropriate name would be
netstress, as tcp_fastopen is very specific.

Move it to from 'tcp_fastopen' test directory (where tcp_fastopen_run.sh
wrapper will be left untouched) to 'netstress' directory.

Signed-off-by: Alexey Kodanev <alexey.kodanev@oracle.com>
---
 testcases/network/netstress/.gitignore        |    1 +
 testcases/network/netstress/Makefile          |   22 +
 testcases/network/netstress/netstress.c       |  840 +++++++++++++++++++++++++
 testcases/network/tcp_fastopen/.gitignore     |    1 -
 testcases/network/tcp_fastopen/Makefile       |    3 +-
 testcases/network/tcp_fastopen/tcp_fastopen.c |  840 -------------------------
 6 files changed, 864 insertions(+), 843 deletions(-)
 create mode 100644 testcases/network/netstress/.gitignore
 create mode 100644 testcases/network/netstress/Makefile
 create mode 100644 testcases/network/netstress/netstress.c
 delete mode 100644 testcases/network/tcp_fastopen/.gitignore
 delete mode 100644 testcases/network/tcp_fastopen/tcp_fastopen.c

diff --git a/testcases/network/netstress/.gitignore b/testcases/network/netstress/.gitignore
new file mode 100644
index 0000000..587460f
--- /dev/null
+++ b/testcases/network/netstress/.gitignore
@@ -0,0 +1 @@
+/netstress
diff --git a/testcases/network/netstress/Makefile b/testcases/network/netstress/Makefile
new file mode 100644
index 0000000..0ada526
--- /dev/null
+++ b/testcases/network/netstress/Makefile
@@ -0,0 +1,22 @@
+# Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+top_srcdir		?= ../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+LDLIBS			+= -lpthread -lrt
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/network/netstress/netstress.c b/testcases/network/netstress/netstress.c
new file mode 100644
index 0000000..4d7ffcc
--- /dev/null
+++ b/testcases/network/netstress/netstress.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 2014-2016 Oracle and/or its affiliates. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
+ *
+ */
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "test.h"
+#include "lapi/posix_clocks.h"
+#include "safe_macros.h"
+
+char *TCID = "netstress";
+
+static const int max_msg_len = 1500;
+
+/* TCP server requiers */
+#ifndef TCP_FASTOPEN
+#define TCP_FASTOPEN	23
+#endif
+
+#ifndef SO_BUSY_POLL
+#define SO_BUSY_POLL	46
+#endif
+
+/* TCP client requiers */
+#ifndef MSG_FASTOPEN
+#define MSG_FASTOPEN	0x20000000 /* Send data in TCP SYN */
+#endif
+
+enum {
+	SERVER_HOST = 0,
+	CLIENT_HOST,
+};
+static int net_mode;
+
+enum {
+	TFO_ENABLED = 0,
+	TFO_DISABLED,
+};
+static int tfo_support;
+static int fastopen_api;
+
+static const char tfo_cfg[]		= "/proc/sys/net/ipv4/tcp_fastopen";
+static const char tcp_tw_reuse[]	= "/proc/sys/net/ipv4/tcp_tw_reuse";
+static int tw_reuse_changed;
+static int tfo_cfg_value;
+static int tfo_bit_num;
+static int tfo_cfg_changed;
+static int tfo_queue_size	= 100;
+static int max_queue_len	= 100;
+static const int client_byte	= 0x43;
+static const int server_byte	= 0x53;
+static const int start_byte	= 0x24;
+static const int start_fin_byte	= 0x25;
+static const int end_byte	= 0x0a;
+static int client_msg_size	= 32;
+static int server_msg_size	= 128;
+static char *client_msg;
+static char *server_msg;
+
+/*
+ * The number of requests from client after
+ *  which server has to close the connection.
+ */
+static int server_max_requests	= 3;
+static int client_max_requests	= 10;
+static int clients_num		= 2;
+static char *tcp_port		= "61000";
+static char *server_addr	= "localhost";
+static int busy_poll		= -1;
+static int use_udp;
+/* server socket */
+static int sfd;
+
+/* how long a client must wait for the server's reply, microsec */
+static long wait_timeout = 60000000;
+
+/* in the end test will save time result in this file */
+static char *rpath		= "./tfo_result";
+
+static int force_run;
+static int verbose;
+
+static char *narg, *Narg, *qarg, *rarg, *Rarg, *aarg, *Targ, *barg;
+
+static const option_t options[] = {
+	/* server params */
+	{"R:", NULL, &Rarg},
+	{"q:", NULL, &qarg},
+
+	/* client params */
+	{"H:", NULL, &server_addr},
+	{"a:", NULL, &aarg},
+	{"n:", NULL, &narg},
+	{"N:", NULL, &Narg},
+	{"T:", NULL, &Targ},
+	{"r:", NULL, &rarg},
+	{"d:", NULL, &rpath},
+
+	/* common */
+	{"g:", NULL, &tcp_port},
+	{"b:", NULL, &barg},
+	{"U", &use_udp, NULL},
+	{"F", &force_run, NULL},
+	{"l", &net_mode, NULL},
+	{"o", &fastopen_api, NULL},
+	{"O", &tfo_support, NULL},
+	{"v", &verbose, NULL},
+	{NULL, NULL, NULL}
+};
+
+static void help(void)
+{
+	printf("\n  -F      Force to run\n");
+	printf("  -v      Verbose\n");
+	printf("  -o      Use old TCP API, default is new TCP API\n");
+	printf("  -O      TFO support is off, default is on\n");
+	printf("  -l      Become TCP Client, default is TCP server\n");
+	printf("  -g x    x - server port, default is %s\n", tcp_port);
+	printf("  -b x    x - low latency busy poll timeout\n");
+	printf("  -U      use UDP\n");
+
+	printf("\n          Client:\n");
+	printf("  -H x    x - server name or ip address, default is '%s'\n",
+		server_addr);
+	printf("  -a x    x - num of clients running in parallel\n");
+	printf("  -r x    x - num of client requests\n");
+	printf("  -n x    Client message size, max msg size is '%d'\n",
+		max_msg_len);
+	printf("  -N x    Server message size, max msg size is '%d'\n",
+		max_msg_len);
+	printf("  -T x    Reply timeout, default is '%ld' (microsec)\n",
+		wait_timeout);
+	printf("  -d x    x is a path to the file where results are saved\n");
+
+	printf("\n          Server:\n");
+	printf("  -R x    x - num of requests, after which conn. closed\n");
+	printf("  -q x    x - server's limit on the queue of TFO requests\n");
+}
+
+/* common structure for TCP/UDP server and TCP/UDP client */
+struct net_func {
+	void (*init)(void);
+	void (*run)(void);
+	void (*cleanup)(void);
+};
+static struct net_func net;
+
+#define MAX_THREADS	10000
+static pthread_attr_t attr;
+static pthread_t *thread_ids;
+
+static struct addrinfo *remote_addrinfo;
+static struct addrinfo *local_addrinfo;
+static struct sockaddr_storage remote_addr;
+static socklen_t remote_addr_len;
+
+static const struct linger clo = { 1, 3 };
+
+static void do_cleanup(void)
+{
+	free(client_msg);
+	free(server_msg);
+
+	if (net.cleanup)
+		net.cleanup();
+
+	if (tfo_cfg_changed) {
+		SAFE_FILE_SCANF(NULL, tfo_cfg, "%d", &tfo_cfg_value);
+		tfo_cfg_value &= ~tfo_bit_num;
+		tfo_cfg_value |= !tfo_support << (tfo_bit_num - 1);
+		tst_resm(TINFO, "unset '%s' back to '%d'",
+			tfo_cfg, tfo_cfg_value);
+		SAFE_FILE_PRINTF(NULL, tfo_cfg, "%d", tfo_cfg_value);
+	}
+
+	if (tw_reuse_changed) {
+		SAFE_FILE_PRINTF(NULL, tcp_tw_reuse, "0");
+		tst_resm(TINFO, "unset '%s' back to '0'", tcp_tw_reuse);
+	}
+}
+TST_DECLARE_ONCE_FN(cleanup, do_cleanup)
+
+static int sock_recv_poll(int fd, char *buf, int buf_size, int offset)
+{
+	struct pollfd pfd;
+	pfd.fd = fd;
+	pfd.events = POLLIN;
+	int len = -1;
+
+	while (1) {
+		errno = 0;
+		int ret = poll(&pfd, 1, wait_timeout / 1000);
+		if (ret == -1) {
+			if (errno == EINTR)
+				continue;
+			break;
+		}
+
+		if (ret == 0) {
+			errno = ETIME;
+			break;
+		}
+
+		if (ret != 1 || !(pfd.revents & POLLIN))
+			break;
+
+		errno = 0;
+		len = recvfrom(fd, buf + offset, buf_size - offset,
+			       MSG_DONTWAIT, (struct sockaddr *)&remote_addr,
+			       &remote_addr_len);
+
+		if (len == -1 && errno == EINTR)
+			continue;
+		else
+			break;
+	}
+
+	return len;
+}
+
+static int client_recv(int *fd, char *buf)
+{
+	int len, offset = 0;
+
+	while (1) {
+		errno = 0;
+		len = sock_recv_poll(*fd, buf, server_msg_size, offset);
+
+		/* socket closed or msg is not valid */
+		if (len < 1 || (offset + len) > server_msg_size ||
+		   (buf[0] != start_byte && buf[0] != start_fin_byte)) {
+			if (!errno)
+				errno = ENOMSG;
+			break;
+		}
+		offset += len;
+		if (buf[offset - 1] != end_byte)
+			continue;
+
+		if (verbose) {
+			tst_resm_hexd(TINFO, buf, offset,
+				"msg recv from sock %d:", *fd);
+		}
+
+		/* recv last msg, close socket */
+		if (buf[0] == start_fin_byte)
+			break;
+		return 0;
+	}
+
+	shutdown(*fd, SHUT_WR);
+	SAFE_CLOSE(cleanup, *fd);
+	*fd = -1;
+	return (errno) ? -1 : 0;
+}
+
+static int client_connect_send(const char *msg, int size)
+{
+	int cfd = socket(remote_addrinfo->ai_family,
+			 remote_addrinfo->ai_socktype, 0);
+	const int flag = 1;
+
+	if (cfd == -1)
+		return cfd;
+
+	setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+	if (busy_poll >= 0) {
+		setsockopt(cfd, SOL_SOCKET, SO_BUSY_POLL,
+			   &busy_poll, sizeof(busy_poll));
+	}
+
+	if (fastopen_api == TFO_ENABLED) {
+		/* Replaces connect() + send()/write() */
+		if (sendto(cfd, msg, size, MSG_FASTOPEN | MSG_NOSIGNAL,
+		    remote_addrinfo->ai_addr,
+		    remote_addrinfo->ai_addrlen) != size) {
+			SAFE_CLOSE(cleanup, cfd);
+			return -1;
+		}
+	} else {
+		/* old TCP API */
+		if (connect(cfd, remote_addrinfo->ai_addr,
+		    remote_addrinfo->ai_addrlen)) {
+			SAFE_CLOSE(cleanup, cfd);
+			return -1;
+		}
+
+		if (send(cfd, msg, size, MSG_NOSIGNAL) != client_msg_size) {
+			SAFE_CLOSE(cleanup, cfd);
+			return -1;
+		}
+	}
+
+	return cfd;
+}
+
+void *client_fn(LTP_ATTRIBUTE_UNUSED void *arg)
+{
+	char buf[server_msg_size];
+	int cfd, i;
+	intptr_t err = 0;
+
+	/* connect & send requests */
+	cfd = client_connect_send(client_msg, client_msg_size);
+	if (cfd == -1) {
+		err = errno;
+		goto out;
+	}
+
+	if (client_recv(&cfd, buf)) {
+		err = errno;
+		goto out;
+	}
+
+	for (i = 1; i < client_max_requests; ++i) {
+		if (use_udp)
+			goto send;
+
+		/* check connection, it can be closed */
+		int ret = 0;
+		if (cfd != -1)
+			ret = recv(cfd, buf, 1, MSG_DONTWAIT);
+
+		if (ret == 0) {
+			/* try to reconnect and send */
+			if (cfd != -1)
+				SAFE_CLOSE(cleanup, cfd);
+
+			cfd = client_connect_send(client_msg, client_msg_size);
+			if (cfd == -1) {
+				err = errno;
+				goto out;
+			}
+
+			if (client_recv(&cfd, buf)) {
+				err = errno;
+				break;
+			}
+
+			continue;
+
+		} else if (ret > 0) {
+			err = EMSGSIZE;
+			break;
+		}
+
+send:
+		if (verbose) {
+			tst_resm_hexd(TINFO, client_msg, client_msg_size,
+				"try to send msg[%d]", i);
+		}
+
+		if (send(cfd, client_msg, client_msg_size,
+			MSG_NOSIGNAL) != client_msg_size) {
+			err = ECOMM;
+			break;
+		}
+		if (client_recv(&cfd, buf)) {
+			err = errno;
+			break;
+		}
+	}
+
+	if (cfd != -1)
+		SAFE_CLOSE(cleanup, cfd);
+
+out:
+	return (void *) err;
+}
+
+union net_size_field {
+	char bytes[2];
+	uint16_t value;
+};
+
+static void make_client_request(void)
+{
+	client_msg[0] = start_byte;
+
+	/* set size for reply */
+	union net_size_field net_size;
+	net_size.value = htons(server_msg_size);
+	client_msg[1] = net_size.bytes[0];
+	client_msg[2] = net_size.bytes[1];
+
+	client_msg[client_msg_size - 1] = end_byte;
+}
+
+static int parse_client_request(const char *msg)
+{
+	union net_size_field net_size;
+	net_size.bytes[0] = msg[1];
+	net_size.bytes[1] = msg[2];
+	int size = ntohs(net_size.value);
+	if (size < 2 || size > max_msg_len)
+		return -1;
+
+	return size;
+}
+
+static struct timespec tv_client_start;
+static struct timespec tv_client_end;
+
+static void client_init(void)
+{
+	if (clients_num >= MAX_THREADS) {
+		tst_brkm(TBROK, cleanup,
+			"Unexpected num of clients '%d'",
+			clients_num);
+	}
+
+	thread_ids = SAFE_MALLOC(NULL, sizeof(pthread_t) * clients_num);
+
+	client_msg = SAFE_MALLOC(NULL, client_msg_size);
+	memset(client_msg, client_byte, client_msg_size);
+
+	make_client_request();
+
+	struct addrinfo hints;
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = (use_udp) ? SOCK_DGRAM : SOCK_STREAM;
+	hints.ai_flags = 0;
+	hints.ai_protocol = 0;
+
+	int err = getaddrinfo(server_addr, tcp_port, &hints, &remote_addrinfo);
+	if (err) {
+		tst_brkm(TBROK, cleanup, "getaddrinfo of '%s' failed, %s",
+			server_addr, gai_strerror(err));
+	}
+
+	tst_resm(TINFO, "Running the test over IPv%s",
+		(remote_addrinfo->ai_family == AF_INET6) ? "6" : "4");
+
+	clock_gettime(CLOCK_MONOTONIC_RAW, &tv_client_start);
+	int i;
+	for (i = 0; i < clients_num; ++i) {
+		if (pthread_create(&thread_ids[i], 0, client_fn, NULL) != 0) {
+			tst_brkm(TBROK | TERRNO, cleanup,
+				"pthread_create failed at %s:%d",
+				__FILE__, __LINE__);
+		}
+	}
+}
+
+static void client_run(void)
+{
+	void *res = NULL;
+	long clnt_time = 0;
+	int i;
+	for (i = 0; i < clients_num; ++i) {
+		pthread_join(thread_ids[i], &res);
+		if (res) {
+			tst_brkm(TBROK, cleanup, "client[%d] failed: %s",
+				i, strerror((intptr_t)res));
+		}
+	}
+
+	clock_gettime(CLOCK_MONOTONIC_RAW, &tv_client_end);
+	clnt_time = (tv_client_end.tv_sec - tv_client_start.tv_sec) * 1000 +
+		(tv_client_end.tv_nsec - tv_client_start.tv_nsec) / 1000000;
+
+	tst_resm(TINFO, "total time '%ld' ms", clnt_time);
+
+	/* ask server to terminate */
+	client_msg[0] = start_fin_byte;
+	int cfd = client_connect_send(client_msg, client_msg_size);
+	if (cfd != -1) {
+		shutdown(cfd, SHUT_WR);
+		SAFE_CLOSE(NULL, cfd);
+	}
+	/* the script tcp_fastopen_run.sh will remove it */
+	SAFE_FILE_PRINTF(cleanup, rpath, "%ld", clnt_time);
+}
+
+static void client_cleanup(void)
+{
+	free(thread_ids);
+
+	if (remote_addrinfo)
+		freeaddrinfo(remote_addrinfo);
+}
+
+static char *make_server_reply(int size)
+{
+	char *send_msg = SAFE_MALLOC(NULL, size);
+	memset(send_msg, server_byte, size - 1);
+	send_msg[0] = start_byte;
+	send_msg[size - 1] = end_byte;
+	return send_msg;
+}
+
+void *server_fn(void *cfd)
+{
+	int client_fd = (intptr_t) cfd;
+	int num_requests = 0, offset = 0;
+
+	/* Reply will be constructed from first client request */
+	char *send_msg = NULL;
+	int send_msg_size = 0;
+
+	char recv_msg[max_msg_len];
+
+	setsockopt(client_fd, SOL_SOCKET, SO_LINGER, &clo, sizeof(clo));
+	if (busy_poll >= 0) {
+		setsockopt(client_fd, SOL_SOCKET, SO_BUSY_POLL,
+			   &busy_poll, sizeof(busy_poll));
+	}
+
+	ssize_t recv_len;
+
+	while (1) {
+		recv_len = sock_recv_poll(client_fd, recv_msg,
+			max_msg_len, offset);
+
+		if (recv_len == 0)
+			break;
+
+		if (recv_len < 0 || (offset + recv_len) > max_msg_len ||
+		   (recv_msg[0] != start_byte &&
+		    recv_msg[0] != start_fin_byte)) {
+			tst_resm(TFAIL, "recv failed, sock '%d'", client_fd);
+			goto out;
+		}
+
+		offset += recv_len;
+
+		if (recv_msg[offset - 1] != end_byte) {
+			/* msg is not complete, continue recv */
+			continue;
+		}
+
+		/* client asks to terminate */
+		if (recv_msg[0] == start_fin_byte)
+			goto out;
+
+		if (verbose) {
+			tst_resm_hexd(TINFO, recv_msg, offset,
+				"msg recv from sock %d:", client_fd);
+		}
+
+		/* if we send reply for the first time, construct it here */
+		if (!send_msg) {
+			send_msg_size = parse_client_request(recv_msg);
+			if (send_msg_size < 0) {
+				tst_resm(TFAIL, "wrong msg size '%d'",
+					send_msg_size);
+				goto out;
+			}
+			send_msg = make_server_reply(send_msg_size);
+		}
+
+		offset = 0;
+
+		/*
+		 * It will tell client that server is going
+		 * to close this connection.
+		 */
+		if (!use_udp && ++num_requests >= server_max_requests)
+			send_msg[0] = start_fin_byte;
+
+		if (sendto(client_fd, send_msg, send_msg_size,
+		    MSG_NOSIGNAL, (struct sockaddr *)&remote_addr,
+		    remote_addr_len) < 0) {
+			tst_resm(TFAIL | TERRNO, "sendto failed");
+			goto out;
+		}
+
+		if (!use_udp && num_requests >= server_max_requests) {
+			/* max reqs, close socket */
+			shutdown(client_fd, SHUT_WR);
+			break;
+		}
+	}
+
+	free(send_msg);
+	SAFE_CLOSE(cleanup, client_fd);
+	return NULL;
+
+out:
+	free(send_msg);
+	SAFE_CLOSE(cleanup, client_fd);
+	cleanup();
+	tst_exit();
+}
+
+static pthread_t server_thread_add(intptr_t client_fd)
+{
+	pthread_t id;
+	if (pthread_create(&id, &attr, server_fn, (void *) client_fd)) {
+		tst_brkm(TBROK | TERRNO, cleanup,
+			"pthread_create failed at %s:%d", __FILE__, __LINE__);
+	}
+	return id;
+}
+
+static void server_init(void)
+{
+	struct addrinfo hints;
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_INET6;
+	hints.ai_socktype = (use_udp) ? SOCK_DGRAM : SOCK_STREAM;
+	hints.ai_flags = AI_PASSIVE;
+
+	if (getaddrinfo(NULL, tcp_port, &hints, &local_addrinfo) != 0)
+		tst_brkm(TBROK | TERRNO, cleanup, "getaddrinfo failed");
+
+	if (!local_addrinfo)
+		tst_brkm(TBROK, cleanup, "failed to get the address");
+
+	/* IPv6 socket is also able to access IPv4 protocol stack */
+	sfd = SAFE_SOCKET(cleanup, AF_INET6, local_addrinfo->ai_socktype, 0);
+
+	tst_resm(TINFO, "assigning a name to the server socket...");
+	SAFE_BIND(cleanup, sfd, local_addrinfo->ai_addr,
+		local_addrinfo->ai_addrlen);
+
+	freeaddrinfo(local_addrinfo);
+
+	if (use_udp)
+		return;
+
+	const int flag = 1;
+	setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+
+	if (fastopen_api == TFO_ENABLED) {
+		if (setsockopt(sfd, IPPROTO_TCP, TCP_FASTOPEN, &tfo_queue_size,
+			sizeof(tfo_queue_size)) == -1)
+			tst_brkm(TBROK, cleanup, "Can't set TFO sock. options");
+	}
+
+	SAFE_LISTEN(cleanup, sfd, max_queue_len);
+	tst_resm(TINFO, "Listen on the socket '%d', port '%s'", sfd, tcp_port);
+}
+
+static void server_cleanup(void)
+{
+	SAFE_CLOSE(NULL, sfd);
+}
+
+static void server_run_udp(void)
+{
+	pthread_t p_id = server_thread_add(sfd);
+
+	if (!pthread_join(p_id, NULL))
+		tst_brkm(TBROK | TERRNO, cleanup, "pthread_join() failed");
+}
+
+static void server_run(void)
+{
+	/* IPv4 source address will be mapped to IPv6 address */
+	struct sockaddr_in6 addr6;
+	socklen_t addr_size = sizeof(addr6);
+
+	pthread_attr_init(&attr);
+
+	/*
+	 * detaching threads allow to reclaim thread's resources
+	 * once a thread finishes its work.
+	 */
+	if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
+		tst_brkm(TBROK | TERRNO, cleanup, "setdetachstate failed");
+
+	while (1) {
+		int client_fd = accept(sfd, (struct sockaddr *)&addr6,
+			&addr_size);
+
+		if (client_fd == -1)
+			tst_brkm(TBROK, cleanup, "Can't create client socket");
+
+		if (verbose) {
+			char addr_buf[INET6_ADDRSTRLEN];
+			tst_resm(TINFO, "conn: port '%d', addr '%s'",
+				addr6.sin6_port, inet_ntop(AF_INET6,
+				&addr6.sin6_addr, addr_buf, INET6_ADDRSTRLEN));
+		}
+
+		server_thread_add(client_fd);
+	}
+}
+
+static void check_opt(const char *name, char *arg, int *val, int lim)
+{
+	if (arg) {
+		if (sscanf(arg, "%i", val) != 1)
+			tst_brkm(TBROK, NULL, "-%s option arg is not a number",
+				 name);
+		if (*val < lim)
+			tst_brkm(TBROK, NULL, "-%s option arg is less than %d",
+				name, lim);
+	}
+}
+
+static void check_opt_l(const char *name, char *arg, long *val, long lim)
+{
+	if (arg) {
+		if (sscanf(arg, "%ld", val) != 1)
+			tst_brkm(TBROK, NULL, "-%s option arg is not a number",
+				 name);
+		if (*val < lim)
+			tst_brkm(TBROK, NULL, "-%s option arg is less than %ld",
+				name, lim);
+	}
+}
+
+static void setup(int argc, char *argv[])
+{
+	tst_parse_opts(argc, argv, options, help);
+
+	/* if client_num is not set, use num of processors */
+	clients_num = sysconf(_SC_NPROCESSORS_ONLN);
+
+	check_opt("a", aarg, &clients_num, 1);
+	check_opt("r", rarg, &client_max_requests, 1);
+	check_opt("R", Rarg, &server_max_requests, 1);
+	check_opt("n", narg, &client_msg_size, 1);
+	check_opt("N", Narg, &server_msg_size, 1);
+	check_opt("q", qarg, &tfo_queue_size, 1);
+	check_opt_l("T", Targ, &wait_timeout, 0L);
+	check_opt("b", barg, &busy_poll, 0);
+
+	if (!force_run)
+		tst_require_root();
+
+	if (!force_run && tst_kvercmp(3, 7, 0) < 0) {
+		tst_brkm(TCONF, NULL,
+			"Test must be run with kernel 3.7 or newer");
+	}
+
+	if (!force_run && busy_poll >= 0 && tst_kvercmp(3, 11, 0) < 0) {
+		tst_brkm(TCONF, NULL,
+			"Test must be run with kernel 3.11 or newer");
+	}
+
+	/* check tcp fast open knob */
+	if (!force_run && access(tfo_cfg, F_OK) == -1)
+		tst_brkm(TCONF, NULL, "Failed to find '%s'", tfo_cfg);
+
+	if (!force_run) {
+		SAFE_FILE_SCANF(NULL, tfo_cfg, "%d", &tfo_cfg_value);
+		tst_resm(TINFO, "'%s' is %d", tfo_cfg, tfo_cfg_value);
+	}
+
+	tst_sig(FORK, DEF_HANDLER, cleanup);
+
+	switch (net_mode) {
+	case SERVER_HOST:
+		tst_resm(TINFO, "max requests '%d'",
+			server_max_requests);
+		net.init	= server_init;
+		net.run		= (use_udp) ? server_run_udp : server_run;
+		net.cleanup	= (use_udp) ? NULL : server_cleanup;
+		tfo_bit_num = 2;
+	break;
+	case CLIENT_HOST:
+		tst_resm(TINFO, "connection: addr '%s', port '%s'",
+			server_addr, tcp_port);
+		tst_resm(TINFO, "client max req: %d", client_max_requests);
+		tst_resm(TINFO, "clients num: %d", clients_num);
+		tst_resm(TINFO, "client msg size: %d", client_msg_size);
+		tst_resm(TINFO, "server msg size: %d", server_msg_size);
+
+		net.init	= client_init;
+		net.run		= client_run;
+		net.cleanup	= client_cleanup;
+		tfo_bit_num = 1;
+	break;
+	}
+
+	remote_addr_len = sizeof(struct sockaddr_storage);
+
+	if (use_udp) {
+		tst_resm(TINFO, "using UDP");
+		fastopen_api = TFO_DISABLED;
+	} else {
+		tst_resm(TINFO, "TCP %s is using %s TCP API.",
+			(net_mode == SERVER_HOST) ? "server" : "client",
+			(fastopen_api == TFO_ENABLED) ? "Fastopen" : "old");
+
+		tfo_support = TFO_ENABLED == tfo_support;
+		if (((tfo_cfg_value & tfo_bit_num) == tfo_bit_num)
+		      != tfo_support) {
+			int value = (tfo_cfg_value & ~tfo_bit_num)
+				| (tfo_support << (tfo_bit_num - 1));
+			tst_resm(TINFO, "set '%s' to '%d'", tfo_cfg, value);
+			SAFE_FILE_PRINTF(cleanup, tfo_cfg, "%d", value);
+			tfo_cfg_changed = 1;
+		}
+
+		int reuse_value = 0;
+		SAFE_FILE_SCANF(cleanup, tcp_tw_reuse, "%d", &reuse_value);
+		if (!reuse_value) {
+			SAFE_FILE_PRINTF(cleanup, tcp_tw_reuse, "1");
+			tw_reuse_changed = 1;
+			tst_resm(TINFO, "set '%s' to '1'", tcp_tw_reuse);
+		}
+
+		tst_resm(TINFO, "TFO support %s",
+			(tfo_support) ? "enabled" : "disabled");
+	}
+
+	net.init();
+}
+
+int main(int argc, char *argv[])
+{
+	setup(argc, argv);
+
+	net.run();
+
+	cleanup();
+
+	tst_exit();
+}
diff --git a/testcases/network/tcp_fastopen/.gitignore b/testcases/network/tcp_fastopen/.gitignore
deleted file mode 100644
index f321cf0..0000000
--- a/testcases/network/tcp_fastopen/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/tcp_fastopen
diff --git a/testcases/network/tcp_fastopen/Makefile b/testcases/network/tcp_fastopen/Makefile
index aa60bb0..68c8c44 100644
--- a/testcases/network/tcp_fastopen/Makefile
+++ b/testcases/network/tcp_fastopen/Makefile
@@ -1,4 +1,4 @@
-# Copyright (c) 2014 Oracle and/or its affiliates. All Rights Reserved.
+# Copyright (c) 2014-2016 Oracle and/or its affiliates. All Rights Reserved.
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License as
@@ -19,6 +19,5 @@ top_srcdir		?= ../../..
 include $(top_srcdir)/include/mk/testcases.mk
 
 INSTALL_TARGETS		:= tcp_fastopen_run.sh
-LDLIBS			+= -lpthread -lrt
 
 include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/network/tcp_fastopen/tcp_fastopen.c b/testcases/network/tcp_fastopen/tcp_fastopen.c
deleted file mode 100644
index c219e71..0000000
--- a/testcases/network/tcp_fastopen/tcp_fastopen.c
+++ /dev/null
@@ -1,840 +0,0 @@
-/*
- * Copyright (c) 2014-2016 Oracle and/or its affiliates. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
- *
- */
-
-#include <pthread.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <poll.h>
-#include <time.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "test.h"
-#include "lapi/posix_clocks.h"
-#include "safe_macros.h"
-
-char *TCID = "tcp_fastopen";
-
-static const int max_msg_len = 1500;
-
-/* TCP server requiers */
-#ifndef TCP_FASTOPEN
-#define TCP_FASTOPEN	23
-#endif
-
-#ifndef SO_BUSY_POLL
-#define SO_BUSY_POLL	46
-#endif
-
-/* TCP client requiers */
-#ifndef MSG_FASTOPEN
-#define MSG_FASTOPEN	0x20000000 /* Send data in TCP SYN */
-#endif
-
-enum {
-	SERVER_HOST = 0,
-	CLIENT_HOST,
-};
-static int net_mode;
-
-enum {
-	TFO_ENABLED = 0,
-	TFO_DISABLED,
-};
-static int tfo_support;
-static int fastopen_api;
-
-static const char tfo_cfg[]		= "/proc/sys/net/ipv4/tcp_fastopen";
-static const char tcp_tw_reuse[]	= "/proc/sys/net/ipv4/tcp_tw_reuse";
-static int tw_reuse_changed;
-static int tfo_cfg_value;
-static int tfo_bit_num;
-static int tfo_cfg_changed;
-static int tfo_queue_size	= 100;
-static int max_queue_len	= 100;
-static const int client_byte	= 0x43;
-static const int server_byte	= 0x53;
-static const int start_byte	= 0x24;
-static const int start_fin_byte	= 0x25;
-static const int end_byte	= 0x0a;
-static int client_msg_size	= 32;
-static int server_msg_size	= 128;
-static char *client_msg;
-static char *server_msg;
-
-/*
- * The number of requests from client after
- *  which server has to close the connection.
- */
-static int server_max_requests	= 3;
-static int client_max_requests	= 10;
-static int clients_num		= 2;
-static char *tcp_port		= "61000";
-static char *server_addr	= "localhost";
-static int busy_poll		= -1;
-static int use_udp;
-/* server socket */
-static int sfd;
-
-/* how long a client must wait for the server's reply, microsec */
-static long wait_timeout = 60000000;
-
-/* in the end test will save time result in this file */
-static char *rpath		= "./tfo_result";
-
-static int force_run;
-static int verbose;
-
-static char *narg, *Narg, *qarg, *rarg, *Rarg, *aarg, *Targ, *barg;
-
-static const option_t options[] = {
-	/* server params */
-	{"R:", NULL, &Rarg},
-	{"q:", NULL, &qarg},
-
-	/* client params */
-	{"H:", NULL, &server_addr},
-	{"a:", NULL, &aarg},
-	{"n:", NULL, &narg},
-	{"N:", NULL, &Narg},
-	{"T:", NULL, &Targ},
-	{"r:", NULL, &rarg},
-	{"d:", NULL, &rpath},
-
-	/* common */
-	{"g:", NULL, &tcp_port},
-	{"b:", NULL, &barg},
-	{"U", &use_udp, NULL},
-	{"F", &force_run, NULL},
-	{"l", &net_mode, NULL},
-	{"o", &fastopen_api, NULL},
-	{"O", &tfo_support, NULL},
-	{"v", &verbose, NULL},
-	{NULL, NULL, NULL}
-};
-
-static void help(void)
-{
-	printf("\n  -F      Force to run\n");
-	printf("  -v      Verbose\n");
-	printf("  -o      Use old TCP API, default is new TCP API\n");
-	printf("  -O      TFO support is off, default is on\n");
-	printf("  -l      Become TCP Client, default is TCP server\n");
-	printf("  -g x    x - server port, default is %s\n", tcp_port);
-	printf("  -b x    x - low latency busy poll timeout\n");
-	printf("  -U      use UDP\n");
-
-	printf("\n          Client:\n");
-	printf("  -H x    x - server name or ip address, default is '%s'\n",
-		server_addr);
-	printf("  -a x    x - num of clients running in parallel\n");
-	printf("  -r x    x - num of client requests\n");
-	printf("  -n x    Client message size, max msg size is '%d'\n",
-		max_msg_len);
-	printf("  -N x    Server message size, max msg size is '%d'\n",
-		max_msg_len);
-	printf("  -T x    Reply timeout, default is '%ld' (microsec)\n",
-		wait_timeout);
-	printf("  -d x    x is a path to the file where results are saved\n");
-
-	printf("\n          Server:\n");
-	printf("  -R x    x - num of requests, after which conn. closed\n");
-	printf("  -q x    x - server's limit on the queue of TFO requests\n");
-}
-
-/* common structure for TCP/UDP server and TCP/UDP client */
-struct net_func {
-	void (*init)(void);
-	void (*run)(void);
-	void (*cleanup)(void);
-};
-static struct net_func net;
-
-#define MAX_THREADS	10000
-static pthread_attr_t attr;
-static pthread_t *thread_ids;
-
-static struct addrinfo *remote_addrinfo;
-static struct addrinfo *local_addrinfo;
-static struct sockaddr_storage remote_addr;
-static socklen_t remote_addr_len;
-
-static const struct linger clo = { 1, 3 };
-
-static void do_cleanup(void)
-{
-	free(client_msg);
-	free(server_msg);
-
-	if (net.cleanup)
-		net.cleanup();
-
-	if (tfo_cfg_changed) {
-		SAFE_FILE_SCANF(NULL, tfo_cfg, "%d", &tfo_cfg_value);
-		tfo_cfg_value &= ~tfo_bit_num;
-		tfo_cfg_value |= !tfo_support << (tfo_bit_num - 1);
-		tst_resm(TINFO, "unset '%s' back to '%d'",
-			tfo_cfg, tfo_cfg_value);
-		SAFE_FILE_PRINTF(NULL, tfo_cfg, "%d", tfo_cfg_value);
-	}
-
-	if (tw_reuse_changed) {
-		SAFE_FILE_PRINTF(NULL, tcp_tw_reuse, "0");
-		tst_resm(TINFO, "unset '%s' back to '0'", tcp_tw_reuse);
-	}
-}
-TST_DECLARE_ONCE_FN(cleanup, do_cleanup)
-
-static int sock_recv_poll(int fd, char *buf, int buf_size, int offset)
-{
-	struct pollfd pfd;
-	pfd.fd = fd;
-	pfd.events = POLLIN;
-	int len = -1;
-
-	while (1) {
-		errno = 0;
-		int ret = poll(&pfd, 1, wait_timeout / 1000);
-		if (ret == -1) {
-			if (errno == EINTR)
-				continue;
-			break;
-		}
-
-		if (ret == 0) {
-			errno = ETIME;
-			break;
-		}
-
-		if (ret != 1 || !(pfd.revents & POLLIN))
-			break;
-
-		errno = 0;
-		len = recvfrom(fd, buf + offset, buf_size - offset,
-			       MSG_DONTWAIT, (struct sockaddr *)&remote_addr,
-			       &remote_addr_len);
-
-		if (len == -1 && errno == EINTR)
-			continue;
-		else
-			break;
-	}
-
-	return len;
-}
-
-static int client_recv(int *fd, char *buf)
-{
-	int len, offset = 0;
-
-	while (1) {
-		errno = 0;
-		len = sock_recv_poll(*fd, buf, server_msg_size, offset);
-
-		/* socket closed or msg is not valid */
-		if (len < 1 || (offset + len) > server_msg_size ||
-		   (buf[0] != start_byte && buf[0] != start_fin_byte)) {
-			if (!errno)
-				errno = ENOMSG;
-			break;
-		}
-		offset += len;
-		if (buf[offset - 1] != end_byte)
-			continue;
-
-		if (verbose) {
-			tst_resm_hexd(TINFO, buf, offset,
-				"msg recv from sock %d:", *fd);
-		}
-
-		/* recv last msg, close socket */
-		if (buf[0] == start_fin_byte)
-			break;
-		return 0;
-	}
-
-	shutdown(*fd, SHUT_WR);
-	SAFE_CLOSE(cleanup, *fd);
-	*fd = -1;
-	return (errno) ? -1 : 0;
-}
-
-static int client_connect_send(const char *msg, int size)
-{
-	int cfd = socket(remote_addrinfo->ai_family,
-			 remote_addrinfo->ai_socktype, 0);
-	const int flag = 1;
-
-	if (cfd == -1)
-		return cfd;
-
-	setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
-	if (busy_poll >= 0) {
-		setsockopt(cfd, SOL_SOCKET, SO_BUSY_POLL,
-			   &busy_poll, sizeof(busy_poll));
-	}
-
-	if (fastopen_api == TFO_ENABLED) {
-		/* Replaces connect() + send()/write() */
-		if (sendto(cfd, msg, size, MSG_FASTOPEN | MSG_NOSIGNAL,
-		    remote_addrinfo->ai_addr,
-		    remote_addrinfo->ai_addrlen) != size) {
-			SAFE_CLOSE(cleanup, cfd);
-			return -1;
-		}
-	} else {
-		/* old TCP API */
-		if (connect(cfd, remote_addrinfo->ai_addr,
-		    remote_addrinfo->ai_addrlen)) {
-			SAFE_CLOSE(cleanup, cfd);
-			return -1;
-		}
-
-		if (send(cfd, msg, size, MSG_NOSIGNAL) != client_msg_size) {
-			SAFE_CLOSE(cleanup, cfd);
-			return -1;
-		}
-	}
-
-	return cfd;
-}
-
-void *client_fn(LTP_ATTRIBUTE_UNUSED void *arg)
-{
-	char buf[server_msg_size];
-	int cfd, i;
-	intptr_t err = 0;
-
-	/* connect & send requests */
-	cfd = client_connect_send(client_msg, client_msg_size);
-	if (cfd == -1) {
-		err = errno;
-		goto out;
-	}
-
-	if (client_recv(&cfd, buf)) {
-		err = errno;
-		goto out;
-	}
-
-	for (i = 1; i < client_max_requests; ++i) {
-		if (use_udp)
-			goto send;
-
-		/* check connection, it can be closed */
-		int ret = 0;
-		if (cfd != -1)
-			ret = recv(cfd, buf, 1, MSG_DONTWAIT);
-
-		if (ret == 0) {
-			/* try to reconnect and send */
-			if (cfd != -1)
-				SAFE_CLOSE(cleanup, cfd);
-
-			cfd = client_connect_send(client_msg, client_msg_size);
-			if (cfd == -1) {
-				err = errno;
-				goto out;
-			}
-
-			if (client_recv(&cfd, buf)) {
-				err = errno;
-				break;
-			}
-
-			continue;
-
-		} else if (ret > 0) {
-			err = EMSGSIZE;
-			break;
-		}
-
-send:
-		if (verbose) {
-			tst_resm_hexd(TINFO, client_msg, client_msg_size,
-				"try to send msg[%d]", i);
-		}
-
-		if (send(cfd, client_msg, client_msg_size,
-			MSG_NOSIGNAL) != client_msg_size) {
-			err = ECOMM;
-			break;
-		}
-		if (client_recv(&cfd, buf)) {
-			err = errno;
-			break;
-		}
-	}
-
-	if (cfd != -1)
-		SAFE_CLOSE(cleanup, cfd);
-
-out:
-	return (void *) err;
-}
-
-union net_size_field {
-	char bytes[2];
-	uint16_t value;
-};
-
-static void make_client_request(void)
-{
-	client_msg[0] = start_byte;
-
-	/* set size for reply */
-	union net_size_field net_size;
-	net_size.value = htons(server_msg_size);
-	client_msg[1] = net_size.bytes[0];
-	client_msg[2] = net_size.bytes[1];
-
-	client_msg[client_msg_size - 1] = end_byte;
-}
-
-static int parse_client_request(const char *msg)
-{
-	union net_size_field net_size;
-	net_size.bytes[0] = msg[1];
-	net_size.bytes[1] = msg[2];
-	int size = ntohs(net_size.value);
-	if (size < 2 || size > max_msg_len)
-		return -1;
-
-	return size;
-}
-
-static struct timespec tv_client_start;
-static struct timespec tv_client_end;
-
-static void client_init(void)
-{
-	if (clients_num >= MAX_THREADS) {
-		tst_brkm(TBROK, cleanup,
-			"Unexpected num of clients '%d'",
-			clients_num);
-	}
-
-	thread_ids = SAFE_MALLOC(NULL, sizeof(pthread_t) * clients_num);
-
-	client_msg = SAFE_MALLOC(NULL, client_msg_size);
-	memset(client_msg, client_byte, client_msg_size);
-
-	make_client_request();
-
-	struct addrinfo hints;
-	memset(&hints, 0, sizeof(struct addrinfo));
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = (use_udp) ? SOCK_DGRAM : SOCK_STREAM;
-	hints.ai_flags = 0;
-	hints.ai_protocol = 0;
-
-	int err = getaddrinfo(server_addr, tcp_port, &hints, &remote_addrinfo);
-	if (err) {
-		tst_brkm(TBROK, cleanup, "getaddrinfo of '%s' failed, %s",
-			server_addr, gai_strerror(err));
-	}
-
-	tst_resm(TINFO, "Running the test over IPv%s",
-		(remote_addrinfo->ai_family == AF_INET6) ? "6" : "4");
-
-	clock_gettime(CLOCK_MONOTONIC_RAW, &tv_client_start);
-	int i;
-	for (i = 0; i < clients_num; ++i) {
-		if (pthread_create(&thread_ids[i], 0, client_fn, NULL) != 0) {
-			tst_brkm(TBROK | TERRNO, cleanup,
-				"pthread_create failed at %s:%d",
-				__FILE__, __LINE__);
-		}
-	}
-}
-
-static void client_run(void)
-{
-	void *res = NULL;
-	long clnt_time = 0;
-	int i;
-	for (i = 0; i < clients_num; ++i) {
-		pthread_join(thread_ids[i], &res);
-		if (res) {
-			tst_brkm(TBROK, cleanup, "client[%d] failed: %s",
-				i, strerror((intptr_t)res));
-		}
-	}
-
-	clock_gettime(CLOCK_MONOTONIC_RAW, &tv_client_end);
-	clnt_time = (tv_client_end.tv_sec - tv_client_start.tv_sec) * 1000 +
-		(tv_client_end.tv_nsec - tv_client_start.tv_nsec) / 1000000;
-
-	tst_resm(TINFO, "total time '%ld' ms", clnt_time);
-
-	/* ask server to terminate */
-	client_msg[0] = start_fin_byte;
-	int cfd = client_connect_send(client_msg, client_msg_size);
-	if (cfd != -1) {
-		shutdown(cfd, SHUT_WR);
-		SAFE_CLOSE(NULL, cfd);
-	}
-	/* the script tcp_fastopen_run.sh will remove it */
-	SAFE_FILE_PRINTF(cleanup, rpath, "%ld", clnt_time);
-}
-
-static void client_cleanup(void)
-{
-	free(thread_ids);
-
-	if (remote_addrinfo)
-		freeaddrinfo(remote_addrinfo);
-}
-
-static char *make_server_reply(int size)
-{
-	char *send_msg = SAFE_MALLOC(NULL, size);
-	memset(send_msg, server_byte, size - 1);
-	send_msg[0] = start_byte;
-	send_msg[size - 1] = end_byte;
-	return send_msg;
-}
-
-void *server_fn(void *cfd)
-{
-	int client_fd = (intptr_t) cfd;
-	int num_requests = 0, offset = 0;
-
-	/* Reply will be constructed from first client request */
-	char *send_msg = NULL;
-	int send_msg_size = 0;
-
-	char recv_msg[max_msg_len];
-
-	setsockopt(client_fd, SOL_SOCKET, SO_LINGER, &clo, sizeof(clo));
-	if (busy_poll >= 0) {
-		setsockopt(client_fd, SOL_SOCKET, SO_BUSY_POLL,
-			   &busy_poll, sizeof(busy_poll));
-	}
-
-	ssize_t recv_len;
-
-	while (1) {
-		recv_len = sock_recv_poll(client_fd, recv_msg,
-			max_msg_len, offset);
-
-		if (recv_len == 0)
-			break;
-
-		if (recv_len < 0 || (offset + recv_len) > max_msg_len ||
-		   (recv_msg[0] != start_byte &&
-		    recv_msg[0] != start_fin_byte)) {
-			tst_resm(TFAIL, "recv failed, sock '%d'", client_fd);
-			goto out;
-		}
-
-		offset += recv_len;
-
-		if (recv_msg[offset - 1] != end_byte) {
-			/* msg is not complete, continue recv */
-			continue;
-		}
-
-		/* client asks to terminate */
-		if (recv_msg[0] == start_fin_byte)
-			goto out;
-
-		if (verbose) {
-			tst_resm_hexd(TINFO, recv_msg, offset,
-				"msg recv from sock %d:", client_fd);
-		}
-
-		/* if we send reply for the first time, construct it here */
-		if (!send_msg) {
-			send_msg_size = parse_client_request(recv_msg);
-			if (send_msg_size < 0) {
-				tst_resm(TFAIL, "wrong msg size '%d'",
-					send_msg_size);
-				goto out;
-			}
-			send_msg = make_server_reply(send_msg_size);
-		}
-
-		offset = 0;
-
-		/*
-		 * It will tell client that server is going
-		 * to close this connection.
-		 */
-		if (!use_udp && ++num_requests >= server_max_requests)
-			send_msg[0] = start_fin_byte;
-
-		if (sendto(client_fd, send_msg, send_msg_size,
-		    MSG_NOSIGNAL, (struct sockaddr *)&remote_addr,
-		    remote_addr_len) < 0) {
-			tst_resm(TFAIL | TERRNO, "sendto failed");
-			goto out;
-		}
-
-		if (!use_udp && num_requests >= server_max_requests) {
-			/* max reqs, close socket */
-			shutdown(client_fd, SHUT_WR);
-			break;
-		}
-	}
-
-	free(send_msg);
-	SAFE_CLOSE(cleanup, client_fd);
-	return NULL;
-
-out:
-	free(send_msg);
-	SAFE_CLOSE(cleanup, client_fd);
-	cleanup();
-	tst_exit();
-}
-
-static pthread_t server_thread_add(intptr_t client_fd)
-{
-	pthread_t id;
-	if (pthread_create(&id, &attr, server_fn, (void *) client_fd)) {
-		tst_brkm(TBROK | TERRNO, cleanup,
-			"pthread_create failed at %s:%d", __FILE__, __LINE__);
-	}
-	return id;
-}
-
-static void server_init(void)
-{
-	struct addrinfo hints;
-	memset(&hints, 0, sizeof(struct addrinfo));
-	hints.ai_family = AF_INET6;
-	hints.ai_socktype = (use_udp) ? SOCK_DGRAM : SOCK_STREAM;
-	hints.ai_flags = AI_PASSIVE;
-
-	if (getaddrinfo(NULL, tcp_port, &hints, &local_addrinfo) != 0)
-		tst_brkm(TBROK | TERRNO, cleanup, "getaddrinfo failed");
-
-	if (!local_addrinfo)
-		tst_brkm(TBROK, cleanup, "failed to get the address");
-
-	/* IPv6 socket is also able to access IPv4 protocol stack */
-	sfd = SAFE_SOCKET(cleanup, AF_INET6, local_addrinfo->ai_socktype, 0);
-
-	tst_resm(TINFO, "assigning a name to the server socket...");
-	SAFE_BIND(cleanup, sfd, local_addrinfo->ai_addr,
-		local_addrinfo->ai_addrlen);
-
-	freeaddrinfo(local_addrinfo);
-
-	if (use_udp)
-		return;
-
-	const int flag = 1;
-	setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
-
-	if (fastopen_api == TFO_ENABLED) {
-		if (setsockopt(sfd, IPPROTO_TCP, TCP_FASTOPEN, &tfo_queue_size,
-			sizeof(tfo_queue_size)) == -1)
-			tst_brkm(TBROK, cleanup, "Can't set TFO sock. options");
-	}
-
-	SAFE_LISTEN(cleanup, sfd, max_queue_len);
-	tst_resm(TINFO, "Listen on the socket '%d', port '%s'", sfd, tcp_port);
-}
-
-static void server_cleanup(void)
-{
-	SAFE_CLOSE(NULL, sfd);
-}
-
-static void server_run_udp(void)
-{
-	pthread_t p_id = server_thread_add(sfd);
-
-	if (!pthread_join(p_id, NULL))
-		tst_brkm(TBROK | TERRNO, cleanup, "pthread_join() failed");
-}
-
-static void server_run(void)
-{
-	/* IPv4 source address will be mapped to IPv6 address */
-	struct sockaddr_in6 addr6;
-	socklen_t addr_size = sizeof(addr6);
-
-	pthread_attr_init(&attr);
-
-	/*
-	 * detaching threads allow to reclaim thread's resources
-	 * once a thread finishes its work.
-	 */
-	if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
-		tst_brkm(TBROK | TERRNO, cleanup, "setdetachstate failed");
-
-	while (1) {
-		int client_fd = accept(sfd, (struct sockaddr *)&addr6,
-			&addr_size);
-
-		if (client_fd == -1)
-			tst_brkm(TBROK, cleanup, "Can't create client socket");
-
-		if (verbose) {
-			char addr_buf[INET6_ADDRSTRLEN];
-			tst_resm(TINFO, "conn: port '%d', addr '%s'",
-				addr6.sin6_port, inet_ntop(AF_INET6,
-				&addr6.sin6_addr, addr_buf, INET6_ADDRSTRLEN));
-		}
-
-		server_thread_add(client_fd);
-	}
-}
-
-static void check_opt(const char *name, char *arg, int *val, int lim)
-{
-	if (arg) {
-		if (sscanf(arg, "%i", val) != 1)
-			tst_brkm(TBROK, NULL, "-%s option arg is not a number",
-				 name);
-		if (*val < lim)
-			tst_brkm(TBROK, NULL, "-%s option arg is less than %d",
-				name, lim);
-	}
-}
-
-static void check_opt_l(const char *name, char *arg, long *val, long lim)
-{
-	if (arg) {
-		if (sscanf(arg, "%ld", val) != 1)
-			tst_brkm(TBROK, NULL, "-%s option arg is not a number",
-				 name);
-		if (*val < lim)
-			tst_brkm(TBROK, NULL, "-%s option arg is less than %ld",
-				name, lim);
-	}
-}
-
-static void setup(int argc, char *argv[])
-{
-	tst_parse_opts(argc, argv, options, help);
-
-	/* if client_num is not set, use num of processors */
-	clients_num = sysconf(_SC_NPROCESSORS_ONLN);
-
-	check_opt("a", aarg, &clients_num, 1);
-	check_opt("r", rarg, &client_max_requests, 1);
-	check_opt("R", Rarg, &server_max_requests, 1);
-	check_opt("n", narg, &client_msg_size, 1);
-	check_opt("N", Narg, &server_msg_size, 1);
-	check_opt("q", qarg, &tfo_queue_size, 1);
-	check_opt_l("T", Targ, &wait_timeout, 0L);
-	check_opt("b", barg, &busy_poll, 0);
-
-	if (!force_run)
-		tst_require_root();
-
-	if (!force_run && tst_kvercmp(3, 7, 0) < 0) {
-		tst_brkm(TCONF, NULL,
-			"Test must be run with kernel 3.7 or newer");
-	}
-
-	if (!force_run && busy_poll >= 0 && tst_kvercmp(3, 11, 0) < 0) {
-		tst_brkm(TCONF, NULL,
-			"Test must be run with kernel 3.11 or newer");
-	}
-
-	/* check tcp fast open knob */
-	if (!force_run && access(tfo_cfg, F_OK) == -1)
-		tst_brkm(TCONF, NULL, "Failed to find '%s'", tfo_cfg);
-
-	if (!force_run) {
-		SAFE_FILE_SCANF(NULL, tfo_cfg, "%d", &tfo_cfg_value);
-		tst_resm(TINFO, "'%s' is %d", tfo_cfg, tfo_cfg_value);
-	}
-
-	tst_sig(FORK, DEF_HANDLER, cleanup);
-
-	switch (net_mode) {
-	case SERVER_HOST:
-		tst_resm(TINFO, "max requests '%d'",
-			server_max_requests);
-		net.init	= server_init;
-		net.run		= (use_udp) ? server_run_udp : server_run;
-		net.cleanup	= (use_udp) ? NULL : server_cleanup;
-		tfo_bit_num = 2;
-	break;
-	case CLIENT_HOST:
-		tst_resm(TINFO, "connection: addr '%s', port '%s'",
-			server_addr, tcp_port);
-		tst_resm(TINFO, "client max req: %d", client_max_requests);
-		tst_resm(TINFO, "clients num: %d", clients_num);
-		tst_resm(TINFO, "client msg size: %d", client_msg_size);
-		tst_resm(TINFO, "server msg size: %d", server_msg_size);
-
-		net.init	= client_init;
-		net.run		= client_run;
-		net.cleanup	= client_cleanup;
-		tfo_bit_num = 1;
-	break;
-	}
-
-	remote_addr_len = sizeof(struct sockaddr_storage);
-
-	if (use_udp) {
-		tst_resm(TINFO, "using UDP");
-		fastopen_api = TFO_DISABLED;
-	} else {
-		tst_resm(TINFO, "TCP %s is using %s TCP API.",
-			(net_mode == SERVER_HOST) ? "server" : "client",
-			(fastopen_api == TFO_ENABLED) ? "Fastopen" : "old");
-
-		tfo_support = TFO_ENABLED == tfo_support;
-		if (((tfo_cfg_value & tfo_bit_num) == tfo_bit_num)
-		      != tfo_support) {
-			int value = (tfo_cfg_value & ~tfo_bit_num)
-				| (tfo_support << (tfo_bit_num - 1));
-			tst_resm(TINFO, "set '%s' to '%d'", tfo_cfg, value);
-			SAFE_FILE_PRINTF(cleanup, tfo_cfg, "%d", value);
-			tfo_cfg_changed = 1;
-		}
-
-		int reuse_value = 0;
-		SAFE_FILE_SCANF(cleanup, tcp_tw_reuse, "%d", &reuse_value);
-		if (!reuse_value) {
-			SAFE_FILE_PRINTF(cleanup, tcp_tw_reuse, "1");
-			tw_reuse_changed = 1;
-			tst_resm(TINFO, "set '%s' to '1'", tcp_tw_reuse);
-		}
-
-		tst_resm(TINFO, "TFO support %s",
-			(tfo_support) ? "enabled" : "disabled");
-	}
-
-	net.init();
-}
-
-int main(int argc, char *argv[])
-{
-	setup(argc, argv);
-
-	net.run();
-
-	cleanup();
-
-	tst_exit();
-}
-- 
1.7.1



More information about the ltp mailing list