Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package osmo-ggsn for openSUSE:Factory 
checked in at 2021-11-20 22:47:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/osmo-ggsn (Old)
 and      /work/SRC/openSUSE:Factory/.osmo-ggsn.new.1895 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "osmo-ggsn"

Sat Nov 20 22:47:58 2021 rev:8 rq:932599 version:1.8.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/osmo-ggsn/osmo-ggsn.changes      2021-03-15 
10:56:16.285344031 +0100
+++ /work/SRC/openSUSE:Factory/.osmo-ggsn.new.1895/osmo-ggsn.changes    
2021-11-20 22:48:20.303800960 +0100
@@ -1,0 +2,12 @@
+Fri Nov 19 10:50:35 UTC 2021 - Martin Hauke <mar...@gmx.de>
+
+- Update to version 1.8.0
+  * Fix vty PDP lookups by IMSI
+  * apn_start: avoid segfault if missing tun-device
+  * Add --vty-ref-mode option
+  * ggsn: Reject PDP CTX ACT for static IP addresses
+  * gtp: Support tx/rx RAN Information Relay message
+  * ggsn: Fix heap-use-after-free during Recovery without
+    associated PDP
+
+-------------------------------------------------------------------

Old:
----
  1.7.1.tar.gz

New:
----
  1.8.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ osmo-ggsn.spec ++++++
--- /var/tmp/diff_new_pack.Gj3dXP/_old  2021-11-20 22:48:20.803799303 +0100
+++ /var/tmp/diff_new_pack.Gj3dXP/_new  2021-11-20 22:48:20.807799290 +0100
@@ -16,11 +16,9 @@
 #
 
 
-%define _lto_cflags %nil
-
 Name:           osmo-ggsn
 %define lname   libgtp6
-Version:        1.7.1
+Version:        1.8.0
 Release:        0
 Summary:        GPRS Support Node
 License:        GPL-2.0-only AND LGPL-2.1-or-later
@@ -65,15 +63,26 @@
 This subpackage contains libraries and header files for developing
 applications that want to make use of libgtp.
 
+%package -n gtp-echo-responder
+Summary:        Small program answering GTP ECHO Request with GTP ECHO Response
+License:        MIT
+Group:          Productivity/Telephony/Utilities
+
+%description -n gtp-echo-responder
+Small program answering GTP ECHO Request with GTP ECHO Response for both GTPCv1
+and GTPCv2.
+
 %prep
 %autosetup -p1
-sed -i 's|/usr/bin/osmo-ggsn|/usr/sbin/osmo-ggsn|g' 
contrib/systemd/osmo-ggsn.service
+sed -i 's|%_bindir/osmo-ggsn|%_sbindir/osmo-ggsn|g' 
contrib/systemd/osmo-ggsn.service
 
 %build
 echo "%version" >.tarball-version
 autoreconf -fi
 # bugzilla.opensuse.org/795968 for rationale
-%configure --includedir="%_includedir/%name" \
+%configure \
+       --enable-gtp-linux \
+       --includedir="%_includedir/%name" \
        --disable-static --docdir="%_docdir/%name" \
        --with-systemdsystemunitdir="%_unitdir"
 %make_build
@@ -113,7 +122,9 @@
 %_unitdir/%name.service
 %_sbindir/rc%name
 %dir %_docdir/%name/examples
+%_docdir/%name/examples/osmo-ggsn-kernel-gtp.cfg
 %_docdir/%name/examples/osmo-ggsn.cfg
+%_docdir/%name/examples/sgsnemu.conf
 %dir %_sysconfdir/osmocom
 %config(noreplace) %_sysconfdir/osmocom/osmo-ggsn.cfg
 %config(noreplace) %_sysconfdir/osmocom/sgsnemu.conf
@@ -126,4 +137,7 @@
 %_libdir/libgtp.so
 %_libdir/pkgconfig/libgtp.pc
 
+%files -n gtp-echo-responder
+%_bindir/gtp-echo-responder
+
 %changelog

++++++ 1.7.1.tar.gz -> 1.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/.gitignore 
new/osmo-ggsn-1.8.0/.gitignore
--- old/osmo-ggsn-1.7.1/.gitignore      2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/.gitignore      2021-11-16 13:49:16.000000000 +0100
@@ -79,5 +79,6 @@
 doc/manuals/osmomsc-usermanual.xml
 doc/manuals/common
 doc/manuals/build
+doc/manuals/vty/ggsn_vty_reference.xml
 
 contrib/osmo-ggsn.spec
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/Makefile.am 
new/osmo-ggsn-1.8.0/Makefile.am
--- old/osmo-ggsn-1.7.1/Makefile.am     2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/Makefile.am     2021-11-16 13:49:16.000000000 +0100
@@ -1,5 +1,5 @@
 ## Process this file with automake to produce Makefile.in
-SUBDIRS = lib gtp ggsn sgsnemu doc contrib tests
+SUBDIRS = lib gtp ggsn sgsnemu doc contrib utils tests
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libgtp.pc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/configure.ac 
new/osmo-ggsn-1.8.0/configure.ac
--- old/osmo-ggsn-1.7.1/configure.ac    2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/configure.ac    2021-11-16 13:49:16.000000000 +0100
@@ -257,8 +257,9 @@
                  lib/Makefile
                  intl/Makefile
                  po/Makefile
+                 utils/Makefile
                  sgsnemu/Makefile
-    doc/manuals/Makefile
+                 doc/manuals/Makefile
                  contrib/Makefile
                  contrib/systemd/Makefile
                  contrib/osmo-ggsn.spec
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/contrib/osmo-ggsn.spec.in 
new/osmo-ggsn-1.8.0/contrib/osmo-ggsn.spec.in
--- old/osmo-ggsn-1.7.1/contrib/osmo-ggsn.spec.in       2021-02-23 
17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/contrib/osmo-ggsn.spec.in       2021-11-16 
13:49:16.000000000 +0100
@@ -61,6 +61,15 @@
 This subpackage contains libraries and header files for developing
 applications that want to make use of libgtp.
 
+%package -n gtp-echo-responder
+Summary:        Small program answering GTP ECHO Request with GTP ECHO Response
+License:        MIT
+Group:          System/Libraries
+
+%description -n gtp-echo-responder
+Small program answering GTP ECHO Request with GTP ECHO Response for both GTPCv1
+and GTPCv2.
+
 %prep
 %setup -q
 
@@ -108,7 +117,9 @@
 %{_mandir}/man8/sgsnemu.8%{?ext_man}
 %{_unitdir}/%{name}.service
 %dir %{_docdir}/%{name}/examples
+%{_docdir}/%{name}/examples/osmo-ggsn-kernel-gtp.cfg
 %{_docdir}/%{name}/examples/osmo-ggsn.cfg
+%{_docdir}/%{name}/examples/sgsnemu.conf
 %dir %{_sysconfdir}/osmocom
 %config(noreplace) %{_sysconfdir}/osmocom/osmo-ggsn.cfg
 
@@ -120,4 +131,7 @@
 %{_libdir}/libgtp.so
 %{_libdir}/pkgconfig/libgtp.pc
 
