osmith has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libgtpnl/+/35991?usp=email )


Change subject: Add QEMU tests
......................................................................

Add QEMU tests

Add tests to ensure libgtpnl + kernel driver work as expected.

Right now a kernel needs to be built from source, using Pablo's tree:
https://git.kernel.org/pub/scm/linux/kernel/git/pablo/gtp.git/

Make sure to enable:
  CONFIG_GTP=y
  CONFIG_NET_NS=y
  CONFIG_VETH=y

$ cp bzImage tests/qemu/_linux
$ ./configure --enable-qemu-tests
$ make
$ make check

Once patches are upstreamed, it will be possible to use a pre-built
kernel from jenkins with: make -C tests qemu-download-kernel

Related: OS#1952
Change-Id: Ibf75514b866fffb11e90529e4705f126b23d7415
---
M .gitignore
M Makefile.am
M configure.ac
A tests/Makefile.am
A tests/qemu/00_test_functions.sh
A tests/qemu/01_ms_ip4_sgsn_ip4.sh
A tests/qemu/02_ms_ip4_sgsn_ip6.sh
A tests/qemu/03_ms_ip6_sgsn_ip4.sh
A tests/qemu/04_ms_ip6_sgsn_ip6.sh
A tests/qemu/check-depends.sh
A tests/qemu/initrd-build.sh
A tests/qemu/initrd-init.sh
A tests/qemu/run-qemu.sh
13 files changed, 411 insertions(+), 1 deletion(-)



  git pull ssh://gerrit.osmocom.org:29418/libgtpnl refs/changes/91/35991/1

diff --git a/.gitignore b/.gitignore
index bf5732d..56eefa6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,5 @@

 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 @@

 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 @@
        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 @@
        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..c4813ab
--- /dev/null
+++ b/tests/qemu/00_test_functions.sh
@@ -0,0 +1,75 @@
+#!/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() {
+       test -n "$MS"
+       test -n "$MS_PREFLEN"
+       test -n "$SGSN_GGSN_PROTO"
+       test -n "$SGSN"
+       test -n "$SGSN_PREFLEN"
+       test -n "$GGSN"
+       test -n "$WEBSERVER"
+
+       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_GGSN_PROTO" --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 "$SGSN_GGSN_PROTO" &
+       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() {
+       ip addr show
+       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
+
+       if [ "$SGSN_GGSN_PROTO" == "ip" ]; then  # FIXME: doesn't work with ip6
+               ip addr del "$SGSN"/"$SGSN_PREFLEN" dev veth_sgsn
+       fi
+
+       ip link del veth_sgsn
+       ip route del "$WEBSERVER"/"$MS_PREFLEN" dev gtp_sgsn
+       gtp-tunnel delete gtp_sgsn v1 200 "$SGSN_GGSN_PROTO"
+       gtp-link del gtp_sgsn
+       ggsn_side gtp-tunnel delete gtp_ggsn v1 100 "$SGSN_GGSN_PROTO"
+       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..42e2842
--- /dev/null
+++ b/tests/qemu/01_ms_ip4_sgsn_ip4.sh
@@ -0,0 +1,14 @@
+#!/bin/sh -ex
+. /tests/00_test_functions.sh
+
+MS="172.99.0.1"
+MS_PREFLEN="32"
+SGSN_GGSN_PROTO="ip"
+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..b5858ab
--- /dev/null
+++ b/tests/qemu/02_ms_ip4_sgsn_ip6.sh
@@ -0,0 +1,14 @@
+#!/bin/sh -ex
+. /tests/00_test_functions.sh
+
+MS="172.99.0.1"
+MS_PREFLEN="32"
+SGSN_GGSN_PROTO="ip6"
+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..12793b0
--- /dev/null
+++ b/tests/qemu/03_ms_ip6_sgsn_ip4.sh
@@ -0,0 +1,14 @@
+#!/bin/sh -ex
+. /tests/00_test_functions.sh
+
+MS="fd00::"
+MS_PREFLEN="64"
+SGSN_GGSN_PROTO="ip"
+SGSN="172.0.0.1"
+SGSN_PREFLEN="24"
+GGSN="172.0.0.2"
+WEBSERVER="fe00::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..6c2f13f
--- /dev/null
+++ b/tests/qemu/04_ms_ip6_sgsn_ip6.sh
@@ -0,0 +1,14 @@
+#!/bin/sh -ex
+. /tests/00_test_functions.sh
+
+MS="fc00::"
+MS_PREFLEN="64"
+SGSN_GGSN_PROTO="ip6"
+SGSN="fd00::1"
+SGSN_PREFLEN="7"
+GGSN="fd00::2"
+WEBSERVER="fe00::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..9e7ac8e
--- /dev/null
+++ b/tests/qemu/initrd-init.sh
@@ -0,0 +1,36 @@
+#!/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
+run_test 02_ms_ip4_sgsn_ip6.sh
+run_test 03_ms_ip6_sgsn_ip4.sh
+run_test 04_ms_ip6_sgsn_ip6.sh
+
+# 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..35bf756
--- /dev/null
+++ b/tests/qemu/run-qemu.sh
@@ -0,0 +1,51 @@
+#!/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
+       echo "Or build your own kernel. Make sure to set:"
+       echo "  CONFIG_GTP=y"
+       echo "  CONFIG_NET_NS=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

--
To view, visit https://gerrit.osmocom.org/c/libgtpnl/+/35991?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libgtpnl
Gerrit-Branch: master
Gerrit-Change-Id: Ibf75514b866fffb11e90529e4705f126b23d7415
Gerrit-Change-Number: 35991
Gerrit-PatchSet: 1
Gerrit-Owner: osmith <[email protected]>
Gerrit-MessageType: newchange

Reply via email to