1
0
Fork 0
mirror of https://passt.top/passt synced 2025-05-05 18:28:52 +02:00

rampstream: Add utility to test for corruption of data streams

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2025-02-12 18:07:19 +11:00 committed by Stefano Brivio
parent 6f122f0171
commit a301158456
7 changed files with 267 additions and 4 deletions

1
test/.gitignore vendored
View file

@ -8,5 +8,6 @@ QEMU_EFI.fd
*.raw.xz
*.bin
nstool
rampstream
guest-key
guest-key.pub

View file

@ -52,7 +52,8 @@ UBUNTU_IMGS = $(UBUNTU_OLD_IMGS) $(UBUNTU_NEW_IMGS)
DOWNLOAD_ASSETS = mbuto podman \
$(DEBIAN_IMGS) $(FEDORA_IMGS) $(OPENSUSE_IMGS) $(UBUNTU_IMGS)
TESTDATA_ASSETS = small.bin big.bin medium.bin
TESTDATA_ASSETS = small.bin big.bin medium.bin \
rampstream
LOCAL_ASSETS = mbuto.img mbuto.mem.img podman/bin/podman QEMU_EFI.fd \
$(DEBIAN_IMGS:%=prepared-%) $(FEDORA_IMGS:%=prepared-%) \
$(UBUNTU_NEW_IMGS:%=prepared-%) \
@ -85,7 +86,7 @@ podman/bin/podman: pull-podman
guest-key guest-key.pub:
ssh-keygen -f guest-key -N ''
mbuto.img: passt.mbuto mbuto/mbuto guest-key.pub $(TESTDATA_ASSETS)
mbuto.img: passt.mbuto mbuto/mbuto guest-key.pub rampstream-check.sh $(TESTDATA_ASSETS)
./mbuto/mbuto -p ./$< -c lz4 -f $@
mbuto.mem.img: passt.mem.mbuto mbuto ../passt.avx2

View file

@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/basic - Check basic migration functionality
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
set RAMPS 6000000
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test DHCPv6: address
# Link is up now, wait for DAD to complete
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv4: host > guest
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
guest1b socat -u TCP4-LISTEN:10001 EXEC:"rampstream-check.sh __RAMPS__"
sleep 1
hostb socat -u EXEC:"test/rampstream send __RAMPS__" TCP4:__ADDR1__:10001
sleep 1
#mon echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
hostw
guest2 cat rampstream.err
guest2 [ $(cat rampstream.status) -eq 0 ]

View file

@ -0,0 +1,55 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/basic - Check basic migration functionality
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
set RAMPS 6000000
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test DHCPv6: address
# Link is up now, wait for DAD to complete
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv4: guest > host
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
hostb socat -u TCP4-LISTEN:10006 EXEC:"test/rampstream check __RAMPS__"
sleep 1
guest1b socat -u EXEC:"rampstream send __RAMPS__" TCP4:__MAP_HOST4__:10006
sleep 1
mon echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
hostw

View file