+%files -n gtp-echo-responder
+%{_bindir}/gtp-echo-responder
+
 %changelog
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/debian/changelog 
new/osmo-ggsn-1.8.0/debian/changelog
--- old/osmo-ggsn-1.7.1/debian/changelog        2021-02-23 17:31:24.000000000 
+0100
+++ new/osmo-ggsn-1.8.0/debian/changelog        2021-11-16 13:49:16.000000000 
+0100
@@ -1,3 +1,33 @@
+osmo-ggsn (1.8.0) unstable; urgency=medium
+
+  [ Oliver Smith ]
+  * doc/examples/Makefile.am: add sgsnemu.conf
+  * doc/examples/osmo-ggsn-kernel-gtp.cfg: new file
+  * doc/manuals: describe GTP-U kernel module
+  * gitignore: add ggsn_vty_reference.xml
+
+  [ Harald Welte ]
+  * Don't install osmo-ggsn-kernel-gtp.cfg to /etc/osmocom/
+  * Don't install sgsnemu.conf to /etc/osmocom/
+  * ggsn: Reject PDP CTX ACT for static IP addresses
+  * vty: Inform user that static IP addresses are not supported
+
+  [ Pau Espin Pedrol ]
+  * gtp: Update teic_confirmed only on resp success
+  * gtp: Rework parsing logic of UpdatePdpCtxResponse
+  * ggsn: Improve logging on incoming DL data packets
+  * gtp: Improve logging of failing pdp ctx resolution from TEI/TID
+  * cosmetic: gtpie.c: Fix trailing whitespace
+  * gtp: constify pointer arg
+  * gtp: Support tx/rx RAN Information Relay message
+  * ggsn: Log tun fd write errors
+  * ggsn: Fix heap-use-after-free during Recovery without associated PDP
+  * cosmetic: configure.ac: Fix tabulation in line
+  * Introduce program gtp-echo-responder
+  * gtp_echo_responder: report invalid chars present in node-feautres cmdline 
arg as error
+
+ -- Pau Espin Pedrol <pes...@sysmocom.de>  Tue, 16 Nov 2021 13:49:16 +0100
+
 osmo-ggsn (1.7.1) unstable; urgency=medium
 
   [ Harald Welte ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/debian/control 
new/osmo-ggsn-1.8.0/debian/control
--- old/osmo-ggsn-1.7.1/debian/control  2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/debian/control  2021-11-16 13:49:16.000000000 +0100
@@ -38,6 +38,12 @@
  This library is part of OsmoGGSN and implements the GTP protocol between
  SGSN (Serving GPRS support node) and GGSN.
 
+Package: gtp-echo-responder
+Architecture: any
+Depends: ${shlibs:Depends},
+         ${misc:Depends}
+Description: Small program answering GTP ECHO Request with GTP ECHO Response
+
 Package: libgtp-dev
 Architecture: any
 Multi-Arch: same
@@ -63,6 +69,15 @@
  operators as the interface between the Internet and the rest of the
  mobile network infrastructure.
 
+Package: gtp-echo-responder-dbg
+Section: debug
+Architecture: any
+Priority: extra
+Depends: ${shlibs:Depends}, ${misc:Depends}, gtp-echo-responder (= 
${binary:Version})
+Multi-Arch: same
+Description: Debug symbols for gtp-echo-responder
+ Small program answering GTP ECHO Request with GTP ECHO Response.
+
 Package: libgtp-dbg
 Section: debug
 Architecture: any
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/debian/copyright 
new/osmo-ggsn-1.8.0/debian/copyright
--- old/osmo-ggsn-1.7.1/debian/copyright        2021-02-23 17:31:24.000000000 
+0100
+++ new/osmo-ggsn-1.8.0/debian/copyright        2021-11-16 13:49:16.000000000 
+0100
@@ -16,6 +16,11 @@
 Copyright: 1987-2001 Free Software Foundation, Inc.
 License: LGPL-2.1+
 
+Files: utils/gtp_echo_responder.c
+       utils/gtp_echo_responder_test.py
+Copyright: 2021 sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+License: MIT
+
 Files: debian/*
 Copyright: 2010-2017  Harald Welte <lafo...@gnumonks.org>
            2016       Ruben Undheim <ruben.undh...@gmail.com>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/debian/gtp-echo-responder.install 
new/osmo-ggsn-1.8.0/debian/gtp-echo-responder.install
--- old/osmo-ggsn-1.7.1/debian/gtp-echo-responder.install       1970-01-01 
01:00:00.000000000 +0100
+++ new/osmo-ggsn-1.8.0/debian/gtp-echo-responder.install       2021-11-16 
13:49:16.000000000 +0100
@@ -0,0 +1 @@
+/usr/bin/gtp-echo-responder
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/debian/osmo-ggsn.examples 
new/osmo-ggsn-1.8.0/debian/osmo-ggsn.examples
--- old/osmo-ggsn-1.7.1/debian/osmo-ggsn.examples       2021-02-23 
17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/debian/osmo-ggsn.examples       2021-11-16 
13:49:16.000000000 +0100
@@ -1,2 +1,3 @@
 doc/examples/osmo-ggsn.cfg
+doc/examples/osmo-ggsn-kernel-gtp.cfg
 doc/examples/sgsnemu.conf
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/doc/examples/Makefile.am 
new/osmo-ggsn-1.8.0/doc/examples/Makefile.am
--- old/osmo-ggsn-1.7.1/doc/examples/Makefile.am        2021-02-23 
17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/doc/examples/Makefile.am        2021-11-16 
13:49:16.000000000 +0100
@@ -1,9 +1,13 @@
+OSMOCONF_FILES = \
+       osmo-ggsn.cfg \
+       $(NULL)
+
 osmoconfdir = $(sysconfdir)/osmocom
-osmoconf_DATA = osmo-ggsn.cfg
+osmoconf_DATA = $(OSMOCONF_FILES)
 
-EXTRA_DIST = osmo-ggsn.cfg
+EXTRA_DIST = $(OSMOCONF_FILES)
 
-CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
+CFG_FILES = find $(srcdir) -name '*.cfg' -o -name '*.conf' | sed -e 
's,^$(srcdir),,'
 
 dist-hook:
        for f in $$($(CFG_FILES)); do \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osmo-ggsn-1.7.1/doc/examples/osmo-ggsn-kernel-gtp.cfg 
new/osmo-ggsn-1.8.0/doc/examples/osmo-ggsn-kernel-gtp.cfg
--- old/osmo-ggsn-1.7.1/doc/examples/osmo-ggsn-kernel-gtp.cfg   1970-01-01 
01:00:00.000000000 +0100
+++ new/osmo-ggsn-1.8.0/doc/examples/osmo-ggsn-kernel-gtp.cfg   2021-11-16 
13:49:16.000000000 +0100
@@ -0,0 +1,51 @@
+!
+! OpenGGSN (0.94.1-adac) configuration saved from vty
+!!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 0
+ logging timestamp 0
+ logging level ip info
+ logging level tun info
+ logging level ggsn info
+ logging level sgsn notice
+ logging level icmp6 notice
+ logging level lglobal notice
+ logging level llapd notice
+ logging level linp notice
+ logging level lmux notice
+ logging level lmi notice
+ logging level lmib notice
+ logging level lsms notice
+ logging level lctrl notice
+ logging level lgtp info
+ logging level lstats notice
+ logging level lgsup notice
+ logging level loap notice
+ logging level lss7 notice
+ logging level lsccp notice
+ logging level lsua notice
+ logging level lm3ua notice
+ logging level lmgcp notice
+!
+stats interval 5
+!
+line vty
+ no login
+!
+ggsn ggsn0
+ gtp state-dir /tmp
+ gtp bind-ip 127.0.0.2
+ apn internet
+  gtpu-mode kernel-gtp
+  tun-device tun4
+  type-support v4
+  ip prefix dynamic 172.16.222.0/24
+  ip dns 0 8.8.8.8
+  ip dns 1 8.8.4.4
+  ip ifconfig 172.16.222.0/24
+  no shutdown
+ default-apn internet
+ no shutdown ggsn
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/doc/manuals/chapters/running.adoc 
new/osmo-ggsn-1.8.0/doc/manuals/chapters/running.adoc
--- old/osmo-ggsn-1.7.1/doc/manuals/chapters/running.adoc       2021-02-23 
17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/doc/manuals/chapters/running.adoc       2021-11-16 
13:49:16.000000000 +0100
@@ -80,3 +80,47 @@
 ggsn ggsn0
  gtp bind-ip 127.0.0.2
 ----
+
+=== GTP-U kernel module
+
+WARNING: As of writing, the kernel module does not support IPv6.
+
+OsmoGGSN has support to use the Linux kernel GTP-U tunnel driver to accelerate
+the data/user plane while still implementing the control plane (GTP-C) in
+userspace in OsmoGGSN. The kernel module is included in Linux 4.7.0 and higher.
+Notably the Debian GNU/Linux distribution has it enabled by default.
+
+In order to use this feature, make sure that your Linux kernel was configured
+to support it (`CONFIG_GTP=m` or `=y`). Furthermore, `osmo-ggsn` must have been
+built with `./configure` argument `--enable-gtp-linux` (which requires libgtpnl
+to be installed).
+
+Load the kernel module with:
+
+----
+$ sudo modprobe gtp
+----
+
+Then start OsmoGGSN with a configuration file that uses `gtpu-mode kernel-gtp`.
+
+A full example configuration is in `osmo-ggsn-kernel-gtp.cfg`.
+
+----
+$ sudo osmo-ggsn -c /usr/share/doc/osmo-ggsn/examples/osmo-ggsn-kernel-gtp.cfg
+----
+
+.Example: APN with kernel-gtp
+----
+ggsn ggsn0
+ gtp state-dir /tmp
+ gtp bind-ip 127.0.0.2
+ apn internet
+  gtpu-mode kernel-gtp
+  tun-device tun4
+  type-support v4
+  ip prefix dynamic 172.16.222.0/24
+  ip dns 0 8.8.8.8
+  ip dns 1 8.8.4.4
+  ip ifconfig 172.16.222.0/24
+  no shutdown
+----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/ggsn/ggsn.c 
new/osmo-ggsn-1.8.0/ggsn/ggsn.c
--- old/osmo-ggsn-1.7.1/ggsn/ggsn.c     2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/ggsn/ggsn.c     2021-11-16 13:49:16.000000000 +0100
@@ -467,9 +467,13 @@
                return 0;
        }
 
-       /* FIXME: we manually force all context requests to dynamic here! */
-       if (pdp->eua.l > 2)
-               pdp->eua.l = 2;
+       /* FIXME: implement context request for static IP addresses! */
+       if (pdp->eua.l > 2) {
+               LOGPPDP(LOGL_ERROR, pdp, "Static IP addresses not supported: 
%s\n",
+                       osmo_hexdump(pdp->eua.v, pdp->eua.l));
+               gtp_create_context_resp(gsn, pdp, GTPCAUSE_NOT_SUPPORTED);
+               return 0;
+       }
 
        memcpy(pdp->qos_neg0, pdp->qos_req0, sizeof(pdp->qos_req0));
 
@@ -589,7 +593,7 @@
        struct iphdr *iph = (struct iphdr *)pack;
        struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
        struct ippool_t *pool;
-       char straddr[INET6_ADDRSTRLEN];
+       char straddr[2][INET6_ADDRSTRLEN];
        uint8_t pref_offset;
 
        switch (iph->version) {
@@ -622,17 +626,41 @@
                return 0;
 
        if (ippool_getip(pool, &ipm, &dst)) {
-               LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s) with no 
PDP contex! (%s)\n",
-                       apn->cfg.name,
-                       iph->version == 4 ?
-                       inet_ntop(AF_INET, &iph->saddr, straddr, 
sizeof(straddr)) :
-                       inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, 
sizeof(straddr)));
+               LOGTUN(LOGL_DEBUG, tun, "APN(%s) Rx DL data packet for IP 
address outside "
+                      "pool of managed addresses: %s <- %s\n",
+                      apn->cfg.name,
+                      iph->version == 4 ?
+                        inet_ntop(AF_INET, &iph->daddr, straddr[0], 
sizeof(straddr[0])) :
+                        inet_ntop(AF_INET6, &ip6h->ip6_dst, straddr[0], 
sizeof(straddr[0])),
+                      iph->version == 4 ?
+                        inet_ntop(AF_INET, &iph->saddr, straddr[1], 
sizeof(straddr[1])) :
+                        inet_ntop(AF_INET6, &ip6h->ip6_src, straddr[1], 
sizeof(straddr[1])));
                return 0;
        }
