mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 09:58:09 +00:00
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>
This commit is contained in:
parent
d9132d45cc
commit
1fb852bb20
19
soccr/test/Makefile
Normal file
19
soccr/test/Makefile
Normal file
@ -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
|
||||
|
1
soccr/test/local.sh
Executable file
1
soccr/test/local.sh
Executable file
@ -0,0 +1 @@
|
||||
unshare -Urn sh -c 'ip link set up dev lo && make test'
|
62
soccr/test/run.py
Normal file
62
soccr/test/run.py
Normal file
@ -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"
|
151
soccr/test/tcp-constructor.c
Normal file
151
soccr/test/tcp-constructor.c
Normal file
@ -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]);
|
||||
}
|
19
soccr/test/tcp-test.py
Executable file
19
soccr/test/tcp-test.py
Executable file
@ -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())
|
Loading…
x
Reference in New Issue
Block a user