1
0
mirror of https://passt.top/passt synced 2025-02-22 02:42:22 +00:00

test: Add migration tests

PCAP=1 ./run migrate/bidirectional gives an overview of how the
whole thing is working.

Add 12 tests in total, checking basic functionality with and without
flows in both directions, with and without sockets in half-closed
states (both inbound and outbound), migration behaviour under traffic
flood, under traffic flood with > 253 flows, and strict checking of
sequences under flood with ramp patterns in both directions.

These tests need preparation and teardown for each case, as we need
to restore the source guest in its own context and pane before we can
test again. Eventually, we could consider alternating source and
target so that we don't need to restart from scratch every time, but
that's beyond the scope of this initial test implementation.

Trick: './run migrate/*' runs all the tests with preparation and
teardown steps.

Co-authored-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
Stefano Brivio 2025-02-13 23:14:14 +11:00
parent 89ecf2fd40
commit a1e48a02ff
16 changed files with 871 additions and 14 deletions

View File

@ -135,7 +135,7 @@ layout_two_guests() {
get_info_cols
pane_watch_contexts ${PANE_GUEST_1} "guest #1 in namespace #1" qemu_1 guest_1
pane_watch_contexts ${PANE_GUEST_2} "guest #2 in namespace #2" qemu_2 guest_2
pane_watch_contexts ${PANE_GUEST_2} "guest #2 in namespace #1" qemu_2 guest_2
tmux send-keys -l -t ${PANE_INFO} 'while cat '"$STATEBASE/log_pipe"'; do :; done'
tmux send-keys -t ${PANE_INFO} -N 100 C-m
@ -143,13 +143,66 @@ layout_two_guests() {
pane_watch_contexts ${PANE_HOST} host host
pane_watch_contexts ${PANE_PASST_1} "passt #1 in namespace #1" pasta_1 passt_1
pane_watch_contexts ${PANE_PASST_2} "passt #2 in namespace #2" pasta_2 passt_2
pane_watch_contexts ${PANE_PASST_2} "passt #2 in namespace #1" pasta_1 passt_2
info_layout "two guests, two passt instances, in namespaces"
sleep 1
}
# layout_migrate() - Two guest panes, two passt panes, two passt-repair panes,
# plus host and log
layout_migrate() {
sleep 1
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
tmux split-window -v -t passt_test:1.4
tmux split-window -v -t passt_test:1.6
tmux split-window -v -t passt_test:1.3
PANE_GUEST_1=0
PANE_GUEST_2=1
PANE_INFO=2
PANE_MON=3
PANE_HOST=4
PANE_PASST_REPAIR_1=5
PANE_PASST_1=6
PANE_PASST_REPAIR_2=7
PANE_PASST_2=8
get_info_cols
pane_watch_contexts ${PANE_GUEST_1} "guest #1 in namespace #1" qemu_1 guest_1
pane_watch_contexts ${PANE_GUEST_2} "guest #2 in namespace #2" qemu_2 guest_2
tmux send-keys -l -t ${PANE_INFO} 'while cat '"$STATEBASE/log_pipe"'; do :; done'
tmux send-keys -t ${PANE_INFO} -N 100 C-m
tmux select-pane -t ${PANE_INFO} -T "test log"
pane_watch_contexts ${PANE_MON} "QEMU monitor" mon mon
pane_watch_contexts ${PANE_HOST} host host
pane_watch_contexts ${PANE_PASST_REPAIR_1} "passt-repair #1 in namespace #1" repair_1 passt_repair_1
pane_watch_contexts ${PANE_PASST_1} "passt #1 in namespace #1" pasta_1 passt_1
pane_watch_contexts ${PANE_PASST_REPAIR_2} "passt-repair #2 in namespace #2" repair_2 passt_repair_2
pane_watch_contexts ${PANE_PASST_2} "passt #2 in namespace #2" pasta_2 passt_2
info_layout "two guests, two passt + passt-repair instances, in namespaces"
sleep 1
}
# layout_demo_pasta() - Four panes for pasta demo
layout_demo_pasta() {
sleep 1

View File

@ -305,6 +305,117 @@ setup_two_guests() {
context_setup_guest guest_2 ${GUEST_2_CID}
}
# setup_migrate() - Set up two namespace, run qemu, passt/passt-repair in both
setup_migrate() {
context_setup_host host
context_setup_host mon
context_setup_host pasta_1
context_setup_host pasta_2
layout_migrate
# Ports:
#
# guest #1 | guest #2 | ns #1 | host
# --------- |-----------|-----------|------------
# 10001 as server | | to guest | to ns #1
# 10002 | | as server | to ns #1
# 10003 | | to init | as server
# 10004 | as server | to guest | to ns #1
__opts=
[ ${PCAP} -eq 1 ] && __opts="${__opts} -p ${LOGDIR}/pasta_1.pcap"
[ ${DEBUG} -eq 1 ] && __opts="${__opts} -d"
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
__map_host4=192.0.2.1
__map_host6=2001:db8:9a55::1
__map_ns4=192.0.2.2
__map_ns6=2001:db8:9a55::2
# Option 1: send stuff via spliced path in pasta
# context_run_bg pasta_1 "./pasta ${__opts} -P ${STATESETUP}/pasta_1.pid -t 10001,10002 -T 10003 -u 10001,10002 -U 10003 --config-net ${NSTOOL} hold ${STATESETUP}/ns1.hold"
# Option 2: send stuff via tap (--map-guest-addr) instead (useful to see capture of full migration)
context_run_bg pasta_1 "./pasta ${__opts} -P ${STATESETUP}/pasta_1.pid -t 10001,10002,10004 -T 10003 -u 10001,10002,10004 -U 10003 --map-guest-addr ${__map_host4} --map-guest-addr ${__map_host6} --config-net ${NSTOOL} hold ${STATESETUP}/ns1.hold"
context_setup_nstool passt_1 ${STATESETUP}/ns1.hold
context_setup_nstool passt_repair_1 ${STATESETUP}/ns1.hold
context_setup_nstool passt_2 ${STATESETUP}/ns1.hold
context_setup_nstool passt_repair_2 ${STATESETUP}/ns1.hold
context_setup_nstool qemu_1 ${STATESETUP}/ns1.hold
context_setup_nstool qemu_2 ${STATESETUP}/ns1.hold
__ifname="$(context_run qemu_1 "ip -j link show | jq -rM '.[] | select(.link_type == \"ether\").ifname'")"
sleep 1
__opts="--vhost-user"
[ ${PCAP} -eq 1 ] && __opts="${__opts} -p ${LOGDIR}/passt_1.pcap"
[ ${DEBUG} -eq 1 ] && __opts="${__opts} -d"
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
context_run_bg passt_1 "./passt -s ${STATESETUP}/passt_1.socket -P ${STATESETUP}/passt_1.pid -f ${__opts} -t 10001 -u 10001"
wait_for [ -f "${STATESETUP}/passt_1.pid" ]
context_run_bg passt_repair_1 "./passt-repair ${STATESETUP}/passt_1.socket.repair"
__opts="--vhost-user"
[ ${PCAP} -eq 1 ] && __opts="${__opts} -p ${LOGDIR}/passt_2.pcap"
[ ${DEBUG} -eq 1 ] && __opts="${__opts} -d"
[ ${TRACE} -eq 1 ] && __opts="${__opts} --trace"
context_run_bg passt_2 "./passt -s ${STATESETUP}/passt_2.socket -P ${STATESETUP}/passt_2.pid -f ${__opts} -t 10004 -u 10004"
wait_for [ -f "${STATESETUP}/passt_2.pid" ]
context_run_bg passt_repair_2 "./passt-repair ${STATESETUP}/passt_2.socket.repair"
__vmem="512M" # Keep migration fast
__qemu_netdev1=" \
-chardev socket,id=c,path=${STATESETUP}/passt_1.socket \
-netdev vhost-user,id=v,chardev=c \
-device virtio-net,netdev=v \
-object memory-backend-memfd,id=m,share=on,size=${__vmem} \
-numa node,memdev=m"
__qemu_netdev2=" \
-chardev socket,id=c,path=${STATESETUP}/passt_2.socket \
-netdev vhost-user,id=v,chardev=c \
-device virtio-net,netdev=v \
-object memory-backend-memfd,id=m,share=on,size=${__vmem} \
-numa node,memdev=m"
GUEST_1_CID=94557
context_run_bg qemu_1 'qemu-system-'"${QEMU_ARCH}" \
' -M accel=kvm:tcg' \
' -m '${__vmem}' -cpu host -smp '${VCPUS} \
' -kernel '"${KERNEL}" \
' -initrd '${INITRAMFS}' -nographic -serial stdio' \
' -nodefaults' \
' -append "console=ttyS0 mitigations=off apparmor=0" ' \
" ${__qemu_netdev1}" \
" -pidfile ${STATESETUP}/qemu_1.pid" \
" -device vhost-vsock-pci,guest-cid=$GUEST_1_CID" \
" -monitor unix:${STATESETUP}/qemu_1_mon.sock,server,nowait"
GUEST_2_CID=94558
context_run_bg qemu_2 'qemu-system-'"${QEMU_ARCH}" \
' -M accel=kvm:tcg' \
' -m '${__vmem}' -cpu host -smp '${VCPUS} \
' -kernel '"${KERNEL}" \
' -initrd '${INITRAMFS}' -nographic -serial stdio' \
' -nodefaults' \
' -append "console=ttyS0 mitigations=off apparmor=0" ' \
" ${__qemu_netdev2}" \
" -pidfile ${STATESETUP}/qemu_2.pid" \
" -device vhost-vsock-pci,guest-cid=$GUEST_2_CID" \
" -monitor unix:${STATESETUP}/qemu_2_mon.sock,server,nowait" \
" -incoming tcp:0:20005"
context_setup_guest guest_1 ${GUEST_1_CID}
# Only available after migration:
( context_setup_guest guest_2 ${GUEST_2_CID} & )
}
# teardown_context_watch() - Remove contexts and stop panes watching them
# $1: Pane number watching
# $@: Context names
@ -375,7 +486,8 @@ teardown_two_guests() {
context_wait pasta_1
context_wait pasta_2
rm -f "${STATESETUP}/passt__[12].pid" "${STATESETUP}/pasta_[12].pid"
rm "${STATESETUP}/passt_1.pid" "${STATESETUP}/passt_2.pid"
rm "${STATESETUP}/pasta_1.pid" "${STATESETUP}/pasta_2.pid"
teardown_context_watch ${PANE_HOST} host
teardown_context_watch ${PANE_GUEST_1} qemu_1 guest_1
@ -384,6 +496,30 @@ teardown_two_guests() {
teardown_context_watch ${PANE_PASST_2} pasta_2 passt_2
}
# teardown_migrate() - Exit namespaces, kill qemu processes, passt and pasta
teardown_migrate() {
${NSTOOL} exec ${STATESETUP}/ns1.hold -- kill $(cat "${STATESETUP}/qemu_1.pid")
${NSTOOL} exec ${STATESETUP}/ns1.hold -- kill $(cat "${STATESETUP}/qemu_2.pid")
context_wait qemu_1
context_wait qemu_2
${NSTOOL} exec ${STATESETUP}/ns1.hold -- kill $(cat "${STATESETUP}/passt_2.pid")
context_wait passt_1
context_wait passt_2
${NSTOOL} stop "${STATESETUP}/ns1.hold"
context_wait pasta_1
rm -f "${STATESETUP}/passt_1.pid" "${STATESETUP}/passt_2.pid"
rm -f "${STATESETUP}/pasta_1.pid" "${STATESETUP}/pasta_2.pid"
teardown_context_watch ${PANE_HOST} host
teardown_context_watch ${PANE_GUEST_1} qemu_1 guest_1
teardown_context_watch ${PANE_GUEST_2} qemu_2 guest_2
teardown_context_watch ${PANE_PASST_1} pasta_1 passt_1
teardown_context_watch ${PANE_PASST_2} pasta_1 passt_2
}
# teardown_demo_passt() - Exit namespace, kill qemu, passt and pasta
teardown_demo_passt() {
tmux send-keys -t ${PANE_GUEST} "C-c"

View File

@ -68,6 +68,45 @@ test_iperf3() {
TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__var}__" "${__bw}" )"
}
# test_iperf3m() - Ugly helper for iperf3 directive, guest migration variant
# $1: Variable name: to put the measure bandwidth into
# $2: Initial source/client context
# $3: Second source/client context the guest is moving to
# $4: Destination name or address for client
# $5: Port number, ${i} is translated to process index
# $6: Run time, in seconds
# $7: Client options
test_iperf3m() {
__var="${1}"; shift
__cctx="${1}"; shift
__cctx2="${1}"; shift
__dest="${1}"; shift
__port="${1}"; shift
__time="${1}"; shift
pane_or_context_run "${__cctx}" 'rm -f c.json'
# A 1s wait for connection on what's basically a local link
# indicates something is pretty wrong
__timeout=1000
pane_or_context_run_bg "${__cctx}" \
'iperf3 -J -c '${__dest}' -p '${__port} \
' --connect-timeout '${__timeout} \
' -t'${__time}' -i0 '"${@}"' > c.json' \
__jval=".end.sum_received.bits_per_second"
sleep $((${__time} + 3))
pane_or_context_output "${__cctx2}" \
'cat c.json'
__bw=$(pane_or_context_output "${__cctx2}" \
'cat c.json | jq -rMs "map('${__jval}') | add"')
TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__var}__" "${__bw}" )"
}
test_one_line() {
__line="${1}"
@ -177,6 +216,12 @@ test_one_line() {
"guest2w")
pane_or_context_wait guest_2 || TEST_ONE_nok=1
;;
"mon")
pane_or_context_run mon "${__arg}" || TEST_ONE_nok=1
;;
"monb")
pane_or_context_run_bg mon "${__arg}"
;;
"ns")
pane_or_context_run ns "${__arg}" || TEST_ONE_nok=1
;;
@ -292,6 +337,9 @@ test_one_line() {
"iperf3")
test_iperf3 ${__arg}
;;
"iperf3m")
test_iperf3m ${__arg}
;;
"set")
TEST_ONE_subs="$(list_add_pair "${TEST_ONE_subs}" "__${__arg%% *}__" "${__arg#* }")"
;;

59
test/migrate/basic Normal file
View File

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

62
test/migrate/basic_fin Normal file
View File

@ -0,0 +1,62 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/basic_fin - Outbound traffic across migration, half-closed socket
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test DHCPv6: address
# Link is up now, wait for DAD to complete
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv4: guest1, half-close, guest2 > host
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
hostb echo FIN | socat TCP4-LISTEN:10006,shut-down STDIO,ignoreeof > __STATESETUP__/msg
#hostb socat -u TCP4-LISTEN:10006 OPEN:__STATESETUP__/msg,create,trunc
#sleep 20
# Option 1: via spliced path in pasta, namespace to host
# guest1b { printf "Hello from guest 1"; sleep 10; printf " and from guest 2\n"; } | socat -u STDIN TCP4:__GW1__:10003
# Option 2: via --map-guest-addr (tap) in pasta, namespace to host
guest1b { printf "Hello from guest 1"; sleep 3; printf " and from guest 2\n"; } | socat -u STDIN TCP4:__MAP_HOST4__:10006
sleep 1
mon echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
hostw
hout MSG cat __STATESETUP__/msg
check [ "__MSG__" = "Hello from guest 1 and from guest 2" ]

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/bidirectional - Check migration with messages in both directions
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test TCP/IPv4: guest1/guest2 > host, host > guest1/guest2
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
hostb socat -u TCP4-LISTEN:10006 OPEN:__STATESETUP__/msg,create,trunc
guest1b socat -u TCP4-LISTEN:10001 OPEN:msg,create,trunc
sleep 1
guest1b socat -u UNIX-RECV:proxy.sock,null-eof TCP4:__MAP_HOST4__:10006
hostb socat -u UNIX-RECV:__STATESETUP__/proxy.sock,null-eof TCP4:__ADDR1__:10001
sleep 1
guest1 printf "Hello from guest 1" | socat -u STDIN UNIX:proxy.sock
host printf "Dear guest 1," | socat -u STDIN UNIX:__STATESETUP__/proxy.sock
sleep 1
mon echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
sleep 2
guest2 printf " and from guest 2" | socat -u STDIN UNIX:proxy.sock,shut-null
host printf " you are now guest 2" | socat -u STDIN UNIX:__STATESETUP__/proxy.sock,shut-null
hostw
# FIXME: guest2w doesn't work here because shell jobs are (also) from guest #1,
# use sleep 1 for the moment
sleep 1
hout MSG cat __STATESETUP__/msg
check [ "__MSG__" = "Hello from guest 1 and from guest 2" ]
g2out MSG cat msg
check [ "__MSG__" = "Dear guest 1, you are now guest 2" ]

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/bidirectional_fin - Both directions, half-closed sockets
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test TCP/IPv4: guest1/guest2 <- (half closed) -> host
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
hostb echo FIN | socat TCP4-LISTEN:10006,shut-down STDIO,ignoreeof > __STATESETUP__/msg
guest1b echo FIN | socat TCP4-LISTEN:10001,shut-down STDIO,ignoreeof > msg
sleep 1
guest1b socat -u UNIX-RECV:proxy.sock,null-eof TCP4:__MAP_HOST4__:10006
hostb socat -u UNIX-RECV:__STATESETUP__/proxy.sock,null-eof TCP4:__ADDR1__:10001
sleep 1
guest1 printf "Hello from guest 1" | socat -u STDIN UNIX:proxy.sock
host printf "Dear guest 1," | socat -u STDIN UNIX:__STATESETUP__/proxy.sock
sleep 1
mon echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
sleep 2
guest2 printf " and from guest 2" | socat -u STDIN UNIX:proxy.sock,shut-null
host printf " you are now guest 2" | socat -u STDIN UNIX:__STATESETUP__/proxy.sock,shut-null
hostw
# FIXME: guest2w doesn't work here because shell jobs are (also) from guest #1,
# use sleep 1 for the moment
sleep 1
hout MSG cat __STATESETUP__/msg
check [ "__MSG__" = "Hello from guest 1 and from guest 2" ]
g2out MSG cat msg
check [ "__MSG__" = "Dear guest 1, you are now guest 2" ]

View File

@ -0,0 +1,58 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/iperf3_bidir6 - Migration behaviour with many bidirectional flows
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
set THREADS 128
set TIME 3
set OMIT 0.1
set OPTS -Z -P __THREADS__ -O__OMIT__ -N --bidir
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test DHCPv6: address
# Link is up now, wait for DAD to complete
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv6 host <-> guest flood, many flows, during migration
monb sleep 1; echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
iperf3s host 10006
iperf3m BW guest_1 guest_2 __MAP_HOST6__ 10006 __TIME__ __OPTS__
bw __BW__ 1 2
iperf3k host

50
test/migrate/iperf3_in4 Normal file
View File

@ -0,0 +1,50 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/iperf3_in4 - Migration behaviour under inbound IPv4 flood
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
guest1 /sbin/sysctl -w net.core.rmem_max=33554432
guest1 /sbin/sysctl -w net.core.wmem_max=33554432
set THREADS 1
set TIME 4
set OMIT 0.1
set OPTS -Z -P __THREADS__ -O__OMIT__ -N -R
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test TCP/IPv4 host to guest throughput during migration
monb sleep 1; echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
iperf3s host 10006
iperf3m BW guest_1 guest_2 __MAP_HOST4__ 10006 __TIME__ __OPTS__
bw __BW__ 1 2
iperf3k host

58
test/migrate/iperf3_in6 Normal file
View File

@ -0,0 +1,58 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/iperf3_in6 - Migration behaviour under inbound IPv6 flood
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
set THREADS 4
set TIME 3
set OMIT 0.1
set OPTS -Z -P __THREADS__ -O__OMIT__ -N -R
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test DHCPv6: address
# Link is up now, wait for DAD to complete
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv6 host to guest throughput during migration
monb sleep 1; echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
iperf3s host 10006
iperf3m BW guest_1 guest_2 __MAP_HOST6__ 10006 __TIME__ __OPTS__
bw __BW__ 1 2
iperf3k host

View File

@ -0,0 +1,60 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/iperf3_many_out6 - Migration behaviour with many outbound flows
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
set THREADS 16
set TIME 3
set OMIT 0.1
set OPTS -Z -P __THREADS__ -O__OMIT__ -N -l 1M
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test DHCPv6: address
# Link is up now, wait for DAD to complete
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv6 guest to host flood, many flows, during migration
test TCP/IPv6 host to guest throughput during migration
monb sleep 1; echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
iperf3s host 10006
iperf3m BW guest_1 guest_2 __MAP_HOST6__ 10006 __TIME__ __OPTS__
bw __BW__ 1 2
iperf3k host

47
test/migrate/iperf3_out4 Normal file
View File

@ -0,0 +1,47 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/iperf3_out4 - Migration behaviour under outbound IPv4 flood
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
set THREADS 6
set TIME 2
set OMIT 0.1
set OPTS -P __THREADS__ -O__OMIT__ -Z -N -l 1M
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test TCP/IPv4 guest to host throughput during migration
monb sleep 1; echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
iperf3s host 10006
iperf3m BW guest_1 guest_2 __MAP_HOST4__ 10006 __TIME__ __OPTS__
bw __BW__ 1 2
iperf3k host

58
test/migrate/iperf3_out6 Normal file
View File

@ -0,0 +1,58 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# PASST - Plug A Simple Socket Transport
# for qemu/UNIX domain socket mode
#
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/iperf3_out6 - Migration behaviour under outbound IPv6 flood
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
g1tools ip jq dhclient socat cat
htools ip jq
set MAP_HOST4 192.0.2.1
set MAP_HOST6 2001:db8:9a55::1
set MAP_NS4 192.0.2.2
set MAP_NS6 2001:db8:9a55::2
set THREADS 6
set TIME 2
set OMIT 0.1
set OPTS -P __THREADS__ -O__OMIT__ -Z -N -l 1M
test Interface name
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
check [ -n "__IFNAME1__" ]
test DHCP: address
guest1 ip link set dev __IFNAME1__ up
guest1 /sbin/dhclient -4 __IFNAME1__
g1out ADDR1 ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[0].local'
hout HOST_ADDR ip -j -4 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[0].local'
check [ "__ADDR1__" = "__HOST_ADDR__" ]
test DHCPv6: address
# Link is up now, wait for DAD to complete
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
guest1 /sbin/dhclient -6 __IFNAME1__
# Wait for DAD to complete on the DHCP address
guest1 while ip -j -6 addr show tentative | jq -e '.[].addr_info'; do sleep 0.1; done
g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local] | .[0]'
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv6 guest to host throughput during migration
monb sleep 1; echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
iperf3s host 10006
iperf3m BW guest_1 guest_2 __MAP_HOST6__ 10006 __TIME__ __OPTS__
bw __BW__ 1 2
iperf3k host

