diff options
author | Oliver Smith <osmith@sysmocom.de> | 2023-10-26 15:14:16 +0200 |
---|---|---|
committer | Oliver Smith <osmith@sysmocom.de> | 2023-10-27 13:26:56 +0200 |
commit | a3108a3457182417a25e039590a95a57d74fcc4f (patch) | |
tree | b3aac92af4718794d0e6df2f7a309b64f03c90d6 | |
parent | 988b8f383d0525c821f890f424f304d3e51c9e00 (diff) |
WIP: add QEMU testsosmith/qemu-tests
Change-Id: Ibf75514b866fffb11e90529e4705f126b23d7415
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | tests/Makefile.am | 15 | ||||
-rw-r--r-- | tests/qemu/00_test_functions.sh | 63 | ||||
-rw-r--r-- | tests/qemu/01_ms_ip4_sgsn_ip4.sh | 13 | ||||
-rw-r--r-- | tests/qemu/02_ms_ip4_sgsn_ip6.sh | 13 | ||||
-rw-r--r-- | tests/qemu/03_ms_ip6_sgsn_ip4.sh | 13 | ||||
-rw-r--r-- | tests/qemu/04_ms_ip6_sgsn_ip6.sh | 13 | ||||
-rwxr-xr-x | tests/qemu/check-depends.sh | 18 | ||||
-rwxr-xr-x | tests/qemu/initrd-build.sh | 110 | ||||
-rwxr-xr-x | tests/qemu/initrd-init.sh | 37 | ||||
-rwxr-xr-x | tests/qemu/run-qemu.sh | 52 |
13 files changed, 369 insertions, 1 deletions
@@ -37,3 +37,5 @@ contrib/libgtpnl.spec tools/gtp-link tools/gtp-tunnel + +tests/qemu/_* diff --git a/Makefile.am b/Makefile.am index c34b6db..c6b9682 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,7 +4,12 @@ include $(top_srcdir)/Make_global.am ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src include tools +SUBDIRS = \ + include \ + src \ + tests \ + tools \ + $(NULL) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libgtpnl.pc diff --git a/configure.ac b/configure.ac index 2665b70..b846c8c 100644 --- a/configure.ac +++ b/configure.ac @@ -72,6 +72,19 @@ then CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" fi +AC_ARG_ENABLE(qemu_tests, + [AS_HELP_STRING( + [--enable-qemu-tests], + [Run automated tests in QEMU] + )], + [qemu_tests=$enableval], [qemu_tests="no"]) +AC_MSG_CHECKING([whether to enable QEMU tests]) +AC_MSG_RESULT([$qemu_tests]) +AM_CONDITIONAL(ENABLE_QEMU_TESTS, test x"$qemu_tests" = x"yes") +if test x"$qemu_tests" = x"yes" && ! $srcdir/tests/qemu/check-depends.sh; then + AC_MSG_ERROR([missing programs for --enable-qemu-tests]) +fi + AC_SUBST([CPPFLAGS]) AC_SUBST([CFLAGS]) AC_CONFIG_FILES([ @@ -82,6 +95,7 @@ AC_CONFIG_FILES([ include/linux/Makefile libgtpnl.pc src/Makefile + tests/Makefile tools/Makefile ]) AC_OUTPUT() diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..54af52d --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,15 @@ +check-local: + $(MAKE) qemu-tests + +if ENABLE_QEMU_TESTS +qemu-download-kernel: + rm -f qemu/_linux + wget -O qemu/_linux \ + https://jenkins.osmocom.org/jenkins/job/ttcn3-ggsn-test-kernel-latest-net-next/ws/_cache/kernel-test/linux +qemu-tests: + qemu/initrd-build.sh + qemu/run-qemu.sh +else +qemu-tests: + @echo "Not running QEMU tests (determined at configure-time)" +endif diff --git a/tests/qemu/00_test_functions.sh b/tests/qemu/00_test_functions.sh new file mode 100644 index 0000000..6215ead --- /dev/null +++ b/tests/qemu/00_test_functions.sh @@ -0,0 +1,63 @@ +#!/bin/sh -ex + +# Use ip from iproute2 instead of busybox ip, because iproute2's version has +# "ip netns" implemented. Calling /bin/ip explicitly is needed here, otherwise +# busybox sh will use busybox ip, regardless of PATH. +alias ip="/bin/ip" +alias ggsn_side="ip netns exec ggsn_side" + +# MS - SGSN -gtp- GGSN - WEBSERVER +tunnel_start() { + ip netns add ggsn_side + + # SGSN side: prepare veth_sgsn (SGSN), lo (MS) + ip link add veth_sgsn type veth peer name veth_ggsn + ip addr add "$SGSN"/"$SGSN_PREFLEN" dev veth_sgsn + ip link set veth_sgsn up + ip addr add "$MS"/"$MS_PREFLEN" dev lo + ip link set lo up + + # SGSN side: prepare gtp-tunnel + gtp-link add gtp_sgsn --sgsn & + sleep 1 + gtp-tunnel add gtp_sgsn v1 200 100 "$MS" "$GGSN" + ip route add "$WEBSERVER"/"$MS_PREFLEN" dev gtp_sgsn + + # GGSN side: prepare veth_ggsn (GGSN), lo (WEBSERVER) + ip link set veth_ggsn netns ggsn_side + ggsn_side ip addr add "$GGSN"/"$SGSN_PREFLEN" dev veth_ggsn + ggsn_side ip link set veth_ggsn up + ggsn_side ip addr add "$WEBSERVER"/"$MS_PREFLEN" dev lo + ggsn_side ip link set lo up + + # GGSN side: prepare gtp-tunnel + ggsn_side gtp-link add gtp_ggsn & + sleep 1 + ggsn_side gtp-tunnel add gtp_ggsn v1 100 200 "$MS" "$SGSN" + ggsn_side ip route add "$MS"/"$MS_PREFLEN" dev gtp_ggsn + + # List tunnel from both sides + gtp-tunnel list + ggsn_side gtp-tunnel list +} + +tunnel_ping() { + ping -c 1 "$WEBSERVER" + ggsn_side ping -c 1 "$MS" +} + +tunnel_stop() { + killall gtp-link + + ip addr del "$MS"/"$MS_PREFLEN" dev lo + ip link set veth_sgsn down + ip addr del "$SGSN"/"$SGSN_PREFLEN" dev veth_sgsn + ip link del veth_sgsn + ip route del "$WEBSERVER"/"$MS_PREFLEN" dev gtp_sgsn + gtp-tunnel delete gtp_sgsn v1 200 + gtp-link del gtp_sgsn + + ggsn_side gtp-tunnel delete gtp_ggsn v1 100 + ggsn_side gtp-link del gtp_ggsn + ip netns del ggsn_side +} diff --git a/tests/qemu/01_ms_ip4_sgsn_ip4.sh b/tests/qemu/01_ms_ip4_sgsn_ip4.sh new file mode 100644 index 0000000..7aaf17b --- /dev/null +++ b/tests/qemu/01_ms_ip4_sgsn_ip4.sh @@ -0,0 +1,13 @@ +#!/bin/sh -ex +. /tests/00_test_functions.sh + +MS="172.99.0.1" +MS_PREFLEN="32" +SGSN="172.0.0.1" +SGSN_PREFLEN="24" +GGSN="172.0.0.2" +WEBSERVER="172.99.0.2" + +tunnel_start +tunnel_ping +tunnel_stop diff --git a/tests/qemu/02_ms_ip4_sgsn_ip6.sh b/tests/qemu/02_ms_ip4_sgsn_ip6.sh new file mode 100644 index 0000000..67b605a --- /dev/null +++ b/tests/qemu/02_ms_ip4_sgsn_ip6.sh @@ -0,0 +1,13 @@ +#!/bin/sh -ex +. /tests/00_test_functions.sh + +MS="172.99.0.1" +MS_PREFLEN="32" +SGSN="fd00::1" +SGSN_PREFLEN="7" +GGSN="fd00::2" +WEBSERVER="172.99.0.2" + +tunnel_start +tunnel_ping +tunnel_stop diff --git a/tests/qemu/03_ms_ip6_sgsn_ip4.sh b/tests/qemu/03_ms_ip6_sgsn_ip4.sh new file mode 100644 index 0000000..23e5980 --- /dev/null +++ b/tests/qemu/03_ms_ip6_sgsn_ip4.sh @@ -0,0 +1,13 @@ +#!/bin/sh -ex +. /tests/00_test_functions.sh + +MS="fd00::1" +MS_PREFLEN="7" +SGSN="172.0.0.1" +SGSN_PREFLEN="24" +GGSN="172.0.0.2" +WEBSERVER="fd00::2" + +tunnel_start +tunnel_ping +tunnel_stop diff --git a/tests/qemu/04_ms_ip6_sgsn_ip6.sh b/tests/qemu/04_ms_ip6_sgsn_ip6.sh new file mode 100644 index 0000000..1fdd05f --- /dev/null +++ b/tests/qemu/04_ms_ip6_sgsn_ip6.sh @@ -0,0 +1,13 @@ +#!/bin/sh -ex +. /tests/00_test_functions.sh + +MS="fc00::1" +MS_PREFLEN="7" +SGSN="fd00::1" +SGSN_PREFLEN="7" +GGSN="fd00::2" +WEBSERVER="fc00::2" + +tunnel_start +tunnel_ping +tunnel_stop diff --git a/tests/qemu/check-depends.sh b/tests/qemu/check-depends.sh new file mode 100755 index 0000000..15a7600 --- /dev/null +++ b/tests/qemu/check-depends.sh @@ -0,0 +1,18 @@ +#!/bin/sh +RET=0 + +require_program() { + if [ -z "$(command -v "$1")" ]; then + RET=1 + echo "ERROR: missing program: $1" + fi +} + +require_program busybox +require_program cpio +require_program find +require_program gzip +require_program ip +require_program qemu-system-x86_64 + +exit "$RET" diff --git a/tests/qemu/initrd-build.sh b/tests/qemu/initrd-build.sh new file mode 100755 index 0000000..34d3bc5 --- /dev/null +++ b/tests/qemu/initrd-build.sh @@ -0,0 +1,110 @@ +#!/bin/sh -e +DIR="$(cd "$(dirname "$0")" && pwd)" +DIR_INITRD="$DIR/_initrd" +SRC_LIBS="$(realpath "$DIR/../../src/.libs/")" +TOOLS_LIBS="$(realpath "$DIR/../../tools/.libs/")" + +# Add one or more files to the initramfs, with parent directories. +# usr-merge: resolve symlinks for /lib -> /usr/lib etc. so "cp --parents" does +# not fail with "cp: cannot make directory '/tmp/initrd/lib': File exists" +# $@: path to files +initrd_add_file() { + local i + + for i in "$@"; do + case "$i" in + /bin/*|/sbin/*|/lib/*|/lib64/*) + cp -a --parents "$i" "$DIR_INITRD"/usr + ;; + *) + cp -a --parents "$i" "$DIR_INITRD" + ;; + esac + done +} + +# Add binaries with depending libraries +# $@: paths to binaries +initrd_add_bin() { + local bin + local bin_path + local file + + for bin in "$@"; do + local bin_path="$(which "$bin")" + if [ -z "$bin_path" ]; then + echo "ERROR: file not found: $bin" + exit 1 + fi + + lddtree_out="$(lddtree -l "$bin_path")" + if [ -z "$lddtree_out" ]; then + echo "ERROR: lddtree failed on '$bin_path'" + exit 1 + fi + + for file in $lddtree_out; do + initrd_add_file "$file" + + # Copy resolved symlink + if [ -L "$file" ]; then + initrd_add_file "$(realpath "$file")" + fi + done + done +} + +# Add command to run inside the initramfs +# $@: commands +initrd_add_cmd() { + local i + + if ! [ -e "$DIR_INITRD"/cmd.sh ]; then + echo "#!/bin/sh -ex" > "$DIR_INITRD"/cmd.sh + chmod +x "$DIR_INITRD"/cmd.sh + fi + + for i in "$@"; do + echo "$i" >> "$DIR_INITRD"/cmd.sh + done +} + +rm -rf "$DIR_INITRD" +mkdir -p "$DIR_INITRD" +cd "$DIR_INITRD" + +for dir in bin sbin lib lib64; do + ln -s usr/"$dir" "$dir" +done + +mkdir -p \ + dev/net \ + proc \ + run \ + sys \ + tmp \ + usr/bin \ + usr/sbin + +initrd_add_bin \ + busybox \ + ip + +initrd_add_cmd \ + "export LD_LIBRARY_PATH=$SRC_LIBS:$LD_LIBRARY_PATH" + +export LD_LIBRARY_PATH="$SRC_LIBS:$LD_LIBRARY_PATH" + +for i in gtp-link gtp-tunnel; do + initrd_add_bin "$TOOLS_LIBS"/"$i" + ln -s "$TOOLS_LIBS"/"$i" usr/bin/"$i" +done + +mkdir tests +cp "$DIR"/*.sh tests + +cp "$DIR"/initrd-init.sh init + +find . -print0 \ + | cpio --quiet -o -0 -H newc \ + | gzip -1 > "$DIR"/_initrd.gz diff --git a/tests/qemu/initrd-init.sh b/tests/qemu/initrd-init.sh new file mode 100755 index 0000000..2b5f824 --- /dev/null +++ b/tests/qemu/initrd-init.sh @@ -0,0 +1,37 @@ +#!/bin/busybox sh +echo "Running initrd-init.sh" +set -x + +run_test() { + echo + echo "QEMU test: $1" + echo + if ! sh -ex "/tests/$1"; then + poweroff -f + fi +} + +export HOME=/root +export LD_LIBRARY_PATH=/usr/local/lib +export PATH=/usr/local/bin:/usr/bin:/bin:/sbin:/usr/local/sbin:/usr/sbin +export TERM=screen + +/bin/busybox --install -s +hostname qemu +mount -t proc proc /proc +mount -t sysfs sys /sys +mknod /dev/null c 1 3 +. /cmd.sh +set +x + +# Run all tests + +run_test 01_ms_ip4_sgsn_ip4.sh # OK +# run_test 02_ms_ip4_sgsn_ip6.sh # NOK: kernel panic +# run_test 03_ms_ip6_sgsn_ip4.sh # NOK: ping doesn't work +# run_test 04_ms_ip6_sgsn_ip6.sh # NOK: ping doesn't work +# run_test 05_ms_ip46_sgsn_ip4.sh # WIP: not implemented yet + +# Success (run-qemu.sh checks for this line) +echo "QEMU_TEST_SUCCESSFUL" +poweroff -f diff --git a/tests/qemu/run-qemu.sh b/tests/qemu/run-qemu.sh new file mode 100755 index 0000000..63f5087 --- /dev/null +++ b/tests/qemu/run-qemu.sh @@ -0,0 +1,52 @@ +#!/bin/sh -e +DIR="$(cd "$(dirname "$0")" && pwd)" + +if [ -e /dev/kvm ]; then + MACHINE_ARG="-machine pc,accel=kvm" +else + echo "WARNING: /dev/kvm not found, emulation will be slower" + MACHINE_ARG="-machine pc" +fi + +if ! [ -e "$DIR"/_linux ]; then + echo "ERROR: linux kernel not found: $DIR/_linux" + echo "Put a kernel there, either download it from the Osmocom jenkins:" + echo "$ make -C tests qemu-download-kernel" + echo "(FIXME: isn't built with required config options yet)" + echo + echo "Or build your own kernel. Make sure to set:" + echo " CONFIG_GTP=y" + echo " CONFIG_NETNS=y" + echo " CONFIG_VETH=y" + exit 1 +fi + +KERNEL_CMDLINE="root=/dev/ram0 console=ttyS0 panic=-1 init=/init" + +set -x +qemu-system-x86_64 \ + $MACHINE_ARG \ + -smp 1 \ + -m 512M \ + -no-user-config -nodefaults -display none \ + -gdb unix:"$DIR"/_gdb.pipe,server=on,wait=off \ + -no-reboot \ + -kernel "$DIR"/_linux \ + -initrd "$DIR"/_initrd.gz \ + -append "${KERNEL_CMDLINE}" \ + -serial stdio \ + -chardev socket,id=charserial1,path="$DIR"/_gdb-serial.pipe,server=on,wait=off \ + -device isa-serial,chardev=charserial1,id=serial1 \ + 2>&1 | tee "$DIR/_output" + +set +x +if grep -q "QEMU_TEST_SUCCESSFUL" "$DIR/_output"; then + echo + echo "QEMU tests: successful" + echo +else + echo + echo "QEMU tests: failed" + echo + exit 1 +fi |