public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: Stefano Brivio <sbrivio@redhat.com>
Cc: passt-dev@passt.top
Subject: Re: [PATCH v27 2/2] test: Add migration tests
Date: Mon, 17 Feb 2025 14:47:03 +1100	[thread overview]
Message-ID: <Z7KxN5DAlygeo1_R@zatzit> (raw)
In-Reply-To: <20250216222227.2017788-3-sbrivio@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 45198 bytes --]

On Sun, Feb 16, 2025 at 11:22:27PM +0100, Stefano Brivio wrote:
> 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>
> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

> ---
>  test/lib/layout                |  55 ++++++++++++-
>  test/lib/setup                 | 138 ++++++++++++++++++++++++++++++++-
>  test/lib/test                  |  48 ++++++++++++
>  test/migrate/basic             |  59 ++++++++++++++
>  test/migrate/basic_fin         |  62 +++++++++++++++
>  test/migrate/bidirectional     |  64 +++++++++++++++
>  test/migrate/bidirectional_fin |  64 +++++++++++++++
>  test/migrate/iperf3_bidir6     |  58 ++++++++++++++
>  test/migrate/iperf3_in4        |  50 ++++++++++++
>  test/migrate/iperf3_in6        |  58 ++++++++++++++
>  test/migrate/iperf3_many_out6  |  60 ++++++++++++++
>  test/migrate/iperf3_out4       |  47 +++++++++++
>  test/migrate/iperf3_out6       |  58 ++++++++++++++
>  test/migrate/rampstream_in     |  12 +--
>  test/migrate/rampstream_out    |   8 +-
>  test/run                       |  42 +++++++++-
>  16 files changed, 870 insertions(+), 13 deletions(-)
>  create mode 100644 test/migrate/basic
>  create mode 100644 test/migrate/basic_fin
>  create mode 100644 test/migrate/bidirectional
>  create mode 100644 test/migrate/bidirectional_fin
>  create mode 100644 test/migrate/iperf3_bidir6
>  create mode 100644 test/migrate/iperf3_in4
>  create mode 100644 test/migrate/iperf3_in6
>  create mode 100644 test/migrate/iperf3_many_out6
>  create mode 100644 test/migrate/iperf3_out4
>  create mode 100644 test/migrate/iperf3_out6
> 
> diff --git a/test/lib/layout b/test/lib/layout
> index 4d03572..fddcdc4 100644
> --- a/test/lib/layout
> +++ b/test/lib/layout
> @@ -134,6 +134,54 @@ 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 #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
> +	tmux select-pane -t ${PANE_INFO} -T "test log"
> +
> +	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 #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
>  
> @@ -141,11 +189,16 @@ layout_two_guests() {
>  	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 instances, in namespaces"
> +	info_layout "two guests, two passt + passt-repair instances, in namespaces"
>  
>  	sleep 1
>  }
> diff --git a/test/lib/setup b/test/lib/setup
> index ee67152..575bc21 100755
> --- a/test/lib/setup
> +++ b/test/lib/setup
> @@ -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"
> diff --git a/test/lib/test b/test/lib/test
> index e6726be..758250a 100755
> --- a/test/lib/test
> +++ b/test/lib/test
> @@ -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#* }")"
>  		;;
> diff --git a/test/migrate/basic b/test/migrate/basic
> new file mode 100644
> index 0000000..3f11f7d
> --- /dev/null
> +++ b/test/migrate/basic
> @@ -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" ]
> diff --git a/test/migrate/basic_fin b/test/migrate/basic_fin
> new file mode 100644
> index 0000000..aa61ec5
> --- /dev/null
> +++ b/test/migrate/basic_fin
> @@ -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" ]
> diff --git a/test/migrate/bidirectional b/test/migrate/bidirectional
> new file mode 100644
> index 0000000..4c04081
> --- /dev/null
> +++ b/test/migrate/bidirectional
> @@ -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" ]
> diff --git a/test/migrate/bidirectional_fin b/test/migrate/bidirectional_fin
> new file mode 100644
> index 0000000..1c13527
> --- /dev/null
> +++ b/test/migrate/bidirectional_fin
> @@ -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" ]
> diff --git a/test/migrate/iperf3_bidir6 b/test/migrate/iperf3_bidir6
> new file mode 100644
> index 0000000..4bfefb5
> --- /dev/null
> +++ b/test/migrate/iperf3_bidir6
> @@ -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
> diff --git a/test/migrate/iperf3_in4 b/test/migrate/iperf3_in4
> new file mode 100644
> index 0000000..c5f3916
> --- /dev/null
> +++ b/test/migrate/iperf3_in4
> @@ -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
> diff --git a/test/migrate/iperf3_in6 b/test/migrate/iperf3_in6
> new file mode 100644
> index 0000000..16cf504
> --- /dev/null
> +++ b/test/migrate/iperf3_in6
> @@ -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
> diff --git a/test/migrate/iperf3_many_out6 b/test/migrate/iperf3_many_out6
> new file mode 100644
> index 0000000..88133f2
> --- /dev/null
> +++ b/test/migrate/iperf3_many_out6
> @@ -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
> diff --git a/test/migrate/iperf3_out4 b/test/migrate/iperf3_out4
> new file mode 100644
> index 0000000..968057b
> --- /dev/null
> +++ b/test/migrate/iperf3_out4
> @@ -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
> diff --git a/test/migrate/iperf3_out6 b/test/migrate/iperf3_out6
> new file mode 100644
> index 0000000..21fbfcd
> --- /dev/null
> +++ b/test/migrate/iperf3_out6
> @@ -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
> diff --git a/test/migrate/rampstream_in b/test/migrate/rampstream_in
> index 46f4143..df333ba 100644
> --- a/test/migrate/rampstream_in
> +++ b/test/migrate/rampstream_in
> @@ -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
>  
> diff --git a/test/migrate/rampstream_out b/test/migrate/rampstream_out
> index 91b9c63..8ed3229 100644
> --- a/test/migrate/rampstream_out
> +++ b/test/migrate/rampstream_out
> @@ -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
> diff --git a/test/run b/test/run
> index f188d8e..4e86f30 100755
> --- a/test/run
> +++ b/test/run
> @@ -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}"

-- 
David Gibson (he or they)	| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you, not the other way
				| around.
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

      reply	other threads:[~2025-02-17  3:47 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-16 22:22 [PATCH v27 0/2] State migration, perhaps final (#2)? Stefano Brivio
2025-02-16 22:22 ` [PATCH v27 1/2] migrate: Migrate TCP flows Stefano Brivio
2025-02-17  3:46   ` David Gibson
2025-02-16 22:22 ` [PATCH v27 2/2] test: Add migration tests Stefano Brivio
2025-02-17  3:47   ` David Gibson [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Z7KxN5DAlygeo1_R@zatzit \
    --to=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).