[CRIU] [PATCH] soccr: add a test

Andrei Vagin avagin at openvz.org
Tue Jan 10 14:54:43 PST 2017


From: Andrei Vagin <avagin at virtuozzo.com>

This test construct both ends of tcp connections and
check that it works in both directions.

Signed-off-by: Andrei Vagin <avagin at virtuozzo.com>
---
 soccr/test/Makefile          |  19 ++++++
 soccr/test/local.sh          |   1 +
 soccr/test/run.py            |  62 ++++++++++++++++++
 soccr/test/tcp-constructor.c | 151 +++++++++++++++++++++++++++++++++++++++++++
 soccr/test/tcp-test.py       |  19 ++++++
 5 files changed, 252 insertions(+)
 create mode 100644 soccr/test/Makefile
 create mode 100755 soccr/test/local.sh
 create mode 100644 soccr/test/run.py
 create mode 100644 soccr/test/tcp-constructor.c
 create mode 100755 soccr/test/tcp-test.py

diff --git a/soccr/test/Makefile b/soccr/test/Makefile
new file mode 100644
index 0000000..96ee647
--- /dev/null
+++ b/soccr/test/Makefile
@@ -0,0 +1,19 @@
+CFLAGS += -Wall -g -I../../
+LDFLAGS += -L../ -lsoccr ../libsoccr.a -lnet
+
+RUN ?= tcp-constructor
+
+run:
+	./local.sh
+
+tcp-constructor: tcp-constructor.c ../libsoccr.a
+	$(CC) $(CFLAGS) tcp-constructor.c -o tcp-constructor $(LDFLAGS)
+
+clean:
+	rm -f tcp-constructor
+
+test: tcp-constructor
+	python run.py ./$(RUN)
+
+.PHONY: test
+
diff --git a/soccr/test/local.sh b/soccr/test/local.sh
new file mode 100755
index 0000000..aac3a58
--- /dev/null
+++ b/soccr/test/local.sh
@@ -0,0 +1 @@
+unshare -Urn sh -c 'ip link set up dev lo && make test'
diff --git a/soccr/test/run.py b/soccr/test/run.py
new file mode 100644
index 0000000..c4d81fb
--- /dev/null
+++ b/soccr/test/run.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python2
+
+import sys, os
+import hashlib
+from subprocess import Popen, PIPE
+
+str2 = "test_test" * (1 << 20)
+str1 = "Test_Test!"
+
+src = os.getenv("TCP_SRC", "127.0.0.1")
+dst = os.getenv("TCP_DST", "127.0.0.1")
+sport = os.getenv("TCP_SPORT", "12345")
+dport = os.getenv("TCP_DPORT", "54321")
+
+print sys.argv[1]
+args = [sys.argv[1],
+        "--addr", src, "--port", sport, "--seq", "555",
+        "--next",
+        "--addr", dst, "--port", dport, "--seq", "666",
+        "--reverse", "--", "./tcp-test.py"]
+
+p1 = Popen(args + ["dst"], stdout = PIPE, stdin = PIPE)
+
+args.remove("--reverse");
+
+p2 = Popen(args + ["src"], stdout = PIPE, stdin = PIPE)
+
+p1.stdout.read(5)
+p2.stdout.read(5)
+p1.stdin.write("start")
+p2.stdin.write("start")
+
+p1.stdin.write(str1)
+p1.stdin.close()
+p2.stdin.write(str2)
+p2.stdin.close()
+
+s = p1.stdout.read()
+m = hashlib.md5()
+m.update(str2)
+str2 = m.hexdigest()
+
+if str2 != eval(s):
+    print "FAIL", repr(str2), repr(s)
+    sys.exit(5);
+
+s = p1.stdout.read()
+m = hashlib.md5()
+m.update(str1)
+str1 = m.hexdigest()
+
+s = p2.stdout.read()
+if str1 != eval(s):
+    print "FAIL", repr(str1), s
+    sys.exit(5);
+
+if p1.wait():
+    sys.exit(1)
+if p2.wait():
+    sys.exit(1)
+
+print "PASS"
diff --git a/soccr/test/tcp-constructor.c b/soccr/test/tcp-constructor.c
new file mode 100644
index 0000000..89f2010
--- /dev/null
+++ b/soccr/test/tcp-constructor.c
@@ -0,0 +1,151 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <linux/socket.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+
+#include "soccr/soccr.h"
+
+#define pr_perror(fmt, ...) ({ fprintf(stderr, "%s:%d: " fmt " : %m\n", __func__, __LINE__, ##__VA_ARGS__); 1; })
+
+struct tcp {
+	char *addr;
+	uint32_t port;
+	uint32_t seq;
+	uint16_t mss_clamp;
+	uint16_t wscale;
+};
+
+static void usage()
+{
+	printf(
+		"Usage: --addr ADDR -port PORT --seq SEQ --next --addr ADDR -port PORT --seq SEQ -- CMD ...\n"
+		"\t Describe a source side of a connection, then set the --next option\n"
+		"\t and describe a destination side.\n"
+		"\t --reverse - swap source and destination sides\n"
+		"\t The idea is that the same command line is execute on both sides,\n"
+		"\t but the --reverse is added to one of them.\n"
+		"\n"
+		"\t CMD ... - a user command to handle a socket, which is the descriptor 3.\n"
+		"\n"
+		"\t It prints the \"start\" on stdout when a socket is created and\n"
+		"\t resumes it when you write \"start\" to stdin.\n"
+	);
+}
+
+int main(int argc, char **argv)
+{
+	static const char short_opts[] = "";
+	static struct option long_opts[] = {
+		{ "addr",	required_argument, 0, 'a' },
+		{ "port",	required_argument, 0, 'p' },
+		{ "seq",	required_argument, 0, 's' },
+		{ "next",	no_argument, 0, 'n'},
+		{ "reverse",	no_argument, 0, 'r'},
+		{},
+	};
+	struct tcp tcp[2] = {
+				{"127.0.0.1", 12345, 5000000, 1460, 7},
+				{"127.0.0.1", 54321, 6000000, 1460, 7}
+			};
+
+	int sk, yes = 1, val, idx, opt, i, src = 0, dst = 1;
+	union libsoccr_addr src_addr, dst_addr;
+	struct libsoccr_sk_data data = {};
+	struct libsoccr_sk *so;
+	char buf[1024];
+
+	i = 0;
+	while (1) {
+		idx = -1;
+		opt = getopt_long(argc, argv, short_opts, long_opts, &idx);
+		if (opt == -1)
+			break;
+
+		switch (opt) {
+		case 'a':
+			tcp[i].addr = optarg;
+			break;
+		case 'p':
+			tcp[i].port = atol(optarg);
+			break;
+		case 's':
+			tcp[i].seq = atol(optarg);
+			break;
+		case 'n':
+			i++;
+			if (i > 1)
+				return pr_perror("--next is used twice or more");
+			break;
+		case 'r':
+			src = 1; dst = 0;
+			break;
+		default:
+			usage();
+			return 3;
+		}
+	}
+	if (i != 1)
+		return pr_perror("--next is required");
+
+	if (optind == argc) {
+		usage();
+		return 1;
+	}
+
+	for (i = 0; i < 2; i++)
+		fprintf(stderr, "%s:%d:%d\n", tcp[i].addr, tcp[i].port, tcp[i].seq);
+
+	data.state = TCP_ESTABLISHED;
+	data.inq_seq = tcp[dst].seq;
+	data.outq_seq = tcp[src].seq;
+
+	sk = socket(AF_INET, SOCK_STREAM, 0);
+	if (sk < 0)
+		return pr_perror("socket");
+
+	so = libsoccr_pause(sk);
+
+	if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1)
+		return pr_perror("setsockopt");
+
+	src_addr.v4.sin_family = AF_INET;
+	src_addr.v4.sin_port = htons(tcp[src].port);
+	if (inet_pton(AF_INET, tcp[src].addr, &src_addr.v4.sin_addr) != 1)
+		return pr_perror("inet_pton");
+
+	dst_addr.v4.sin_family = AF_INET;
+	dst_addr.v4.sin_port = htons(tcp[dst].port);
+	if (inet_pton(AF_INET, tcp[dst].addr, &(dst_addr.v4.sin_addr)) != 1)
+		return pr_perror("inet_pton");
+
+	libsoccr_set_addr(so, 1, &src_addr, 0);
+	libsoccr_set_addr(so, 0, &dst_addr, 0);
+
+	data.snd_wscale = tcp[src].wscale;
+	data.rcv_wscale = tcp[dst].wscale;
+	data.mss_clamp = tcp[src].mss_clamp;
+
+	data.opt_mask = TCPI_OPT_WSCALE | TCPOPT_MAXSEG;
+
+	if (libsoccr_restore(so, &data, sizeof(data)))
+		return 1;
+
+	/* Let's go */
+	if (write(STDOUT_FILENO, "start", 5) != 5)
+		return pr_perror("write");
+	if (read(STDIN_FILENO, buf, 5) != 5)
+		return pr_perror("read");
+
+	val = 0;
+	if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val)))
+		return pr_perror("TCP_REPAIR");
+
+	execv(argv[optind], argv + optind);
+
+	return pr_perror("Unable to exec %s", argv[optind]);
+}
diff --git a/soccr/test/tcp-test.py b/soccr/test/tcp-test.py
new file mode 100755
index 0000000..b7e8ee6
--- /dev/null
+++ b/soccr/test/tcp-test.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python2
+
+import os, sys, socket
+import hashlib
+
+sk = socket.fromfd(3, socket.AF_INET, socket.SOCK_STREAM)
+
+s = sys.stdin.read()
+ret = sk.send(s)
+print >> sys.stderr, "%s: send() -> %d" % (sys.argv[1], ret)
+sk.shutdown(socket.SHUT_WR)
+m = hashlib.md5()
+while True:
+    s = sk.recv((1 << 20) * 10)
+    if not s:
+        break
+    print >> sys.stderr, "%s: recv() -> %d" % (sys.argv[1], len(s))
+    m.update(s)
+print repr(m.hexdigest())
-- 
2.7.4



More information about the CRIU mailing list