diff options
author | Andrei Vagin <avagin@virtuozzo.com> | 2017-01-11 01:54:00 +0300 |
---|---|---|
committer | Pavel Emelyanov <xemul@virtuozzo.com> | 2017-01-16 11:04:50 +0300 |
commit | 1fb852bb20043f7aa6dc58c0eb044c914ae57878 (patch) | |
tree | c5f0073e32f6a229f5cc3401789aacd8be9ced53 /soccr | |
parent | d9132d45cc99a49641dbddd3f1b0a998d0251767 (diff) |
soccr: add a test
This test construct both ends of tcp connections and
check that it works in both directions.
travis-ci: success for soccr: add a test
Signed-off-by: Andrei Vagin <avagin@virtuozzo.com>
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Diffstat (limited to 'soccr')
-rw-r--r-- | soccr/test/Makefile | 19 | ||||
-rwxr-xr-x | soccr/test/local.sh | 1 | ||||
-rw-r--r-- | soccr/test/run.py | 62 | ||||
-rw-r--r-- | soccr/test/tcp-constructor.c | 151 | ||||
-rwxr-xr-x | soccr/test/tcp-test.py | 19 |
5 files changed, 252 insertions, 0 deletions
diff --git a/soccr/test/Makefile b/soccr/test/Makefile new file mode 100644 index 000000000..96ee647e4 --- /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 000000000..aac3a58de --- /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 000000000..c4d81fbd2 --- /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 000000000..89f201000 --- /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 000000000..b7e8ee6d3 --- /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()) |