-       LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s)\n", apn->cfg.name);
 
-       if (ipm->peer)          /* Check if a peer protocol is defined */
-               gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, 
len);
+       if (ipm->peer)  {       /* Check if a peer protocol is defined */
+               struct pdp_t *pdp = (struct pdp_t *)ipm->peer;
+               LOGTUN(LOGL_DEBUG, tun, "APN(%s) Rx DL data packet for 
PDP(%s:%u): %s <- %s\n",
+                      apn->cfg.name,
+                      imsi_gtp2str(&(pdp)->imsi), (pdp)->nsapi,
+                      iph->version == 4 ?
+                        inet_ntop(AF_INET, &iph->daddr, straddr[0], 
sizeof(straddr[0])) :
+                        inet_ntop(AF_INET6, &ip6h->ip6_dst, straddr[0], 
sizeof(straddr[0])),
+                      iph->version == 4 ?
+                        inet_ntop(AF_INET, &iph->saddr, straddr[1], 
sizeof(straddr[1])) :
+                        inet_ntop(AF_INET6, &ip6h->ip6_src, straddr[1], 
sizeof(straddr[1])));
+               gtp_data_req(apn->ggsn->gsn, pdp, pack, len);
+       } else {
+               LOGTUN(LOGL_DEBUG, tun, "APN(%s) Rx DL data packet for IP 
address with no "
+                      "associated PDP Ctx: %s <- %s\n",
+                      apn->cfg.name,
+                      iph->version == 4 ?
+                        inet_ntop(AF_INET, &iph->daddr, straddr[0], 
sizeof(straddr[0])) :
+                        inet_ntop(AF_INET6, &ip6h->ip6_dst, straddr[0], 
sizeof(straddr[0])),
+                      iph->version == 4 ?
+                        inet_ntop(AF_INET, &iph->saddr, straddr[1], 
sizeof(straddr[1])) :
+                        inet_ntop(AF_INET6, &ip6h->ip6_src, straddr[1], 
sizeof(straddr[1])));
+       }
        return 0;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/ggsn/ggsn.h 
new/osmo-ggsn-1.8.0/ggsn/ggsn.h
--- old/osmo-ggsn-1.7.1/ggsn/ggsn.h     2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/ggsn/ggsn.h     2021-11-16 13:49:16.000000000 +0100
@@ -167,6 +167,3 @@
        LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
 
 #define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## 
args)
-
-#define LOGTUN(level, tun, fmt, args...) \
-       LOGP(DTUN, level, "TUN(%s): " fmt, (tun)->devname, ## args)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/ggsn/ggsn_vty.c 
new/osmo-ggsn-1.8.0/ggsn/ggsn_vty.c
--- old/osmo-ggsn-1.7.1/ggsn/ggsn_vty.c 2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/ggsn/ggsn_vty.c 2021-11-16 13:49:16.000000000 +0100
@@ -533,9 +533,11 @@
        struct in46_prefix *pfx;
 
        /* first update our parsed prefix */
-       if (!strcmp(argv[0], "static"))
+       if (!strcmp(argv[0], "static")) {
                pfx = &apn->v4.cfg.static_prefix;
-       else
+               vty_out(vty, "%% static IP addresses currently not yet 
supported%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       } else
                pfx = &apn->v4.cfg.dynamic_prefix;
        str2prefix(pfx, argv[1]);
 
@@ -567,9 +569,11 @@
        struct apn_ctx *apn = (struct apn_ctx *) vty->index;
        struct in46_prefix *pfx;
 
-       if (!strcmp(argv[0], "static"))
+       if (!strcmp(argv[0], "static")) {
                pfx = &apn->v6.cfg.static_prefix;
-       else
+               vty_out(vty, "%% static IP addresses currently not yet 
supported%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       } else
                pfx = &apn->v6.cfg.dynamic_prefix;
        str2prefix(pfx, argv[1]);
        return CMD_SUCCESS;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/ggsn/sgsn.c 
new/osmo-ggsn-1.8.0/ggsn/sgsn.c
--- old/osmo-ggsn-1.7.1/ggsn/sgsn.c     2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/ggsn/sgsn.c     2021-11-16 13:49:16.000000000 +0100
@@ -116,6 +116,7 @@
 {
        unsigned int num = 0;
        char buf[INET_ADDRSTRLEN];
+       unsigned int count = llist_count(&sgsn->pdp_list);
 
        inet_ntop(AF_INET, &sgsn->addr, buf, sizeof(buf));
 
@@ -125,10 +126,17 @@
                        continue;
                ggsn_close_one_pdp(pdp->lib);
                num++;
+               if (num == count) {
+                       /* Note: if except is NULL, all pdp contexts are freed 
and sgsn
+                        * is most probably already freed at this point.
+                        * As a result, last access to sgsn->pdp_list before 
exiting
+                        * loop would access already freed memory. Avoid it by 
exiting
+                        * the loop without the last check, and make sure sgsn 
is not
+                        * accessed after this loop. */
+                        break;
+               }
        }
 
-       /* Note: if except is NULL, all pdp contexts are freed and sgsn is
-          already freed at this point */
        LOGP(DGGSN, LOGL_INFO, "SGSN(%s) Dropped %u PDP contexts\n", buf, num);
 
        return num;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/gtp/Makefile.am 
new/osmo-ggsn-1.8.0/gtp/Makefile.am
--- old/osmo-ggsn-1.7.1/gtp/Makefile.am 2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/gtp/Makefile.am 2021-11-16 13:49:16.000000000 +0100
@@ -2,7 +2,7 @@
 # Please read chapter "Library interface versions" of the libtool documentation
 # before making any modifications: 
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
 # If major=current-age is increased, remember to update the dh_strip line in 
debian/rules!
-LIBVERSION=7:0:1
+LIBVERSION=8:0:2
 
 lib_LTLIBRARIES = libgtp.la
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/gtp/gtp.c 
new/osmo-ggsn-1.8.0/gtp/gtp.c
--- old/osmo-ggsn-1.7.1/gtp/gtp.c       2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/gtp/gtp.c       2021-11-16 13:49:16.000000000 +0100
@@ -196,6 +196,13 @@
        return 0;
 }
 
+int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
+                            int (*cb) (struct sockaddr_in * peer, union 
gtpie_member **ie))
+{
+       gsn->cb_ran_info_relay_ind = cb;
+       return 0;
+}
+
 /* API: Initialise delete context callback */
 /* Called whenever a pdp context is deleted for any reason */
 int gtp_set_cb_delete_context(struct gsn_t *gsn, int (*cb) (struct pdp_t * 
pdp))
@@ -773,7 +780,7 @@
 
 static int gtp_notification(struct gsn_t *gsn, uint8_t version,
                     union gtp_packet *packet, int len,
-                    struct sockaddr_in *peer, int fd, uint16_t seq)
+                    const struct sockaddr_in *peer, int fd, uint16_t seq)
 {
 
        uint8_t ver = GTPHDR_F_GET_VER(packet->flags);
@@ -1200,6 +1207,57 @@
        return 0;
 }
 