View File

@ -6,10 +6,10 @@
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/basic - Check basic migration functionality
# test/migrate/rampstream_in - Check sequence correctness with inbound ramp
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
# Copyright (c) 2025 Red Hat
# Author: David Gibson <david@gibson.dropbear.id.au>
g1tools ip jq dhclient socat cat
htools ip jq
@ -43,15 +43,15 @@ g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__")
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv4: host > guest
test TCP/IPv4: sequence check, ramps, inbound
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
guest1b socat -u TCP4-LISTEN:10001 EXEC:"rampstream-check.sh __RAMPS__"
sleep 1
hostb socat -u EXEC:"test/rampstream send __RAMPS__" TCP4:__ADDR1__:10001
sleep 1
sleep 1
#mon echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
monb echo "migrate tcp:0:20005" | socat -u STDIN UNIX:__STATESETUP__/qemu_1_mon.sock
hostw

View File

@ -6,10 +6,10 @@
# PASTA - Pack A Subtle Tap Abstraction
# for network namespace/tap device mode
#
# test/migrate/basic - Check basic migration functionality
# test/migrate/rampstream_out - Check sequence correctness with outbound ramp
#
# Copyright (c) 2025 Red Hat GmbH
# Author: Stefano Brivio <sbrivio@redhat.com>
# Copyright (c) 2025 Red Hat
# Author: David Gibson <david@gibson.dropbear.id.au>
g1tools ip jq dhclient socat cat
htools ip jq
@ -43,7 +43,7 @@ g1out ADDR1_6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__IFNAME1__")
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '[.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global" and .deprecated != true).local] | .[0]'
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
test TCP/IPv4: guest > host
test TCP/IPv4: sequence check, ramps, outbound
g1out GW1 ip -j -4 route show|jq -rM '.[] | select(.dst == "default").gateway'
hostb socat -u TCP4-LISTEN:10006 EXEC:"test/rampstream check __RAMPS__"
sleep 1

