diff --git a/test/build/all b/test/build/all new file mode 100644 index 0000000..9aa6c61 --- /dev/null +++ b/test/build/all @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: AGPL-3.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/build/all - Build targets, one by one, then all together, check output +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +htools make cc rm uname getconf + +test Build passt +host make clean +check ! [ -e passt ] +host CFLAGS="-Werror" make passt +check [ -f passt ] + +test Build pasta +host make clean +check ! [ -e pasta ] +host CFLAGS="-Werror" make pasta +check [ -h pasta ] + +test Build qrap +host make clean +check ! [ -e qrap ] +host CFLAGS="-Werror" make qrap +check [ -f qrap ] + +test Build all +host make clean +check ! [ -e passt ] +check ! [ -e pasta ] +check ! [ -e qrap ] +host CFLAGS="-Werror" make +check [ -f passt ] +check [ -h pasta ] +check [ -f qrap ] + +test Build AVX2 +host make clean +check ! [ -e passt ] +check ! [ -e pasta ] +check ! [ -e qrap ] +host CFLAGS="-Werror" make avx2 +check [ -f passt ] +check [ -h pasta ] +check [ -f qrap ] diff --git a/test/build/install b/test/build/install new file mode 100644 index 0000000..0a09fc7 --- /dev/null +++ b/test/build/install @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: AGPL-3.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/build/install - Check that binaries and man pages can be installed +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +req build/all +htools make mkdir cp rm man +tempdir TEMP + +test Install +host prefix=__TEMP__ make install +check [ -f __TEMP__/bin/passt ] +check [ -h __TEMP__/bin/pasta ] +check [ -f __TEMP__/bin/qrap ] +check man -M __TEMP__/man -W passt +check man -M __TEMP__/man -W pasta +check man -M __TEMP__/man -W qrap + +test Uninstall +host prefix=__TEMP__ make uninstall +check ! [ -f __TEMP__/bin/passt ] +check ! [ -h __TEMP__/bin/pasta ] +check ! [ -f __TEMP__/bin/qrap ] +check ! man -M __TEMP__/man -W passt 2>/dev/null +check ! man -M __TEMP__/man -W pasta 2>/dev/null +check ! man -M __TEMP__/man -W qrap 2>/dev/null diff --git a/test/ci b/test/ci new file mode 120000 index 0000000..e5224d5 --- /dev/null +++ b/test/ci @@ -0,0 +1 @@ +run \ No newline at end of file diff --git a/test/demo/passt b/test/demo/passt new file mode 100644 index 0000000..a6edbf0 --- /dev/null +++ b/test/demo/passt @@ -0,0 +1,248 @@ +# SPDX-License-Identifier: AGPL-3.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/demo/passt - Quick introduction to passt +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt +say This is a short introduction to +em passt +say . +nl +nl +sleep 3 + +say Let's fetch the source +sleep 1 +tempdir TEMPDIR +host cd __TEMPDIR__ +host git clone https://passt.top/passt +sleep 1 + +say and build it. +sleep 1 +host cd passt +host make avx2 +sleep 1 + +nl +nl +say A quick look at the man page... +sleep 1 +hostb man ./passt.1 +sleep 5 +hostb /ports +sleep 2 +hostb n +sleep 2 +hostb n +sleep 10 + +nl +say '-t' to forward TCP ports. +sleep 3 +host q + +nl +nl +say Let's create a small initramfs image for the guest. +guest cd __TEMPDIR__ +guest git clone https://mbuto.lameexcu.se/mbuto +guest cd mbuto +guest ./mbuto -f passt.img -p passt -c lz4 +sleep 2 + +nl +nl +say We want to isolate passt and guest in a +nl +say network namespace. For convenience, we'll +nl +say create it with 'pasta', see also the +nl +say 'pasta' demo above. +sleep 3 + +passt cd __TEMPDIR__/passt +passt ./pasta +sleep 3 +passt /sbin/dhclient +sleep 2 +passt /sbin/dhclient -6 +sleep 2 + +nl +nl +say Now let's run 'passt' in the new namespace, and +nl +say enter this namespace from the guest terminal too. +sleep 3 +pout TARGET_PID echo $$ +sleep 1 + +passtb ./passt -f -t 5201,5203 +sleep 2 + +guest nsenter -t __TARGET_PID__ -U -n --preserve-credentials +sleep 5 + +nl +nl +say We're ready to start qemu with the qrap wrapper, +nl +say that we currently need to connect the netdev +nl +say back-end to passt's UNIX domain socket. +sleep 2 +hout VMLINUZ echo "/boot/vmlinuz-$(uname -r)" +guest ../passt/qrap 5 kvm -m 4096 -cpu host -smp 4 -kernel __VMLINUZ__ -initrd passt.img -nographic -serial stdio -nodefaults -append "console=ttyS0 virtio-net.napi_tx=1" -device virtio-net-pci,netdev=hostnet0,x-txburst=16384 -netdev socket,fd=5,id=hostnet0 +sleep 10 + +nl +nl +guest ip li sh +sleep 3 +say Guest is up. Let's configure IPv4 first... +sleep 2 +guest dhclient +sleep 2 +guest ip ad sh +sleep 5 + +nl +say SLAAC is already done, but we can also +nl +say get another address via DHCPv6. +sleep 3 +guest dhclient -6 +sleep 3 + +nl +nl +say Let's try to communicate between host and guest. +sleep 2 +guestb nc -6 -l -p 5201 +sleep 2 +host echo "Hello from the host" | nc -N ::1 5201 +sleep 5 + +nl +nl +say Now the other way around... using +nl +say the address of the default gateway. +sleep 2 +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +sleep 5 +hostb nc -l -p 31337 +sleep 2 +guest echo "Hello from the guest" | nc -N __GW__ 31337 +sleep 3 + +nl +nl +say Let's have a (quick!) look at performance +nl +say more in the "Performance" section below. +sleep 3 + +host nsenter -t __TARGET_PID__ -U -n --preserve-credentials + +guest /sbin/sysctl -w net.core.rmem_max=536870912 +guest /sbin/sysctl -w net.core.wmem_max=536870912 +guest /sbin/sysctl -w net.core.rmem_default=33554432 +guest /sbin/sysctl -w net.core.wmem_default=33554432 +guest /sbin/sysctl -w net.ipv4.tcp_rmem="4096 131072 268435456" +guest /sbin/sysctl -w net.ipv4.tcp_wmem="4096 131072 268435456" +guest /sbin/sysctl -w net.ipv4.tcp_timestamps=0 + +host sysctl -w net.ipv4.tcp_rmem="4096 524288 134217728" +host sysctl -w net.ipv4.tcp_wmem="4096 524288 134217728" +host sysctl -w net.ipv4.tcp_timestamps=0 + +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +nl +nl +info Throughput in Gbps, latency in µs +th flow host>guest guest>host + +set OPTS -P4 -w 64M -l 1M -i1 --pacing-timer 100000 + +tr TCP/IPv6 throughput +hostb sleep 10; iperf3 -c ::1 __OPTS__ +gout BW iperf3 -s1J | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 2.0 3.0 +sleep 5 +guestb sleep 10; iperf3 -c __GW6__%__IFNAME__ -p 5202 __OPTS__ -O3 +hout BW iperf3 -s1J -p 5202 | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 2.0 3.0 + +tl TCP/IPv6 RR latency +guestb tcp_rr -C 5201 -P 5203 -6 --nolog +sleep 2 +hout LAT tcp_rr -C 5201 -P 5203 --nolog -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 +hostb tcp_rr -6 --nolog +sleep 2 +gout LAT tcp_rr --nolog -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 + +tl TCP/IPv6 CRR latency +guestb tcp_crr -C 5201 -P 5203 -6 --nolog +sleep 2 +hout LAT tcp_crr -C 5201 -P 5203 --nolog -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 +hostb tcp_crr -6 --nolog +sleep 2 +gout LAT tcp_crr --nolog -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 + +tr TCP/IPv4 throughput +hostb sleep 10; iperf3 -c 127.0.0.1 __OPTS__ +gout BW iperf3 -s1J | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 2.0 3.0 +sleep 5 +guestb sleep 10; iperf3 -c __GW__ -p 5202 __OPTS__ -O3 +hout BW iperf3 -s1J -p 5202 | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 2.0 3.0 + +tl TCP/IPv4 RR latency +guestb tcp_rr -C 5201 -P 5203 -4 --nolog +sleep 2 +hout LAT tcp_rr -C 5201 -P 5203 --nolog -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 +hostb tcp_rr -4 --nolog +sleep 2 +gout LAT tcp_rr --nolog -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 + +tl TCP/IPv4 CRR latency +guestb tcp_crr -C 5201 -P 5203 -4 --nolog +sleep 2 +hout LAT tcp_crr -C 5201 -P 5203 --nolog -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 +hostb tcp_crr -4 --nolog +sleep 2 +gout LAT tcp_crr --nolog -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 800 +sleep 2 + +nl +nl +say Thanks for watching! +sleep 5 diff --git a/test/demo/pasta b/test/demo/pasta new file mode 100644 index 0000000..508323a --- /dev/null +++ b/test/demo/pasta @@ -0,0 +1,274 @@ +# SPDX-License-Identifier: AGPL-3.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/demo/pasta - Quick introduction to pasta +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor pasta +say This is a short introduction to +em pasta +say . +nl +nl +sleep 3 + +say Let's fetch the source +sleep 1 +tempdir TEMPDIR +host cd __TEMPDIR__ +host git clone https://passt.top/passt +sleep 1 + +say and build it. +sleep 1 +host cd passt +host make +sleep 1 + +nl +nl +say A quick look at the man page... +sleep 1 +hostb man ./pasta.1 +sleep 5 +hostb /pasta +sleep 2 +hostb n +sleep 2 +hostb n +sleep 10 + +nl +say without TARGET_PID, it will create a namespace. +sleep 3 +passt cd __TEMPDIR__/passt +passt ./pasta +sleep 3 + +nl +nl +say For convenience, let's enter this namespace +nl +say from another terminal. +sleep 3 +pout TARGET_PID echo $$ +sleep 1 + +ns nsenter -t __TARGET_PID__ -U -n --preserve-credentials +sleep 5 + +nl +nl +say Now, we're ready to configure networking. +sleep 2 +host q + +nl +nl +ns ip li sh +sleep 3 +say Let's configure IPv4 first... +sleep 2 +ns dhclient +sleep 2 +ns ip ad sh +sleep 5 + +nl +say SLAAC is already done, but we can also +nl +say get another address via DHCPv6. +sleep 3 +ns dhclient -6 +sleep 3 + +nl +nl +say Let's try to communicate between host and namespace +sleep 2 +nl +say ...there's no need to configure port forwarding, +nl +say pasta detects bound ports and forwards them. +sleep 3 + +nsb nc -6 -l -p 31337 +sleep 2 +host echo "Hello from the host" | nc -N ::1 31337 +sleep 5 + +nl +nl +say Now the other way around... +nl +say we can use a loopback address +sleep +hostb nc -l -p 31337 +sleep 2 +ns echo "Hello from the namespace" | nc -N 127.0.0.1 31337 +sleep 5 + +nl +say or the address of the default gateway. +sleep 2 +nsout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +sleep 5 +hostb nc -l -p 31337 +sleep 2 +ns echo "Hello from the namespace" | nc -N __GW__ 31337 +sleep 3 + +nl +nl +say UDP... +sleep 2 +ns host -t A passt.top +sleep 3 +say seems to work too. +sleep 3 + +nl +nl +em pasta +say can also take packet captures. +sleep 3 +passt exit +sleep 2 +temp TEMP +passtb ./pasta -p __TEMP__.pcap +sleep 2 +passt +passt /sbin/dhclient +sleep 2 +hostb tshark -r __TEMP__.pcap +sleep 5 + +nl +nl +say And there are tons of totally useless +sleep 1 +bsp 14 +say absolutely useful features +nl +say you can find described in the man page. +sleep 5 + +nl +nl +say Let's have a (quick!) look at performance +nl +say more in the "Performance" section below. +sleep 3 +passt exit +passt CFLAGS="-g" make avx2 +sleep 2 +passt perf record -g ./pasta +sleep 2 + +pout TARGET_PID echo $$ +sleep 1 +ns nsenter -t __TARGET_PID__ -U -n --preserve-credentials +sleep 5 + +nl +nl +info Throughput in Gbps, latency in µs +th flow init>ns ns>init + +set OPTS -P4 -l 1M -w 32M -i1 --pacing-timer 100000 + +tr TCP/IPv6 throughput +nsb sleep 10; iperf3 -c ::1 __OPTS__ +hout BW iperf3 -s1J | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 10.0 20.0 +sleep 5 +hostb sleep 10; iperf3 -c ::1 __OPTS__ +nsout BW iperf3 -s1J | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 10.0 20.0 + +tl TCP/IPv6 RR latency +hostb tcp_rr -6 --nolog +sleep 2 +nsout LAT tcp_rr --nolog -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 +nsb tcp_rr -6 --nolog +sleep 2 +hout LAT tcp_rr --nolog -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 + +tl TCP/IPv6 CRR latency +hostb tcp_crr -6 --nolog +sleep 2 +nsout LAT tcp_crr --nolog -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 +nsb tcp_crr -6 --nolog +sleep 2 +hout LAT tcp_crr --nolog -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 + +tr TCP/IPv4 throughput +nsb sleep 10; iperf3 -c 127.0.0.1 __OPTS__ +hout BW iperf3 -s1J | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 10.0 20.0 +sleep 5 +hostb sleep 10; iperf3 -c 127.0.0.1 __OPTS__ +nsout BW iperf3 -s1J | jq -rM ".end.sum_received.bits_per_second" +bw __BW__ 10.0 20.0 + +tl TCP/IPv4 RR latency +hostb tcp_rr -4 --nolog +sleep 2 +nsout LAT tcp_rr --nolog -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 +nsb tcp_rr -4 --nolog +sleep 2 +hout LAT tcp_rr --nolog -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 + +tl TCP/IPv4 CRR latency +hostb tcp_crr -4 --nolog +sleep 2 +nsout LAT tcp_crr --nolog -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 +nsb tcp_crr -4 --nolog +sleep 2 +hout LAT tcp_crr --nolog -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 1000 500 +sleep 2 + +sleep 5 +passt exit +sleep 2 +killp PASST +killp HOST +sleep 2 +nsb perf report -g --max-stack 3 +sleep 10 + +nl +nl +say I +em knew +say it. +em syscalls +say . +sleep 5 + +nl +nl +say Thanks for watching! +sleep 5 diff --git a/test/dhcp/passt b/test/dhcp/passt new file mode 100644 index 0000000..85cb264 --- /dev/null +++ b/test/dhcp/passt @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: AGPL-3.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/dhcp/passt - Check DHCP and DHCPv6 functionality in passt mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt passt_in_ns +gtools ip jq dhclient sed tr +htools ip jq sed tr head + +test Interface name +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +hout HOST_IFNAME ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").dev' +check [ -n "__IFNAME__" ] + +test DHCP: address +guest /sbin/dhclient __IFNAME__ +gout ADDR ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' +hout HOST_ADDR ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local' +check [ "__ADDR__" = "__HOST_ADDR__" ] + +test DHCP: route +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +hout HOST_GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +check [ "__GW__" = "__HOST_GW__" ] + +test DHCP: MTU +gout MTU ip -j li sh | jq -rM '.[] | select(.ifname == "__IFNAME__").mtu' +check [ __MTU__ = 65520 ] + +test DHCP: DNS +gout DNS sed -n 's/^nameserver \([0-9]*\.\)\(.*\)/\1\2/p' /etc/resolv.conf | tr '\n' ',' | sed 's/\(.*\)/\1\n/g' +hout HOST_DNS sed -n 's/^nameserver \([0-9]*\.\)\(.*\)/\1\2/p' /etc/resolv.conf | head -n3 | tr '\n' ',' | sed 's/\(.*\)/\1\n/g' +check [ "__DNS__" = "__HOST_DNS__" ] + +# FQDNs should be terminated by dots, but the guest DHCP client might omit them: +# strip them first +test DHCP: search list +gout SEARCH sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/\(.*\)/\1\n/g' +hout HOST_SEARCH sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/\(.*\)/\1\n/g' +check [ "__SEARCH__" = "__HOST_SEARCH__" ] + +test DHCPv6: address +guest /sbin/dhclient -6 __IFNAME__ +gout ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local' +hout HOST_ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[] | select(.scope == "global").local' +check [ "__ADDR6__" = "__HOST_ADDR6__" ] + +test DHCPv6: route +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +hout HOST_GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +check [ "__GW6__" = "__HOST_GW6__" ] + +# Strip interface specifier: interface names might differ between host and guest +test DHCPv6: DNS +gout DNS6 sed -n 's/^nameserver \([^:]*:\)\([^%]*\).*/\1\2/p' /etc/resolv.conf | tr '\n' ',' | sed 's/\(.*\)/\1\n/g' +hout HOST_DNS6 sed -n 's/^nameserver \([^:]*:\)\([^%]*\).*/\1\2/p' /etc/resolv.conf | tr '\n' ',' | sed 's/\(.*\)/\1\n/g' +check [ "__DNS6__" = "__HOST_DNS6__" ] + +test DHCPv6: search list +gout SEARCH6 sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/\(.*\)/\1\n/g' +hout HOST_SEARCH6 sed 's/\. / /g' /etc/resolv.conf | sed 's/\.$//g' | sed -n 's/^search \(.*\)/\1/p' | tr ' \n' ',' | sed 's/\(.*\)/\1\n/g' +check [ "__SEARCH6__" = "__HOST_SEARCH6__" ] diff --git a/test/dhcp/pasta b/test/dhcp/pasta new file mode 100644 index 0000000..1e8ecaa --- /dev/null +++ b/test/dhcp/pasta @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: AGPL-3.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/dhcp/pasta - Check DHCP and DHCPv6 functionality in pasta mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor pasta +nstools ip jq /sbin/udhcpc /sbin/dhclient +htools ip jq + +test Interface name +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +check [ -n "__IFNAME__" ] + +test DHCP: address +ns /sbin/udhcpc -i __IFNAME__ +nsout ADDR ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' +hout HOST_ADDR ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' +check [ __ADDR__ = __HOST_ADDR__ ] + +test DHCP: route +nsout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +hout HOST_GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +check [ __GW__ = __HOST_GW__ ] + +test DHCP: MTU +nsout MTU ip -j li sh | jq -rM '.[] | select(.ifname == "__IFNAME__").mtu' +check [ __MTU__ = 65520 ] + +test DHCPv6: address +ns /sbin/dhclient -6 __IFNAME__ +nsout ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local' +hout HOST_ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global").local' +check [ __ADDR6__ = __HOST_ADDR6__ ] + +test DHCPv6: route +nsout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +hout HOST_GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +check [ __GW6__ = __HOST_GW6__ ] diff --git a/test/env/mate-terminal.profile b/test/env/mate-terminal.profile new file mode 100644 index 0000000..01379fb --- /dev/null +++ b/test/env/mate-terminal.profile @@ -0,0 +1,39 @@ +[/] +allow-bold=true +background-color='#121212121212' +background-darkness=0.5 +background-image='' +background-type='solid' +backspace-binding='ascii-del' +bold-color='#000000000000' +bold-color-same-as-fg=true +copy-selection=false +cursor-blink-mode='on' +cursor-shape='block' +custom-command='' +default-show-menubar=true +default-size-columns=180 +default-size-rows=40 +delete-binding='escape-sequence' +exit-action='close' +font='Bitstream Vera Sans Mono 9' +foreground-color='#504FA2A26160' +login-shell=false +palette='#000000000000:#FFFF8F8F8F8F:#504FA2A26160:#CBCB6C6C0505:#8E8EA3A2FFFF:#C4C45959C4C4:#0000AAAAAAAA:#AAAAAAAAAAAA:#555455545554:#FFFF8F8F8F8F:#504FA2A26160:#CBCB6C6C0505:#8E8EA3A2FFFF:#C4C45959C4C4:#5554FFFFFFFF:#FFFFFFFFFFFF' +scroll-background=true +scroll-on-keystroke=true +scroll-on-output=false +scrollback-lines=512 +scrollback-unlimited=true +scrollbar-position='hidden' +silent-bell=false +title='Terminal' +title-mode='replace' +use-custom-command=false +use-custom-default-size=true +use-skey=true +use-system-font=false +use-theme-colors=false +use-urls=true +visible-name='passt_ci' +word-chars='-A-Za-z0-9,./?%&#:_=+@~' diff --git a/test/icmp/passt_in_ns b/test/icmp/passt_in_ns new file mode 100644 index 0000000..be5be29 --- /dev/null +++ b/test/icmp/passt_in_ns @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: AGPL-3.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/icmp/passt_in_ns - Check ICMP/ICMPv6 functionality for passt in ns +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio +# +# These tests can work reliably only within an isolated namespace: the host +# might have a net.ipv4.ping_group_range sysctl value not allowing pasta's gid +# to create "ping" sockets. Inside the namespace, there's a single group, which +# is allowed by default to create them. + +onlyfor passt_in_ns +nstools ip jq sleep +gtools ping ip jq + +test ICMP echo: guest to ns +nsout IFNAME_NS ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +ns ip addr add 192.0.2.1/32 dev __IFNAME_NS__ +guest ping -c1 -w1 192.0.2.1 +gout RET echo $? +ns ip addr del 192.0.2.1/32 dev __IFNAME_NS__ +check [ __RET__ -eq 0 ] + +test ICMPv6 echo: guest to ns +ns ip addr add 2001:db8::1 dev __IFNAME_NS__ && sleep 2 # DAD +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +guest ping -c1 -w1 2001:db8::1 +gout RET echo $? +ns ip addr del 2001:db8::1 dev __IFNAME_NS__ +check [ __RET__ -eq 0 ] diff --git a/test/lib/layout b/test/lib/layout new file mode 100644 index 0000000..a07f9c9 --- /dev/null +++ b/test/lib/layout @@ -0,0 +1,276 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/lib/layout - tmux pane layouts +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +# layout_host() - Simple host commands layout with info and host panes +layout_host() { + sleep 3 + + tmux kill-pane -a -t 0 + cmd_write 0 clear + + tmux split-window -h -l '35%' -t passt_test:1.0 + + PANE_HOST=0 + PANE_INFO=1 + + get_info_cols + + tmux send-keys -l -t ${PANE_INFO} 'while cat /tmp/.passt_test_log_pipe; do :; done' + tmux send-keys -t ${PANE_INFO} -N 100 C-m + tmux select-pane -t ${PANE_INFO} -T "test log" + + tmux pipe-pane -O -t ${PANE_HOST} "cat >> ${LOGDIR}/pane_host.log" + tmux select-pane -t ${PANE_HOST} -T "host" + + info_layout "host commands only" + + sleep 1 +} + +# layout_pasta() - Panes for host, pasta, and separate one for namespace +layout_pasta() { + sleep 3 + + tmux kill-pane -a -t 0 + cmd_write 0 clear + + tmux split-window -v -t passt_test + tmux split-window -h -t passt_test + tmux split-window -h -l '42%' -t passt_test:1.0 + + PANE_NS=0 + PANE_INFO=1 + PANE_HOST=2 + PANE_PASST=3 + + get_info_cols + + tmux pipe-pane -O -t ${PANE_NS} "cat >> ${LOGDIR}/pane_ns.log" + tmux select-pane -t ${PANE_NS} -T "namespace" + + tmux send-keys -l -t ${PANE_INFO} 'while cat /tmp/.passt_test_log_pipe; do :; done' + tmux send-keys -t ${PANE_INFO} -N 100 C-m + tmux select-pane -t ${PANE_INFO} -T "test log" + + tmux pipe-pane -O -t ${PANE_HOST} "cat >> ${LOGDIR}/pane_host.log" + tmux select-pane -t ${PANE_HOST} -T "host" + + tmux pipe-pane -O -t ${PANE_PASST} "cat >> ${LOGDIR}/pane_passt.log" + tmux select-pane -t ${PANE_PASST} -T "pasta" + + info_layout "single pasta instance with namespace" + + sleep 1 +} + +# layout_passt() - Panes for host, passt, and guest +layout_passt() { + sleep 3 + + tmux kill-pane -a -t 0 + cmd_write 0 clear + + tmux split-window -v -t passt_test + tmux split-window -h -t passt_test + tmux split-window -h -l '42%' -t passt_test:1.0 + + PANE_GUEST=0 + PANE_INFO=1 + PANE_HOST=2 + PANE_PASST=3 + + get_info_cols + + tmux pipe-pane -O -t ${PANE_GUEST} "cat >> ${LOGDIR}/pane_guest.log" + tmux select-pane -t ${PANE_GUEST} -T "guest" + + tmux send-keys -l -t ${PANE_INFO} 'while cat /tmp/.passt_test_log_pipe; do :; done' + tmux send-keys -t ${PANE_INFO} -N 100 C-m + tmux select-pane -t ${PANE_INFO} -T "test log" + + tmux pipe-pane -O -t ${PANE_HOST} "cat >> ${LOGDIR}/pane_host.log" + tmux select-pane -t ${PANE_HOST} -T "host" + + tmux pipe-pane -O -t ${PANE_PASST} "cat >> ${LOGDIR}/pane_passt.log" + tmux select-pane -t ${PANE_PASST} -T "passt" + + info_layout "single passt instance with guest" + + sleep 1 +} + +# layout_passt_in_pasta() - Host, passt within pasta, namespace and guest +layout_passt_in_pasta() { + sleep 3 + + tmux kill-pane -a -t 0 + cmd_write 0 clear + + tmux split-window -v -l '45%' -t passt_test + tmux split-window -h -t passt_test + tmux split-window -h -l '42%' -t passt_test:1.0 + tmux split-window -v -t passt_test:1.0 + + PANE_GUEST=0 + PANE_NS=1 + PANE_INFO=2 + PANE_HOST=3 + PANE_PASST=4 + + get_info_cols + + tmux pipe-pane -O -t ${PANE_GUEST} "cat >> ${LOGDIR}/pane_guest.log" + tmux select-pane -t ${PANE_GUEST} -T "guest" + + tmux pipe-pane -O -t ${PANE_NS} "cat >> ${LOGDIR}/pane_ns.log" + tmux select-pane -t ${PANE_NS} -T "namespace" + + tmux send-keys -l -t ${PANE_INFO} 'while cat /tmp/.passt_test_log_pipe; do :; done' + tmux send-keys -t ${PANE_INFO} -N 100 C-m + tmux select-pane -t ${PANE_INFO} -T "test log" + + tmux pipe-pane -O -t ${PANE_HOST} "cat >> ${LOGDIR}/pane_host.log" + tmux select-pane -t ${PANE_HOST} -T "host" + + tmux pipe-pane -O -t ${PANE_PASST} "cat >> ${LOGDIR}/pane_passt.log" + tmux select-pane -t ${PANE_PASST} -T "passt in pasta (namespace)" + + info_layout "passt and guest in namespace, connected by pasta" + + sleep 1 +} + +# layout_two_guests() - Two guest panes, two passt panes, plus host and log +layout_two_guests() { + sleep 3 + + tmux kill-pane -a -t 0 + cmd_write 0 clear + + tmux split-window -v -t passt_test + tmux split-window -h -l '33%' + tmux split-window -h -t passt_test:1.1 + + tmux split-window -h -l '35%' -t passt_test:1.0 + tmux split-window -v -t passt_test:1.0 + + for i in `seq 0 5`; do + tmux select-pane -t $i -T "${i}" + done + + PANE_GUEST_1=0 + PANE_GUEST_2=1 + PANE_INFO=2 + PANE_HOST=3 + PANE_PASST_1=4 + PANE_PASST_2=5 + + get_info_cols + + tmux pipe-pane -O -t ${PANE_GUEST_1} "cat >> ${LOGDIR}/pane_guest_1.log" + tmux select-pane -t ${PANE_GUEST_1} -T "guest #1 in namespace #1" + + tmux pipe-pane -O -t ${PANE_GUEST_2} "cat >> ${LOGDIR}/pane_guest_2.log" + tmux select-pane -t ${PANE_GUEST_2} -T "guest #2 in namespace #2" + + tmux send-keys -l -t ${PANE_INFO} 'while cat /tmp/.passt_test_log_pipe; do :; done' + tmux send-keys -t ${PANE_INFO} -N 100 C-m + tmux select-pane -t ${PANE_INFO} -T "test log" + + tmux pipe-pane -O -t ${PANE_HOST} "cat >> ${LOGDIR}/pane_host.log" + tmux select-pane -t ${PANE_HOST} -T "host" + + tmux pipe-pane -O -t ${PANE_PASST_1} "cat >> ${LOGDIR}/pane_passt_1.log" + tmux select-pane -t ${PANE_PASST_1} -T "passt #1 in namespace #1" + + tmux pipe-pane -O -t ${PANE_PASST_2} "cat >> ${LOGDIR}/pane_passt_2.log" + tmux select-pane -t ${PANE_PASST_2} -T "passt #2 in namespace #2" + + info_layout "two guests, two passt instances, in namespaces" + + sleep 1 +} + +# layout_demo_pasta() - Four panes for pasta demo +layout_demo_pasta() { + sleep 3 + + tmux kill-pane -a -t 0 + cmd_write 0 clear + sleep 1 + cmd_write 0 clear + + tmux split-window -v -t passt_test + tmux split-window -h -t passt_test + tmux split-window -h -l '42%' -t passt_test:1.0 + + PANE_NS=0 + PANE_INFO=1 + PANE_HOST=2 + PANE_PASST=3 + + get_info_cols + + tmux pipe-pane -O -t ${PANE_NS} "cat >> ${LOGDIR}/pane_ns.log" + tmux select-pane -t ${PANE_NS} -T "namespace" + + tmux send-keys -l -t ${PANE_INFO} 'while cat /tmp/.passt_test_log_pipe; do :; done' + tmux send-keys -t ${PANE_INFO} -N 100 C-m + tmux select-pane -t ${PANE_INFO} -T "" + + tmux pipe-pane -O -t ${PANE_HOST} "cat >> ${LOGDIR}/pane_host.log" + tmux select-pane -t ${PANE_HOST} -T "host" + + tmux pipe-pane -O -t ${PANE_PASST} "cat >> ${LOGDIR}/pane_passt.log" + tmux select-pane -t ${PANE_PASST} -T "pasta" + + sleep 1 +} + +# layout_demo_passt() - Four panes for passt demo +layout_demo_passt() { + sleep 3 + + tmux kill-pane -a -t 0 + cmd_write 0 clear + sleep 1 + cmd_write 0 clear + + tmux split-window -v -t passt_test + tmux split-window -h -t passt_test + tmux split-window -h -l '42%' -t passt_test:1.0 + + PANE_GUEST=0 + PANE_INFO=1 + PANE_HOST=2 + PANE_PASST=3 + + get_info_cols + + tmux pipe-pane -O -t ${PANE_GUEST} "cat >> ${LOGDIR}/pane_guest.log" + tmux select-pane -t ${PANE_GUEST} -T "guest" + + tmux send-keys -l -t ${PANE_INFO} 'while cat /tmp/.passt_test_log_pipe; do :; done' + tmux send-keys -t ${PANE_INFO} -N 100 C-m + tmux select-pane -t ${PANE_INFO} -T "" + + tmux pipe-pane -O -t ${PANE_HOST} "cat >> ${LOGDIR}/pane_host.log" + tmux select-pane -t ${PANE_HOST} -T "host" + + tmux pipe-pane -O -t ${PANE_PASST} "cat >> ${LOGDIR}/pane_passt.log" + tmux select-pane -t ${PANE_PASST} -T "passt in pasta (namespace)" + + sleep 1 +} diff --git a/test/lib/perf_report b/test/lib/perf_report new file mode 100755 index 0000000..111c154 --- /dev/null +++ b/test/lib/perf_report @@ -0,0 +1,262 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/lib/perf_report - Prepare JavaScript report for performance tests +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +PERF_LINK_COUNT=0 +PERF_JS="${BASEPATH}/perf.js" + +PERF_TEMPLATE_HTML="document.write('"' +Throughput in Gbps, latency in µs. Threads are iperf3 processes, passt and pasta are currently single-threaded.
+Click on numbers to show test execution. Measured at head, commit __commit__. + + + +
    +
  • passt

    + + + + + + + + __passt_tcp_header__ + __passt_udp_header__ + + __passt_tcp_LINE__ __passt_udp_LINE__ +
    + TCP, __passt_tcp_threads__ at __passt_tcp_freq__ GHzUDP, __passt_udp_threads__ at __passt_udp_freq__ GHz
    MTU:
    + + + +
  • pasta: local connections/traffic

    + + + + + + + + __pasta_lo_tcp_header__ + __pasta_lo_udp_header__ + + __pasta_lo_tcp_LINE__ __pasta_lo_udp_LINE__ +
    + TCP, __pasta_lo_tcp_threads__ at __pasta_lo_tcp_freq__ GHzUDP, __pasta_lo_udp_threads__ at __pasta_lo_udp_freq__ GHz
    MTU:
    + +
  • pasta: connections/traffic via tap

    + + + + + + + + __pasta_tap_tcp_header__ + __pasta_tap_udp_header__ + + __pasta_tap_tcp_LINE__ __pasta_tap_udp_LINE__ +
    + TCP, __pasta_tap_tcp_threads__ at __pasta_tap_tcp_freq__ GHzUDP, __pasta_tap_udp_threads__ at __pasta_tap_udp_freq__ GHz
    MTU:
    + +
