[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