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