View File

@ -130,6 +130,43 @@ run() {
test two_guests_vu/basic
teardown two_guests
setup migrate
test migrate/basic
teardown migrate
setup migrate
test migrate/basic_fin
teardown migrate
setup migrate
test migrate/bidirectional
teardown migrate
setup migrate
test migrate/bidirectional_fin
teardown migrate
setup migrate
test migrate/iperf3_out4
teardown migrate
setup migrate
test migrate/iperf3_out6
teardown migrate
setup migrate
test migrate/iperf3_in4
teardown migrate
setup migrate
test migrate/iperf3_in6
teardown migrate
setup migrate
test migrate/iperf3_bidir6
teardown migrate
setup migrate
test migrate/iperf3_many_out6
teardown migrate
setup migrate
test migrate/rampstream_in
teardown migrate
setup migrate
test migrate/rampstream_out
teardown migrate
VALGRIND=0
VHOST_USER=0
setup passt_in_ns
@ -186,7 +223,10 @@ run_selected() {
__setup=
for __test; do
if [ "${__test%%/*}" != "${__setup}" ]; then
# HACK: the migrate tests need the setup repeated for
# each test
if [ "${__test%%/*}" != "${__setup}" -o \
"${__test%%/*}" = "migrate" ]; then
[ -n "${__setup}" ] && teardown "${__setup}"
__setup="${__test%%/*}"
setup "${__setup}"