+/* Handle a RAN Information Relay message */
+static int gtp_ran_info_relay_ind(struct gsn_t *gsn, int version, struct 
sockaddr_in *peer,
+                     void *pack, unsigned len)
+{
+       union gtpie_member *ie[GTPIE_SIZE];
+
+       if (version != 1) {
+               LOGP(DLGTP, LOGL_NOTICE,
+                       "RAN Information Relay expected only on GTPCv1: %u\n", 
version);
+               return -EINVAL;
+       }
+
+       int hlen = get_hlen(pack);
+
+       /* Decode information elements */
+       if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
+               gsn->invalid++;
+               GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                           "Invalid message format (AN Information Relay)\n");
+               return -EINVAL;
+       }
+
+       if (gsn->cb_ran_info_relay_ind)
+               gsn->cb_ran_info_relay_ind(peer, ie);
+
+       return 0;
+}
+
+/* Send off a RAN Information Relay message */
+int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in *peer,
+                          const uint8_t *ran_container, size_t 
ran_container_len,
+                          const uint8_t *rim_route_addr, size_t 
rim_route_addr_len,
+                          uint8_t rim_route_addr_discr)
+{
+       union gtp_packet packet;
+
+       /* GTP 1 is the highest supported protocol */
+       unsigned int length = get_default_gtp(1, GTP_RAN_INFO_RELAY, &packet);
+
+       gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_RAN_T_CONTAIN, 
ran_container_len,
+                 ran_container);
+       if (rim_route_addr) {
+               gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_RIM_ROUT_ADDR,
+                         rim_route_addr_len, rim_route_addr);
+               gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_RIM_RA_DISCR, 1,
+                         &rim_route_addr_discr);
+       }
+
+       return gtp_notification(gsn, 1, &packet, length, peer, gsn->fd1c, 0);
+}
+
 /* ***********************************************************
  * Session management messages
  * Messages: create, update and delete PDP context
@@ -1792,9 +1850,6 @@
                return EOF;
        }
 
-       /* Register that we have received a valid teic from GGSN */
-       pdp->teic_confirmed = 1;
-
        /* Decode information elements */
        if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
                gsn->invalid++;
@@ -1894,6 +1949,8 @@
                                        gsn->cb_conf(type, EOF, pdp, cbp);
                                return EOF;
                        }
+                       /* Register that we have received a valid teic from 
GGSN */
+                       pdp->teic_confirmed = 1;
                }
 
                if (gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid)) {
@@ -2137,8 +2194,9 @@
                /* Find the context in question */
                if (gtp_pdp_tidget(gsn, &pdp, get_tid(pack))) {
                        gsn->err_unknownpdp++;
-                       GTP_LOGPKG(LOGL_ERROR, peer, pack,
-                                   len, "Unknown PDP context\n");
+                       GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                  "Unknown PDP context: TID=0x%" PRIx64 "\n",
+                                  get_tid(pack));
                        return gtp_update_pdp_resp(gsn, version, peer, fd, pack,
                                                   len, NULL,
                                                   GTPCAUSE_NON_EXIST);
@@ -2162,9 +2220,9 @@
                        /* Find the context in question */
                        if (gtp_pdp_getgtp1(gsn, &pdp, get_tei(pack))) {
                                gsn->err_unknownpdp++;
-                               GTP_LOGPKG(LOGL_ERROR, peer,
-                                       pack, len, "Unknown PDP context: %u\n",
-                                       get_tei(pack));
+                               GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                          "Unknown PDP context: TEI=0x%" 
PRIx32 "\n",
+                                          get_tei(pack));
                                return gtp_update_pdp_resp(gsn, version, peer,
                                                           fd, pack, len, NULL,
                                                           GTPCAUSE_NON_EXIST);
@@ -2173,8 +2231,9 @@
                        /* Find the context in question */
                        if (gtp_pdp_getimsi(gsn, &pdp, imsi, nsapi)) {
                                gsn->err_unknownpdp++;
-                               GTP_LOGPKG(LOGL_ERROR, peer,
-                                           pack, len, "Unknown PDP context\n");
+                               GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                          "Unknown PDP context: IMSI=0x%" 
PRIx64
+                                          " NSAPI=%" PRIu8 "\n", imsi, nsapi);
                                return gtp_update_pdp_resp(gsn, version, peer,
                                                           fd, pack, len, NULL,
                                                           GTPCAUSE_NON_EXIST);
@@ -2325,29 +2384,20 @@
 static int gtp_update_pdp_conf(struct gsn_t *gsn, uint8_t version,
                        struct sockaddr_in *peer, void *pack, unsigned len)
 {
-       struct pdp_t *pdp;
+       struct pdp_t *pdp = NULL;
        union gtpie_member *ie[GTPIE_SIZE];
-       uint8_t cause, recovery;
+       uint8_t cause = EOF;
+       uint8_t recovery;
+       int rc = 0;
        void *cbp = NULL;
        uint8_t type = 0;
+       bool trigger_recovery = false;
        int hlen = get_hlen(pack);
 
        /* Remove packet from queue */
        if (gtp_conf(gsn, 0, peer, pack, len, &type, &cbp))
                return EOF;
 
-       /* Find the context in question */
-       if (gtp_pdp_getgtp1(gsn, &pdp, get_tei(pack))) {
-               gsn->err_unknownpdp++;
-               GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
-                           "Unknown PDP context: %u\n", get_tei(pack));
-               pdp = NULL;
-               goto err_out;
-       }
-
-       /* Register that we have received a valid teic from GGSN */
-       pdp->teic_confirmed = 1;
-
        /* Decode information elements */
        if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) {
                gsn->invalid++;
@@ -2356,19 +2406,34 @@
                goto err_out;
        }
 
+       /* Extract recovery (optional) */
+       if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery))
+               trigger_recovery = true;
+
        /* Extract cause value (mandatory) */
        if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) {
                goto err_missing;
        }
 
-       /* Extract recovery (optional) */
-       if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
-               emit_cb_recovery(gsn, peer, pdp, recovery);
+       /*  3GPP TS 29.060 sec 8.2: "Receiving node shall send back to the 
source
+        *  of the message, a response with the appropriate cause value (either
+        *  "Non-existent" or "Context not found"). The Tunnel Endpoint
+        *  Identifier used in the response message shall be set to all zeroes."
+        *  Hence, TEID=0 in this scenario, it makes no sense to infer PDP ctx
+        *  from it. User is responsible to infer it from cbp */
+       if (cause != GTPCAUSE_NON_EXIST && cause != GTPCAUSE_CONTEXT_NOT_FOUND) 
{
+               /* Find the context in question */
+               if (gtp_pdp_getgtp1(gsn, &pdp, get_tei(pack))) {
+                       gsn->err_unknownpdp++;
+                       GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                  "Unknown PDP context: TEI=0x%" PRIx32 "\n", 
get_tei(pack));
+                       goto err_out;
+               }
        }
 
        /* Check all conditional information elements */
        /* TODO: This does not handle GGSN-initiated update responses */