' + +PERF_TEMPLATE_JS="'); + +var perf_links = [ +" + +PERF_TEMPLATE_POST=']; + +for (var i = 0; i < perf_links.length; i++) { + var obj = document.getElementById(perf_links[i][0]); + + obj.addEventListener("click", function(event) { + var ci_video = document.getElementById("ci_video"); + var top = ci_video.offsetTop - 5; + + event.preventDefault(); + ci_video.play(); + ci_video.pause(); + for (var i = 0; i < perf_links.length; i++) { + if (this.id == perf_links[i][0]) { + ci_video.currentTime = perf_links[i][1] - 10; + } + } + window.scrollTo({ top: top, behavior: "smooth" }) + ci_video.play(); + }, false); +} +' + +# perf_init() - Process first part of template +perf_init() { + echo "${PERF_TEMPLATE_HTML}" > "${PERF_JS}" + perf_report_sub commit "$(echo ${COMMIT} | sed "s/'/\\\'/g")" +} + +# perf_fill_lines() - Fill multiple "LINE" directives in template, matching rows +perf_fill_lines() { + while true; do + __file_line="$(sed -n '/__.*_LINE__/{=;q}' "${PERF_JS}")" + [ -z "${__file_line}" ] && break + + __line_no=0 + __done=0 + __line_buf="" + while true; do + __match_first_td=0 + for __t in $(sed -n '/__.*_LINE__/{p;q}' "${PERF_JS}"); do + if [ ${__match_first_td} -eq 1 ]; then + __matching_line_no=0 + while true; do + __line_part= + __var_name="$(echo $__t | sed -n 's/__\(.*\)__/\1_'"${__matching_line_no}"'/p')" + [ -z "$(eval echo \$${__var_name})" ] && break + __line_part="$(eval echo \$${__var_name})" + __td_check="$(echo "${__line_part}" | sed -n 's/^\([^>]*\)<\/td>.*$/\1/p')" + if [ "${__td_check}" = "${__td_match}" ]; then + __line_part="$(echo "${__line_part}" | sed -n 's/^[^>]*<\/td>\(.*\)$/\1/p')" + break + fi + __matching_line_no=$((__matching_line_no + 1)) + done + else + __var_name="$(echo $__t | sed -n 's/__\(.*\)__/\1_'"${__line_no}"'/p')" + [ -z "$(eval echo \$${__var_name})" ] && __done=1 && break + __line_part="$(eval echo \$${__var_name})" + __td_match="$(echo "${__line_part}" | sed -n 's/^\([^>]*\)<\/td>.*$/\1/p')" + fi + __line_buf="${__line_buf}${__line_part}" + __match_first_td=1 + done + [ ${__done} -eq 1 ] && break + __line_no=$((__line_no + 1)) + __line_buf="${__line_buf}" + done + __line_buf="${__line_buf}" + __line_buf="$(printf '%s\n' "${__line_buf}" | sed -e 's/[]\/$*.^[]/\\&/g')" + sed -i "${__file_line}s/.*/${__line_buf}/" "${PERF_JS}" + done +} + +# perf_finish() - Add trailing backslashes and process ending templates +perf_finish() { + perf_fill_lines + sed -i 's/^.*$/&\\/g' "${PERF_JS}" + echo "${PERF_TEMPLATE_JS}" >> "${PERF_JS}" + echo "${PERF_TEMPLATE_POST}" >> "${PERF_JS}" +} + +# perf_report_sub() - Apply simple substitutions in template +perf_report_sub() { + __et="$(printf '%s\n' "${1}" | sed -e 's/[\/&]/\\&/g')" + __es="$(printf '%s\n' "${2}" | sed -e 's/[]\/$*.^[]/\\&/g')" + + sed -i 's/__'"${__et}"'__/'"${__es}"'/g' "${PERF_JS}" +} + +# perf_report_append() - Append generic string to current JavaScript report +perf_report_append() { + echo "${@}" >> "${PERF_JS}" +} + +# perf_report_append() - Append generic string to current template buffer +perf_report_append_js() { + PERF_TEMPLATE_JS="${PERF_TEMPLATE_JS}${@}" +} + +# perf_report() - Start of single test report +perf_report() { + __mode="${1}" + __proto="${2}" + __threads="${3}" + __freq="${4}" + + REPORT_IN="${__mode}_${__proto}" + + [ ${__threads} -eq 1 ] && __threads="one thread" || __threads="${__threads} threads" + perf_report_sub "${__mode}_${__proto}_threads" "${__threads}" + perf_report_sub "${__mode}_${__proto}_freq" "${__freq}" + + perf_report_append_js "[ 'perf_${__mode}_${__proto}', $(video_time_now) ]," +} + +# perf_th() - Table header for a set of tests +perf_th() { + shift + + __th_buf= + __cols_count=0 + for __arg; do + __th_buf="${__th_buf}${__arg}" + __cols_count=$((__cols_count + 1)) + done + perf_report_sub "${REPORT_IN}_header" "${__th_buf}" + perf_report_sub "${REPORT_IN}_cols" ${__cols_count} +} + +# perf_tr() - Main table row +perf_tr() { + __line_no=0 + shift + while true; do + [ -z "$(eval echo \$${REPORT_IN}_LINE_${__line_no})" ] && break + __line_no=$((__line_no + 1)) + done + eval ${REPORT_IN}_LINE_${__line_no}="\"${@}\"" +} + +# perf_td() - Single cell with test result +perf_td() { + __rewind="${1}" + shift + + __line_no=0 + while true; do + [ -z "$(eval echo \$${REPORT_IN}_LINE_${__line_no})" ] && break + __line_no=$((__line_no + 1)) + done + __line_no=$((__line_no - 1)) + [ -z "${1}" ] && __id=0 || __id="perf_${PERF_LINK_COUNT}" + eval ${REPORT_IN}_LINE_${__line_no}=\""\${${REPORT_IN}_LINE_${__line_no}}${1}"\" + [ -z "${1}" ] && return + + perf_report_append_js "[ '${__id}', $(($(video_time_now) - ${__rewind})) ]," + PERF_LINK_COUNT=$((PERF_LINK_COUNT + 1)) +} + +# perf_te() - End of a table, currently unused +pert_te() { + : +} diff --git a/test/lib/setup b/test/lib/setup new file mode 100755 index 0000000..fd68f05 --- /dev/null +++ b/test/lib/setup @@ -0,0 +1,322 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/lib/setup - Set up and tear down passt and pasta environments +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +VCPUS="$( [ $(nproc) -ge 8 ] && echo 4 || echo $(( $(nproc) / 2 + 1 )) )" +__mem_kib="$(sed -n 's/MemTotal:[ ]*\([0-9]*\) kB/\1/p' /proc/meminfo)" +VMEM="$((${__mem_kib} / 1024 / 8))" + +# setup_build() - Set up pane layout for build tests +setup_build() { + MODE=build + + layout_host +} + +# setup_passt() - Build guest initrd with mbuto, start qemu and passt +setup_passt() { + MODE=passt + + layout_passt + + __mbuto_dir="$(mktemp -d)" + pane_run GUEST "git -C ${__mbuto_dir} clone https://mbuto.lameexcu.se/mbuto/" + pane_wait GUEST + + pane_run GUEST "${__mbuto_dir}/mbuto/mbuto -p passt -c lz4 -f mbuto.img" + pane_wait GUEST + + rm -rf "${__mbuto_dir}" + + # Ports: + # + # guest | host + # --------------|--------------------- + # 10001 as server | forwarded to guest + # 10003 | as server + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/passt.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + pane_run PASST "./passt ${__opts} -f -t 10001 -u 10001" + sleep 1 + + pane_run GUEST './qrap 5 kvm -m '${VMEM}' -cpu host -smp '${VCPUS} \ + '-kernel' "/boot/vmlinuz-$(uname -r)" \ + '-initrd mbuto.img -nographic -serial stdio' \ + '-nodefaults ' \ + '-append "console=ttyS0 mitigations=off apparmor=0 ' \ + 'virtio-net.napi_tx=1"' \ + "-device virtio-net-pci,netdev=hostnet0,x-txburst=16384"\ + "-netdev socket,fd=5,id=hostnet0" + pane_wait GUEST +} + +# setup_pasta() - Create a network and user namespace, connect pasta to it +setup_pasta() { + MODE=pasta + + layout_pasta + + pane_run NS "unshare -rUn /bin/sh " + pane_wait NS + + pane_run NS 'echo $$' + pane_wait NS + __pasta_pid="$(pane_parse NS)" + + # Ports: + # + # ns | host + # ------------------|--------------------- + # 10002 as server | spliced to ns + # 10003 spliced to init | as server + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/pasta.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + + pane_run PASST "./pasta ${__opts} -f -t 10002 -T 10003 -u 10002 -U 10003 ${__pasta_pid}" + sleep 1 +} + +# setup_passt_in_ns() - Set up namespace (with pasta), run qemu and passt into it +setup_passt_in_ns() { + MODE=passt_in_ns + + layout_passt_in_pasta + + # Ports: + # + # guest | ns | host + # -------------|--------------------|----------------- + # 10001 as server | forwarded to guest | spliced to ns + # 10002 | as server | spliced to ns + # 10003 | spliced to init | as server + # 10011 as server | forwarded to guest | spliced to ns + # 10012 | as server | spliced to ns + # 10013 | spliced to init | as server + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/pasta_with_passt.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + + pane_run PASST "./pasta ${__opts} -t 10001,10002,10011,10012 -T 10003,10013 -u 10001,10002,10011,10012 -U 10003,10013" + sleep 1 + pane_run PASST '' + pane_wait PASST + pane_run PASST 'echo $$' + pane_wait PASST + __ns_pid="$(pane_parse PASST)" + + pane_run GUEST "nsenter -t ${__ns_pid} -U -n --preserve-credentials" + pane_run NS "nsenter -t ${__ns_pid} -U -n --preserve-credentials" + pane_wait GUEST + pane_wait NS + + pane_run NS "ip -j li sh | jq -rM '.[] | select(.link_type == \"ether\").ifname'" + pane_wait NS + __ifname="$(pane_parse NS)" + pane_run NS "/sbin/udhcpc -i ${__ifname}" + pane_wait NS + sleep 2 + pane_run NS "/sbin/dhclient -6 ${__ifname}" + pane_wait NS + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/passt_in_pasta.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + + #pane_run PASST "valgrind --max-stackframe=2097208 ./passt -f ${__opts} -t 10001,10011 -u 10001,10011" + pane_run PASST "./passt -f ${__opts} -t 10001,10011 -u 10001,10011" + sleep 1 + + pane_run GUEST './qrap 5 kvm -m '${VMEM}' -cpu host -smp '${VCPUS} \ + '-kernel' "/boot/vmlinuz-$(uname -r)" \ + '-initrd mbuto.img -nographic -serial stdio' \ + '-nodefaults ' \ + '-append "console=ttyS0 mitigations=off apparmor=0 ' \ + 'virtio-net.napi_tx=1"' \ + "-device virtio-net-pci,netdev=hostnet0,x-txburst=131072"\ + "-netdev socket,fd=5,id=hostnet0" + pane_wait GUEST +} + +# setup_two_guests() - Set up two namespace, run qemu and passt in both of them +setup_two_guests() { + MODE=passt_in_ns + + layout_two_guests + + # Ports: + # + # guest #1 | guest #2 | ns #1 | ns #2 | host + # --------- |-----------|-----------|------------|------------ + # 10001 as server | | to guest | to init | to ns #1 + # 10002 | | as server | | to ns #1 + # 10003 | | to init | to init | as server + # 10004 | as server | to init | to guest | to ns #2 + # 10005 | | | as server | to ns #2 + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/pasta_1.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + pane_run PASST_1 "./pasta ${__opts} -t 10001,10002 -T 10003,10004 -u 10001,10002 -U 10003,10004" + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/pasta_2.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + pane_run PASST_2 "./pasta ${__opts} -t 10004,10005 -T 10003,10001 -u 10004,10005 -U 10003,10001" + + sleep 1 + pane_run PASST_1 '' + pane_run PASST_2 '' + + pane_wait PASST_1 + pane_wait PASST_2 + pane_run PASST_1 'echo $$' + pane_run PASST_2 'echo $$' + pane_wait PASST_1 + pane_wait PASST_2 + __ns1_pid="$(pane_parse PASST_1)" + __ns2_pid="$(pane_parse PASST_2)" + + pane_run GUEST_1 "nsenter -t ${__ns1_pid} -U -n --preserve-credentials" + pane_run GUEST_2 "nsenter -t ${__ns2_pid} -U -n --preserve-credentials" + + pane_run PASST_1 "ip -j li sh | jq -rM '.[] | select(.link_type == \"ether\").ifname'" + pane_wait PASST_1 + __ifname="$(pane_parse PASST_1)" + + pane_run GUEST_1 "/sbin/udhcpc -i ${__ifname}" + pane_run GUEST_2 "/sbin/udhcpc -i ${__ifname}" + pane_wait GUEST_1 + pane_wait GUEST_2 + sleep 2 + pane_run GUEST_1 "/sbin/dhclient -6 ${__ifname}" + pane_run GUEST_2 "/sbin/dhclient -6 ${__ifname}" + pane_wait GUEST_1 + pane_wait GUEST_2 + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/passt_1.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + pane_run PASST_1 "./passt -f ${__opts} -t 10001 -u 10001" + sleep 1 + + __opts= + [ ${PCAP} -eq 1 ] && __opts="${__opts} -p /tmp/passt_2.pcap" + [ ${DEBUG} -eq 1 ] && __opts="${__opts} -d" + pane_run PASST_2 "./passt -f ${__opts} -t 10004 -u 10004" + + pane_run GUEST_2 'cp mbuto.img mbuto_2.img' + pane_wait GUEST_2 + + pane_run GUEST_1 './qrap 5 kvm -m '${VMEM}' -cpu host -smp '${VCPUS} \ + '-kernel' "/boot/vmlinuz-$(uname -r)" \ + '-initrd mbuto.img -nographic -serial stdio' \ + '-nodefaults ' \ + '-append "console=ttyS0 mitigations=off apparmor=0 ' \ + 'virtio-net.napi_tx=1"' \ + "-device virtio-net-pci,netdev=hostnet0,x-txburst=16384"\ + "-netdev socket,fd=5,id=hostnet0" + pane_run GUEST_2 './qrap 5 kvm -m '${VMEM}' -cpu host -smp '${VCPUS} \ + '-kernel' "/boot/vmlinuz-$(uname -r)" \ + '-initrd mbuto_2.img -nographic -serial stdio' \ + '-nodefaults ' \ + '-append "console=ttyS0 mitigations=off apparmor=0 ' \ + 'virtio-net.napi_tx=1"' \ + "-device virtio-net-pci,netdev=hostnet0,x-txburst=16384"\ + "-netdev socket,fd=5,id=hostnet0" + pane_wait GUEST_1 + pane_wait GUEST_2 +} + +# teardown_passt() - Kill qemu and passt +teardown_passt() { + tmux send-keys -t ${PANE_PASST} "C-c" + pane_wait PASST + tmux send-keys -t ${PANE_GUEST} "C-c" + pane_wait GUEST +} + +# teardown_passt() - Exit namespace, kill pasta process +teardown_pasta() { + tmux send-keys -t ${PANE_PASST} "C-c" + pane_wait PASST + tmux send-keys -t ${PANE_NS} "C-d" + pane_wait NS +} + +# teardown_passt_in_ns() - Exit namespace, kill qemu, passt and pasta +teardown_passt_in_ns() { + tmux send-keys -t ${PANE_GUEST} "C-c" + pane_wait GUEST + tmux send-keys -t ${PANE_GUEST} "C-d" + + tmux send-keys -t ${PANE_PASST} "C-c" + pane_wait PASST + tmux send-keys -t ${PANE_PASST} "C-d" + + tmux send-keys -t ${PANE_NS} "C-d" + + pane_wait GUEST + pane_wait NS + pane_wait PASST +} + +# teardown_two_guests() - Exit namespaces, kill qemu processes, passt and pasta +teardown_two_guests() { + tmux send-keys -t ${PANE_GUEST_1} "C-c" + pane_wait GUEST_1 + tmux send-keys -t ${PANE_GUEST_1} "C-d" + + tmux send-keys -t ${PANE_GUEST_2} "C-c" + pane_wait GUEST_2 + tmux send-keys -t ${PANE_GUEST_2} "C-d" + + tmux send-keys -t ${PANE_PASST_1} "C-c" + pane_wait PASST_1 + tmux send-keys -t ${PANE_PASST_1} "C-d" + + tmux send-keys -t ${PANE_PASST_2} "C-c" + pane_wait PASST_2 + tmux send-keys -t ${PANE_PASST_2} "C-d" + + tmux send-keys -t ${PANE_NS_1} "C-d" + tmux send-keys -t ${PANE_NS_2} "C-d" + + pane_wait GUEST_1 + pane_wait GUEST_2 + ns_1_wait + ns_2_wait + pane_wait PASST_1 + pane_wait PASST_2 +} + +# setup() - Run setup_*() functions +# $*: Suffix list of setup_*() functions to be called +setup() { + for arg do + eval setup_${arg} + done +} + +# teardown() - Run teardown_*() functions +# $*: Suffix list of teardown_*() functions to be called +teardown() { + for arg do + eval teardown_${arg} + done +} diff --git a/test/lib/term b/test/lib/term new file mode 100755 index 0000000..48f0f6a --- /dev/null +++ b/test/lib/term @@ -0,0 +1,610 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/lib/term - Set up tmux sessions and panes, handle terminals and logs +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +# Commands of X terminals for "CI" and "demo" runs +DEMO_XTERM="cool-retro-term --verbose --workdir" +CI_XTERM="mate-terminal --hide-menubar --profile=passt_ci --working-directory" + +STATUS_FILE= +STATUS_FILE_NTESTS= +STATUS_FILE_INDEX=0 +STATUS_COLS= +STATUS_PASS=0 +STATUS_FAIL=0 + +PR_RED='\033[1;31m' +PR_GREEN='\033[1;32m' +PR_YELLOW='\033[1;33m' +PR_BLUE='\033[1;34m' +PR_NC='\033[0m' +PR_DELAY_INIT=100 # ms + +# info() - Highlight test log pane, print message to it and to log file +# $@: Message to print +info() { + tmux select-pane -t ${PANE_INFO} + echo "${@}" >> /tmp/.passt_test_log_pipe + echo "${@}" >> "${LOGFILE}" +} + +# info_n() - Highlight, print message to pane and to log file without newline +# $@: Message to print +info_n() { + tmux select-pane -t ${PANE_INFO} + printf "${@}" >> /tmp/.passt_test_log_pipe + printf "${@}" >> "${LOGFILE}" +} + +# info_nolog() - Highlight test log pane, print message to it +# $@: Message to print +info_nolog() { + tmux select-pane -t ${PANE_INFO} + echo "${@}" >> /tmp/.passt_test_log_pipe +} + +# info_nolog() - Print message to log file +# $@: Message to print +log() { + echo "${@}" >> "${LOGFILE}" +} + +# info_nolog_n() - Send message to pane without highlighting it, without newline +# $@: Message to print +info_nolog_n() { + tmux send-keys -l -t ${PANE_INFO} "${@}" +} + +# info_sep() - Print given separator, horizontally filling test log pane +# $1: Separator character +info_sep() { + tmux send-keys -l -N ${STATUS_COLS} -t ${PANE_INFO} "${1}" + tmux send-keys -t ${PANE_INFO} C-m +} + +# sleep_char() - Sleep for typed characted resembling interactive input +# $1: Character typed to pane +sleep_char() { + [ ${FAST} -eq 1 ] && return + + if [ "${1}" = " " ]; then + PR_DELAY=$((PR_DELAY + 40)) + elif [ -n "$(printf '%s' "${1}" | tr -d [:alnum:])" ]; then + PR_DELAY=$((PR_DELAY + 30)) + elif [ ${PR_DELAY} -ge 30 ]; then + PR_DELAY=$((PR_DELAY / 3 * 2)) + fi + + sleep "$(printf 0.%03i ${PR_DELAY})" || sleep 1 +} + +# display_delay() - Simple delay, omitted if $FAST is set +display_delay() { + [ ${FAST} -eq 1 ] && return + + sleep "${1}" || sleep 1 +} + +# switch_pane() - Highlight given pane and reset character delay +# $1: Pane number +switch_pane() { + tmux select-pane -t ${1} + PR_DELAY=${PR_DELAY_INIT} + display_delay "0.2" +} + +# cmd_write() - Write a command to a pane, letter by letter, and execute it +# $1: Pane number +# $@: Command to issue +cmd_write() { + __pane_no=${1} + shift + + switch_pane ${__pane_no} + + __str="${@}" + while [ -n "${__str}" ]; do + __rem="${__str#?}" + __first="${__str%"$__rem"}" + if [ "${__first}" = ";" ]; then + tmux send-keys -t ${__pane_no} -l '\;' + else + tmux send-keys -t ${__pane_no} -l "${__first}" + fi + sleep_char "${__first}" + __str="${__rem}" + done + tmux send-keys -t ${__pane_no} "C-m" +} + +# text_write() - Write text to info pane, letter by letter +# $1: Pane number +# $@: Command to issue +text_write() { + __str="${@}" + while [ -n "${__str}" ]; do + __rem="${__str#?}" + __first="${__str%"$__rem"}" + if [ "${__first}" = ";" ]; then + tmux send-keys -t ${PANE_INFO} -l '\;' + else + tmux send-keys -t ${PANE_INFO} -l "${__first}" + fi + sleep_char "${__first}" + __str="${__rem}" + done +} + +# text_backspace() - Slow backspace motion for demo +# $1: Number of backspace characters +text_backspace() { + for __count in $(seq 0 ${1}); do + tmux send-keys -t ${PANE_INFO} Bspace + sleep 0.1 + done +} + +# em_write() - Write to log pane in red, for demo +# $@: Text +em_write() { + info_n "${PR_RED}${@}${PR_NC}" +} + +# pane_kill() - Kill a single pane given its name +# $1: Pane name +pane_kill() { + __pane_number=$(eval echo \$PANE_${1}) + tmux kill-pane -t ${__pane_number} +} + +# pane_highlight() - Highlight a single pane given its name +# $1: Pane name +pane_highlight() { + __pane_number=$(eval echo \$PANE_${1}) + switch_pane ${__pane_number} + sleep 3 +} + +# pane_run() - Issue a command in given pane name +# $1: Pane name +# $@: Command to issue +pane_run() { + __pane_name="${1}" + shift + + __pane_number=$(eval echo \$PANE_${__pane_name}) + + eval ${__pane_name}_LAST_CMD=\"\${@}\" + + cmd_write ${__pane_number} "${@}" +} + +# pane_wait() - Wait for command to be done in given pane name +# $1: Pane name +pane_wait() { + __pane_lc="$(echo "${1}" | tr [A-Z] [a-z])" + + while [ "$(tail -n1 ${LOGDIR}/pane_${__pane_lc}.log)" != '$ ' ] && \ + [ "$(tail -n1 ${LOGDIR}/pane_${__pane_lc}.log)" != '# ' ] && \ + [ "$(tail -n1 ${LOGDIR}/pane_${__pane_lc}.log)" != '# # ' ]; do + sleep 0.1 || sleep 1 + done +} + +# pane_parse() - Print last line, @EMPTY@ if command had no output +# $1: Pane name +pane_parse() { + __pane_lc="$(echo "${1}" | tr [A-Z] [a-z])" + + __buf="$(tail -n2 ${LOGDIR}/pane_${__pane_lc}.log | head -n1 | tr -d -c [:print:])" + + [ "# $(eval printf '%s' \"\$${1}_LAST_CMD\")" != "${__buf}" ] && \ + [ "$ $(eval printf '%s' \"\$${1}_LAST_CMD\")" != "${__buf}" ] && + printf '%s' "${__buf}" || printf '@EMPTY@' +} + +# status_file_end() - Display and log messages when tests from one file are done +status_file_end() { + [ -z "${STATUS_FILE}" ] && return + + info_sep "=" + log + tmux select-pane -t ${PANE_INFO} -T "" + STATUS_FILE= +} + +# status_file_start() - Display and log messages when tests from one file start +status_file_start() { + switch_pane ${PANE_INFO} + + status_file_end + + info_nolog "Starting tests in file: ${1}\n" + log "=== ${1}" + tmux select-pane -t ${PANE_INFO} -T "${1}" + + STATUS_FILE="${1}" + STATUS_FILE_NTESTS="${2}" + STATUS_FILE_INDEX=0 +} + +# status_file_start() - Display and log messages when a single test starts +status_test_start() { + switch_pane ${PANE_INFO} + + info_nolog "Starting test: ${1}" + log "> ${1}" + + STATUS_FILE_INDEX=$((STATUS_FILE_INDEX + 1)) + tmux select-pane -t ${PANE_INFO} -T "${STATUS_FILE} [${STATUS_FILE_INDEX}/${STATUS_FILE_NTESTS}] - ${1}" +} + +# info_check() - Display and log messages for a single test condition check +info_check() { + switch_pane ${PANE_INFO} + + printf "${PR_YELLOW}?${PR_NC} ${@}" >> /tmp/.passt_test_log_pipe + printf "? ${@}" >> "${LOGFILE}" +} + +# info_check_passed() - Display and log a new line when a check passes +info_check_passed() { + switch_pane ${PANE_INFO} + + printf "\n" >> /tmp/.passt_test_log_pipe + printf "\n" >> ${LOGFILE} +} + +# info_check_failed() - Display and log messages when a check fails +info_check_failed() { + switch_pane ${PANE_INFO} + + printf " ${PR_RED}!${PR_NC}\n" >> /tmp/.passt_test_log_pipe + printf " < failed.\n" >> "${LOGFILE}" +} + +# info_passed() - Display, log, and make status bar blink when a test passes +info_passed() { + switch_pane ${PANE_INFO} + + info_nolog "...${PR_GREEN}passed${PR_NC}.\n" + log "...passed." + log + + for i in `seq 1 3`; do + tmux set status-right-style 'bg=colour1 fg=colour2 bold' + sleep "0.1" + tmux set status-right-style 'bg=colour1 fg=colour233 bold' + sleep "0.1" + done +} + +# info_failed() - Display, log, and make status bar blink when a test passes +info_failed() { + switch_pane ${PANE_INFO} + + info_nolog "...${PR_RED}failed${PR_NC}.\n" + log "...failed." + log + + for i in `seq 1 3`; do + tmux set status-right-style 'bg=colour1 fg=colour196 bold' + sleep "0.1" + tmux set status-right-style 'bg=colour1 fg=colour233 bold' + sleep "0.1" + done + + pause_continue \ + "Press any key to pause test session" \ + "Resuming in " \ + "Paused, press any key to continue" \ + 5 +} + +# info_skipped() - Display and log skipped test +info_skipped() { + switch_pane ${PANE_INFO} + + info_nolog "...${PR_YELLOW}skipped${PR_NC}.\n" + log "...skipped." + log +} + +# info_layout() - Display string for new test layout +info_layout() { + switch_pane ${PANE_INFO} + + info_nolog "Test layout: ${PR_BLUE}${@}${PR_NC}.\n" +} + +# status_test_ok() - Update counter of passed tests, log and display message +status_test_ok() { + STATUS_PASS=$((STATUS_PASS + 1)) + tmux set status-right "PASS: ${STATUS_PASS} | FAIL: ${STATUS_FAIL} | #(TZ="UTC" date -Iseconds)" + info_passed +} + +# status_test_fail() - Update counter of failed tests, log and display message +status_test_fail() { + STATUS_FAIL=$((STATUS_FAIL + 1)) + tmux set status-right "PASS: ${STATUS_PASS} | FAIL: ${STATUS_FAIL} | #(TZ="UTC" date -Iseconds)" + info_failed +} + +# status_test_fail() - Update counter of failed tests, log and display message +status_test_skip() { + info_skipped +} + +# table_header() - Print table header to log pane +# $1: Header description +# $@: Column headers +table_header() { + perf_th ${@} + + __ifs="${IFS}" + IFS=" " + + __desc="${1}" + shift + + __max_len=4 + __count=0 + for __h in ${@}; do + [ ${#__h} -gt ${__max_len} ] && __max_len=${#__h} + __count=$((__count + 1)) + done + + # > xxxx |< + __outer_len=$((__max_len + 3)) + __width_fields=$((__outer_len * __count + 1)) + + TABLE_HEADER_LEFT=$((STATUS_COLS - __width_fields)) + TABLE_CELL_SIZE=$((__max_len + 2)) + TABLE_COLS=${__count} + + __pad_left=$((TABLE_HEADER_LEFT - ${#__desc} - 2)) + __buf="$(printf %-${__pad_left}s%s "" "${__desc}: ")" + for __h in ${@}; do + __pad_left=$(( (TABLE_CELL_SIZE - ${#__h} + 1) / 2)) + __pad_right=$(( (TABLE_CELL_SIZE - ${#__h}) / 2)) + __buf="${__buf}$(printf "|%-${__pad_left}s%s%-${__pad_right}s" "" ${__h} "")" + done + + info_n "${__buf}|" + + IFS="${__ifs}" +} + +# table_row() - Print main table row to log pane +# $@: Column headers +table_row() { + perf_tr ${@} + + __line="${@}" + __buf="$(printf %-${TABLE_HEADER_LEFT}s "")" + for __i in $(seq 1 ${TABLE_COLS}); do + __buf="${__buf}|" + for __j in $(seq 1 ${TABLE_CELL_SIZE}); do + __buf="${__buf}-" + done + done + info_n "\n${__buf}|\n" + + __pad_left=$(( (TABLE_HEADER_LEFT - ${#__line} + 1) / 2)) + __pad_right=$(( (TABLE_HEADER_LEFT - ${#__line}) / 2)) + info_n "$(printf "%-${__pad_left}s%s%-${__pad_right}s|" "" "${__line}" "")" +} + +# table_line() - Print simple line to log pane +# $@: Column headers +table_line() { + perf_tr ${@} + + __line="${@}" + info_n "\n" + + __pad_left=$(( (TABLE_HEADER_LEFT - ${#__line} + 1) / 2)) + __pad_right=$(( (TABLE_HEADER_LEFT - ${#__line}) / 2)) + info_n "$(printf "%-${__pad_left}s%s%-${__pad_right}s|" "" "${__line}" "")" +} + +table_cell() { + __len="${1}" + shift + + __content="${@}" + + __pad_left=$((TABLE_CELL_SIZE - __len - 1)) + info_n "$(printf "%-${__pad_left}s%s |" "" "${__content}")" +} + +table_end() { + __buf="$(printf %-${TABLE_HEADER_LEFT}s "")" + for __i in $(seq 1 ${TABLE_COLS}); do + __buf="${__buf}'" + for __j in $(seq 1 ${TABLE_CELL_SIZE}); do + __buf="${__buf}-" + done + done + info_n "\n${__buf}'\n" +} + +table_value_throughput() { + [ "${1}" = "-" ] && table_cell 1 "-" && perf_td 0 "" && return 0 + __v="$(echo "scale=1; x=( ${1} + 10^8 / 2 ) / 10^9; if ( x < 1 && x > 0 ) print 0; x" | bc -l)" + perf_td 31 "${__v}" + + __red="${2}" + __yellow="${3}" + if [ "$(echo "${__v} < ${__red}" | bc -l)" = "1" ]; then + table_cell ${#__v} "${PR_RED}${__v}${PR_NC}" + return 1 + elif [ "$(echo "${__v} < ${__yellow}" | bc -l)" = "1" ]; then + table_cell ${#__v} "${PR_YELLOW}${__v}${PR_NC}" + return 1 + else + table_cell ${#__v} "${PR_GREEN}${__v}${PR_NC}" + return 0 + fi +} + +table_value_latency() { + [ "${1}" = "-" ] && table_cell 1 "-" && perf_td 0 "" && return 0 + + __v="$(echo "scale=6; 1 / ${1} * 10^6" | bc -l)" + __v="${__v%.*}" + + perf_td 11 "${__v}" + + __red="${2}" + __yellow="${3}" + if [ "$(echo "${__v} > ${__red}" | bc -l)" = "1" ]; then + table_cell ${#__v} "${PR_RED}${__v}${PR_NC}" + return 1 + elif [ "$(echo "${__v} > ${__yellow}" | bc -l)" = "1" ]; then + table_cell ${#__v} "${PR_YELLOW}${__v}${PR_NC}" + return 1 + else + table_cell ${#__v} "${PR_GREEN}${__v}${PR_NC}" + return 0 + fi +} + +# pause_continue() - Pause for a while, wait for keystroke, resume on second one +pause_continue() { + tmux select-pane -t ${PANE_INFO} + info_nolog "${1}" + info_nolog_n "${2}" + + __pause_tmp="$(mktemp)" + echo >> "${__pause_tmp}" + tmux pipe-pane -O -t ${PANE_INFO} "cat >> ${__pause_tmp}" + __pane_buf= + __wait=0 + sleep 1 + for __i in $(seq ${4} -1 0); do + if [ "$(tail -n1 ${__pause_tmp} | tr -d -c [:print:])" != "${__pane_buf}" ]; then + __wait=1 + break + fi + + if [ ${__i} -ne ${4} ]; then + tmux send-keys -t ${PANE_INFO} Bspace + tmux send-keys -t ${PANE_INFO} Bspace + __pane_buf="${__pane_buf} " + fi + info_nolog_n "${__i} " + __pane_buf="${__pane_buf}${__i} " + sleep 1 + done + + if [ ${__wait} -eq 1 ]; then + tmux send-keys -t ${PANE_INFO} Bspace + tmux send-keys -t ${PANE_INFO} Bspace + info_nolog "" + info_nolog "${3}" + __pane_buf="$(tail -n1 ${__pause_tmp})" + while true; do + [ "$(tail -n1 ${__pause_tmp})" != "${__pane_buf}" ] && break + sleep 1 + done + fi + tmux pipe-pane -O -t ${PANE_INFO} "" + rm "${__pause_tmp}" + info_nolog "" +} + +# run_term() - Start tmux session, X terminal if requested, running entry point +run_term() { + export SHELL="/bin/sh" + + if [ ${CI} -eq 1 ]; then + __xterm_done="$(mktemp)" + ${CI_XTERM} "$(pwd)" -e "sh -c \"printf '\e[8;50;240t'; tmux new-session -s passt_test ./ci from_term; echo >${__xterm_done}\"" + while ! [ -s "${__xterm_done}" ]; do sleep 1; done + rm "${__xterm_done}" + elif [ ${DEMO} -eq 1 ]; then + while true; do + ${DEMO_XTERM} "$(pwd)" -e sh -c 'tmux new-session -s passt_test ./run_demo from_term' + [ $? -ne 0 ] && { tmux kill-session -t passt_test; continue; } + break + done + else + tmux new-session -s passt_test ./run from_term + fi +} + +# term() - Set up terminal window and panes for regular tests or CI +term() { + tmux set status-interval 1 + tmux rename-window '' + + tmux set window-status-format '#W' + tmux set window-status-current-format '#W' + tmux set status-left '' + tmux set window-status-separator '' + + tmux set window-status-style 'bg=colour1 fg=colour233 bold' + tmux set status-style 'bg=colour1 fg=colour233 bold' + tmux set status-right-style 'bg=colour1 fg=colour233 bold' + + tmux new-window -n "Testing commit: ${COMMIT}" + + tmux set window-status-format '#W' + tmux set window-status-current-format '#W' + tmux set status-left '' + tmux set window-status-separator '' + + tmux set window-status-current-style 'bg=colour1 fg=colour233 bold' + tmux set status-right '#(TZ="UTC" date -Iseconds)' + tmux set status-right-length 50 + tmux set status-right-style 'bg=colour1 fg=colour233 bold' + + tmux set history-limit 500000 + tmux select-pane -t 0 -T '' + tmux set pane-border-format '#T' + tmux set pane-border-style 'fg=colour2 bg=colour233' + tmux set pane-active-border-style 'fg=colour233 bg=colour4 bold' + tmux set pane-border-status bottom +} + +# term_demo() - Set up terminal window and panes for demo +term_demo() { + tmux set status-interval 1 + tmux rename-window '' + + tmux set window-status-format '#W' + tmux set window-status-current-format '#W' + tmux set status-left '' + tmux set window-status-separator '' + + tmux set window-status-style 'bg=colour1 fg=colour15 bold' + tmux set status-right '' + tmux set status-style 'bg=colour1 fg=colour15 bold' + tmux set status-right-style 'bg=colour1 fg=colour15 bold' + + tmux new-window -n "Demo at commit: ${COMMIT}" + + tmux set window-status-format '#W' + tmux set window-status-current-format '#W' + tmux set status-left '' + tmux set window-status-separator '' + + tmux select-pane -t 0 -T '' + tmux set pane-border-format '#T' + tmux set pane-border-style 'fg=colour2 bg=colour233' + tmux set pane-active-border-style 'fg=colour15 bg=colour4 bold' + tmux set pane-border-status bottom +} diff --git a/test/lib/test b/test/lib/test new file mode 100755 index 0000000..2e3f4ba --- /dev/null +++ b/test/lib/test @@ -0,0 +1,378 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/lib/test - List tests and run them, evaluating directives from files +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +# Empty, 'passt' or 'pasta', to match against 'onlyfor' directive +MODE= + +# test_iperf3() - Ugly helper for iperf3c/iperf3s directives +# $1: Role: client or server +# $2: Pane name, can be lowercase +# $3: Destination name or address for client +# $4: Port number, ${i} is translated to process index +# $5: Number of processes to run in parallel +# $@: Options +test_iperf3() { + __role="${1}"; shift + __pane="$(echo "${1}" | tr [a-z] [A-Z])"; shift + [ "${__role}" = "client" ] && __dest="${1}" && shift || __dest="" + __port="${1}"; shift + __procs="$((${1} - 1))"; shift + + [ "${__role}" = "server" ] && __role_opt="-c" || __role_opt="-s1J" + + if [ ${__role} = "client" ]; then + UDP_CLIENT=0 + for __opt in ${@}; do + [ "${__opt}" = "-u" ] && UDP_CLIENT=1 + done + + ( + sleep 2 + pane_run "${__pane}" 'for i in $(seq 0 '${__procs}');' \ + 'do ( iperf3 -c '"${__dest}"' -p '"${__port}" \ + "${@}" ' -T s${i} & echo $! > c${i}.pid & ); done' + sleep 36 + pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do'\ + 'kill -INT $(cat c${i}.pid) 2>/dev/null; done' + ) & + return + fi + + pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do' \ + ':> s${i}.bw; done' + pane_wait "${__pane}" + + if [ ${UDP_CLIENT} -eq 0 ]; then + pane_run "${__pane}" 'for i in $(seq 0 '${__procs}');' \ + 'do ( ( iperf3 -s1J -p '"${__port} ${@}" \ + '& echo $! > s${i}.pid ) 2>/dev/null' \ + '| jq -rM ".end.sum_received.bits_per_second"' \ + '> s${i}.bw & );' \ + 'done' + else + pane_run "${__pane}" 'for i in $(seq 0 '${__procs}');' \ + 'do ( ( iperf3 -s1J -i 30 -p '"${__port} ${@}" \ + '& echo $! > s${i}.pid ) 2>/dev/null' \ + '| jq -rM ".intervals[0].sum.bits_per_second"' \ + '> s${i}.bw & );' \ + 'done' + fi + + pane_wait "${__pane}" + sleep 38 + pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do' \ + 'kill -INT $(cat s${i}.pid) 2>/dev/null; done' + sleep 1 + pane_wait "${__pane}" + pane_run "${__pane}" '(cat s*.bw |' \ + 'sed '"'"'s/\(.*\)/\1\+/g'"'"' |' \ + 'tr -d "\n"; echo 0) | bc -l' + pane_wait "${__pane}" + pane_parse "${__pane}" + pane_run "${__pane}" 'for i in $(seq 0 '${__procs}'); do' \ + 'rm -f s${i}.bw; done' + pane_wait "${__pane}" +} + +# test_one() - Run a single test file evaluating directives +# $1: Name of test file, relative to test/ directory +test_one() { + __dirclean= + __test_file="test/${1}" + + __type="$(file -b --mime-type ${__test_file})" + if [ "${__type}" = "text/x-shellscript" ]; then + status_file_start "${1}" 1 + "${__test_file}" && status_test_ok || status_test_fail + return + fi + + __ntests="$(grep -c "^test$(printf '\t')" "${__test_file}")" + [ ${DEMO} -eq 0 ] && status_file_start "${1}" "${__ntests}" + + [ ${CI} -eq 1 ] && video_link "${1}" + + __subs= + __nok=-1 + __perf_nok=0 + __skip=0 + while IFS= read -r __line; do + # Strip comments + __line="${__line%%#*}" + + # tab-split command and arguments, apply variable substitutions + __cmd="${__line%%$(printf '\t')*}" + __arg="${__line#*$(printf '\t')*}" + __arg="$(subs_apply "${__subs}" "${__arg}")" + + [ ${__nok} -eq 1 ] && [ "${__cmd}" != "test" ] && continue + case ${__cmd} in + "tempdir") + __tmpdir="$(mktemp -d)" + __subs="$(list_add_pair "${__subs}" "__${__arg}__" "${__tmpdir}")" + __dirclean="$(list_add "${__dirclean}" "${__tmpdir}")" + ;; + "temp") + __tmpfile="$(mktemp)" + __subs="$(list_add_pair "${__subs}" "__${__arg}__" "${__tmpfile}")" + __dirclean="$(list_add "${__dirclean}" "${__tmpfile}")" + ;; + "test") + [ ${__perf_nok} -eq 0 ] || __nok=1 + [ ${__nok} -eq 1 ] && status_test_fail + [ ${__nok} -eq 0 ] && status_test_ok + + status_test_start "${__arg}" + __nok=0 + __perf_nok=0 + ;; + "host") + pane_run HOST "${__arg}" + pane_wait HOST + ;; + "hostb") + pane_run HOST "${__arg}" + ;; + "hostw") + pane_wait HOST + ;; + "htools") + pane_run HOST 'which '"${__arg}"' >/dev/null || echo skip' + pane_wait HOST + [ "$(pane_parse HOST)" = "skip" ] && { __skip=1; break; } + ;; + "passt") + pane_run PASST "${__arg}" + pane_wait PASST + ;; + "passtb") + pane_run PASST "${__arg}" + ;; + "passtw") + pane_wait PASST + ;; + "pout") + __varname="${__arg%% *}" + pane_run PASST "${__arg#* }" + pane_wait PASST + __subs="$(list_add_pair "${__subs}" "__${__varname}__" "$(pane_parse PASST)")" + ;; + "guest") + pane_run GUEST "${__arg}" + pane_wait GUEST + ;; + "guestb") + pane_run GUEST "${__arg}" + ;; + "guestw") + pane_wait GUEST + ;; + "guest1") + pane_run GUEST_1 "${__arg}" + pane_wait GUEST_1 + ;; + "guest1b") + pane_run GUEST_1 "${__arg}" + ;; + "guest1w") + pane_wait GUEST_1 + ;; + "gtools") + pane_run GUEST 'which '"${__arg}"' >/dev/null || echo skip' + pane_wait GUEST + [ "$(pane_parse GUEST)" = "skip" ] && { __skip=1; break; } + ;; + "g1tools") + pane_run GUEST_1 'which '"${__arg}"' >/dev/null || echo skip' + pane_wait GUEST_1 + [ "$(pane_parse GUEST_1)" = "skip" ] && { __skip=1; break; } + ;; + "g2tools") + pane_run GUEST_2 'which '"${__arg}"' >/dev/null || echo skip' + pane_wait GUEST_2 + [ "$(pane_parse GUEST_2)" = "skip" ] && { __skip=1; break; } + ;; + "guest2") + pane_run GUEST_2 "${__arg}" + pane_wait GUEST_2 + ;; + "guest2b") + pane_run GUEST_2 "${__arg}" + ;; + "guest2w") + pane_wait GUEST_2 + ;; + "ns") + pane_run NS "${__arg}" + pane_wait NS + ;; + "nsb") + pane_run NS "${__arg}" + ;; + "nsw") + pane_wait NS + ;; + "nstools") + pane_run NS 'which '"${__arg}"' >/dev/null || echo skip' + pane_wait NS + [ "$(pane_parse NS)" = "skip" ] && { __skip=1; break; } + ;; + "gout") + __varname="${__arg%% *}" + pane_run GUEST "${__arg#* }" + pane_wait GUEST + __subs="$(list_add_pair "${__subs}" "__${__varname}__" "$(pane_parse GUEST)")" + ;; + "g1out") + __varname="${__arg%% *}" + pane_run GUEST_1 "${__arg#* }" + pane_wait GUEST_1 + __subs="$(list_add_pair "${__subs}" "__${__varname}__" "$(pane_parse GUEST_1)")" + ;; + "g2out") + __varname="${__arg%% *}" + pane_run GUEST_2 "${__arg#* }" + pane_wait GUEST_2 + __subs="$(list_add_pair "${__subs}" "__${__varname}__" "$(pane_parse GUEST_2)")" + ;; + "hout") + __varname="${__arg%% *}" + pane_run HOST "${__arg#* }" + pane_wait HOST + __subs="$(list_add_pair "${__subs}" "__${__varname}__" "$(pane_parse HOST)")" + ;; + "nsout") + __varname="${__arg%% *}" + pane_run NS "${__arg#* }" + pane_wait NS + __subs="$(list_add_pair "${__subs}" "__${__varname}__" "$(pane_parse NS)")" + ;; + "check") + info_check "${__arg}" + eval "${__arg} || __nok=1" + if [ ${__nok} -eq 1 ]; then + info_check_failed + else + info_check_passed + fi + ;; + "sleep") + sleep "${__arg}" + ;; + "info") + info "${__arg}" + ;; + "report") + perf_report ${__arg} + ;; + "th") + table_header ${__arg} + ;; + "tr") + table_row "${__arg}" + ;; + "tl") + table_line "${__arg}" + ;; + "te") + table_end + ;; + "bw") + table_value_throughput ${__arg} || __perf_nok=1 + ;; + "lat") + table_value_latency ${__arg} || __perf_nok=1 + ;; + "iperf3c") + set -x + test_iperf3 client ${__arg} + set +x + ;; + "iperf3s") + set -x + __subs="$(list_add_pair "${__subs}" "__${__arg%% *}__" "$(test_iperf3 server ${__arg#* })" )" + set +x + ;; + "set") + __subs="$(list_add_pair "${__subs}" "__${__arg%% *}__" "${__arg#* }")" + ;; + + # Demo commands + "say") + text_write "${__arg}" + ;; + "em") + em_write "${__arg}" + ;; + "nl") + info_nolog "" + ;; + "hl") + pane_highlight "${__arg}" + ;; + "bsp") + text_backspace "${__arg}" + ;; + "killp") + pane_kill "${__arg}" + ;; + esac + done < "${__test_file}" + + for __d in ${__dirclean}; do + rm -rf ${__d} + done + + [ ${DEMO} -eq 1 ] && return + + [ ${__skip} -eq 1 ] && status_test_skip && return + [ ${__perf_nok} -eq 0 ] || __nok=1 + [ ${__nok} -eq 0 ] && status_test_ok || status_test_fail +} + +# test() - Build list of tests to run, in order, then issue test_one() +# $1: Name of directory containing set of test files, relative to test/ +test() { + __list= + __rem=1 + + cd test + while [ ${__rem} -eq 1 ]; do + __rem=0 + for __f in "${1}"/*; do + __type="$(file -b --mime-type ${__f})" + if [ "${__type}" = "text/x-shellscript" ]; then + __list="$(list_add "${__list}" "${__f}")" + continue + fi + + if [ -n "$(file_def "${__f}" onlyfor)" ] && \ + ! list_has "$(file_def "${__f}" onlyfor)" "${MODE}"; then + continue + fi + + if list_has_all "${__list}" "$(file_def "${__f}" req)"; then + __list="$(list_add "${__list}" "${__f}")" + else + __rem=1 + fi + done + done + cd .. + + for __f in ${__list}; do + test_one "${__f}" + done +} diff --git a/test/lib/util b/test/lib/util new file mode 100755 index 0000000..52ae171 --- /dev/null +++ b/test/lib/util @@ -0,0 +1,142 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/lib/util - Convenience functions +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +# list_has() - Check whether a tab-separated list contains a given token +# $1: List +# $2: Token +# Return: 0 if token was found or is empty, 1 otherwise +list_has() { + [ -z "${2}" ] && return 0 + + __ifs="${IFS}" + IFS=' ' + for __t in ${1}; do + [ "${__t}" = "${2}" ] && IFS="${__ifs}" && return 0 + done + + IFS="${__ifs}" + return 1 +} + +# list_add() - Add token to tab-separated list, unless it's already present +# $1: List +# $2: Token +list_add() { + list_has "${1}" "${2}" && return + [ -n "${1}" ] && printf '%s\t%s\n' "${1}" "${2}" || printf '%s\n' "${2}" +} + +# list_remove_pair() - Drop pair with given key if present +# $1: List +# $2: Key +list_remove_pair() +{ + __ifs="${IFS}" + IFS=' ' + __skip_next=0 + for __t in ${1}; do + [ ${__skip_next} -eq 1 ] && __skip_next=0 && continue + [ "${__t}" = "${2}" ] && __skip_next=1 && continue + printf '%s\t' "${__t}" + done + printf "\n" + IFS="${__ifs}" +} + +# list_add_pair() - Add token pair to list, replace if the first one is present +# $1: List +# $2: First token +# $3: Second token +list_add_pair() { + [ -z "${3}" ] && return + + + if [ -n "${1}" ]; then + __new_list="$(list_remove_pair "${1}" "${2}")" + printf '%s\t%s\t%s' "${__new_list}" "${2}" "${3}" + else + printf '%s\t%s' "${2}" "${3}" + fi + printf "\n" +} + +# list_has_all() - Check whether a list contains all given IFS-separated tokens +# $1: List +# $2: List of tokens +# Return: 0 if list of tokens was found or is empty, 1 otherwise +list_has_all() { + [ -z "${2}" ] && return 0 + + for __i in ${2}; do + list_has "${1}" "${__i}" || return 1 + done + return 0 +} + +# file_def() - List of tokens tab-separated line from file, starting with key +# $1: Filename +# $2: Token +file_def() { + sed -n 's/^'"${2}"'\t\(.*\)/\1/p' "${1}" | tr ' ' '\t' +} + +# subs_apply() - Apply substitutions using a list of token pairs +# $1: List of substitutions +# $2: String where substitutions have to be applied +subs_apply() { + echo "in subs_apply" >> /tmp/subs_apply + + __ifs="${IFS}" + IFS=' ' + __newarg="${2}" + __s= + for __t in ${1}; do + [ -z "${__s}" ] && __s="${__t}" && continue + + echo "t: --${__t}--, --${__s}--" >> /tmp/subs_apply + + __et="$(printf '%s\n' "$__t" | sed -e 's/[\/&]/\\&/g')" + __es="$(printf '%s\n' "$__s" | sed -e 's/[]\/$*.^[]/\\&/g')" + + __newarg="$(printf '%s' "${__newarg}" | sed "s/${__es}/${__et}/g")" + __s= + done + + printf '%s' "${__newarg}" + IFS="${__ifs}" +} + +# set_mode() - Set 'passt' or 'pasta' mode for terminal control, renaming panes +# $1: Mode to be set +set_mode() { + MODE="${1}" + if [ "${1}" = "pasta" ]; then + tmux select-pane -t ${PANE_GUEST} -T "namespace" + tmux select-pane -t ${PANE_PASST} -T "pasta" + else + tmux select-pane -t ${PANE_GUEST} -T "guest" + tmux select-pane -t ${PANE_PASST} -T "passt" + fi +} + +# get_info_cols() - Get number of columns for info pane +get_info_cols() { + __log_pane_cols= + __j=0 + for __i in $(tmux list-panes -t passt_test:1.0 -F "#{pane_width}"); do + [ ${__j} -eq ${PANE_INFO} ] && STATUS_COLS=${__i} && break + __j=$((__j + 1)) + done +} diff --git a/test/lib/video b/test/lib/video new file mode 100755 index 0000000..fd5849b --- /dev/null +++ b/test/lib/video @@ -0,0 +1,119 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/lib/video - Video grabbing, JavaScript fragments with links +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +FFMPEG_PID_FILE="$(mktemp)" +VIDEO_START_SECONDS= +VIDEO_NAME= + +VIDEO_LINKS_TEMPLATE="document.write('"' + Skip to: +' + +VIDEO_LINKS_TEMPLATE_JS=" +'); + +var video___VIDEO_NAME__links = [ +" + +VIDEO_LINKS_TEMPLATE_POST=']; + +for (var i = 0; i < video___VIDEO_NAME__links.length; i++) { + var obj = document.getElementById(video___VIDEO_NAME__links[i][0]); + + obj.addEventListener("click", function(event) { + var __VIDEO_NAME___video = document.getElementById("__VIDEO_NAME___video"); + var top = __VIDEO_NAME___video.offsetTop - 5; + + event.preventDefault(); + __VIDEO_NAME___video.play(); + __VIDEO_NAME___video.pause(); + for (var i = 0; i < video___VIDEO_NAME__links.length; i++) { + if (this.id == video___VIDEO_NAME__links[i][0]) { + __VIDEO_NAME___video.currentTime = video___VIDEO_NAME__links[i][1]; + } + } + window.scrollTo({ top: top, behavior: "smooth" }) + __VIDEO_NAME___video.play(); + }, false); +} +' + +VIDEO_LINKS_BUF= +VIDEO_LINKS_COUNT=0 + +# video_append_links() - Append generic string to JavaScript links file +video_append_links() +{ + printf "${@}" >> "${BASEPATH}/${VIDEO_NAME}.js" +} + +# video_append_links() - Append generic string to buffer for links +video_append_links_js() +{ + VIDEO_LINKS_BUF="${VIDEO_LINKS_BUF}${@}" +} + +# video_grab() - Fetch window geometry, start grabbing video +video_grab() { + VIDEO_NAME="${1}" + + rm -f "${BASEPATH}/${VIDEO_NAME}.mp4" "${BASEPATH}/${VIDEO_NAME}.webm" "${BASEPATH}/${VIDEO_NAME}.js" + + echo "${VIDEO_LINKS_TEMPLATE}" > "${BASEPATH}/${VIDEO_NAME}.js" + + __x=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Absolute upper-left X:[ ]*\([0-9]*\)$/\1/p') + __y=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Absolute upper-left Y:[ ]*\([0-9]*\)$/\1/p') + __width=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Width:[ ]*\([0-9]*\)$/\1/p') + __height=$(xwininfo -id $(xdotool getactivewindow) | sed -n 's/[ ]*Height:[ ]*\([0-9]*\)$/\1/p') + + [ $((__width % 2)) ] && __width=$((__width - 1)) + [ $((__height % 2)) ] && __height=$((__height - 1)) + + sleep 3 + VIDEO_START_SECONDS=$(sed -n 's/\([0-9]*\).[0-9]* [0-9]*.[0-9]*/\1/p' /proc/uptime) + ffmpeg -f x11grab -framerate 15 -video_size "${__width}x${__height}" -i "+${__x},${__y}" -vcodec libx264 -preset ultrafast -qp 0 -pix_fmt yuv444p -draw_mouse 0 "${BASEPATH}/${VIDEO_NAME}.mp4" & echo $! > "${FFMPEG_PID_FILE}" +} + +# video_time_now() - Print current video timestamp, in seconds +video_time_now() { + __now=$(sed -n 's/\([0-9]*\).[0-9]* [0-9]*.[0-9]*/\1/p' /proc/uptime) + echo $((__now - VIDEO_START_SECONDS)) +} + +# video_stop() - Stop grabbing, finalise JavaScript templates, convert to webm +video_stop() { + sed -i 's/^.*$/&\\/g' "${BASEPATH}/${VIDEO_NAME}.js" + echo "${VIDEO_LINKS_TEMPLATE_JS}" | sed "s/__VIDEO_NAME__/${VIDEO_NAME}/g" >> "${BASEPATH}/${VIDEO_NAME}.js" + echo "${VIDEO_LINKS_BUF}" >> "${BASEPATH}/${VIDEO_NAME}.js" + echo "${VIDEO_LINKS_TEMPLATE_POST}" | sed "s/__VIDEO_NAME__/${VIDEO_NAME}/g" >> "${BASEPATH}/${VIDEO_NAME}.js" + + kill -INT $(cat "${FFMPEG_PID_FILE}") + while ps -p $(cat "${FFMPEG_PID_FILE}") >/dev/null; do sleep 1; done + rm "${FFMPEG_PID_FILE}" + + [ ${1} -ne 0 ] && return + + ffmpeg -an -fflags +genpts -i "${BASEPATH}/${VIDEO_NAME}.mp4" -c:v libvpx-vp9 -row-mt 1 -minrate 10k -maxrate 200k -b:v 200k "${BASEPATH}/${VIDEO_NAME}.webm" +} + +# video_link() - Append single link to given video chapter +video_link() { + [ ${VIDEO_LINKS_COUNT} -eq 0 ] && __sep="" || __sep=" |" + __id="video_link_${VIDEO_LINKS_COUNT}" + video_append_links "${__sep} ${1}" + video_append_links_js "[ '${__id}', $(($(video_time_now) - 1)) ]," + + VIDEO_LINKS_COUNT=$((VIDEO_LINKS_COUNT + 1)) +} diff --git a/test/ndp/passt b/test/ndp/passt new file mode 100644 index 0000000..33e97a3 --- /dev/null +++ b/test/ndp/passt @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: AGPL-3.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/ndp/passt - Check NDP functionality in passt mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt passt_in_ns +gtools ip jq sipcalc grep +htools ip jq sipcalc grep cut + +test Interface name +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +guest ip link set dev __IFNAME__ up && sleep 2 +hout HOST_IFNAME ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").dev' +check [ -n "__IFNAME__" ] + +test SLAAC: prefix +gout ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global" and .prefixlen == 64).local' +gout PREFIX6 sipcalc __ADDR6__/64 | grep prefix | cut -d' ' -f4 +hout HOST_ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[] | select(.scope == "global").local' +hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4 +check [ "__PREFIX6__" = "__HOST_PREFIX6__" ] + +test SLAAC: route +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +hout HOST_GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +check [ __GW6__ = __HOST_GW6__ ] diff --git a/test/ndp/pasta b/test/ndp/pasta new file mode 100644 index 0000000..c3eceae --- /dev/null +++ b/test/ndp/pasta @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: AGPL-3.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/ndp/pasta - Check DHCP and DHCPv6 functionality in pasta mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor pasta +nstools ip jq sipcalc grep cut +htools ip jq sipcalc grep cut + +test Interface name +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +ns ip link set dev __IFNAME__ up +sleep 2 +hout HOST_IFNAME ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").dev' +check [ -n "__IFNAME__" ] + +test SLAAC: prefix +nsout ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global" and .prefixlen == 64).local' +nsout PREFIX6 sipcalc __ADDR6__/64 | grep prefix | cut -d' ' -f4 +hout HOST_ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[] | select(.scope == "global").local' +hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4 +check [ "__PREFIX6__" = "__HOST_PREFIX6__" ] + +test SLAAC: route +nsout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +hout HOST_GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +check [ __GW6__ = __HOST_GW6__ ] diff --git a/test/perf/passt_tcp b/test/perf/passt_tcp new file mode 100644 index 0000000..047072d --- /dev/null +++ b/test/perf/passt_tcp @@ -0,0 +1,228 @@ +# SPDX-License-Identifier: AGPL-3.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/perf/passt_tcp - Check TCP performance in passt mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt_in_ns +gtools sysctl ip jq nproc seq sleep bc iperf3 tcp_rr tcp_crr # From neper +nstools sysctl ip jq nproc seq sleep bc iperf3 tcp_rr tcp_crr +htools cpupower sed seq + +test passt: throughput and latency + +guest /sbin/sysctl -w net.core.rmem_max=536870912 +guest /sbin/sysctl -w net.core.wmem_max=536870912 +guest /sbin/sysctl -w net.core.rmem_default=33554432 +guest /sbin/sysctl -w net.core.wmem_default=33554432 +guest /sbin/sysctl -w net.ipv4.tcp_rmem="4096 131072 268435456" +guest /sbin/sysctl -w net.ipv4.tcp_wmem="4096 131072 268435456" +guest /sbin/sysctl -w net.ipv4.tcp_timestamps=0 + +ns sysctl -w net.ipv4.tcp_rmem="4096 524288 134217728" +ns sysctl -w net.ipv4.tcp_wmem="4096 524288 134217728" +ns sysctl -w net.ipv4.tcp_timestamps=0 + +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +hout FREQ cpupower frequency-info -lm | sed -n 's/.*- \(.*\) GHz$/\1/p' + + +set THREADS 1 +set STREAMS 8 +set OPTS -Z -P __STREAMS__ -l 1M -i1 -t30 -O5 --pacing-timer 1000000 + +info Throughput in Gbps, latency in µs, one thread at __FREQ__ GHz, __STREAMS__ streams +report passt tcp __THREADS__ __FREQ__ + +th MTU 256B 576B 1280B 1500B 9000B 65520B + + +tr TCP throughput over IPv6: guest to host +bw - +bw - + +guest ip link set dev __IFNAME__ mtu 1280 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -w 4M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 1.2 1.5 +guest ip link set dev __IFNAME__ mtu 1500 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -w 4M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 1.6 1.8 +guest ip link set dev __IFNAME__ mtu 9000 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -w 32M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 4.0 5.0 +guest ip link set dev __IFNAME__ mtu 65520 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -w 128M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 7.0 8.0 + +tl TCP RR latency over IPv6: guest to host +lat - +lat - +lat - +lat - +lat - +nsb tcp_rr --nolog -6 +gout LAT tcp_rr --nolog -6 -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 + +tl TCP CRR latency over IPv6: guest to host +lat - +lat - +lat - +lat - +lat - +nsb tcp_crr --nolog -6 +gout LAT tcp_crr --nolog -6 -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 500 400 + + +tr TCP throughput over IPv4: guest to host +guest ip link set dev __IFNAME__ mtu 256 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -w 1M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 0.2 0.3 +guest ip link set dev __IFNAME__ mtu 576 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -w 1M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 0.5 0.8 +guest ip link set dev __IFNAME__ mtu 1280 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -w 4M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 1.2 1.5 +guest ip link set dev __IFNAME__ mtu 1500 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -w 4M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 1.6 1.8 +guest ip link set dev __IFNAME__ mtu 9000 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -w 32M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 4.0 5.0 +guest ip link set dev __IFNAME__ mtu 65520 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -w 128M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 7.0 8.0 + +tl TCP RR latency over IPv4: guest to host +lat - +lat - +lat - +lat - +lat - +nsb tcp_rr --nolog -4 +gout LAT tcp_rr --nolog -4 -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 + +tl TCP CRR latency over IPv4: guest to host +lat - +lat - +lat - +lat - +lat - +nsb tcp_crr --nolog -4 +gout LAT tcp_crr --nolog -4 -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 500 400 + + +tr TCP throughput over IPv6: host to guest +bw - +bw - +ns ip link set dev lo mtu 1280 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -w 4M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 1.0 1.2 +ns ip link set dev lo mtu 1500 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -w 4M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 2.0 3.0 +ns ip link set dev lo mtu 9000 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -w 32M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 5.0 6.0 +ns ip link set dev lo mtu 65520 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -w 64M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 6.0 6.8 +ns ip link set dev lo mtu 65535 + +tl TCP RR latency over IPv6: host to guest +lat - +lat - +lat - +lat - +lat - +guestb tcp_rr --nolog -P 10001 -C 10011 -6 +nsout LAT tcp_rr --nolog -P 10001 -C 10011 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 + +tl TCP CRR latency over IPv6: host to guest +lat - +lat - +lat - +lat - +lat - +guestb tcp_crr --nolog -P 10001 -C 10011 -6 +nsout LAT tcp_crr --nolog -P 10001 -C 10011 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 500 350 + + +tr TCP throughput over IPv4: host to guest +ns ip link set dev lo mtu 256 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -w 1M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 0.3 0.5 +ns ip link set dev lo mtu 576 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -w 1M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 0.5 1.0 +ns ip link set dev lo mtu 1280 +ns ip addr add ::1 dev lo +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -w 4M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 2.0 3.0 +ns ip link set dev lo mtu 1500 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -w 4M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 2.0 3.0 +ns ip link set dev lo mtu 9000 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -w 32M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 5.0 6.0 +ns ip link set dev lo mtu 65520 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -w 64M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 6.0 6.8 +ns ip link set dev lo mtu 65535 + +tl TCP RR latency over IPv4: host to guest +lat - +lat - +lat - +lat - +lat - +guestb tcp_rr --nolog -P 10001 -C 10011 -4 +nsout LAT tcp_rr --nolog -P 10001 -C 10011 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 + +tl TCP CRR latency over IPv6: host to guest +lat - +lat - +lat - +lat - +lat - +guestb tcp_crr --nolog -P 10001 -C 10011 -4 +nsout LAT tcp_crr --nolog -P 10001 -C 10011 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 500 300 + +te diff --git a/test/perf/passt_udp b/test/perf/passt_udp new file mode 100644 index 0000000..528a75b --- /dev/null +++ b/test/perf/passt_udp @@ -0,0 +1,180 @@ +# SPDX-License-Identifier: AGPL-3.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/perf/passt_udp - Check UDP performance in passt mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt_in_ns +gtools sysctl ip jq nproc sleep iperf3 udp_rr # From neper +nstools ip jq sleep iperf3 udp_rr +htools cpupower sed + +test passt: throughput and latency + +guest /sbin/sysctl -w net.core.rmem_max=16777216 +guest /sbin/sysctl -w net.core.wmem_max=16777216 +guest /sbin/sysctl -w net.core.rmem_default=16777216 +guest /sbin/sysctl -w net.core.wmem_default=16777216 + +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +hout FREQ cpupower frequency-info -lm | sed -n 's/.*- \(.*\) GHz$/\1/p' + +set THREADS 2 +set STREAMS 1 +set OPTS -u -i1 -t30 -P __STREAMS__ --pacing-timer 10000 + +info Throughput in Gbps, latency in µs, __THREADS__ threads at __FREQ__ GHz, one stream each + +report passt udp __THREADS__ __FREQ__ + +th MTU 256B 576B 1280B 1500B 9000B 65520B + + +tr UDP throughput over IPv6: guest to host +bw - +bw - +guest ip link set dev __IFNAME__ mtu 1280 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -b 2G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 0.8 1.2 +guest ip link set dev __IFNAME__ mtu 1500 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -b 2G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 1.0 1.5 +guest ip link set dev __IFNAME__ mtu 9000 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -b 4G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 4.0 5.0 +guest ip link set dev __IFNAME__ mtu 65520 +iperf3c guest __GW6__%__IFNAME__ 100${i}2 __THREADS__ __OPTS__ -b 5G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 4.0 5.0 + +tl UDP RR latency over IPv6: guest to host +lat - +lat - +lat - +lat - +lat - +nsb udp_rr --nolog -6 +gout LAT udp_rr --nolog -6 -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 + + +tr UDP throughput over IPv4: guest to host +guest ip link set dev __IFNAME__ mtu 256 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -b 300M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 0.1 0.2 +guest ip link set dev __IFNAME__ mtu 576 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -b 500M +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 0.4 0.6 +guest ip link set dev __IFNAME__ mtu 1280 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -b 2G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 0.8 1.2 +guest ip link set dev __IFNAME__ mtu 1500 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -b 2G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 1.0 1.5 +guest ip link set dev __IFNAME__ mtu 9000 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -b 4G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 4.0 5.0 +guest ip link set dev __IFNAME__ mtu 65520 +iperf3c guest __GW__ 100${i}2 __THREADS__ __OPTS__ -b 5G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 4.0 5.0 + +tl UDP RR latency over IPv4: guest to host +lat - +lat - +lat - +lat - +lat - +nsb udp_rr --nolog -4 +gout LAT udp_rr --nolog -4 -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 + + +tr UDP throughput over IPv6: host to guest +bw - +bw - +ns ip link set dev lo mtu 1280 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -b 2G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 0.8 1.2 +ns ip link set dev lo mtu 1500 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -b 2G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 1.0 1.5 +ns ip link set dev lo mtu 9000 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -b 4G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 4.0 5.0 +ns ip link set dev lo mtu 65520 +iperf3c ns ::1 100${i}1 __THREADS__ __OPTS__ -b 6G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 5.0 5.5 + +tl UDP RR latency over IPv6: host to guest +lat - +lat - +lat - +lat - +lat - +guestb udp_rr --nolog -P 10001 -C 10011 -6 +nsout LAT udp_rr --nolog -P 10001 -C 10011 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 +ns ip link set dev lo mtu 65535 + + +tr UDP throughput over IPv4: host to guest +ns ip link set dev lo mtu 256 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -b 300M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 0.1 0.2 +ns ip link set dev lo mtu 576 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -b 500M +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 0.4 0.6 +ns ip link set dev lo mtu 1280 +ns ip addr add ::1 dev lo +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -b 2G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 0.8 1.2 +ns ip link set dev lo mtu 1500 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -b 2G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 1.0 1.5 +ns ip link set dev lo mtu 9000 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -b 4G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 4.0 5.0 +ns ip link set dev lo mtu 65520 +iperf3c ns 127.0.0.1 100${i}1 __THREADS__ __OPTS__ -b 6G +iperf3s BW guest 100${i}1 __THREADS__ +bw __BW__ 5.0 5.5 + +tl UDP RR latency over IPv4: host to guest +lat - +lat - +lat - +lat - +lat - +guestb udp_rr --nolog -P 10001 -C 10011 -4 +nsout LAT udp_rr --nolog -P 10001 -C 10011 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +lat __LAT__ 200 150 +ns ip link set dev lo mtu 65535 + +te diff --git a/test/perf/pasta_tcp b/test/perf/pasta_tcp new file mode 100644 index 0000000..a403eca --- /dev/null +++ b/test/perf/pasta_tcp @@ -0,0 +1,256 @@ +# SPDX-License-Identifier: AGPL-3.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/perf/pasta_tcp - Check TCP performance in pasta mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt_in_ns +htools cpupower ip seq bc sleep iperf3 tcp_rr tcp_crr jq sed +nstools sysctl nproc ip seq bc sleep iperf3 tcp_rr tcp_crr jq sed + +test pasta: throughput and latency (local connections) + +ns sysctl -w net.ipv4.tcp_rmem="131072 524288 134217728" +ns sysctl -w net.ipv4.tcp_wmem="131072 524288 134217728" +ns sysctl -w net.ipv4.tcp_timestamps=0 + + +set THREADS 2 +set STREAMS 2 +set OPTS -Z -w 4M -l 1M -P __STREAMS__ -i1 -t30 -O5 --pacing-timer 10000 +hout FREQ cpupower frequency-info -lm | sed -n 's/.*- \(.*\) GHz$/\1/p' + + +info Throughput in Gbps, latency in µs, __THREADS__ threads at __FREQ__ GHz, __STREAMS__ streams each +report pasta lo_tcp __THREADS__ __FREQ__ + +th MTU 1500B 4000B 16384B 65535B + + +tr TCP throughput over IPv6: ns to host +ns ip link set dev lo mtu 1500 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 +ns ip link set dev lo mtu 4000 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 +ns ip link set dev lo mtu 16384 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 +ns ip link set dev lo mtu 65535 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 + +tl TCP RR latency over IPv6: ns to host +lat - +lat - +lat - +hostb tcp_rr --nolog -P 10003 -C 10013 -6 +nsout LAT tcp_rr --nolog -P 10003 -C 10013 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 150 100 + +tl TCP CRR latency over IPv6: ns to host +lat - +lat - +lat - +hostb tcp_crr --nolog -P 10003 -C 10013 -6 +nsout LAT tcp_crr --nolog -P 10003 -C 10013 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 500 350 + + +tr TCP throughput over IPv4: ns to host +ns ip link set dev lo mtu 1500 +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 +ns ip link set dev lo mtu 4000 +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 +ns ip link set dev lo mtu 16384 +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 +ns ip link set dev lo mtu 65535 +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 15.0 20.0 + +tl TCP RR latency over IPv4: ns to host +lat - +lat - +lat - +hostb tcp_rr --nolog -P 10003 -C 10013 -4 +nsout LAT tcp_rr --nolog -P 10003 -C 10013 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 150 100 + +tl TCP CRR latency over IPv4: ns to host +lat - +lat - +lat - +hostb tcp_crr --nolog -P 10003 -C 10013 -4 +nsout LAT tcp_crr --nolog -P 10003 -C 10013 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 500 350 + + +tr TCP throughput over IPv6: host to ns +bw - +bw - +bw - +iperf3c host ::1 100${i}2 __THREADS__ __OPTS__ +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 15.0 20.0 + +tl TCP RR latency over IPv6: host to ns +lat - +lat - +lat - +nsb tcp_rr --nolog -P 10002 -C 10012 -6 +hout LAT tcp_rr --nolog -P 10002 -C 10012 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +nsw +lat __LAT__ 150 100 + +tl TCP CRR latency over IPv6: host to ns +lat - +lat - +lat - +nsb tcp_crr --nolog -P 10002 -C 10012 -6 +hout LAT tcp_crr --nolog -P 10002 -C 10012 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +nsw +lat __LAT__ 1000 700 + + +tr TCP throughput over IPv4: host to ns +bw - +bw - +bw - +iperf3c host 127.0.0.1 100${i}2 __THREADS__ __OPTS__ +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 15.0 20.0 + +tl TCP RR latency over IPv4: host to ns +lat - +lat - +lat - +nsb tcp_rr --nolog -P 10002 -C 10012 -4 +hout LAT tcp_rr --nolog -P 10002 -C 10012 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +nsw +lat __LAT__ 150 100 + +tl TCP CRR latency over IPv4: host to ns +lat - +lat - +lat - +sleep 1 +nsb tcp_crr --nolog -P 10002 -C 10012 -4 +hout LAT tcp_crr --nolog -P 10002 -C 10012 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +nsw +lat __LAT__ 1000 700 + +te + + +test pasta: throughput and latency (connections via tap) + +nsout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +nsout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +set THREADS 1 +set STREAMS 2 +set OPTS -Z -P __STREAMS__ -i1 -t30 -O5 --pacing-timer 100000 + +info Throughput in Gbps, latency in µs, one thread at __FREQ__ GHz, __STREAMS__ streams +report pasta tap_tcp __THREADS__ __FREQ__ + +th MTU 1500B 4000B 16384B 65520B + + +tr TCP throughput over IPv6: ns to host +ns ip link set dev __IFNAME__ mtu 1500 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -w 512k +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.2 0.4 +ns ip link set dev __IFNAME__ mtu 4000 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -w 1M +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.3 0.5 +ns ip link set dev __IFNAME__ mtu 16384 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -w 8M +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 1.5 2.0 +ns ip link set dev __IFNAME__ mtu 65520 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -w 8M +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 2.0 2.5 + +tl TCP RR latency over IPv6: ns to host +lat - +lat - +lat - +hostb tcp_rr --nolog -P 10003 -C 10013 -6 +nsout LAT tcp_rr --nolog -P 10003 -C 10013 -6 -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 150 100 + +tl TCP CRR latency over IPv6: ns to host +lat - +lat - +lat - +hostb tcp_crr --nolog -P 10003 -C 10013 -6 +nsout LAT tcp_crr --nolog -P 10003 -C 10013 -6 -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 1500 500 + + +tr TCP throughput over IPv4: ns to host +ns ip link set dev __IFNAME__ mtu 1500 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -w 512k +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.2 0.4 +ns ip link set dev __IFNAME__ mtu 4000 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -w 1M +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.3 0.5 +ns ip link set dev __IFNAME__ mtu 16384 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -w 8M +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 1.5 2.0 +ns ip link set dev __IFNAME__ mtu 65520 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -w 8M +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 2.0 2.5 + +tl TCP RR latency over IPv4: ns to host +lat - +lat - +lat - +hostb tcp_rr --nolog -P 10003 -C 10013 -4 +nsout LAT tcp_rr --nolog -P 10003 -C 10013 -4 -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 150 100 + +tl TCP CRR latency over IPv4: ns to host +lat - +lat - +lat - +hostb tcp_crr --nolog -P 10003 -C 10013 -4 +nsout LAT tcp_crr --nolog -P 10003 -C 10013 -4 -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 1500 500 + +te diff --git a/test/perf/pasta_udp b/test/perf/pasta_udp new file mode 100644 index 0000000..6dedbad --- /dev/null +++ b/test/perf/pasta_udp @@ -0,0 +1,195 @@ +# SPDX-License-Identifier: AGPL-3.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/perf/pasta_udp - Check UDP performance in pasta mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt_in_ns +htools cpupower ip sleep iperf3 udp_rr jq sed +nstools ip sleep iperf3 udp_rr jq sed + +test pasta: throughput and latency (local traffic) + +hout FREQ cpupower frequency-info -lm | sed -n 's/.*- \(.*\) GHz$/\1/p' + +set THREADS 1 +set STREAMS 4 +set OPTS -u -i1 -t30 -P __STREAMS__ + +info Throughput in Gbps, latency in µs, one thread at __FREQ__ GHz, __STREAMS__ streams + +report pasta lo_udp 1 __FREQ__ + +th MTU 1500B 4000B 16384B 65535B + + +tr UDP throughput over IPv6: ns to host +ns ip link set dev lo mtu 1500 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 1.0 1.5 +ns ip link set dev lo mtu 4000 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 1.2 1.8 +ns ip link set dev lo mtu 16384 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ -b 10G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 5.0 6.0 +ns ip link set dev lo mtu 65535 +iperf3c ns ::1 100${i}3 __THREADS__ __OPTS__ -b 15G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 7.0 9.0 + +tl UDP RR latency over IPv6: ns to host +lat - +lat - +lat - +hostb udp_rr --nolog -P 10003 -C 10013 -6 +nsout LAT udp_rr --nolog -P 10003 -C 10013 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 200 150 + + +tr UDP throughput over IPv4: ns to host +ns ip link set dev lo mtu 1500 +ns ip addr add ::1 dev lo +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 1.0 1.5 +ns ip link set dev lo mtu 4000 +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 1.2 1.8 +ns ip link set dev lo mtu 16384 +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ -b 10G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 5.0 6.0 +ns ip link set dev lo mtu 65535 +iperf3c ns 127.0.0.1 100${i}3 __THREADS__ __OPTS__ -b 15G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 7.0 9.0 + +tl UDP RR latency over IPv4: ns to host +lat - +lat - +lat - +hostb udp_rr --nolog -P 10003 -C 10013 -4 +nsout LAT udp_rr --nolog -P 10003 -C 10013 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 200 150 + + +tr UDP throughput over IPv6: host to ns +bw - +bw - +bw - +iperf3c host ::1 100${i}2 __THREADS__ __OPTS__ -b 15G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 7.0 9.0 + +tl UDP RR latency over IPv6: host to ns +lat - +lat - +lat - +nsb udp_rr --nolog -P 10002 -C 10012 -6 +hout LAT udp_rr --nolog -P 10002 -C 10012 -6 -c -H ::1 | sed -n 's/^throughput=\(.*\)/\1/p' +nsw +lat __LAT__ 200 150 + + +tr UDP throughput over IPv4: host to ns +bw - +bw - +bw - +iperf3c host 127.0.0.1 100${i}2 __THREADS__ __OPTS__ -b 15G +iperf3s BW ns 100${i}2 __THREADS__ +bw __BW__ 7.0 9.0 + +tl UDP RR latency over IPv4: host to ns +lat - +lat - +lat - +nsb udp_rr --nolog -P 10002 -C 10012 -4 +hout LAT udp_rr --nolog -P 10002 -C 10012 -4 -c -H 127.0.0.1 | sed -n 's/^throughput=\(.*\)/\1/p' +nsw +lat __LAT__ 200 150 + +te + + + +test pasta: throughput and latency (traffic via tap) + +nsout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +nsout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' + +info Throughput in Gbps, latency in µs, one thread at __FREQ__ GHz, __STREAMS__ streams +report pasta tap_udp 1 __FREQ__ + +th MTU 1500B 4000B 16384B 65520B + +tr UDP throughput over IPv6: ns to host +ns ip link set dev __IFNAME__ mtu 1500 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -b 1G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.3 0.5 +ns ip link set dev __IFNAME__ mtu 4000 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -b 2G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.5 0.8 +ns ip link set dev __IFNAME__ mtu 16384 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 3.0 4.0 +ns ip link set dev __IFNAME__ mtu 65520 +iperf3c ns __GW6__%__IFNAME__ 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 6.0 7.0 + +tl UDP RR latency over IPv6: ns to host +lat - +lat - +lat - +hostb udp_rr --nolog -P 10003 -C 10013 -6 +nsout LAT udp_rr --nolog -P 10003 -C 10013 -6 -c -H __GW6__%__IFNAME__ | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 200 150 + + +tr UDP throughput over IPv4: ns to host +ns ip link set dev __IFNAME__ mtu 1500 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -b 1G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.3 0.5 +ns ip link set dev __IFNAME__ mtu 4000 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -b 2G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 0.5 0.8 +ns ip link set dev __IFNAME__ mtu 16384 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 3.0 4.0 +ns ip link set dev __IFNAME__ mtu 65520 +iperf3c ns __GW__ 100${i}3 __THREADS__ __OPTS__ -b 3G +iperf3s BW host 100${i}3 __THREADS__ +bw __BW__ 6.0 7.0 + +tl UDP RR latency over IPv4: ns to host +lat - +lat - +lat - +hostb udp_rr --nolog -P 10003 -C 10013 -4 +nsout LAT udp_rr --nolog -P 10003 -C 10013 -4 -c -H __GW__ | sed -n 's/^throughput=\(.*\)/\1/p' +hostw +lat __LAT__ 200 150 + +te diff --git a/test/run b/test/run new file mode 100755 index 0000000..9d33951 --- /dev/null +++ b/test/run @@ -0,0 +1,154 @@ +#!/bin/sh +# +# SPDX-License-Identifier: AGPL-3.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/run - Entry point to run test cases and demo +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +# Start an X terminal and capture a video of the test run (also set for ./ci) +CI=${CI:-0} + +# Start an X terminal and show the demo (also set for ./demo) +DEMO=${DEMO:-0} + +# Base path for output files +BASEPATH=${BASEPATH:-"$(pwd)"} + +# Location of log files for test run +LOGDIR=${LOGDIR:-"${BASEPATH}/test_logs"} +LOGFILE=${LOGFILE:-"${LOGDIR}/test.log"} + +# If set, skip typing delays while issuing commands in panes +FAST=${FAST:-1} + +# If set, run passt and pasta with debug options +DEBUG=${DEBUG:-0} + +# If set, tell passt and pasta to take packet captures +PCAP=${PCAP:-0} + +COMMIT="$(git log --oneline --no-decorate -1)" + +. lib/util +. lib/setup +. lib/term +. lib/perf_report +. lib/layout +. lib/test +. lib/video + +# cleanup() - Remove temporary files +cleanup() { + rm -f /tmp/.passt_test_log_pipe +} + +# run() - Call setup functions, run tests, handle exit from test session +run() { + rm -f /tmp/.passt_test_log_pipe + mkfifo /tmp/.passt_test_log_pipe + + term + perf_init + [ ${CI} -eq 1 ] && video_grab ci + + setup build + test build + + setup pasta + test ndp + test dhcp + test tcp + test udp + teardown pasta + + setup passt + test ndp + test dhcp + test tcp + test udp + teardown passt + + setup passt_in_ns + test ndp + test dhcp + test icmp + test tcp + test udp + test perf + teardown passt_in_ns + + setup two_guests + test two_guests + teardown two_guests + + perf_finish + [ ${CI} -eq 1 ] && video_stop ${STATUS_FAIL} + + log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}" + + pause_continue \ + "Press any key to keep test session open" \ + "Closing in " \ + "Interrupted, press any key to quit" \ + 9 + + return ${STATUS_FAIL} +} + +# demo() - Simpler path for demo purposes +demo() { + rm -f /tmp/.passt_test_log_pipe + mkfifo /tmp/.passt_test_log_pipe + + FAST=0 + + term_demo + + layout_demo_passt + video_grab demo_passt + MODE=passt + test demo + video_stop 0 + tmux send-keys -t ${PANE_GUEST} "C-c" + + layout_demo_pasta + video_grab demo_pasta + MODE=pasta + test demo + video_stop 0 + + return 0 +} + +[ "$(basename "${0}")" = "ci" ] && CI=1 +[ "$(basename "${0}")" = "run_demo" ] && DEMO=1 + +if [ "${1}" = "from_term" ]; then + cd .. + if [ ${DEMO} -eq 1 ]; then + demo + else + run + fi + tmux kill-session -t passt_test + exit +else + rm -rf "${LOGDIR}" + mkdir -p "${LOGDIR}" + :> "${LOGFILE}" + trap "cleanup" EXIT + run_term + trap "" EXIT +fi + +tail -n1 ${LOGFILE} +echo "Log at ${LOGFILE}" +exit $(tail -n1 ${LOGFILE} | sed -n 's/.*FAIL: \(.*\)$/\1/p') diff --git a/test/run_demo b/test/run_demo new file mode 120000 index 0000000..e5224d5 --- /dev/null +++ b/test/run_demo @@ -0,0 +1 @@ +run \ No newline at end of file diff --git a/test/tcp/passt b/test/tcp/passt new file mode 100644 index 0000000..f31f111 --- /dev/null +++ b/test/tcp/passt @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: AGPL-3.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/tcp/passt - Check TCP functionality in passt mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt +gtools nc ip jq cat md5sum cut +htools dd nc ip jq cat md5sum cut + +test TCP/IPv4: host to guest: big transfer +temp TEMP_BIG +guestb nc -4 -l 10001 > test_big.bin +host dd if=/dev/urandom bs=1M count=10 > __TEMP_BIG__ +host cat __TEMP_BIG__ | nc -N 127.0.0.1 10001 +guestw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__HOST_MD5_BIG__" ] + +test TCP/IPv4: guest to host: big transfer +hostb nc -4 -l 10003 > __TEMP_BIG__ +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +guest cat test_big.bin | nc -N __GW__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__HOST_MD5_BIG__" ] + +test TCP/IPv4: host to guest: small transfer +temp TEMP_SMALL +guestb nc -4 -l 10001 > test_small.bin +host dd if=/dev/urandom bs=2k count=1 > __TEMP_SMALL__ +host cat __TEMP_SMALL__ | nc -N 127.0.0.1 10001 +guestw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__HOST_MD5_SMALL__" ] + +test TCP/IPv4: guest to host: small transfer +hostb nc -4 -l 10003 > __TEMP_SMALL__ +sleep 1 +guest cat test_small.bin | nc -N __GW__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__HOST_MD5_SMALL__" ] + + +test TCP/IPv6: host to guest: big transfer +guestb nc -6 -l 10001 > test_big.bin +sleep 1 +host cat __TEMP_BIG__ | nc -N ::1 10001 +guestw +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__HOST_MD5_BIG__" ] + +test TCP/IPv6: guest to host: big transfer +hostb nc -6 -l 10003 > __TEMP_BIG__ +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +guest cat test_big.bin | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__HOST_MD5_BIG__" ] + +test TCP/IPv6: host to guest: small transfer +guestb nc -6 -l 10001 > test_small.bin +sleep 1 +host cat __TEMP_SMALL__ | nc -N ::1 10001 +guestw +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__HOST_MD5_SMALL__" ] + +test TCP/IPv6: guest to host: small transfer +hostb nc -6 -l 10003 > __TEMP_SMALL__ +sleep 1 +guest cat test_small.bin | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__HOST_MD5_SMALL__" ] diff --git a/test/tcp/passt_in_ns b/test/tcp/passt_in_ns new file mode 100644 index 0000000..fc4c7f4 --- /dev/null +++ b/test/tcp/passt_in_ns @@ -0,0 +1,262 @@ +# SPDX-License-Identifier: AGPL-3.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/tcp/passt_in_ns - Check TCP functionality for passt in ns with pasta +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt_in_ns +gtools nc ip jq cat md5sum cut +htools dd nc ip jq cat md5sum cut +nstools nc ip jq cat md5sum cut + +test TCP/IPv4: host to guest: big transfer +temp TEMP_BIG +guestb nc -4 -l 10001 > test_big.bin +host dd if=/dev/urandom bs=1M count=10 > __TEMP_BIG__ +host cat __TEMP_BIG__ | nc -N 127.0.0.1 10001 +guestw +hout MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: host to ns: big transfer +temp TEMP_NS_BIG +nsb nc -4 -l 10002 > __TEMP_NS_BIG__ +host cat __TEMP_BIG__ | nc -N 127.0.0.1 10002 +nsw +nsout NS_MD5_BIG md5sum __TEMP_NS_BIG__ | cut -d' ' -f1 +check [ "__NS_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: guest to host: big transfer +hostb nc -4 -l 10003 > __TEMP_BIG__ +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +guest cat test_big.bin | nc -N __GW__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: guest to ns: big transfer +nsb nc -4 -l 10002 > __TEMP_BIG__ +guest cat test_big.bin | nc -N __GW__ 10002 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: ns to host (spliced): big transfer +sleep 1 +hostb nc -4 -l 10003 > __TEMP_BIG__ +ns cat __TEMP_NS_BIG__ | nc -N 127.0.0.1 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: ns to host (via tap): big transfer +hostb nc -4 -l 10003 > __TEMP_BIG__ +ns cat __TEMP_NS_BIG__ | nc -N __GW__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: ns to guest (using loopback address): big transfer +guestb nc -4 -l 10001 > test_big.bin +ns cat __TEMP_NS_BIG__ | nc -N 127.0.0.1 10001 +guestw +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: ns to guest (using namespace address): big transfer +guestb nc -4 -l 10001 > test_big.bin +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +nsout ADDR ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' +ns cat __TEMP_NS_BIG__ | nc -N __ADDR__ 10001 +guestw +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: host to guest: small transfer +temp TEMP_SMALL +guestb nc -4 -l 10001 > test_small.bin +host dd if=/dev/urandom bs=2k count=100 > __TEMP_SMALL__ +host cat __TEMP_SMALL__ | nc -N 127.0.0.1 10001 +guestw +hout MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: host to ns: small transfer +temp TEMP_NS_SMALL +nsb nc -4 -l 10002 > __TEMP_NS_SMALL__ +host cat __TEMP_SMALL__ | nc -N 127.0.0.1 10002 +nsw +nsout NS_MD5_SMALL md5sum __TEMP_NS_SMALL__ | cut -d' ' -f1 +check [ "__NS_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: guest to host: small transfer +hostb nc -4 -l 10003 > __TEMP_SMALL__ +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +guest cat test_small.bin | nc -N __GW__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: guest to ns: small transfer +nsb nc -4 -l 10002 > __TEMP_SMALL__ +guest cat test_small.bin | nc -N __GW__ 10002 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: ns to host (spliced): small transfer +sleep 1 +hostb nc -4 -l 10003 > __TEMP_SMALL__ +ns cat __TEMP_NS_SMALL__ | nc -N 127.0.0.1 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: ns to host (via tap): small transfer +hostb nc -4 -l 10003 > __TEMP_SMALL__ +ns cat __TEMP_NS_SMALL__ | nc -N __GW__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: ns to guest (using loopback address): small transfer +guestb nc -4 -l 10001 > test_small.bin +ns cat __TEMP_NS_SMALL__ | nc -N 127.0.0.1 10001 +guestw +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: ns to guest (using namespace address): small transfer +guestb nc -4 -l 10001 > test_small.bin +ns cat __TEMP_NS_SMALL__ | nc -N __ADDR__ 10001 +guestw +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: host to guest: big transfer +guestb nc -6 -l 10001 > test_big.bin +host cat __TEMP_BIG__ | nc -N ::1 10001 +guestw +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: host to ns: big transfer +nsb nc -6 -l 10002 > __TEMP_NS_BIG__ +host cat __TEMP_BIG__ | nc -N ::1 10002 +nsw +nsout NS_MD5_BIG md5sum __TEMP_NS_BIG__ | cut -d' ' -f1 +check [ "__NS_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: guest to host: big transfer +hostb nc -6 -l 10003 > __TEMP_BIG__ +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +guest cat test_big.bin | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: guest to ns: big transfer +nsb nc -6 -l 10002 > __TEMP_BIG__ +guest cat test_big.bin | nc -N __GW6__%__IFNAME__ 10002 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: ns to host (spliced): big transfer +hostb nc -6 -l 10003 > __TEMP_BIG__ +ns cat __TEMP_NS_BIG__ | nc -N ::1 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: ns to host (via tap): big transfer +hostb nc -6 -l 10003 > __TEMP_BIG__ +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +ns cat __TEMP_NS_BIG__ | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: ns to guest (using loopback address): big transfer +guestb nc -6 -l 10001 > test_big.bin +ns cat __TEMP_NS_BIG__ | nc -N ::1 10001 +guestw +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: ns to guest (using namespace address): big transfer +guestb nc -6 -l 10001 > test_big.bin +nsout ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' +ns cat __TEMP_NS_BIG__ | nc -N __ADDR6__ 10001 +guestw +gout GUEST_MD5_BIG md5sum test_big.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: host to guest: small transfer +guestb nc -6 -l 10001 > test_small.bin +host cat __TEMP_SMALL__ | nc -N ::1 10001 +guestw +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: host to ns: small transfer +nsb nc -6 -l 10002 > __TEMP_NS_SMALL__ +host cat __TEMP_SMALL__ | nc -N ::1 10002 +nsw +nsout NS_MD5_SMALL md5sum __TEMP_NS_SMALL__ | cut -d' ' -f1 +check [ "__NS_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: guest to host: small transfer +hostb nc -6 -l 10003 > __TEMP_SMALL__ +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +guest cat test_small.bin | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: guest to ns: small transfer +nsb nc -6 -l 10002 > __TEMP_SMALL__ +guest cat test_small.bin | nc -N __GW6__%__IFNAME__ 10002 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: ns to host (spliced): small transfer +hostb nc -6 -l 10003 > __TEMP_SMALL__ +ns cat __TEMP_NS_SMALL__ | nc -N ::1 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: ns to host (via tap): small transfer +hostb nc -6 -l 10003 > __TEMP_SMALL__ +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +ns cat __TEMP_NS_SMALL__ | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: ns to guest (using loopback address): small transfer +guestb nc -6 -l 10001 > test_small.bin +ns cat __TEMP_NS_SMALL__ | nc -N ::1 10001 +guestw +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: ns to guest (using namespace address): small transfer +guestb nc -6 -l 10001 > test_small.bin +ns cat __TEMP_NS_SMALL__ | nc -N __ADDR6__ 10001 +guestw +gout GUEST_MD5_SMALL md5sum test_small.bin | cut -d' ' -f1 +check [ "__GUEST_MD5_SMALL__" = "__MD5_SMALL__" ] diff --git a/test/tcp/pasta b/test/tcp/pasta new file mode 100644 index 0000000..55548e8 --- /dev/null +++ b/test/tcp/pasta @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: AGPL-3.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/tcp/pasta - Check TCP functionality for pasta +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor pasta +htools dd nc ip jq cat md5sum cut +nstools nc ip jq cat md5sum cut + +test TCP/IPv4: host to ns: big transfer +temp TEMP_BIG +temp TEMP_NS_BIG +nsb nc -4 -l 10002 > __TEMP_NS_BIG__ +host dd if=/dev/urandom bs=1M count=10 > __TEMP_BIG__ +host cat __TEMP_BIG__ | nc -N 127.0.0.1 10002 +nsw +hout MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +nsout NS_MD5_BIG md5sum __TEMP_NS_BIG__ | cut -d' ' -f1 +check [ "__NS_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: ns to host (spliced): big transfer +hostb nc -4 -l 10003 > __TEMP_BIG__ +ns cat __TEMP_NS_BIG__ | nc -N 127.0.0.1 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: ns to host (via tap): big transfer +hostb nc -4 -l 10003 > __TEMP_BIG__ +nsout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +ns cat __TEMP_NS_BIG__ | nc -N __GW__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv4: host to ns: small transfer +temp TEMP_SMALL +temp TEMP_NS_SMALL +nsb nc -4 -l 10002 > __TEMP_NS_SMALL__ +host dd if=/dev/urandom bs=2k count=1 > __TEMP_SMALL__ +host cat __TEMP_SMALL__ | nc -N 127.0.0.1 10002 +nsw +hout MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +nsout NS_MD5_SMALL md5sum __TEMP_NS_SMALL__ | cut -d' ' -f1 +check [ "__NS_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: ns to host (spliced): small transfer +hostb nc -4 -l 10003 > __TEMP_SMALL__ +ns cat __TEMP_NS_SMALL__ | nc -N 127.0.0.1 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv4: ns to host (via tap): small transfer +hostb nc -4 -l 10003 > __TEMP_SMALL__ +nsout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +ns cat __TEMP_NS_SMALL__ | nc -N __GW__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: host to ns: big transfer +nsb nc -6 -l 10002 > __TEMP_NS_BIG__ +host cat __TEMP_BIG__ | nc -N ::1 10002 +nsw +hout MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +nsout NS_MD5_BIG md5sum __TEMP_NS_BIG__ | cut -d' ' -f1 +check [ "__NS_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: ns to host (spliced): big transfer +hostb nc -6 -l 10003 > __TEMP_BIG__ +ns cat __TEMP_NS_BIG__ | nc -N ::1 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] +nsw + +test TCP/IPv6: ns to host (via tap): big transfer +hostb nc -6 -l 10003 > __TEMP_BIG__ +nsout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +ns cat __TEMP_NS_BIG__ | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_BIG md5sum __TEMP_BIG__ | cut -d' ' -f1 +check [ "__HOST_MD5_BIG__" = "__MD5_BIG__" ] + +test TCP/IPv6: host to ns: small transfer +temp TEMP_SMALL +temp TEMP_NS_SMALL +nsb nc -6 -l 10002 > __TEMP_NS_SMALL__ +host dd if=/dev/urandom bs=2k count=1 > __TEMP_SMALL__ +host cat __TEMP_SMALL__ | nc -N ::1 10002 +nsw +hout MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +nsout NS_MD5_SMALL md5sum __TEMP_NS_SMALL__ | cut -d' ' -f1 +check [ "__NS_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: ns to host (spliced): small transfer +hostb nc -6 -l 10003 > __TEMP_SMALL__ +ns cat __TEMP_NS_SMALL__ | nc -N ::1 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] + +test TCP/IPv6: ns to host (via tap): small transfer +hostb nc -6 -l 10003 > __TEMP_SMALL__ +ns cat __TEMP_NS_SMALL__ | nc -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5_SMALL md5sum __TEMP_SMALL__ | cut -d' ' -f1 +check [ "__HOST_MD5_SMALL__" = "__MD5_SMALL__" ] diff --git a/test/two_guests/basic b/test/two_guests/basic new file mode 100644 index 0000000..13277d8 --- /dev/null +++ b/test/two_guests/basic @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: AGPL-3.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/two_guests/basic - Check basic functionality with two guests +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +g1tools ip jq dhclient nc cat +g2tools ip jq dhclient nc cat +htools ip jq cat md5sum cut + +test Interface names +g1out IFNAME1 ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +g2out IFNAME2 ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +hout HOST_IFNAME ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").dev' +check [ -n "__IFNAME1__" ] +check [ -n "__IFNAME2__" ] + +test DHCP: addresses +guest1 /sbin/dhclient __IFNAME1__ +guest2 /sbin/dhclient __IFNAME1__ +g1out ADDR1 ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local' +g2out ADDR2 ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME2__").addr_info[0].local' +hout HOST_ADDR ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local' +check [ "__ADDR1__" = "__HOST_ADDR__" ] +check [ "__ADDR2__" = "__HOST_ADDR__" ] + +test DHCPv6: addresses +guest1 /sbin/dhclient -6 __IFNAME1__ +guest2 /sbin/dhclient -6 __IFNAME1__ +g1out ADDR1_6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local' +g2out ADDR2_6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME2__").addr_info[] | select(.prefixlen == 128).local' +hout HOST_ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[] | select(.scope == "global").local' +check [ "__ADDR1_6__" = "__HOST_ADDR6__" ] +check [ "__ADDR2_6__" = "__HOST_ADDR6__" ] + +test TCP/IPv4: guest 1 > guest 2 +g1out GW1 ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +guest2b nc -4 -l 10004 > msg +guest1 echo "Hello_from_guest_1" | nc -N __GW1__ 10004 +guest2w +sleep 1 +g2out MSG2 cat msg +check [ "__MSG2__" = "Hello_from_guest_1" ] + +test TCP/IPv6: guest 2 > guest 1 +g2out GW2_6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +guest1b nc -6 -l 10001 > msg +guest2 echo "Hello_from_guest_2" | nc -N __GW2_6__%__IFNAME2__ 10001 +guest1w +sleep 1 +g1out MSG1 cat msg +check [ "__MSG1__" = "Hello_from_guest_2" ] + +test UDP/IPv4: guest 1 > guest 2 +guest2b nc -u -W1 -4 -l 10004 > msg +guest1 echo "Hello_from_guest_1" | nc -u -q1 __GW1__ 10004 +guest2w +sleep 1 +g2out MSG2 cat msg +check [ "__MSG2__" = "Hello_from_guest_1" ] + +test UDP/IPv6: guest 2 > guest 1 +guest1b nc -u -W1 -6 -l 10001 > msg +guest2 echo "Hello_from_guest_2" | nc -u -q1 -N __GW2_6__%__IFNAME2__ 10001 +guest1w +sleep 1 +g1out MSG1 cat msg +check [ "__MSG1__" = "Hello_from_guest_2" ] diff --git a/test/udp/passt b/test/udp/passt new file mode 100644 index 0000000..223871f --- /dev/null +++ b/test/udp/passt @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: AGPL-3.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/udp/passt - Check UDP functionality in passt mode +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt +gtools nc tee grep cat ip jq md5sum cut +htools printf dd nc tee grep cat ip jq md5sum cut + +test UDP/IPv4: host to guest +temp TEMP +temp NC_PID +guestb (nc -u -q1 -4 -l 10001 & echo $! > __NC_PID__) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +host dd if=/dev/urandom bs=1k count=5 > __TEMP_BIG__ && printf "\nEND_OF_TEST\n" >> __TEMP__ +host cat __TEMP__ | nc -u -q1 -N 127.0.0.1 10001 +guestw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__HOST_MD5__" ] + +test UDP/IPv4: guest to host +host :> __TEMP__ +hostb (nc -u -q1 -4 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +guest cat test.bin | nc -u -q1 -N __GW__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__HOST_MD5__" ] + +test UDP/IPv6: host to guest +guestb (nc -u -q1 -6 -l 10001 & echo $! > nc.pid) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat nc.pid)) +sleep 1 +host cat __TEMP__ | nc -u -q1 -N ::1 10001 +guestw +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__HOST_MD5__" ] + +test UDP/IPv6: guest to host +host :> __TEMP__ +hostb (nc -u -q1 -6 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +guest cat test.bin | nc -u -q1 -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__HOST_MD5__" ] diff --git a/test/udp/passt_in_ns b/test/udp/passt_in_ns new file mode 100644 index 0000000..b9b1a97 --- /dev/null +++ b/test/udp/passt_in_ns @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: AGPL-3.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/udp/passt_in_ns - Check UDP functionality for passt in ns and pasta +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor passt_in_ns +gtools nc tee grep cat ip jq md5sum cut +nstools nc tee grep cat ip jq md5sum cut +htools printf dd nc tee grep cat ip jq md5sum cut + +test UDP/IPv4: host to guest +temp TEMP +temp NC_PID +guestb (nc -u -q1 -4 -l 10001 & echo $! > nc.pic) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat nc.pic)) +host dd if=/dev/urandom bs=1k count=5 > __TEMP__ && printf "\nEND_OF_TEST\n" >> __TEMP__ +host cat __TEMP__ | nc -u -q1 -N 127.0.0.1 10001 +guestw +hout MD5 md5sum __TEMP__ | cut -d' ' -f1 +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__MD5__" ] + +test UDP/IPv4: host to ns +temp TEMP_NS +ns :> __TEMP_NS__ +nsb (nc -u -q1 -4 -l 10002 & echo $! > __NC_PID__) | tee __TEMP_NS__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +host cat __TEMP__ | nc -u -q1 -N 127.0.0.1 10002 +nsw +nsout NS_MD5 md5sum __TEMP_NS__ | cut -d' ' -f1 +check [ "__NS_MD5__" = "__MD5__" ] + +test UDP/IPv4: guest to host +host :> __TEMP__ +hostb (nc -u -q1 -4 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +gout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +guest cat test.bin | nc -u -q1 -N __GW__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv4: guest to ns +ns :> __TEMP_NS__ +nsb (nc -u -q1 -4 -l 10002 & echo $! > __NC_PID__) | tee __TEMP_NS__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +guest cat test.bin | nc -u -q1 -N __GW__ 10002 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv4: ns to host (recvmmsg/sendmmsg) +sleep 1 +host :> __TEMP__ +hostb (nc -u -q1 -4 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +ns cat __TEMP_NS__ | nc -u -q1 -N 127.0.0.1 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv4: ns to host (via tap) +host :> __TEMP__ +hostb (nc -u -q1 -4 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +ns cat __TEMP_NS__ | nc -u -q1 -N __GW__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv4: ns to guest (using loopback address) +guestb (nc -u -q1 -4 -l 10001 & echo $! > nc.pid) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat nc.pid)) +ns cat __TEMP_NS__ | nc -u -q1 -N 127.0.0.1 10001 +guestw +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__MD5__" ] + +test UDP/IPv4: ns to guest (using namespace address) +guestb (nc -u -q1 -4 -l 10001 & echo $! > nc.pid) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat nc.pid)) +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +nsout ADDR ip -j -4 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' +ns cat __TEMP_NS__ | nc -u -q1 -N __ADDR__ 10001 +guestw +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__MD5__" ] + +test UDP/IPv6: host to guest +guestb (nc -u -q1 -6 -l 10001 & echo $! > nc.pid) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat nc.pid)) +host cat __TEMP__ | nc -u -q1 -N ::1 10001 +guestw +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__MD5__" ] + +test UDP/IPv6: host to ns +ns :> __TEMP_NS__ +nsb (nc -u -q1 -6 -l 10002 & echo $! > __NC_PID__) | tee __TEMP_NS__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +host cat __TEMP__ | nc -u -q1 -N ::1 10002 +nsw +nsout NS_MD5 md5sum __TEMP_NS__ | cut -d' ' -f1 +check [ "__NS_MD5__" = "__MD5__" ] + +test UDP/IPv6: guest to host +host :> __TEMP__ +hostb (nc -u -q1 -6 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +gout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +gout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +guest cat test.bin | nc -u -q1 -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv6: guest to ns +ns :> __TEMP_NS__ +nsb (nc -u -q1 -6 -l 10002 & echo $! > __NC_PID__) | tee __TEMP_NS__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +guest cat test.bin | nc -u -q1 -N __GW6__%__IFNAME__ 10002 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv6: ns to host (recvmmsg/sendmmsg) +host :> __TEMP__ +hostb (nc -u -q1 -6 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +ns cat __TEMP_NS__ | nc -u -q1 -N ::1 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv6: ns to host (via tap) +host :> __TEMP__ +hostb (nc -u -q1 -6 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +ns cat __TEMP_NS__ | nc -u -q1 -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv6: ns to guest (using loopback address) +guestb (nc -u -q1 -6 -l 10001 & echo $! > nc.pid) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat nc.pid)) +ns cat __TEMP_NS__ | nc -u -q1 -N ::1 10001 +guestw +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__MD5__" ] + +test UDP/IPv6: ns to guest (using namespace address) +guestb (nc -u -q1 -6 -l 10001 & echo $! > nc.pid) | tee test.bin | (grep -qm1 "END_OF_TEST" && kill $(cat nc.pid)) +nsout ADDR6 ip -j -6 ad sh|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[0].local' +ns cat __TEMP_NS__ | nc -u -q1 -N __ADDR6__ 10001 +guestw +gout GUEST_MD5 md5sum test.bin | cut -d' ' -f1 +check [ "__GUEST_MD5__" = "__MD5__" ] diff --git a/test/udp/pasta b/test/udp/pasta new file mode 100644 index 0000000..dd4b72d --- /dev/null +++ b/test/udp/pasta @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: AGPL-3.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/udp/pasta - Check UDP functionality for pasta +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +onlyfor pasta +nstools nc tee grep cat ip jq md5sum cut +htools printf dd nc tee grep cat ip jq md5sum cut + +test UDP/IPv4: host to ns +temp TEMP +temp TEMP_NS +temp NC_PID +ns :> __TEMP_NS__ +nsb (nc -u -q1 -4 -l 10002 & echo $! > __NC_PID__) | tee __TEMP_NS__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +host dd if=/dev/urandom bs=1k count=5 > __TEMP__ && printf "\nEND_OF_TEST\n" >> __TEMP__ +host cat __TEMP__ | nc -u -q1 -N 127.0.0.1 10002 +nsw +hout MD5 md5sum __TEMP__ | cut -d' ' -f1 +nsout NS_MD5 md5sum __TEMP_NS__ | cut -d' ' -f1 +check [ "__NS_MD5__" = "__MD5__" ] + +test UDP/IPv4: ns to host (recvmmsg/sendmmsg) +host :> __TEMP__ +hostb (nc -u -q1 -4 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +sleep 1 +ns cat __TEMP_NS__ | nc -u -q1 -N 127.0.0.1 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv4: ns to host (via tap) +host :> __TEMP__ +hostb (nc -u -q1 -4 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +nsout GW ip -j -4 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +ns cat __TEMP_NS__ | nc -u -q1 -N __GW__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] + +test UDP/IPv6: host to ns +ns :> __TEMP_NS__ +nsb (nc -u -q1 -6 -l 10002 & echo $! > __NC_PID__) | tee __TEMP_NS__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +host cat __TEMP__ | nc -u -q1 -N ::1 10002 +nsw +hout MD5 md5sum __TEMP__ | cut -d' ' -f1 +nsout NS_MD5 md5sum __TEMP_NS__ | cut -d' ' -f1 +check [ "__NS_MD5__" = "__MD5__" ] + +test UDP/IPv6: ns to host (recvmmsg/sendmmsg) +host :> __TEMP__ +hostb (nc -u -q1 -6 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +sleep 1 +ns cat __TEMP_NS__ | nc -u -q1 -N ::1 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ] +nsw + +test UDP/IPv6: ns to host (via tap) +host :> __TEMP__ +hostb (nc -u -q1 -6 -l 10003 & echo $! > __NC_PID__) | tee __TEMP__ | (grep -qm1 "END_OF_TEST" && kill $(cat __NC_PID__)) +nsout GW6 ip -j -6 ro sh|jq -rM '.[] | select(.dst == "default").gateway' +nsout IFNAME ip -j li sh | jq -rM '.[] | select(.link_type == "ether").ifname' +ns cat __TEMP_NS__ | nc -u -q1 -N __GW6__%__IFNAME__ 10003 +hostw +hout HOST_MD5 md5sum __TEMP__ | cut -d' ' -f1 +check [ "__HOST_MD5__" = "__MD5__" ]