@ -13,7 +13,8 @@
PROGS="${PROGS:-ash,dash,bash ip mount ls insmod mkdir ln cat chmod lsmod
modprobe find grep mknod mv rm umount jq iperf3 dhclient hostname
sed tr chown sipcalc cut socat dd strace ping tail killall sleep sysctl
nproc tcp_rr tcp_crr udp_rr which tee seq bc sshd ssh-keygen cmp tcpdump env}"
nproc tcp_rr tcp_crr udp_rr which tee seq bc sshd ssh-keygen cmp tcpdump
env}"
# OpenSSH 9.8 introduced split binaries, with sshd being the daemon, and
# sshd-session the per-session program. We need the latter as well, and the path
@ -31,7 +32,7 @@ LINKS="${LINKS:-
DIRS="${DIRS} /tmp /usr/sbin /usr/share /var/log /var/lib /etc/ssh /run/sshd /root/.ssh"
COPIES="${COPIES} small.bin,/root/small.bin medium.bin,/root/medium.bin big.bin,/root/big.bin"
COPIES="${COPIES} small.bin,/root/small.bin medium.bin,/root/medium.bin big.bin,/root/big.bin rampstream,/bin/rampstream rampstream-check.sh,/bin/rampstream-check.sh"
FIXUP="${FIXUP}"'
mv /sbin/* /usr/sbin || :

3
test/rampstream-check.sh Executable file
View file

@ -0,0 +1,3 @@
#! /bin/sh
(rampstream check "$@" 2>&1; echo $? > rampstream.status) | tee rampstream.err

143
test/rampstream.c Normal file
View file

@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* rampstream - Generate a check and stream of bytes in a ramp pattern
*
* Copyright Red Hat
* Author: David Gibson <david@gibson.dropbear.id.au>
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
/* Length of the repeating ramp. This is a deliberately not a "round" number so
* that we're very likely to misalign with likely block or chunk sizes of the
* transport. That means we'll detect gaps in the stream, even if they occur
* neatly on block boundaries. Specifically this is the largest 8-bit prime. */
#define RAMPLEN 251
#define INTERVAL 10000
#define ARRAY_SIZE(a) ((int)(sizeof(a) / sizeof((a)[0])))
#define die(...) \
do { \
fprintf(stderr, "rampstream: " __VA_ARGS__); \
exit(1); \
} while (0)
static void usage(void)
{
die("Usage:\n"
" rampstream send <number>\n"
" Generate a ramp pattern of bytes on stdout, repeated <number>\n"
" times\n"
" rampstream check <number>\n"
" Check a ramp pattern of bytes on stdin, repeater <number>\n"
" times\n");
}
static void ramp_send(unsigned long long num, const uint8_t *ramp)
{
unsigned long long i;
for (i = 0; i < num; i++) {
int off = 0;
ssize_t rc;
if (i % INTERVAL == 0)
fprintf(stderr, "%llu...\r", i);
while (off < RAMPLEN) {
rc = write(1, ramp + off, RAMPLEN - off);
if (rc < 0) {
if (errno == EINTR ||
errno == EAGAIN ||
errno == EWOULDBLOCK)
continue;
die("Error writing ramp: %s\n",
strerror(errno));
}
if (rc == 0)
die("Zero length write\n");
off += rc;
}
}
}
static void ramp_check(unsigned long long num, const uint8_t *ramp)
{
unsigned long long i;
for (i = 0; i < num; i++) {
uint8_t buf[RAMPLEN];
int off = 0;
ssize_t rc;
if (i % INTERVAL == 0)
fprintf(stderr, "%llu...\r", i);
while (off < RAMPLEN) {
rc = read(0, buf + off, RAMPLEN - off);
if (rc < 0) {
if (errno == EINTR ||
errno == EAGAIN ||
errno == EWOULDBLOCK)
continue;
die("Error reading ramp: %s\n",
strerror(errno));
}
if (rc == 0)
die("Unexpected EOF, ramp %llu, byte %d\n",
i, off);
off += rc;
}
if (memcmp(buf, ramp, sizeof(buf)) != 0) {
int j, k;
for (j = 0; j < RAMPLEN; j++)
if (buf[j] != ramp[j])
break;
for (k = j; k < RAMPLEN && k < j + 16; k++)
fprintf(stderr,
"Byte %d: expected 0x%02x, got 0x%02x\n",
k, ramp[k], buf[k]);
die("Data mismatch, ramp %llu, byte %d\n", i, j);
}
}
}
int main(int argc, char *argv[])
{
const char *subcmd = argv[1];
unsigned long long num;
uint8_t ramp[RAMPLEN];
char *e;
int i;
if (argc < 2)
usage();
errno = 0;
num = strtoull(argv[2], &e, 0);
if (*e || errno)
usage();
/* Initialize the ramp block */
for (i = 0; i < RAMPLEN; i++)
ramp[i] = i;
if (strcmp(subcmd, "send") == 0)
ramp_send(num, ramp);
else if (strcmp(subcmd, "check") == 0)
ramp_check(num, ramp);
else
usage();
exit(0);
}