aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Smith <osmith@sysmocom.de>2023-10-26 15:14:16 +0200
committerOliver Smith <osmith@sysmocom.de>2023-10-27 13:26:56 +0200
commita3108a3457182417a25e039590a95a57d74fcc4f (patch)
treeb3aac92af4718794d0e6df2f7a309b64f03c90d6
parent988b8f383d0525c821f890f424f304d3e51c9e00 (diff)
WIP: add QEMU testsosmith/qemu-tests
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am7
-rw-r--r--configure.ac14
-rw-r--r--tests/Makefile.am15
-rw-r--r--tests/qemu/00_test_functions.sh63
-rw-r--r--tests/qemu/01_ms_ip4_sgsn_ip4.sh13
-rw-r--r--tests/qemu/02_ms_ip4_sgsn_ip6.sh13
-rw-r--r--tests/qemu/03_ms_ip6_sgsn_ip4.sh13
-rw-r--r--tests/qemu/04_ms_ip6_sgsn_ip6.sh13
-rwxr-xr-xtests/qemu/check-depends.sh18
-rwxr-xr-xtests/qemu/initrd-build.sh110
-rwxr-xr-xtests/qemu/initrd-init.sh37
-rwxr-xr-xtests/qemu/run-qemu.sh52
13 files changed, 369 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index bf5732d..56eefa6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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