-       if (GTPCAUSE_ACC_REQ == cause) {
+       if (cause == GTPCAUSE_ACC_REQ) {
                if (version == 0) {
                        if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
                                         &pdp->qos_neg0,
@@ -2393,6 +2458,8 @@
                        if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn)) {
                                goto err_missing;
                        }
+                       /* Register that we have received a valid teic from 
GGSN */
+                       pdp->teic_confirmed = 1;
                }
 
                if (gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid)) {
@@ -2418,18 +2485,20 @@
                }
        }
 
+generic_ret:
+       if (trigger_recovery)
+               emit_cb_recovery(gsn, peer, pdp, recovery);
        if (gsn->cb_conf)
                gsn->cb_conf(type, cause, pdp, cbp);
-       return 0;       /* Succes */
+       return rc;      /* Succes */
 
 err_missing:
        gsn->missing++;
        GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
                    "Missing information field\n");
 err_out:
-       if (gsn->cb_conf)
-               gsn->cb_conf(type, EOF, pdp, cbp);
-       return EOF;
+       rc = EOF;
+       goto generic_ret;
 }
 
 /* API: Deprecated. Send Delete PDP Context Request And free pdp ctx. */
@@ -2594,7 +2663,7 @@
        if (gtp_pdp_getgtp1(gsn, &linked_pdp, get_tei(pack))) {
                gsn->err_unknownpdp++;
                GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
-                           "Unknown PDP context: %u\n", get_tei(pack));
+                          "Unknown PDP context: TEI=0x%" PRIx32 "\n", 
get_tei(pack));
                return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len,
                                           NULL, NULL, GTPCAUSE_NON_EXIST,
                                           teardown);
@@ -2623,7 +2692,7 @@
                if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &nsapi)) {
                        gsn->missing++;
                        GTP_LOGPKG(LOGL_ERROR, peer, pack,
-                                   len, "Missing mandatory information 
field\n");
+                                  len, "Missing mandatory information 
field\n");
                        return gtp_delete_pdp_resp(gsn, version, peer, fd, pack,
                                                   len, NULL, NULL,
                                                   GTPCAUSE_MAN_IE_MISSING,
@@ -2633,8 +2702,9 @@
                /* Find the context in question */
                if (gtp_pdp_getgtp1(gsn, &pdp, linked_pdp->secondary_tei[nsapi 
& 0x0f])) {
                        gsn->err_unknownpdp++;
-                       GTP_LOGPKG(LOGL_ERROR, peer, pack,
-                                   len, "Unknown PDP context\n");
+                       GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                  "Unknown PDP context: Secondary TEI=0x%" 
PRIx32 "\n",
+                                  linked_pdp->secondary_tei[nsapi & 0x0f]);
                        return gtp_delete_pdp_resp(gsn, version, peer, fd, pack,
                                                   len, NULL, NULL,
                                                   GTPCAUSE_NON_EXIST,
@@ -2687,8 +2757,9 @@
        if (gtp_pdp_getgtp1(gsn, &pdp, get_tei(pack))) {
                gsn->err_unknownpdp++;
                GTP_LOGPKG(LOGL_NOTICE, peer, pack, len,
-                           "Unknown PDP context: %u (expected if 
gtp_delete_context_req is used or pdp ctx was freed manually before 
response)\n",
-                            get_tei(pack));
+                          "Unknown PDP context: TEI=0x%" PRIx32 " (expected if 
"
+                          "gtp_delete_context_req is used or pdp ctx was freed 
"
+                          "manually before response)\n", get_tei(pack));
                if (gsn->cb_conf)
                        gsn->cb_conf(type, EOF, NULL, cbp);
                return EOF;
@@ -2765,7 +2836,8 @@
                if (gtp_pdp_tidget(gsn, &pdp, get_tid(pack))) {
                        gsn->err_unknownpdp++;
                        GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
-                                   "Unknown PDP context\n");
+                                  "Unknown PDP context: TID=0x%" PRIx64 "\n",
+                                  get_tid(pack));
                        return EOF;
                }
        } else if (version == 1) {
@@ -2790,7 +2862,9 @@
 
                if (gtp_pdp_getgtp1_peer_d(gsn, &pdp, peer, teid_gn)) {
                        gsn->err_unknownpdp++;
-                       GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP 
context\n");
+                       GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                  "Unknown PDP context: Peer TEID=0x%" PRIx32 
"\n",
+                                  teid_gn);
                        return EOF;
                }
        } else {
@@ -2821,22 +2895,22 @@
 
        switch (version) {
        case 0:
-               if (gtp_pdp_getgtp0(gsn, &pdp,
-                                   ntoh16(((union gtp_packet 
*)pack)->gtp0.h.flow))) {
+               if (gtp_pdp_getgtp0(gsn, &pdp, get_tei(pack))) {
                        gsn->err_unknownpdp++;
-                       GTP_LOGPKG(LOGL_ERROR, peer, pack,
-                                   len, "Unknown PDP context, GTPv0\n");
+                       GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                  "Unknown PDP context: TEI=0x%" PRIx32 "\n",
+                                  get_tei(pack));
                        return gtp_error_ind_resp(gsn, version, peer, fd, pack,
                                                  len);
                }
                hlen = GTP0_HEADER_SIZE;
                break;
        case 1:
-               if (gtp_pdp_getgtp1(gsn, &pdp,
-                                   ntoh32(((union gtp_packet 
*)pack)->gtp1l.h.tei))) {
+               if (gtp_pdp_getgtp1(gsn, &pdp, get_tei(pack))) {
                        gsn->err_unknownpdp++;
-                       GTP_LOGPKG(LOGL_ERROR, peer, pack,
-                                   len, "Unknown PDP context, GTPv1\n");
+                       GTP_LOGPKG(LOGL_ERROR, peer, pack, len,
+                                  "Unknown PDP context: TEI=0x%" PRIx32 "\n",
+                                  get_tei(pack));
                        return gtp_error_ind_resp(gsn, version, peer, fd, pack,
                                                  len);
                }
@@ -3186,6 +3260,9 @@
                case GTP_ERROR:
                        gtp_error_ind_conf(gsn, version, &peer, buffer, status);
                        break;
+               case GTP_RAN_INFO_RELAY:
+                       gtp_ran_info_relay_ind(gsn, version, &peer, buffer, 
status);
+                       break;
                default:
                        gsn->unknown++;
                        GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/gtp/gtp.h 
new/osmo-ggsn-1.8.0/gtp/gtp.h
--- old/osmo-ggsn-1.7.1/gtp/gtp.h       2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/gtp/gtp.h       2021-11-16 13:49:16.000000000 +0100
@@ -16,6 +16,7 @@
 #include <osmocom/core/defs.h>
 #include <osmocom/core/timer.h>
 
+#include "gtpie.h"
 #include "pdp.h"
 
 #define GTP_MODE_GGSN 1
@@ -85,6 +86,7 @@
 #define GTP_FWD_SRNS         58        /* Forward SRNS Context */
 #define GTP_FWD_RELOC_ACK    59        /* Forward Relocation Complete 
Acknowledge */
 #define GTP_FWD_SRNS_ACK     60        /* Forward SRNS Context Acknowledge */
+#define GTP_RAN_INFO_RELAY   70        /* RAN Information Relay */
 /* 61-239 For future use. */
 #define GTP_DATA_TRAN_REQ   240        /* Data Record Transfer Request */
 #define GTP_DATA_TRAN_RSP   241        /* Data Record Transfer Response */
@@ -276,6 +278,7 @@
        int (*cb_create_context_ind) (struct pdp_t *);
        int (*cb_unsup_ind) (struct sockaddr_in * peer);
        int (*cb_extheader_ind) (struct sockaddr_in * peer);
+       int (*cb_ran_info_relay_ind) (struct sockaddr_in *peer, union 
gtpie_member **ie);
        int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp);
        int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len);
        int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery);
@@ -343,6 +346,11 @@
 extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
                        void *pack, unsigned len);
 
+extern int gtp_ran_info_relay_req(struct gsn_t *gsn, const struct sockaddr_in 
*peer,
+                                 const uint8_t *ran_container, size_t 
ran_container_len,
+                                 const uint8_t *rim_route_addr, size_t 
rim_route_addr_len,
+                                 uint8_t rim_route_addr_discr);
+
 extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
                               int (*cb_data_ind) (struct pdp_t * pdp,
                                                   void *pack, unsigned len));
@@ -366,6 +374,9 @@
 extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
                                    int (*cb) (struct sockaddr_in * peer));
 
+extern int gtp_set_cb_ran_info_relay_ind(struct gsn_t *gsn,
+                                   int (*cb) (struct sockaddr_in * peer, union 
gtpie_member **ie));
+
 extern int gtp_set_cb_conf(struct gsn_t *gsn,
                           int (*cb) (int type, int cause, struct pdp_t * pdp,
                                      void *cbp));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/gtp/gtpie.c 
new/osmo-ggsn-1.8.0/gtp/gtpie.c
--- old/osmo-ggsn-1.7.1/gtp/gtpie.c     2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/gtp/gtpie.c     2021-11-16 13:49:16.000000000 +0100
@@ -1,17 +1,17 @@
-/* 
+/*
  *  OsmoGGSN - Gateway GPRS Support Node
  *  Copyright (C) 2002 Mondru AB.
- * 
+ *
  *  The contents of this file may be used under the terms of the GNU
  *  General Public License Version 2, provided that the above copyright
  *  notice and this permission notice is included in all copies or
  *  substantial portions of the software.
- * 
+ *
  */
 
 /*
- * gtpie.c: Contains functions to encapsulate and decapsulate GTP 
- * information elements 
+ * gtpie.c: Contains functions to encapsulate and decapsulate GTP
+ * information elements
  *
  *
  * Encapsulation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/lib/tun.c 
new/osmo-ggsn-1.8.0/lib/tun.c
--- old/osmo-ggsn-1.7.1/lib/tun.c       2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/lib/tun.c       2021-11-16 13:49:16.000000000 +0100
@@ -318,7 +318,14 @@
 
 int tun_encaps(struct tun_t *tun, void *pack, unsigned len)
 {
-       return write(tun->fd, pack, len);
+       int rc;
+       rc = write(tun->fd, pack, len);
+       if (rc < 0) {
+               SYS_ERR(DTUN, LOGL_ERROR, errno, "TUN(%s): write() failed", 
tun->devname);
+       } else if (rc < len) {
+               LOGTUN(LOGL_ERROR, tun, "short write() %d < %u\n", rc, len);
+       }
+       return rc;
 }
 
 int tun_runscript(struct tun_t *tun, char *script)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/lib/tun.h 
new/osmo-ggsn-1.8.0/lib/tun.h
--- old/osmo-ggsn-1.7.1/lib/tun.h       2021-02-23 17:31:24.000000000 +0100
+++ new/osmo-ggsn-1.8.0/lib/tun.h       2021-11-16 13:49:16.000000000 +0100
@@ -59,4 +59,7 @@
 int tun_ip_local_get(const struct tun_t *tun, struct in46_prefix *prefix_list,
                     size_t prefix_size, int flags);
 
+#define LOGTUN(level, tun, fmt, args...) \
+       LOGP(DTUN, level, "TUN(%s): " fmt, (tun)->devname, ## args)
+
 #endif /* !_TUN_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/utils/Makefile.am 
new/osmo-ggsn-1.8.0/utils/Makefile.am
--- old/osmo-ggsn-1.7.1/utils/Makefile.am       1970-01-01 01:00:00.000000000 
+0100
+++ new/osmo-ggsn-1.8.0/utils/Makefile.am       2021-11-16 13:49:16.000000000 
+0100
@@ -0,0 +1,3 @@
+bin_PROGRAMS = gtp-echo-responder
+
+gtp_echo_responder_SOURCES = gtp_echo_responder.c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/utils/gtp_echo_responder.c 
new/osmo-ggsn-1.8.0/utils/gtp_echo_responder.c
--- old/osmo-ggsn-1.7.1/utils/gtp_echo_responder.c      1970-01-01 
01:00:00.000000000 +0100
+++ new/osmo-ggsn-1.8.0/utils/gtp_echo_responder.c      2021-11-16 
13:49:16.000000000 +0100
@@ -0,0 +1,470 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+ * Author: Pau Espin Pedrol <pes...@sysmocom.de>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ */
+
+/* For more info see:
+ * 3GPP TS 29.060 (GTPv1 and GTPv0)
+ * 3GPP TS 29.274 (GTPv2C)
+ */
+
+#include "../config.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+
+#define GTP1C_PORT     2123
+#define GTP_MSGTYPE_ECHO_REQ   1
+#define GTP_MSGTYPE_ECHO_RSP   2
+#define GTP1C_IE_RECOVERY 14
+#define GTP2C_IE_RECOVERY 3
+#define GTP2C_IE_NODE_FEATURES 152
+
+struct gtp1_hdr {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+       uint8_t pn:1, s:1, e:1, spare:1, pt:1, version:3;
+#else
+       uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
+#endif
+       uint8_t type;
+       uint16_t length;
+       uint32_t tei;
+       uint16_t seq;
+       uint8_t npdu;
+       uint8_t next;
+} __attribute__((packed));
+
+struct gtp2_hdr {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+       uint8_t reserved:3, t:1, p:1, version:3;
+#else
+       uint8_t version:3, p:1, t:1, reserved:1;
+#endif
+       uint8_t type;
+       uint16_t length;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+       uint32_t reserved2:8, seq:24;
+#else
+       uint8_t seq:24, reserved2:1;
+#endif
+} __attribute__((packed));
+
+struct gtp_echo_resp_state {
+       struct {
+               char laddr[INET6_ADDRSTRLEN];
+               uint8_t recovery_ctr;
+               uint8_t node_features;
+       } cfg;
+       struct sockaddr_storage laddr_gtpc;
+       int fd_gtpc;
+};
+
+struct gtp_echo_resp_state *g_st;
+
+static void print_usage(void)
+{
+       printf("Usage: gtp-echo-responder [-h] [-V] [-l listen_addr]\n");
+}
+
+static void print_help(void)
+{
+       printf("  Some useful help...\n"
+              "  -h --help             This help text\n"
+              "  -V --version          Print the version of 
gtp-echo-responder\n"
+              "  -l --listen-addr      Listend address for GTPCv1 and GTPCv2\n"
+              "  -R --recovery-counter GTP Recovery Counter to transmit in GTP 
Echo Response message\n"
+              "  -n --node-features    GTPCv2 Node Features bitmask to 
transmit in GTP Echo Response message\n"
+              );
+}
+
+static void print_version(void)
+{
+       printf("gtp-echo-responder version %s\n", PACKAGE_VERSION);
+}
+
+static uint8_t parse_node_features_mask(const char *arg)
+{
+       unsigned long res;
+       char *end;
+       errno = 0;
+
+       res = strtoul(arg, &end, 0);
+       if ((errno == ERANGE && res == ULONG_MAX) || (errno && !res) ||
+           arg == end || *end != '\0') {
+               fprintf(stderr, "Failed parsing Node Features bitmask: '%s'\n", 
arg);
+               exit(1);
+       }
+       if (res > 0xff) {
+               fprintf(stderr, "Failed parsing Node Features bitmask: '%s' > 
0xFF\n", arg);
+               exit(1);
+       }
+       return (uint8_t)res;
+}
+static void handle_options(int argc, char **argv)
+{
+       while (1) {
+               int option_index = 0, c;
+               static struct option long_options[] = {
+                       { "help", 0, 0, 'h' },
+                       { "version", 0, 0, 'V' },
+                       { "listen-addr", 1, 0, 'l'},
+                       { "recovery-counter", 1, 0, 'R'},
+                       { "node-features", 1, 0, 'N'},
+                       { 0, 0, 0, 0 }
+               };
+
+               c = getopt_long(argc, argv, "hVl:R:N:", long_options, 
&option_index);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'h':
+                       print_usage();
+                       print_help();
+                       exit(0);
+               case 'V':
+                       print_version();
+                       exit(0);
+                       break;
+               case 'l':
+                       strncpy(&g_st->cfg.laddr[0], optarg, 
sizeof(g_st->cfg.laddr));
+                       g_st->cfg.laddr[sizeof(g_st->cfg.laddr) - 1] = '\0';
+                       break;
+               case 'R':
+                       g_st->cfg.recovery_ctr = (uint8_t)atoi(optarg);
+                       break;
+               case 'N':
+                       g_st->cfg.node_features = 
parse_node_features_mask(optarg);
+                       break;
+               }
+       }
+}
+
+static int init_socket(void)
+{
+       struct in_addr addr;
+       struct in6_addr addr6;
+       struct sockaddr_in *saddr;
+       struct sockaddr_in6 *saddr6;
+       int family;
+
+       if (inet_pton(AF_INET6, g_st->cfg.laddr, &addr6) == 1) {
+               family = AF_INET6;
+               saddr6 = (struct sockaddr_in6 *)&g_st->laddr_gtpc;
+               saddr6->sin6_family = family;
+               saddr6->sin6_port = htons(GTP1C_PORT);
+               memcpy(&saddr6->sin6_addr, &addr6, sizeof(addr6));
+       } else if (inet_pton(AF_INET, g_st->cfg.laddr, &addr) == 1) {
+               family = AF_INET;
+               saddr = (struct sockaddr_in *)&g_st->laddr_gtpc;
+               saddr->sin_family = family;
+               saddr->sin_port = htons(GTP1C_PORT);
+               memcpy(&saddr->sin_addr, &addr, sizeof(addr));
+       } else {
+               fprintf(stderr, "Failed parsing address %s\n", g_st->cfg.laddr);
+               return -1;
+       }
+
+       if ((g_st->fd_gtpc = socket(family, SOCK_DGRAM, 0)) < 0) {
+               fprintf(stderr, "socket() failed: %s\n", strerror(errno));
+               return -2;
+       }
+
+       if (bind(g_st->fd_gtpc, (struct sockaddr *)&g_st->laddr_gtpc, 
sizeof(g_st->laddr_gtpc)) < 0) {
+               fprintf(stderr, "bind() failed: %s\n", strerror(errno));
+               return -3;
+       }
+
+       return 0;
+}
+
+static const char *sockaddr2str(const struct sockaddr *saddr)
+{
+       static char _rem_addr_str[INET6_ADDRSTRLEN];
+       struct sockaddr_in *saddr4;
+       struct sockaddr_in6 *saddr6;
+
+       switch (saddr->sa_family) {
+       case AF_INET6:
+               saddr6 = (struct sockaddr_in6 *)saddr;
+               if (!inet_ntop(saddr6->sin6_family, &saddr6->sin6_addr, 
_rem_addr_str, sizeof(_rem_addr_str)))
+                       strcpy(_rem_addr_str, "unknown");
+               return _rem_addr_str;
+       case AF_INET:
+               saddr4 = (struct sockaddr_in *)saddr;
+               if (!inet_ntop(saddr4->sin_family, &saddr4->sin_addr, 
_rem_addr_str, sizeof(_rem_addr_str)))
+                       strcpy(_rem_addr_str, "unknown");
+               return _rem_addr_str;
+       default:
+               strcpy(_rem_addr_str, "unknown-family");
+               return _rem_addr_str;
+       }
+}
+
+static int write_cb(int fd, const uint8_t *buf, size_t buf_len, const struct 
sockaddr *rem_saddr)
+{
+       ssize_t rc;
+
+       rc = sendto(fd, buf, buf_len, 0, rem_saddr, sizeof(struct 
sockaddr_storage));
+       if (rc < 0) {
+               fprintf(stderr, "sendto() failed: %s\n", strerror(errno));
+               return -1;
+       }
+       if (rc != buf_len) {
+               fprintf(stderr, "sendto() short write: %zd vs exp %zu\n", rc, 
buf_len);
+               return -1;
+       }
+       return 0;
+}
+
+static int gen_gtpc1_echo_rsp(uint8_t *buf, struct gtp1_hdr *echo_req)
+{
+       int offset = 0;
+       struct gtp1_hdr *echo_rsp = (struct gtp1_hdr *)buf;
+       unsigned exp_hdr_len = (echo_req->s || echo_req->pn || echo_req->e) ? 
12 : 8;
+
+       memcpy(echo_rsp, echo_req, exp_hdr_len);
+       echo_rsp->type = GTP_MSGTYPE_ECHO_RSP;
+       offset = exp_hdr_len;
+       buf[offset++] = GTP1C_IE_RECOVERY;
+       buf[offset++] = g_st->cfg.recovery_ctr;
+
+       /* Update Length */
+       echo_rsp->length = htons(offset - 8);
+       return offset;
+}
+
+static int gen_gtpc2_echo_rsp(uint8_t *buf, struct gtp2_hdr *echo_req)
+{
+       int offset = 0;
+       struct gtp1_hdr *echo_rsp = (struct gtp1_hdr *)buf;
+       unsigned exp_hdr_len = 8;
+
+       memcpy(echo_rsp, echo_req, exp_hdr_len);
+       echo_rsp->type = GTP_MSGTYPE_ECHO_RSP;
+       offset = exp_hdr_len;
+
+       /* 3GPP TS 29.274 sec 8.5 Recovery (Restart Counter) */
+       buf[offset++] = GTP2C_IE_RECOVERY;
+       buf[offset++] = 0; /* IE Length (high) */
+       buf[offset++] = 1; /* IE Length (low) */
+       buf[offset++] = 0; /* Spare=0 | Instance=0 (Table 7.1.1-1) */
+       buf[offset++] = g_st->cfg.recovery_ctr;
+
+       /* 3GPP TS 29.274 sec 8.83 Node Features */
+       if (g_st->cfg.node_features > 0) {
+               buf[offset++] = GTP2C_IE_NODE_FEATURES;
+               buf[offset++] = 0; /* IE Length (high) */
+               buf[offset++] = 1; /* IE Length (low) */
+               buf[offset++] = 0; /* Spare=0 | Instance=0 (Table 7.1.1-1) */
+               buf[offset++] = g_st->cfg.node_features;
+       }
+
+       /* Update Length */
+       echo_rsp->length = htons(offset - 4);
+       return offset;
+}
+
+static int rx_gtpc1_echo_req(struct gtp1_hdr *echo_req, unsigned buf_len, 
const struct sockaddr *rem_saddr)
+{
+       int rc;
+       const size_t tx_buf_len = buf_len + 128; /* Leave some extra room */
+       uint8_t *tx_buf = alloca(tx_buf_len);
+
+       printf("Rx GTPCv1_ECHO_REQ from %s, Tx GTPCv1_ECHO_RSP\n", 
sockaddr2str(rem_saddr));
+
+       memset(tx_buf, 0, tx_buf_len);
+       rc = gen_gtpc1_echo_rsp(tx_buf, echo_req);
+       return write_cb(g_st->fd_gtpc, tx_buf, rc, rem_saddr);
+}
+
+static int rx_gtpc1(struct gtp1_hdr *hdr, unsigned buf_len, const struct 
sockaddr *rem_saddr)
+{
+       unsigned exp_hdr_len = (hdr->s || hdr->pn || hdr->e) ? 12 : 8;
+       unsigned pdu_len;
+
+       if (buf_len < exp_hdr_len) {
+               fprintf(stderr, "GTPCv1 packet size smaller than header! %u < 
exp %u\n", buf_len, exp_hdr_len);
+               return -1;
+       }
+
+       pdu_len = ntohs(hdr->length);
+       if (buf_len < 8 + pdu_len) {
+               fprintf(stderr, "GTPCv1 packet size smaller than announced! %u 
< exp %u\n", buf_len, 8 + pdu_len);
+               return -1;
+       }
+
+       if (hdr->pt != 1) {
+               fprintf(stderr, "GTPCv1 Protocol Type GTP' not supported!\n");
+               return -1;
+       }
+
+       switch (hdr->type) {
+       case GTP_MSGTYPE_ECHO_REQ:
+               return rx_gtpc1_echo_req(hdr, buf_len, rem_saddr);
+       default:
+               fprintf(stderr, "Silently ignoring unexpected packet of type 
%u\n", hdr->type);
+               return 0;
+       }
+}
+
+static int rx_gtpc2_echo_req(struct gtp2_hdr *echo_req, unsigned buf_len, 
const struct sockaddr *rem_saddr)
+{
+       int rc;
+       const size_t tx_buf_len = buf_len + 128; /* Leave some extra room */
+       uint8_t *tx_buf = alloca(tx_buf_len);
+
+       if (echo_req->t) {
+               fprintf(stderr, "GTPCv2 ECHO message should contain T=0!\n");
+               return -1;
+       }
+
+       printf("Rx GTPCv2_ECHO_REQ from %s, Tx GTPCv2_ECHO_RSP\n", 
sockaddr2str(rem_saddr));
+
+       memset(tx_buf, 0, tx_buf_len);
+       rc = gen_gtpc2_echo_rsp(tx_buf, echo_req);
+       return write_cb(g_st->fd_gtpc, tx_buf, rc, rem_saddr);
+}
+
+static int rx_gtpc2(struct gtp2_hdr *hdr, unsigned buf_len, const struct 
sockaddr *rem_saddr)
+{
+       unsigned exp_hdr_len = hdr->t ? 12 : 8;
+       unsigned pdu_len;
+
+       if (hdr->p) {
+               fprintf(stderr, "GTPCv2 piggybacked message not supported!\n");
+               return -1;
+       }
+
+       if (buf_len < exp_hdr_len) {
+               fprintf(stderr, "GTPCv2 packet size smaller than header! %u < 
exp %u\n", buf_len, exp_hdr_len);
+               return -1;
+       }
+
+       pdu_len = ntohs(hdr->length);
+       /* 3GPP TS 29.274 sec 5.5.1: "Octets 3 to 4 represent the Message Length
+        * field. This field shall indicate the length of the message in octets
+        * excluding the mandatory part of the GTP-C header (the first 4
+        * octets). The TEID (if present) and the Sequence  Number shall be
+        * included in the length count" */
+       if (buf_len < 4 + pdu_len) {
+               fprintf(stderr, "GTPCv2 packet size smaller than announced! %u 
< exp %u\n", buf_len, 4 + pdu_len);
+               return -1;
+       }
+
+       switch (hdr->type) {
+       case GTP_MSGTYPE_ECHO_REQ:
+               return rx_gtpc2_echo_req(hdr, buf_len, rem_saddr);
+       default:
+               fprintf(stderr, "Silently ignoring unexpected packet of type 
%u\n", hdr->type);
+               return 0;
+       }
+}
+
+static int read_cb(int fd)
+{
+       ssize_t sz;
+       uint8_t buf[4096];
+       struct sockaddr_storage rem_saddr;
+       socklen_t rem_saddr_len = sizeof(rem_saddr);
+       struct gtp1_hdr *hdr1;
+
+       if ((sz = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr 
*)&rem_saddr, &rem_saddr_len)) < 0) {
+               fprintf(stderr, "recvfrom() failed: %s\n", strerror(errno));
+               return -1;
+       }
+       if (sz == 0) {
+               fprintf(stderr, "recvfrom() read zero bytes!\n");
+               return -1;
+       }
+
+       hdr1 = (struct gtp1_hdr *)&buf[0];
+       switch (hdr1->version) {
+       case 1:
+               return rx_gtpc1(hdr1, sz, (const struct sockaddr *)&rem_saddr);
+       case 2:
+               return rx_gtpc2((struct gtp2_hdr *)&buf[0], sz, (const struct 
sockaddr *)&rem_saddr);
+       default:
+               fprintf(stderr, "Rx GTPv%u: not supported (flags=0x%x)\n", 
hdr1->version, buf[0]);
+               return -1;
+       }
+}
+
+static int loop(void)
+{
+       int rc;
+       fd_set rfds;
+       int nfds;
+
+       while (true) {
+               FD_ZERO(&rfds);
+               FD_SET(g_st->fd_gtpc, &rfds);
+               nfds = g_st->fd_gtpc + 1;
+               rc = select(nfds, &rfds, NULL, NULL, NULL);
+               if (rc == 0)
+                       continue;
+               if (rc < 0) {
+                       fprintf(stderr, "select() failed: %s\n", 
strerror(errno));
+                       return -1;
+               }
+
+               if (FD_ISSET(g_st->fd_gtpc, &rfds))
+                       read_cb(g_st->fd_gtpc);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       g_st = calloc(1, sizeof(struct gtp_echo_resp_state));
+
+       strcpy(g_st->cfg.laddr, "::");
+
+       handle_options(argc, argv);
+
+       printf("Listening on: %s\n", g_st->cfg.laddr);
+
+       if (init_socket() < 0)
+               exit(1);
+
+       printf("Socket bound successfully, listening for requests...\n");
+
+       if (loop() < 0)
+               exit(1);
+
+       return 0;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osmo-ggsn-1.7.1/utils/gtp_echo_responder_test.py 
new/osmo-ggsn-1.8.0/utils/gtp_echo_responder_test.py
--- old/osmo-ggsn-1.7.1/utils/gtp_echo_responder_test.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/osmo-ggsn-1.8.0/utils/gtp_echo_responder_test.py        2021-11-16 
13:49:16.000000000 +0100
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# MIT License
+#
+# Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+# Author: Pau Espin Pedrol <pes...@sysmocom.de>
+#
+# SPDX-License-Identifier: MIT
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+
+import socket
+import argparse
+import struct
+from ipaddress import ip_address, IPv4Address
+
+GTP1C_PORT = 2123
+BUF_SIZE = 4096
+
+GTP_HDRv1_FLAG_PN = (1<<0)
+GTP_HDRv1_FLAG_S = (1<<1)
+GTP_HDRv1_FLAG_E = (1<<2)
+GTP_HDRv1_PT_GTP = (1<<4)
+GTP_HDRv1_VER_GTP1 = (1<<5)
+
+GTP_HDRv2_FLAG_T = (1<<3)
+GTP_HDRv2_FLAG_P = (1<<4)
+GTP_HDRv2_VER_GTP2 = (2<<5)
+
+def gen_gtpc_v1_hdr(flags, type, length, tei, seq=0, npdu=0, next=0):
+    spare = 0
+    if (flags & (GTP_HDRv1_FLAG_PN|GTP_HDRv1_FLAG_S|GTP_HDRv1_FLAG_E)):
+        #long format
+        length += 4
+        d = struct.pack('!BBHIHBB', flags, type, length, tei, seq, npdu, next)
+    else:
+        #short format
+        d = struct.pack('!BBHI', flags, type, length, tei)
+    return d
+
+def gen_gtpc_v2_hdr(flags, type, length, tei=0, seq=0):
+    spare = 0
+    if (flags & (GTP_HDRv2_FLAG_T)):
+        #long format, with TEI
+        length += 4 + 4
+        d = struct.pack('!BBHIHBB', flags, type, length, tei, seq >> 8, seq & 
0xff, spare)
+    else:
+        #short format
+        length += 4
+        d = struct.pack('!BBHHBB', flags, type, length, seq >> 8, seq & 0xff, 
spare)
+    return d
+
+def gen_gtpc_v1_echo_req(tei=0, append_flags=0, seq=0, npdu=0, next=0):
+    return gen_gtpc_v1_hdr(GTP_HDRv1_VER_GTP1 | GTP_HDRv1_PT_GTP | 
append_flags, 1, 0, tei, seq, npdu, next)
+
+def gen_gtpc_v2_echo_req(append_flags=0, seq=0, recovery=0, node_features=-1):
+    length = 0
+    payload = b''
+    if (recovery > 0):
+        recovery_ie = struct.pack('!BHBB', 3, 1, 0, recovery)
+        payload += recovery_ie
+        length += len(recovery_ie)
+    if (node_features > 0):
+        node_features_ie = struct.pack('!BHBB', 152, 1, 0, node_features)
+        payload += node_features_ie
+        length += len(node_features_ie)
+    return gen_gtpc_v2_hdr(GTP_HDRv2_VER_GTP2 | append_flags, 1, length, 0, 
seq) + payload
+
+def tx_rx(sk, rem_addr, tx_buf, exp_rx = True):
+    print('Tx ECHO_REQ to %r: %r' % (repr(rem_addr), repr(tx_buf)))
+    sk.sendto(tx_buf, rem_addr)
+    if exp_rx:
+        rx_buf = sk.recvfrom(BUF_SIZE)
+        msg = "Message from Server {}".format(rx_buf)
+        print(msg)
+
+if __name__ == '__main__':
+    p = argparse.ArgumentParser(description='Tester for gtp-echo-recorder.')
+    p.add_argument('-l', '--local-address', default='127.0.0.2', help="Local 
GTP address")
+    p.add_argument('-r', '--remote-address', default='127.0.0.1', help="Remote 
GTP address")
+    args = p.parse_args()
+
+    print('Binding socket on %r...' % repr((args.local_address, GTP1C_PORT)))
+    family = socket.AF_INET if type(ip_address(args.local_address)) is 
IPv4Address else socket.AF_INET6
+    sk = socket.socket(family=family, type=socket.SOCK_DGRAM)
+    sk.bind((args.local_address, GTP1C_PORT));
+
+    rem_addr = (args.remote_address, GTP1C_PORT)
+
+    tx_rx(sk, rem_addr, gen_gtpc_v1_echo_req())
+    tx_rx(sk, rem_addr, gen_gtpc_v1_echo_req(1, GTP_HDRv1_FLAG_S, seq=67))
+    tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=300, recovery=-1, 
node_features=-1))
+    tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=20, recovery=99, 
node_features=-1))
+    tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=20, recovery=100, 
node_features=0xbb))

Reply via email to