mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn.git
synced 2025-11-03 05:33:23 +00:00
Compare commits
199 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f14c056310 | ||
|
|
bf69ddbfef | ||
|
|
70a4e2e6f8 | ||
|
|
99afe979ef | ||
|
|
35066fb0b0 | ||
|
|
55fe62f634 | ||
|
|
4fac842826 | ||
|
|
97f60e3dca | ||
|
|
a727e6ed38 | ||
|
|
3a55b89777 | ||
|
|
9f1f747d8e | ||
|
|
b9036af7ca | ||
|
|
724ecc6680 | ||
|
|
0d3bd3435f | ||
|
|
3ed252b58e | ||
|
|
ac802e63d7 | ||
|
|
bc583d9763 | ||
|
|
ade4dc191b | ||
|
|
cd05da79e7 | ||
|
|
5545bcea5d | ||
|
|
c97286f839 | ||
|
|
f471800168 | ||
|
|
bdf0697a5a | ||
|
|
674a912fb5 | ||
|
|
1bf3b3d0f9 | ||
|
|
fb9303c610 | ||
|
|
0585769741 | ||
|
|
9b288b788e | ||
|
|
134ac7e7c8 | ||
|
|
46f04343a5 | ||
|
|
a3ca2d185b | ||
|
|
8cbdd21867 | ||
|
|
ae81195418 | ||
|
|
6ee5fa939a | ||
|
|
b6a0e3fd2e | ||
|
|
bd2b55679e | ||
|
|
f32c6a9095 | ||
|
|
2eed6ec5ec | ||
|
|
641206ad5e | ||
|
|
bfd3119ae4 | ||
|
|
4b9b19e998 | ||
|
|
00e0559e17 | ||
|
|
0b1d9dbc40 | ||
|
|
5379273ea3 | ||
|
|
ecef920b8f | ||
|
|
eb9267b15e | ||
|
|
1efb2bcd90 | ||
|
|
878593f205 | ||
|
|
1596463985 | ||
|
|
9d82492e49 | ||
|
|
303aeea8a8 | ||
|
|
18898b4a9f | ||
|
|
17cee2056c | ||
|
|
67a3c833af | ||
|
|
b1f641b5b7 | ||
|
|
f01ce65f5b | ||
|
|
be1cf99e9a | ||
|
|
7710080ffd | ||
|
|
798a81d48d | ||
|
|
51930f7b63 | ||
|
|
00ef1b0d6e | ||
|
|
02a82c3c9b | ||
|
|
349cbfcf50 | ||
|
|
51f99ae250 | ||
|
|
12304c0e5a | ||
|
|
1719abb409 | ||
|
|
3ddf4c6933 | ||
|
|
fb2a7298e0 | ||
|
|
568ac5ee8e | ||
|
|
23c832bb4b | ||
|
|
4831851ca3 | ||
|
|
080dcfaabe | ||
|
|
cbc07bdd82 | ||
|
|
aedae4c971 | ||
|
|
b36eb9d12f | ||
|
|
8df01fad14 | ||
|
|
c8020b959d | ||
|
|
2154607fb0 | ||
|
|
d08a15b343 | ||
|
|
4e37fb356a | ||
|
|
6a8a389c47 | ||
|
|
569e46cbf9 | ||
|
|
91d9410157 | ||
|
|
065ddb6416 | ||
|
|
53244a2132 | ||
|
|
db98f309a9 | ||
|
|
04715d284f | ||
|
|
962146085c | ||
|
|
e2b0961f18 | ||
|
|
ff2ebee03b | ||
|
|
2a1cedd2dc | ||
|
|
c43e887e9e | ||
|
|
e5d71639e5 | ||
|
|
a1b3deefda | ||
|
|
964f08a919 | ||
|
|
ee1529e5ac | ||
|
|
29e7bd0510 | ||
|
|
cdcaeda81c | ||
|
|
98f8126b98 | ||
|
|
a1503b902c | ||
|
|
8398bccb0b | ||
|
|
5552872733 | ||
|
|
61b010c25a | ||
|
|
20d9d154c5 | ||
|
|
1c8ae66654 | ||
|
|
fcdaf31aa8 | ||
|
|
9366f4c034 | ||
|
|
28c6a32677 | ||
|
|
107c813eee | ||
|
|
90d1732be1 | ||
|
|
1c3505b885 | ||
|
|
20539f0271 | ||
|
|
ad6eaa2881 | ||
|
|
b629240a35 | ||
|
|
b283c32027 | ||
|
|
e71e0f2af8 | ||
|
|
bd8f028bff | ||
|
|
a55454d58e | ||
|
|
f1be1df0d3 | ||
|
|
c22205bec8 | ||
|
|
fdf3358959 | ||
|
|
1bf41e4f36 | ||
|
|
c94837c6a4 | ||
|
|
68c5a74557 | ||
|
|
bdf2cf9038 | ||
|
|
00a6171b8d | ||
|
|
26e300fda0 | ||
|
|
4e605b32d4 | ||
|
|
494d873fe3 | ||
|
|
b4c98e7397 | ||
|
|
3eb05d2c1a | ||
|
|
f5fbb419ef | ||
|
|
5d8b226597 | ||
|
|
c602d7cf00 | ||
|
|
a019631c0b | ||
|
|
88ce94c2bd | ||
|
|
310ea1db10 | ||
|
|
012d51ed7d | ||
|
|
1ef2621d3f | ||
|
|
f612ffea82 | ||
|
|
421f22e8cf | ||
|
|
03cce86941 | ||
|
|
95cd897c3f | ||
|
|
f7884e880e | ||
|
|
60ee0dbfa4 | ||
|
|
d950134c53 | ||
|
|
623c5b36e9 | ||
|
|
aab47afe58 | ||
|
|
67aebc9d1c | ||
|
|
ea1cb3fa33 | ||
|
|
0036a60c44 | ||
|
|
e47932976c | ||
|
|
f1e01517bc | ||
|
|
ad252e70aa | ||
|
|
08ca425bc2 | ||
|
|
1eeb113c34 | ||
|
|
d0ba664fec | ||
|
|
ec1d8c4004 | ||
|
|
36e12d4db8 | ||
|
|
2404c5b0b7 | ||
|
|
32b76ee1af | ||
|
|
7bdc80de00 | ||
|
|
83f5266f43 | ||
|
|
e725d87d13 | ||
|
|
8b90bce962 | ||
|
|
f0829ff34b | ||
|
|
e589c6544c | ||
|
|
d1a2ddfee6 | ||
|
|
7b52f00192 | ||
|
|
25ab381c0f | ||
|
|
9fbcb10568 | ||
|
|
eefa30dce8 | ||
|
|
5560001af5 | ||
|
|
a469a90d5e | ||
|
|
84515f4b8b | ||
|
|
1cde2c1691 | ||
|
|
93dd798a99 | ||
|
|
8651573632 | ||
|
|
0d0b0592f0 | ||
|
|
aad77a0acf | ||
|
|
9ee8d3264b | ||
|
|
de72d26f49 | ||
|
|
ceac078d77 | ||
|
|
cd87c5f963 | ||
|
|
154f93da51 | ||
|
|
742a6b55ce | ||
|
|
72ab4bc547 | ||
|
|
fb62504160 | ||
|
|
d7030d268c | ||
|
|
2e8e57a3de | ||
|
|
ca276e01eb | ||
|
|
977b339abe | ||
|
|
9272d212c3 | ||
|
|
f653c5bc33 | ||
|
|
549417e675 | ||
|
|
42c9fa4958 | ||
|
|
df404c4296 | ||
|
|
ffa227307c | ||
|
|
3fc9cc97de |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -16,7 +16,6 @@ install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
osmo-ggsn.spec
|
||||
stamp-h1
|
||||
INSTALL
|
||||
m4/
|
||||
@@ -28,7 +27,7 @@ osmo-ggsn-*.tar*
|
||||
# debian
|
||||
debian/osmo-ggsn/
|
||||
debian/*.debhelper
|
||||
debian/libgtp1
|
||||
debian/libgtp*/
|
||||
debian/osmo-ggsn-dbg
|
||||
debian/*.log
|
||||
debian/autoreconf.*
|
||||
@@ -80,3 +79,10 @@ doc/manuals/generated/
|
||||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
doc/manuals/vty/ggsn_vty_reference.xml
|
||||
|
||||
contrib/osmo-ggsn.spec
|
||||
/debian/gtp-echo-responder-dbg/
|
||||
/debian/gtp-echo-responder/
|
||||
/debian/osmo-ggsn-doc/
|
||||
/utils/gtp-echo-responder
|
||||
|
||||
12
Makefile.am
12
Makefile.am
@@ -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
|
||||
@@ -10,7 +10,15 @@ $(top_srcdir)/.version:
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
EXTRA_DIST = git-version-gen .version README.md README.FreeBSD README.MacOSX
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
README.FreeBSD \
|
||||
README.MacOSX \
|
||||
README.md \
|
||||
contrib/osmo-ggsn.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@@ -20,9 +20,9 @@ GIT Repository
|
||||
|
||||
You can clone from the official osmo-ggsn.git repository using
|
||||
|
||||
git clone git://git.osmocom.org/osmo-ggsn.git
|
||||
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
|
||||
|
||||
There is a cgit interface at http://git.osmocom.org/osmo-ggsn/
|
||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn>
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
44
configure.ac
44
configure.ac
@@ -1,14 +1,15 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(osmo-ggsn, m4_esyscmd([./git-version-gen .tarball-version]), osmocom-net-gprs@lists.osmocom.org)
|
||||
AC_INIT([osmo-ggsn],[m4_esyscmd(./git-version-gen .tarball-version)],[osmocom-net-gprs@lists.osmocom.org])
|
||||
AC_CONFIG_SRCDIR([gtp/gtp.c])
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
#AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
AC_CANONICAL_SYSTEM
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
@@ -20,6 +21,11 @@ AC_PROG_AWK
|
||||
AC_PROG_CPP
|
||||
LT_INIT
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
@@ -75,8 +81,12 @@ AC_HEADER_STDC
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h])
|
||||
|
||||
# Check for if header
|
||||
AC_CHECK_HEADERS([linux/if.h net/if.h])
|
||||
# Check for if header. Some versions of linux/if.h fail without sys/socket.h included beforehand:
|
||||
# see https://algorithmicallyrandom.blogspot.com/2012/07/error-on-including-include.html
|
||||
AC_CHECK_HEADERS([linux/if.h net/if.h], [], [], [#ifdef HAVE_SYS_SOCKET_H
|
||||
# include <sys/socket.h>
|
||||
# endif
|
||||
])
|
||||
|
||||
# Check for tun header
|
||||
AC_CHECK_HEADERS([linux/if_tun.h net/if_tun.h])
|
||||
@@ -123,6 +133,15 @@ AC_EGREP_HEADER(struct iphdr, netinet/ip.h,
|
||||
AC_DEFINE([HAVE_IPHDR])],
|
||||
AC_MSG_RESULT(no))
|
||||
|
||||
# Address generation modes (enum) implemented in linux 3.17 (bc91b0f07ada5535427373a4e2050877bcc12218)
|
||||
# /proc/sys/net/ipv6/conf/${iface}/addr_gen_mode was added in linux 4.11 (d35a00b8e33dab7385f724e713ae71c8be0a49f4)
|
||||
AC_MSG_CHECKING(whether enum in6_addr_gen_mode.IN6_ADDR_GEN_MODE_NONE exists)
|
||||
AH_TEMPLATE(HAVE_IN6_ADDR_GEN_MODE_NONE)
|
||||
AC_EGREP_HEADER(IN6_ADDR_GEN_MODE_NONE, linux/if_link.h,
|
||||
[AC_MSG_RESULT(yes)
|
||||
AC_DEFINE([HAVE_IN6_ADDR_GEN_MODE_NONE])],
|
||||
AC_MSG_RESULT(no))
|
||||
|
||||
# Checks for library functions.
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
# AC_FUNC_MALLOC
|
||||
@@ -135,9 +154,9 @@ adl_FUNC_GETOPT_LONG
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.8.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -238,15 +257,16 @@ AC_CONFIG_FILES([Makefile
|
||||
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
|
||||
tests/Makefile
|
||||
tests/lib/Makefile
|
||||
tests/gtp/Makefile
|
||||
libgtp.pc
|
||||
osmo-ggsn.spec])
|
||||
libgtp.pc])
|
||||
AC_OUTPUT
|
||||
|
||||
echo "
|
||||
|
||||
@@ -38,7 +38,6 @@ export PATH="$inst/bin:$PATH"
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
@@ -54,10 +53,11 @@ cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure --enable-sanitize --enable-werror $GTP $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
137
contrib/osmo-ggsn.spec.in
Normal file
137
contrib/osmo-ggsn.spec.in
Normal file
@@ -0,0 +1,137 @@
|
||||
#
|
||||
# spec file for package osmo-ggsn
|
||||
#
|
||||
# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
## Disable LTO for now since it breaks compilation of the tests
|
||||
## https://osmocom.org/issues/4114
|
||||
%define _lto_cflags %{nil}
|
||||
|
||||
Name: osmo-ggsn
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: GPRS Support Node
|
||||
License: GPL-2.0-only AND LGPL-2.1-or-later
|
||||
Group: Productivity/Telephony/Servers
|
||||
URL: https://osmocom.org/projects/openggsn
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libgtpnl) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.8.0
|
||||
Obsoletes: openggsn
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
Osmo-GGSN is a C-language implementation of a GGSN (Gateway GPRS
|
||||
Support Node), a core network element of ETSI/3GPP cellular networks
|
||||
such as GPRS, EDGE, UMTS or HSPA.
|
||||
|
||||
%package -n libgtp6
|
||||
Summary: Library implementing GTP between SGSN and GGSN
|
||||
License: GPL-2.0-only
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libgtp6
|
||||
libgtp implements the GPRS Tunneling Protocol between SGSN and GGSN.
|
||||
|
||||
%package -n libgtp-devel
|
||||
Summary: Development files for the GTP library
|
||||
License: GPL-2.0-only
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libgtp6 = %{version}
|
||||
|
||||
%description -n libgtp-devel
|
||||
libgtp implements the GPRS Tunneling Protocol between SGSN and GGSN.
|
||||
|
||||
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
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--enable-gtp-linux \
|
||||
--disable-static \
|
||||
--docdir="%{_docdir}/%{name}" \
|
||||
--with-systemdsystemunitdir=%{_unitdir} \
|
||||
--includedir="%{_includedir}/%{name}"
|
||||
make %{?_smp_mflags} V=1
|
||||
|
||||
%install
|
||||
%make_install
|
||||
find %{buildroot} -type f -name "*.la" -delete -print
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%pre
|
||||
%service_add_pre %{name}.service
|
||||
|
||||
%post
|
||||
%service_add_post %{name}.service
|
||||
|
||||
%preun
|
||||
%service_del_preun %{name}.service
|
||||
|
||||
%postun
|
||||
%service_del_postun %{name}.service
|
||||
%endif
|
||||
|
||||
%post -n libgtp6 -p /sbin/ldconfig
|
||||
%postun -n libgtp6 -p /sbin/ldconfig
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc AUTHORS README.md
|
||||
%{_bindir}/osmo-ggsn
|
||||
%{_bindir}/sgsnemu
|
||||
%{_mandir}/man8/osmo-ggsn.8%{?ext_man}
|
||||
%{_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
|
||||
|
||||
%files -n libgtp6
|
||||
%{_libdir}/libgtp.so.6*
|
||||
|
||||
%files -n libgtp-devel
|
||||
%{_includedir}/%{name}/
|
||||
%{_libdir}/libgtp.so
|
||||
%{_libdir}/pkgconfig/libgtp.pc
|
||||
|
||||
%files -n gtp-echo-responder
|
||||
%{_bindir}/gtp-echo-responder
|
||||
|
||||
%changelog
|
||||
@@ -1,4 +1,8 @@
|
||||
EXTRA_DIST = osmo-ggsn.service
|
||||
EXTRA_DIST = \
|
||||
osmo-ggsn.service \
|
||||
ggsn.network \
|
||||
apn0.netdev \
|
||||
$(NULL)
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
systemdsystemunit_DATA = \
|
||||
|
||||
7
contrib/systemd/apn0.netdev
Normal file
7
contrib/systemd/apn0.netdev
Normal file
@@ -0,0 +1,7 @@
|
||||
[NetDev]
|
||||
Name=apn0
|
||||
Kind=tun
|
||||
|
||||
[Tun]
|
||||
User=username
|
||||
Group=username
|
||||
6
contrib/systemd/ggsn.network
Normal file
6
contrib/systemd/ggsn.network
Normal file
@@ -0,0 +1,6 @@
|
||||
[Match]
|
||||
Name=apn0
|
||||
|
||||
[Network]
|
||||
Address=192.168.7.1/24
|
||||
IPMasquerade=yes
|
||||
@@ -5,6 +5,8 @@ After=networking.service
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
ExecStart=/usr/bin/osmo-ggsn -c /etc/osmocom/osmo-ggsn.cfg
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
|
||||
296
debian/changelog
vendored
296
debian/changelog
vendored
@@ -1,3 +1,299 @@
|
||||
osmo-ggsn (1.10.1) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
* debian/libgtp6.shlibs: new file
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* lib/icmpv6.h: fix struct icmpv6_{radv_hdr,opt_prefix}
|
||||
* gtp/gsn.c: fix 'No newline at end of file'
|
||||
* gtp: use OSMO_ASSERT() in gtp_new()
|
||||
|
||||
-- Vadim Yanitskiy <vyanitskiy@sysmocom.de> Mon, 27 Feb 2023 22:35:47 +0700
|
||||
|
||||
osmo-ggsn (1.10.0) unstable; urgency=medium
|
||||
|
||||
[ Max ]
|
||||
* Set working directory in systemd service file
|
||||
* Ignore .deb build byproducts
|
||||
* ctrl: take both address and port from vty config
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: gtp: Fix typo in comment
|
||||
* Split gsn_t related APIs out of gtp.{c,h}
|
||||
* Use rate_ctr for gsn_t available_counters
|
||||
* ggsn: Introduce tdef and make it configurable over VTY
|
||||
* gtp: Introduce VTY configurable GTP timer X3
|
||||
* Fix typos in comments and VTY descriptions
|
||||
|
||||
[ arehbein ]
|
||||
* osmo-ggsn: Transition to use of 'telnet_init_default'
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 14:29:48 +0100
|
||||
|
||||
osmo-ggsn (1.9.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* tests: in46a_test: Make coverity happy when calling in46a_from_eua
|
||||
* vty: Fix cmd 'no echo-interval' doing nothing
|
||||
* libgtp: Fix ggsn crash if pdp alloc array is full (PDP_MAX)
|
||||
* libgtp: Define retransmit QUEUE_SIZE relative to PDP_MAX (increase)
|
||||
* gtp: Use switch statement in gtp_create_pdp_ind()
|
||||
* gtp: Log detection of rx duplicate
|
||||
* gtp: Small log improvements in gtp_create_pdp_ind()
|
||||
* gtp: Specify retrans queue name & seqnum in log lines
|
||||
* gtp: Log retrans queue register&free entries
|
||||
* gtp: Fix typo in comment
|
||||
* pco.h: Fix typo in reference to spec
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
|
||||
|
||||
[ Harald Welte ]
|
||||
* update git URLs (git -> https; gitea)
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 17:48:22 +0200
|
||||
|
||||
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 <pespin@sysmocom.de> Tue, 16 Nov 2021 13:49:16 +0100
|
||||
|
||||
osmo-ggsn (1.7.1) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||
* manuals: generate vty reference xml at build time
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 17:31:24 +0100
|
||||
|
||||
osmo-ggsn (1.7.0) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* configure.ac: Fix trailing whitespace
|
||||
* doc: Update VTY reference xml file
|
||||
* Support setting rt-prio and cpu-affinity mask through VTY
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* ggsn: generate coredump and exit upon SIGABRT received
|
||||
* tests: Explicitly drop category from log
|
||||
* tests: Replace deprecated API log_set_print_filename
|
||||
|
||||
[ Keith ]
|
||||
* Fix vty PDP lookups by IMSI
|
||||
* Prevent Crash in show pdp-context from vty
|
||||
* Minor: remove code duplication
|
||||
* Use imsi_str2gtp() in sgsnemu
|
||||
* sgsnemu: relax check on length of IMSI cmdline arg.
|
||||
* GTP: Replace recently introduced imsi_str2gtp()
|
||||
|
||||
[ Harald Welte ]
|
||||
* Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||
* gtp-kernel: Remove duplicate #include section
|
||||
* gtp-kernel: don't #include libmnl headers
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
* apn_start: avoid segfault if missing tun-device
|
||||
* .gitignore: ignore debian/libgtp*
|
||||
* deb/rpm: build with --enable-gtp-linux
|
||||
|
||||
-- Pau Espin Pedrol <pespin@espeweb.net> Tue, 23 Feb 2021 13:34:39 +0100
|
||||
|
||||
osmo-ggsn (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: Fix comment typo
|
||||
* netns: Improve error checking
|
||||
* sgsnemu: cmdline: Drop unused function cmdline_parser_params_create()
|
||||
* sgsnemu: Pass array of in64_addr to in46a_from_eua()
|
||||
* sgsnemu: Rename sgsnemu's libgtp cb_conf
|
||||
* sgsnemu: Set its default loglevel category to INFO
|
||||
* Move icmpv6 and checksum files from ggsn/ dir to lib/
|
||||
* netdev_addaddr6: Use prefixlen arg
|
||||
* sgsnemu: Avoid adding extra autogenerated local link ipv6 addr to tun iface
|
||||
* sgsnemu: Fix ping transmitted statistics output
|
||||
* cosmetic: icmpv6.c: fix typo in comment
|
||||
* icmpv6.c: Mark internal function as static
|
||||
* sgsnemu: Get rid of duplicated options.destaddr
|
||||
* sgsnemu: Get rid of duplicated options.net
|
||||
* sgsnemu: tun_addaddr: Don't set local addr as dstaddr
|
||||
* icmpv6.c: Move code generating ipv6 hdr to its own function
|
||||
* Rename netdev_*route to end in route4
|
||||
* sgsnemu: Fix build/run against linux < 4.11 (no sysctl addr_gen_mode support)
|
||||
* sgsnemu: Handle IPv6 SLAAC in tun iface manually
|
||||
* sgsnemu: Implement ping on IPv6 APNs
|
||||
* sgsnemu: Fix assumption ipv6 Interface-Identifier of public addr == announced Prefix
|
||||
* gtp: queue_test: Fix printf gcc warn under ARM
|
||||
|
||||
[ Andreas Schultz ]
|
||||
* add Linux network namespace support for TUN device
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* lib/netns: fix open_ns(): return fd from open()
|
||||
|
||||
[ Philipp Maier ]
|
||||
* doc: do not use random ip address for dns in default conf
|
||||
* doc: use 127.0.0.2 instead of 127.0.0.6 as bind ip.
|
||||
* debug: use LOGL_NOTICE instead of LOGL_DEBUG
|
||||
|
||||
[ Eric ]
|
||||
* configure.ac: fix libtool issue with clang and sanitizer
|
||||
|
||||
[ Harald Welte ]
|
||||
* lib/netns.c: Add comments to the code, including doxygen API docs
|
||||
* lib/netns: OSMO_ASSERT() if user doesn't call init_netns()
|
||||
* lib/netns: Fix up error paths
|
||||
* example config: use RFC1918 addresses for GGSN pools
|
||||
|
||||
[ Dmitri Kalashnik ]
|
||||
* sgsnemu: use real tun device name after the device is up.
|
||||
|
||||
[ Oliver Smith ]
|
||||
* osmo-ggsn.spec.in: remove
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Thu, 13 Aug 2020 12:26:20 +0200
|
||||
|
||||
osmo-ggsn (1.5.0) unstable; urgency=medium
|
||||
|
||||
[ Jan Engelhardt ]
|
||||
* build: switch AC_CANONICAL_TARGET for AC_CANONICAL_HOST
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* libgtp: Remove packets in tx queue belonging pdp being freed
|
||||
* libgtp: announce pdp ctx deletion upon CreatePdpCtx being rejected
|
||||
* Introduce in46a_is_v{4,6}() helpers
|
||||
* ggsn: Move PCO handling code into its own file
|
||||
* in46_addr: Improve in46a_ntop documentation
|
||||
* ggsn_vty.c: Fix wrong use of in46a_from_eua, print IPv6 euas
|
||||
* ggsn: Split application lifecycle related code into ggsn_main.c
|
||||
* Move pdp_get_peer_ipv() to lib/util.*
|
||||
* gtp-kernel.c: Fix wrong use of in46a_from_eua, print IPv6 euas
|
||||
* Introduce LOGTUN log helper
|
||||
* ggsn_vty.c: Avoid printing duplicates for pdp context with v4v6 EUAs
|
||||
* pdp: constify param in pdp_count_secondary()
|
||||
* ggsn_vty.c: Improve output of VTY show pdp-context
|
||||
* doc: Update vty reference xml file
|
||||
* libgtp: Introduce cb_recovery3
|
||||
* ggsn: Implement echo req/resp and recovery
|
||||
* cosmetic: fix formatting in if line
|
||||
* gtp: Log msg retransmits and timeouts
|
||||
* cosmetic: gtp: Drop commented out code calling pdp_freepdp()
|
||||
* cosmetic: gtp: Improve documentation of gtp_delete_context_req2()
|
||||
* ggsn: rx DeletePdpReq confirmation: Improve documentation and use gtp_freepdp()
|
||||
* gtp: Manage queue timers internally
|
||||
* ggsn, sgsnemu: Drop use of no-op deprecated gtp_retrans* APIs
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* gtp_update_pdp_ind(): fix NULL-pointer dereference
|
||||
* gtp_error_ind_conf(): fix: guard against an unknown GTP version
|
||||
* gtp/gtp.c: cosmetic: use get_tid() where we need TID
|
||||
* manuals/configuration.adoc: fix Network Address without prefix length
|
||||
* manuals/configuration.adoc: fix IPv4 address mismatch in <<ggsn_no_root>>
|
||||
* contrib/systemd: add systemd-networkd examples from manuals
|
||||
|
||||
[ Harald Welte ]
|
||||
* sgsnemu: Fix null-pointer format string argument
|
||||
* manual: Fix copy+paste error
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 02 Jan 2020 20:39:39 +0100
|
||||
|
||||
osmo-ggsn (1.4.0) unstable; urgency=medium
|
||||
|
||||
[ Max ]
|
||||
* Don't return error on normal shutdown
|
||||
|
||||
[ Harald Welte ]
|
||||
* process_pco() const-ify 'apn' argument
|
||||
* ggsn: Remove magic numbers from pco_contains_proto()
|
||||
* ggsn: const-ify input / read-only arguments of PCO related functions
|
||||
* ggsn: Remove magic numbers from ipcp_contains_option()
|
||||
* ggsn: Fix build_ipcp_pco() in presence of invalid IPCP content
|
||||
* ggsn.c: Refactor PCO processing during PDP activation
|
||||
* ggsn: Add minimalistic PAP support
|
||||
* ggsn: More logging from PCO handling (e.g. in case of malconfiguration)
|
||||
* sgsnemu: Fix format string argument count
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* osmo-ggsn: fix VTY command for getting PDP contexts by APN
|
||||
* osmo-ggsn: add VTY command to show PDP context by IPv4
|
||||
* osmo-ggsn: check result of osmo_apn_to_str()
|
||||
* osmo-ggsn: print requested / actual APN in PDP info
|
||||
* osmo-ggsn: properly show subscriber's MSISDN in the VTY
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* ggsn: Drop unused param force in apn_stop()
|
||||
* gtp: Document spec reasoning drop of Rx DeleteCtxReq
|
||||
* ggsn: Start gtp retrans timer during startup
|
||||
* gtp: Take queue_resp into account to schedule retrans timer
|
||||
* gtp: Fix typo dublicate->duplicate
|
||||
* pdp: Introduce new API pdp_count_secondary
|
||||
* gtp_create_pdp_ind: simplify code by reordering and compacting parsing
|
||||
* gtp: Refactor code to use gtp_freepdp(_teardown) APIs
|
||||
* cosmetic: gtp: Document free pdp ctx in non-teardown scenario
|
||||
* gtp: Re-arrange free pdp ctx code in non-teardown scenario
|
||||
* pdp: Drop unused code for haship
|
||||
* cosmetic: gtp.h: Remove trailing whitespaces
|
||||
* ggsn: Fix undefined behaviour shifting beyond sign bit
|
||||
* gtp: Introduce new pdp APIs (and deprecate old ones) to support multiple GSN
|
||||
* gtp: Make use of new libgtp APIs with multi-gsn support
|
||||
* ggsn_vty_reference.xml: Update from last code changes
|
||||
* ggsn: vty: Require ggsn param in <show pdp-context> cmd
|
||||
* sgsnemu: Replace use of deprecated libgtp API pdp_newpdp with new one
|
||||
* cosmetic: gtp: queue: remove trailing whitespace
|
||||
* gtp: Add missing headers
|
||||
* gtp: queue.c: Document queue APIs
|
||||
* gtp: queue: Add unit test queue_test
|
||||
* ggsn: Avoid unaligned mem access reading PCO proto id
|
||||
* ggsn: Use structures instead of raw arrays when parsing ipcp_hdr
|
||||
* configure.ac: Replace obosolete macro AC_CANONICAL_SYSTEM
|
||||
* configure.ac: Use brackets in AC_INIT params
|
||||
* configure.ac: Use prefered AC_CONFIG_HEADERS over AM_CONFIG_HEADER
|
||||
* configure.ac: some versions of linux/if.h require including sys/socket.h
|
||||
* sgsnemu: Fix unaligned pointer access during ip/icmp checksum
|
||||
* Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
|
||||
* Require libosmocore 1.1.0
|
||||
|
||||
[ Oliver Smith ]
|
||||
* debian: create -doc subpackage with pdf manuals
|
||||
* ggsn: Use gtp_delete_context_req2() everywhere
|
||||
* contrib/jenkins.sh: run "make maintainer-clean"
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* manuals: Add script to regenerate vty/counter documentation
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 21:28:30 +0200
|
||||
|
||||
osmo-ggsn (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
|
||||
42
debian/control
vendored
42
debian/control
vendored
@@ -1,5 +1,5 @@
|
||||
Source: osmo-ggsn
|
||||
Maintainer: Harald Welte <laforge@gnumonks.org>
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Section: net
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>= 9),
|
||||
@@ -7,10 +7,12 @@ Build-Depends: debhelper (>= 9),
|
||||
pkg-config,
|
||||
libdpkg-perl, git,
|
||||
dh-autoreconf,
|
||||
libosmocore-dev (>= 0.8.0)
|
||||
libosmocore-dev (>= 1.8.0),
|
||||
osmo-gsm-manuals-dev,
|
||||
libgtpnl-dev (>= 1.2.0)
|
||||
Standards-Version: 3.9.6
|
||||
Vcs-Browser: http://git.osmocom.org/osmo-ggsn/
|
||||
Vcs-Git: git://git.osmocom.org/osmo-ggsn
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-ggsn
|
||||
Homepage: https://projects.osmocom.org/projects/openggsn
|
||||
|
||||
Package: osmo-ggsn
|
||||
@@ -22,7 +24,7 @@ Description: Osmocom Gateway GPRS Support Node (GGSN)
|
||||
operators as the interface between the Internet and the rest of the
|
||||
mobile network infrastructure.
|
||||
|
||||
Package: libgtp4
|
||||
Package: libgtp6
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libs
|
||||
@@ -36,12 +38,18 @@ Description: library implementing the GTP protocol between SGSN and GGSN
|
||||
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
|
||||
Section: libdevel
|
||||
Depends: ${misc:Depends},
|
||||
libgtp4 (= ${binary:Version})
|
||||
libgtp6 (= ${binary:Version})
|
||||
Description: Development files for libgtp
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
operators as the interface between the Internet and the rest of the
|
||||
@@ -54,18 +62,27 @@ Package: osmo-ggsn-dbg
|
||||
Section: debug
|
||||
Architecture: any
|
||||
Priority: extra
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp4 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp6 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
|
||||
Multi-Arch: same
|
||||
Description: Debug symbols for OsmoGGSN
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
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
|
||||
Priority: extra
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp4 (= ${binary:Version})
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp6 (= ${binary:Version})
|
||||
Multi-Arch: same
|
||||
Description: Debug symbols for OsmoGGSN
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
@@ -74,3 +91,12 @@ Description: Debug symbols for OsmoGGSN
|
||||
.
|
||||
The library libgtp implements the GTP protocol between SGSN and GGSN
|
||||
and this package contains the development files for this library.
|
||||
|
||||
Package: osmo-ggsn-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
Priority: optional
|
||||
Depends: ${misc:Depends}
|
||||
Description: ${misc:Package} PDF documentation
|
||||
Various manuals: user manual, VTY reference manual and/or
|
||||
protocol/interface manuals.
|
||||
|
||||
5
debian/copyright
vendored
5
debian/copyright
vendored
@@ -16,6 +16,11 @@ Files: lib/getopt.c
|
||||
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 <info@sysmocom.de>
|
||||
License: MIT
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2010-2017 Harald Welte <laforge@gnumonks.org>
|
||||
2016 Ruben Undheim <ruben.undheim@gmail.com>
|
||||
|
||||
1
debian/gtp-echo-responder.install
vendored
Normal file
1
debian/gtp-echo-responder.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/usr/bin/gtp-echo-responder
|
||||
2
debian/libgtp6.shlibs
vendored
Normal file
2
debian/libgtp6.shlibs
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Most recent version of the package that added new symbols (OS#5318)
|
||||
libgtp 6 libgtp6 (>= 1.8.0)
|
||||
1
debian/osmo-ggsn-doc.install
vendored
Normal file
1
debian/osmo-ggsn-doc.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
usr/share/doc/osmo-ggsn-doc/*.pdf
|
||||
1
debian/osmo-ggsn.examples
vendored
1
debian/osmo-ggsn.examples
vendored
@@ -1,2 +1,3 @@
|
||||
doc/examples/osmo-ggsn.cfg
|
||||
doc/examples/osmo-ggsn-kernel-gtp.cfg
|
||||
doc/examples/sgsnemu.conf
|
||||
|
||||
11
debian/rules
vendored
11
debian/rules
vendored
@@ -16,7 +16,14 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg
|
||||
dh_strip -plibgtp4 --dbg-package=libgtp-dbg
|
||||
dh_strip -plibgtp6 --dbg-package=libgtp-dbg
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
|
||||
dh_auto_configure -- \
|
||||
--enable-gtp-linux \
|
||||
--with-systemdsystemunitdir=/lib/systemd/system \
|
||||
--enable-manuals
|
||||
|
||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
|
||||
override_dh_compress:
|
||||
dh_compress -X.pdf
|
||||
|
||||
@@ -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 \
|
||||
|
||||
51
doc/examples/osmo-ggsn-kernel-gtp.cfg
Normal file
51
doc/examples/osmo-ggsn-kernel-gtp.cfg
Normal file
@@ -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
|
||||
@@ -37,15 +37,15 @@ line vty
|
||||
!
|
||||
ggsn ggsn0
|
||||
gtp state-dir /tmp
|
||||
gtp bind-ip 127.0.0.6
|
||||
gtp bind-ip 127.0.0.2
|
||||
apn internet
|
||||
gtpu-mode tun
|
||||
tun-device tun4
|
||||
type-support v4
|
||||
ip prefix dynamic 176.16.222.0/24
|
||||
ip dns 0 192.168.100.1
|
||||
ip dns 1 8.8.8.8
|
||||
ip ifconfig 176.16.222.0/24
|
||||
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
|
||||
apn inet6
|
||||
gtpu-mode tun
|
||||
@@ -60,10 +60,10 @@ ggsn ggsn0
|
||||
gtpu-mode tun
|
||||
tun-device tun46
|
||||
type-support v4v6
|
||||
ip prefix dynamic 176.16.46.0/24
|
||||
ip dns 0 192.168.100.1
|
||||
ip dns 1 8.8.8.8
|
||||
ip ifconfig 176.16.46.0/24
|
||||
ip prefix dynamic 172.16.46.0/24
|
||||
ip dns 0 8.8.8.8
|
||||
ip dns 1 8.8.4.4
|
||||
ip ifconfig 172.16.46.0/24
|
||||
ipv6 prefix dynamic 2001:780:44:2100:0:0:0:0/56
|
||||
ipv6 dns 0 2001:4860:4860::8888
|
||||
ipv6 dns 1 2001:4860:4860::8844
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
EXTRA_DIST = osmoggsn-usermanual.adoc \
|
||||
osmoggsn-usermanual-docinfo.xml \
|
||||
osmoggsn-vty-reference.xml \
|
||||
regen_doc.sh \
|
||||
chapters \
|
||||
vty
|
||||
|
||||
@@ -10,7 +11,14 @@ if BUILD_MANUALS
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||
|
||||
VTY_REFERENCE = osmoggsn-vty-reference.xml
|
||||
|
||||
BUILT_REFERENCE_XML = $(builddir)/vty/ggsn_vty_reference.xml
|
||||
$(builddir)/vty/ggsn_vty_reference.xml: $(top_builddir)/ggsn/osmo-ggsn
|
||||
mkdir -p $(builddir)/vty
|
||||
$(top_builddir)/ggsn/osmo-ggsn --vty-ref-xml > $@
|
||||
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
OSMO_REPOSITORY=osmo-ggsn
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||
endif
|
||||
|
||||
@@ -270,7 +270,7 @@ using standard means like `ip addr add` or your distribution-specific utilities/
|
||||
to match the `ip prefix dynamic` config item, and activate the link, for example:
|
||||
|
||||
----
|
||||
# ip addr add 192.168.7.0/24 dev apn0
|
||||
# ip addr add 192.168.7.1/24 dev apn0
|
||||
# ip link set apn0 up
|
||||
----
|
||||
|
||||
@@ -299,7 +299,7 @@ Group=username <3>
|
||||
Name=apn0 <1>
|
||||
|
||||
[Network]
|
||||
Address=192.168.7.1 <2>
|
||||
Address=192.168.7.1/24 <2>
|
||||
IPMasquerade=yes <3>
|
||||
----
|
||||
<1> The netowrk device name, which must match the one in the apn0.netdev unit file above
|
||||
|
||||
@@ -12,7 +12,7 @@ arguments:
|
||||
*-h, --help*::
|
||||
Print a short help message about the supported options
|
||||
*-V, --version*::
|
||||
Print the compile-time version number of the OsmoBTS program
|
||||
Print the compile-time version number of the program
|
||||
*-D, --daemonize*::
|
||||
Fork the process as a daemon into background.
|
||||
*-c, --config-file 'CONFIGFILE'*::
|
||||
@@ -80,3 +80,47 @@ possible to pick differing ports on the same IP address), like:
|
||||
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
|
||||
----
|
||||
|
||||
@@ -20,6 +20,8 @@ include::{srcdir}/chapters/configuration.adoc[]
|
||||
|
||||
include::./common/chapters/control_if.adoc[]
|
||||
|
||||
include::./common/chapters/vty_cpu_sched.adoc[]
|
||||
|
||||
include::./common/chapters/port_numbers.adoc[]
|
||||
|
||||
include::./common/chapters/bibliography.adoc[]
|
||||
|
||||
17
doc/manuals/regen_doc.sh
Executable file
17
doc/manuals/regen_doc.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
if [ -z "$DOCKER_PLAYGROUND" ]; then
|
||||
echo "You need to set DOCKER_PLAYGROUND"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT=$(realpath "$0")
|
||||
MANUAL_DIR=$(dirname "$SCRIPT")
|
||||
|
||||
COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
|
||||
|
||||
cd "$DOCKER_PLAYGROUND/scripts" || exit 1
|
||||
|
||||
OSMO_GGSN_BRANCH=$COMMIT ./regen_doc.sh osmo-ggsn 4260 \
|
||||
"$MANUAL_DIR/chapters/counters_generated.adoc" \
|
||||
"$MANUAL_DIR/vty/ggsn_vty_reference.xml"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,4 +12,4 @@ osmo_ggsn_LDADD += $(LIBGTPNL_LIBS)
|
||||
endif
|
||||
|
||||
osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
|
||||
osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h
|
||||
osmo_ggsn_SOURCES = ggsn_main.c ggsn_vty.c ggsn.c ggsn.h sgsn.c sgsn.h pco.c pco.h
|
||||
|
||||
640
ggsn/ggsn.c
640
ggsn/ggsn.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2017-2019 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* The contents of this file may be used under the terms of the GNU
|
||||
* General Public License Version 2, provided that the above copyright
|
||||
@@ -42,21 +42,8 @@
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
|
||||
#include "../lib/tun.h"
|
||||
@@ -64,31 +51,26 @@
|
||||
#include "../lib/syserr.h"
|
||||
#include "../lib/in46_addr.h"
|
||||
#include "../lib/gtp-kernel.h"
|
||||
#include "../lib/util.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include "../gtp/gtp.h"
|
||||
#include "icmpv6.h"
|
||||
#include "../lib/icmpv6.h"
|
||||
#include "pco.h"
|
||||
#include "ggsn.h"
|
||||
|
||||
void *tall_ggsn_ctx;
|
||||
|
||||
static int end = 0;
|
||||
static int daemonize = 0;
|
||||
static struct ctrl_handle *g_ctrlh;
|
||||
|
||||
struct ul255_t qos;
|
||||
struct ul255_t apn;
|
||||
|
||||
#define LOGPAPN(level, apn, fmt, args...) \
|
||||
LOGP(DGGSN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)
|
||||
|
||||
#define LOGPGGSN(level, ggsn, fmt, args...) \
|
||||
LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
|
||||
|
||||
#define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## args)
|
||||
|
||||
static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
|
||||
static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);
|
||||
|
||||
void ggsn_close_one_pdp(struct pdp_t *pdp)
|
||||
{
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n");
|
||||
gtp_delete_context_req2(pdp->gsn, pdp, NULL, 1);
|
||||
/* We have nothing more to do with pdp ctx, free it. Upon cb_delete_context
|
||||
called during this call we'll clean up ggsn related stuff attached to this
|
||||
pdp context. After this call, ippool member is cleared so
|
||||
data is no longer valid and should not be accessed anymore. */
|
||||
gtp_freepdp_teardown(pdp->gsn, pdp);
|
||||
}
|
||||
|
||||
static void pool_close_all_pdp(struct ippool_t *pool)
|
||||
{
|
||||
@@ -106,14 +88,13 @@ static void pool_close_all_pdp(struct ippool_t *pool)
|
||||
pdp = member->peer;
|
||||
if (!pdp)
|
||||
continue;
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n");
|
||||
gtp_delete_context_req(pdp->gsn, pdp, NULL, 1);
|
||||
ggsn_close_one_pdp(pdp);
|
||||
}
|
||||
}
|
||||
|
||||
int apn_stop(struct apn_ctx *apn, bool force)
|
||||
int apn_stop(struct apn_ctx *apn)
|
||||
{
|
||||
LOGPAPN(LOGL_NOTICE, apn, "%sStopping\n", force ? "FORCED " : "");
|
||||
LOGPAPN(LOGL_NOTICE, apn, "Stopping\n");
|
||||
/* check if pools have any active PDP contexts and bail out */
|
||||
pool_close_all_pdp(apn->v4.pool);
|
||||
pool_close_all_pdp(apn->v6.pool);
|
||||
@@ -213,7 +194,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", apn->tun.tun->devname);
|
||||
|
||||
/* Register with libosmcoore */
|
||||
osmo_fd_setup(&apn->tun.fd, apn->tun.tun->fd, BSC_FD_READ, ggsn_tun_fd_cb, apn, 0);
|
||||
osmo_fd_setup(&apn->tun.fd, apn->tun.tun->fd, OSMO_FD_READ, ggsn_tun_fd_cb, apn, 0);
|
||||
osmo_fd_register(&apn->tun.fd);
|
||||
|
||||
/* Set TUN library callback */
|
||||
@@ -223,7 +204,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name);
|
||||
if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Kernel GTP currently supports only IPv4\n");
|
||||
apn_stop(apn, false);
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
if (gsn == NULL) {
|
||||
@@ -257,7 +238,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
apn->v4.cfg.ifconfig_prefix.prefixlen)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
|
||||
in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -270,7 +251,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
|
||||
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -283,7 +264,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 link-local address %s: %s. "
|
||||
"Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
|
||||
in46p_ntoa(&apn->v6.cfg.ll_prefix), strerror(errno));
|
||||
apn_stop(apn, false);
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
|
||||
@@ -301,7 +282,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
if (rc < 1) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
|
||||
rc ? strerror(errno) : "tun interface has no link-local IP assigned");
|
||||
apn_stop(apn, false);
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
|
||||
@@ -318,7 +299,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
blacklist, blacklist_size)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv4 pool\n");
|
||||
talloc_free(blacklist);
|
||||
apn_stop(apn, false);
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
talloc_free(blacklist);
|
||||
@@ -335,7 +316,7 @@ int apn_start(struct apn_ctx *apn)
|
||||
blacklist, blacklist_size)) {
|
||||
LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv6 pool\n");
|
||||
talloc_free(blacklist);
|
||||
apn_stop(apn, false);
|
||||
apn_stop(apn);
|
||||
return -1;
|
||||
}
|
||||
talloc_free(blacklist);
|
||||
@@ -365,7 +346,8 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st
|
||||
static int delete_context(struct pdp_t *pdp)
|
||||
{
|
||||
struct gsn_t *gsn = pdp->gsn;
|
||||
struct apn_ctx *apn = pdp->priv;
|
||||
struct pdp_priv_t *pdp_priv = pdp->priv;
|
||||
struct apn_ctx *apn;
|
||||
struct ippoolm_t *member;
|
||||
int i;
|
||||
|
||||
@@ -376,228 +358,32 @@ static int delete_context(struct pdp_t *pdp)
|
||||
member = pdp->peer[i];
|
||||
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
|
||||
ippool_freeip(member->pool, member);
|
||||
} else if(i == 0)
|
||||
} else if (i == 0) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {
|
||||
if (!pdp_priv) {
|
||||
LOGPPDP(LOGL_NOTICE, pdp, "Deleting PDP context: without private structure!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove from SGSN */
|
||||
sgsn_peer_remove_pdp_priv(pdp_priv);
|
||||
|
||||
apn = pdp_priv->apn;
|
||||
if (apn && apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {
|
||||
if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(pdp_priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
/* RFC 1332 */
|
||||
enum ipcp_options {
|
||||
IPCP_OPT_IPADDR = 3,
|
||||
IPCP_OPT_PRIMARY_DNS = 129,
|
||||
IPCP_OPT_SECONDARY_DNS = 131,
|
||||
};
|
||||
|
||||
struct ipcp_option_hdr {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ipcp_hdr {
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint8_t options[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* determine if IPCP contains given option */
|
||||
static uint8_t *ipcp_contains_option(uint8_t *ipcp, size_t ipcp_len, enum ipcp_options opt, size_t opt_minlen)
|
||||
{
|
||||
uint8_t *cur_opt = ipcp + sizeof(struct ipcp_hdr);
|
||||
|
||||
/* iterate over Options and check if protocol contained */
|
||||
while (cur_opt + 2 <= ipcp + ipcp_len) {
|
||||
uint8_t type = cur_opt[0];
|
||||
uint8_t len = cur_opt[1]; /* length value includes 2 bytes type/length */
|
||||
if (len < 2)
|
||||
return NULL;
|
||||
if (type == opt && len >= 2 + opt_minlen)
|
||||
return cur_opt;
|
||||
cur_opt += len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 10.6.5.3 */
|
||||
enum pco_protocols {
|
||||
PCO_P_LCP = 0xC021,
|
||||
PCO_P_PAP = 0xC023,
|
||||
PCO_P_CHAP = 0xC223,
|
||||
PCO_P_IPCP = 0x8021,
|
||||
PCO_P_PCSCF_ADDR = 0x0001,
|
||||
PCO_P_IM_CN_SS_F = 0x0002,
|
||||
PCO_P_DNS_IPv6_ADDR = 0x0003,
|
||||
PCO_P_POLICY_CTRL_REJ = 0x0004, /* only in Network->MS */
|
||||
PCO_P_MS_SUP_NETREQ_BCI = 0x0005,
|
||||
/* reserved */
|
||||
PCO_P_DSMIPv6_HA_ADDR = 0x0007,
|
||||
PCO_P_DSMIPv6_HN_PREF = 0x0008,
|
||||
PCO_P_DSMIPv6_v4_HA_ADDR= 0x0009,
|
||||
PCO_P_IP_ADDR_VIA_NAS = 0x000a, /* only MS->Network */
|
||||
PCO_P_IPv4_ADDR_VIA_DHCP= 0x000b, /* only MS->Netowrk */
|
||||
PCO_P_PCSCF_IPv4_ADDR = 0x000c,
|
||||
PCO_P_DNS_IPv4_ADDR = 0x000d,
|
||||
PCO_P_MSISDN = 0x000e,
|
||||
PCO_P_IFOM_SUPPORT = 0x000f,
|
||||
PCO_P_IPv4_LINK_MTU = 0x0010,
|
||||
PCO_P_MS_SUPP_LOC_A_TFT = 0x0011,
|
||||
PCO_P_PCSCF_RESEL_SUP = 0x0012, /* only MS->Network */
|
||||
PCO_P_NBIFOM_REQ = 0x0013,
|
||||
PCO_P_NBIFOM_MODE = 0x0014,
|
||||
PCO_P_NONIP_LINK_MTU = 0x0015,
|
||||
PCO_P_APN_RATE_CTRL_SUP = 0x0016,
|
||||
PCO_P_PS_DATA_OFF_UE = 0x0017,
|
||||
PCO_P_REL_DATA_SVC = 0x0018,
|
||||
};
|
||||
|
||||
/* determine if PCO contains given protocol */
|
||||
static uint8_t *pco_contains_proto(struct ul255_t *pco, size_t offset, uint16_t prot, size_t prot_minlen)
|
||||
{
|
||||
uint8_t *cur = pco->v + 1 + offset;
|
||||
|
||||
/* iterate over PCO and check if protocol contained */
|
||||
while (cur + 3 <= pco->v + pco->l) {
|
||||
uint16_t cur_prot = osmo_load16be(cur);
|
||||
uint8_t cur_len = cur[2];
|
||||
if (cur_prot == prot && cur_len >= prot_minlen)
|
||||
return cur;
|
||||
cur += cur_len + 3;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Get the peer of pdp based on IP version used.
|
||||
* \param[in] pdp PDP context to select the peer from.
|
||||
* \param[in] v4v6 IP version to select. Valid values are 4 and 6.
|
||||
* \returns The selected peer matching the given IP version. NULL if not present.
|
||||
*/
|
||||
static struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) {
|
||||
uint8_t len1, len2, i;
|
||||
|
||||
if (is_ipv6) {
|
||||
len1 = 8;
|
||||
len2 = 16;
|
||||
} else {
|
||||
len1 = sizeof(struct in_addr);
|
||||
len2 = len1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct ippoolm_t * ippool = pdp->peer[i];
|
||||
if (ippool && (ippool->addr.len == len1 || ippool->addr.len == len2))
|
||||
return ippool;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* construct an IPCP PCO response from request*/
|
||||
static void build_ipcp_pco(struct apn_ctx *apn, struct pdp_t *pdp, struct msgb *msg)
|
||||
{
|
||||
const struct in46_addr *dns1 = &apn->v4.cfg.dns[0];
|
||||
const struct in46_addr *dns2 = &apn->v4.cfg.dns[1];
|
||||
uint8_t *ipcp;
|
||||
uint16_t ipcp_len;
|
||||
uint8_t *len1, *len2, *pco_ipcp;
|
||||
unsigned int len_appended;
|
||||
ptrdiff_t consumed;
|
||||
size_t remain, offset = 0;
|
||||
|
||||
/* pco_contains_proto() returns a potentially unaligned pointer into pco_req->v (see OS#3194) */
|
||||
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
|
||||
while (pco_ipcp) {
|
||||
uint8_t *start = msg->tail;
|
||||
|
||||
ipcp = (pco_ipcp + 3); /* 2=type + 1=len */
|
||||
consumed = (ipcp - &pdp->pco_req.v[0]);
|
||||
remain = sizeof(pdp->pco_req.v) - consumed;
|
||||
ipcp_len = osmo_load16be(ipcp + 2); /* 1=code + 1=id */
|
||||
if (remain < 0 || remain < ipcp_len)
|
||||
return;
|
||||
|
||||
/* Three byte T16L header */
|
||||
msgb_put_u16(msg, 0x8021); /* IPCP */
|
||||
len1 = msgb_put(msg, 1); /* Length of contents: delay */
|
||||
|
||||
msgb_put_u8(msg, 0x02); /* ACK */
|
||||
msgb_put_u8(msg, ipcp[1]); /* ID: Needs to match request */
|
||||
msgb_put_u8(msg, 0x00); /* Length MSB */
|
||||
len2 = msgb_put(msg, 1); /* Length LSB: delay */
|
||||
|
||||
if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) {
|
||||
msgb_put_u8(msg, 0x81); /* DNS1 Tag */
|
||||
msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */
|
||||
msgb_put_u32(msg, ntohl(dns1->v4.s_addr));
|
||||
}
|
||||
|
||||
if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) {
|
||||
msgb_put_u8(msg, 0x83); /* DNS2 Tag */
|
||||
msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */
|
||||
msgb_put_u32(msg, ntohl(dns2->v4.s_addr));
|
||||
}
|
||||
|
||||
/* patch in length values */
|
||||
len_appended = msg->tail - start;
|
||||
*len1 = len_appended - 3;
|
||||
*len2 = len_appended - 3;
|
||||
|
||||
offset += 3 + ipcp_len;
|
||||
pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* process one PCO request from a MS/UE, putting together the proper responses */
|
||||
static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(256, "PCO");
|
||||
struct ippoolm_t *peer_v4 = pdp_get_peer_ipv(pdp, false);
|
||||
unsigned int i;
|
||||
|
||||
OSMO_ASSERT(msg);
|
||||
msgb_put_u8(msg, 0x80); /* ext-bit + configuration protocol byte */
|
||||
|
||||
if (peer_v4)
|
||||
build_ipcp_pco(apn, pdp, msg);
|
||||
|
||||
if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv6_ADDR, 0)) {
|
||||
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
|
||||
struct in46_addr *i46a = &apn->v6.cfg.dns[i];
|
||||
if (i46a->len != 16)
|
||||
continue;
|
||||
msgb_t16lv_put(msg, PCO_P_DNS_IPv6_ADDR, i46a->len, i46a->v6.s6_addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv4_ADDR, 0)) {
|
||||
for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
|
||||
struct in46_addr *i46a = &apn->v4.cfg.dns[i];
|
||||
if (i46a->len != 4)
|
||||
continue;
|
||||
msgb_t16lv_put(msg, PCO_P_DNS_IPv4_ADDR, i46a->len, (uint8_t *)&i46a->v4);
|
||||
}
|
||||
}
|
||||
|
||||
if (msgb_length(msg) > 1) {
|
||||
memcpy(pdp->pco_neg.v, msgb_data(msg), msgb_length(msg));
|
||||
pdp->pco_neg.l = msgb_length(msg);
|
||||
} else
|
||||
pdp->pco_neg.l = 0;
|
||||
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static bool apn_supports_ipv4(const struct apn_ctx *apn)
|
||||
{
|
||||
if (apn->v4.cfg.static_prefix.addr.len || apn->v4.cfg.dynamic_prefix.addr.len)
|
||||
@@ -612,6 +398,36 @@ static bool apn_supports_ipv6(const struct apn_ctx *apn)
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct sgsn_peer* ggsn_find_sgsn(struct ggsn_ctx *ggsn, struct in_addr *peer_addr)
|
||||
{
|
||||
struct sgsn_peer *sgsn;
|
||||
|
||||
llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) {
|
||||
if (memcmp(&sgsn->addr, peer_addr, sizeof(*peer_addr)) == 0)
|
||||
return sgsn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct sgsn_peer* ggsn_find_or_create_sgsn(struct ggsn_ctx *ggsn, struct pdp_t *pdp)
|
||||
{
|
||||
struct sgsn_peer *sgsn;
|
||||
struct in_addr ia;
|
||||
|
||||
if (gsna2in_addr(&ia, &pdp->gsnrc)) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Failed parsing gsnrc (len=%u) to discover SGSN\n",
|
||||
pdp->gsnrc.l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((sgsn = ggsn_find_sgsn(ggsn, &ia)))
|
||||
return sgsn;
|
||||
|
||||
sgsn = sgsn_peer_allocate(ggsn, &ia, pdp->version);
|
||||
llist_add(&sgsn->entry, &ggsn->sgsn_list);
|
||||
return sgsn;
|
||||
}
|
||||
|
||||
int create_context_ind(struct pdp_t *pdp)
|
||||
{
|
||||
static char name_buf[256];
|
||||
@@ -620,15 +436,19 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
struct in46_addr addr[2];
|
||||
struct ippoolm_t *member = NULL, *addrv4 = NULL, *addrv6 = NULL;
|
||||
char straddrv4[INET_ADDRSTRLEN], straddrv6[INET6_ADDRSTRLEN];
|
||||
struct apn_ctx *apn;
|
||||
struct apn_ctx *apn = NULL;
|
||||
int rc, num_addr, i;
|
||||
char *apn_name;
|
||||
struct sgsn_peer *sgsn;
|
||||
struct pdp_priv_t *pdp_priv;
|
||||
|
||||
osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
|
||||
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "Processing create PDP context request for APN '%s'\n", name_buf);
|
||||
apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "Processing create PDP context request for APN '%s'\n",
|
||||
apn_name ? name_buf : "(NONE)");
|
||||
|
||||
/* First find an exact APN name match */
|
||||
apn = ggsn_find_apn(ggsn, name_buf);
|
||||
if (apn_name != NULL)
|
||||
apn = ggsn_find_apn(ggsn, name_buf);
|
||||
/* ignore if the APN has not been started */
|
||||
if (apn && !apn->started)
|
||||
apn = NULL;
|
||||
@@ -647,9 +467,13 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
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));
|
||||
|
||||
@@ -664,9 +488,15 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store the actual APN for logging and the VTY */
|
||||
rc = osmo_apn_from_str(pdp->apn_use.v, sizeof(pdp->apn_use.v), apn->cfg.name);
|
||||
if (rc < 0) /* Unlikely this would happen, but anyway... */
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Failed to store APN '%s'\n", apn->cfg.name);
|
||||
pdp->apn_use.l = rc;
|
||||
|
||||
/* Allocate dynamic addresses from the pool */
|
||||
for (i = 0; i < num_addr; i++) {
|
||||
if (addr[i].len == sizeof(struct in_addr)) {
|
||||
if (in46a_is_v4(&addr[i])) {
|
||||
/* does this APN actually have an IPv4 pool? */
|
||||
if (!apn_supports_ipv4(apn))
|
||||
goto err_wrong_af;
|
||||
@@ -679,7 +509,7 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
|
||||
addrv4 = member;
|
||||
|
||||
} else if (addr[i].len == sizeof(struct in6_addr)) {
|
||||
} else if (in46a_is_v6(&addr[i])) {
|
||||
|
||||
/* does this APN actually have an IPv6 pool? */
|
||||
if (!apn_supports_ipv6(apn))
|
||||
@@ -716,7 +546,14 @@ int create_context_ind(struct pdp_t *pdp)
|
||||
}
|
||||
|
||||
pdp->ipif = apn->tun.tun; /* TODO */
|
||||
pdp->priv = apn;
|
||||
|
||||
pdp_priv = talloc_zero(ggsn, struct pdp_priv_t);
|
||||
pdp->priv = pdp_priv;
|
||||
pdp_priv->lib = pdp;
|
||||
/* Create sgsn and assign pdp to it */
|
||||
sgsn = ggsn_find_or_create_sgsn(ggsn, pdp);
|
||||
sgsn_peer_add_pdp_priv(sgsn, pdp_priv);
|
||||
pdp_priv->apn = apn;
|
||||
|
||||
/* TODO: change trap to send 2 IPs */
|
||||
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
|
||||
@@ -756,7 +593,7 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||
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) {
|
||||
@@ -780,7 +617,7 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||
pool = apn->v6.pool;
|
||||
break;
|
||||
default:
|
||||
LOGP(DTUN, LOGL_NOTICE, "non-IPv%u packet received from tun\n", iph->version);
|
||||
LOGTUN(LOGL_NOTICE, tun, "non-IPv%u packet received\n", iph->version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -788,26 +625,45 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
||||
if (!pool)
|
||||
return 0;
|
||||
|
||||
DEBUGP(DTUN, "Received packet for APN(%s) from tun %s", apn->cfg.name, tun->devname);
|
||||
|
||||
if (ippool_getip(pool, &ipm, &dst)) {
|
||||
DEBUGPC(DTUN, " with no PDP contex! (%s)\n", 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;
|
||||
}
|
||||
DEBUGPC(DTUN, "\n");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* RFC3307 link-local scope multicast address */
|
||||
static const struct in6_addr all_router_mcast_addr = {
|
||||
.s6_addr = { 0xff,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2 }
|
||||
};
|
||||
|
||||
/* MS-originated GTP1-U packet, needs to be sent via TUN device */
|
||||
static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
{
|
||||
@@ -871,14 +727,12 @@ static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
|
||||
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
|
||||
}
|
||||
|
||||
static char *config_file = "osmo-ggsn.cfg";
|
||||
|
||||
/* callback for tun device osmocom select loop integration */
|
||||
static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct apn_ctx *apn = fd->data;
|
||||
|
||||
OSMO_ASSERT(what & BSC_FD_READ);
|
||||
OSMO_ASSERT(what & OSMO_FD_READ);
|
||||
|
||||
return tun_decaps(apn->tun.tun);
|
||||
}
|
||||
@@ -889,7 +743,7 @@ static int ggsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
|
||||
struct ggsn_ctx *ggsn = fd->data;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(what & BSC_FD_READ);
|
||||
OSMO_ASSERT(what & OSMO_FD_READ);
|
||||
|
||||
switch (fd->priv_nr) {
|
||||
case 0:
|
||||
@@ -908,51 +762,53 @@ static int ggsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void ggsn_gtp_tmr_start(struct ggsn_ctx *ggsn)
|
||||
/* libgtp callback for confirmations */
|
||||
static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
|
||||
{
|
||||
struct timeval next;
|
||||
struct sgsn_peer *sgsn;
|
||||
int rc = 0;
|
||||
|
||||
/* Retrieve next retransmission as timeval */
|
||||
gtp_retranstimeout(ggsn->gsn, &next);
|
||||
if (cause == EOF)
|
||||
LOGP(DGGSN, LOGL_NOTICE, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n",
|
||||
type, pdp, cbp);
|
||||
|
||||
/* re-schedule the timer */
|
||||
osmo_timer_schedule(&ggsn->gtp_timer, next.tv_sec, next.tv_usec/1000);
|
||||
}
|
||||
|
||||
/* timer callback for libgtp retransmission and ping */
|
||||
static void ggsn_gtp_tmr_cb(void *data)
|
||||
{
|
||||
struct ggsn_ctx *ggsn = data;
|
||||
|
||||
/* do all the retransmissions as needed */
|
||||
gtp_retrans(ggsn->gsn);
|
||||
|
||||
ggsn_gtp_tmr_start(ggsn);
|
||||
}
|
||||
|
||||
/* To exit gracefully. Used with GCC compilation flag -pg and gprof */
|
||||
static void signal_handler(int s)
|
||||
{
|
||||
LOGP(DGGSN, LOGL_NOTICE, "signal %d received\n", s);
|
||||
switch (s) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
LOGP(DGGSN, LOGL_NOTICE, "SIGINT received, shutting down\n");
|
||||
end = 1;
|
||||
switch (type) {
|
||||
case GTP_DELETE_PDP_REQ:
|
||||
/* Remark: We actually never reach this path nowadays because
|
||||
only place where we call gtp_delete_context_req2() is during
|
||||
ggsn_close_one_pdp() path, and in that case we free all pdp
|
||||
contexts immediatelly without waiting for confirmation
|
||||
(through gtp_freepdp_teardown()) since we want to tear down
|
||||
the whole APN anyways. As a result, DeleteCtxResponse will
|
||||
never reach here since it will be dropped at some point in
|
||||
lower layers in the Rx path. This code is nevertheless left
|
||||
here in order to ease future developent and avoid possible
|
||||
future memleaks once more scenarios where GGSN sends a
|
||||
DeleteCtxRequest are introduced. */
|
||||
if (pdp)
|
||||
rc = gtp_freepdp(pdp->gsn, pdp);
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_ggsn_ctx, stderr);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_vty_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
case GTP_ECHO_REQ:
|
||||
sgsn = (struct sgsn_peer *)cbp;
|
||||
sgsn_peer_echo_resp(sgsn, cause == EOF);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cb_recovery3(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery)
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *)gsn->priv;
|
||||
struct sgsn_peer *sgsn;
|
||||
|
||||
sgsn = ggsn_find_sgsn(ggsn, &peer->sin_addr);
|
||||
if (!sgsn) {
|
||||
LOGPGGSN(LOGL_NOTICE, ggsn, "Received Recovery IE for unknown SGSN (no PDP contexts active)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return sgsn_peer_handle_recovery(sgsn, pdp, recovery);
|
||||
}
|
||||
|
||||
/* Start a given GGSN */
|
||||
int ggsn_start(struct ggsn_ctx *ggsn)
|
||||
@@ -981,24 +837,23 @@ int ggsn_start(struct ggsn_ctx *ggsn)
|
||||
ggsn->gsn->gsnu = ggsn->cfg.gtpu_addr.v4;
|
||||
|
||||
/* Register File Descriptors */
|
||||
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
|
||||
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
|
||||
rc = osmo_fd_register(&ggsn->gtp_fd0);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
osmo_fd_setup(&ggsn->gtp_fd1c, ggsn->gsn->fd1c, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 1);
|
||||
osmo_fd_setup(&ggsn->gtp_fd1c, ggsn->gsn->fd1c, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 1);
|
||||
rc = osmo_fd_register(&ggsn->gtp_fd1c);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
osmo_fd_setup(&ggsn->gtp_fd1u, ggsn->gsn->fd1u, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 2);
|
||||
osmo_fd_setup(&ggsn->gtp_fd1u, ggsn->gsn->fd1u, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 2);
|
||||
rc = osmo_fd_register(&ggsn->gtp_fd1u);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
/* Start GTP re-transmission timer */
|
||||
osmo_timer_setup(&ggsn->gtp_timer, ggsn_gtp_tmr_cb, ggsn);
|
||||
|
||||
gtp_set_cb_data_ind(ggsn->gsn, encaps_tun);
|
||||
gtp_set_cb_delete_context(ggsn->gsn, delete_context);
|
||||
gtp_set_cb_create_context_ind(ggsn->gsn, create_context_ind);
|
||||
gtp_set_cb_conf(ggsn->gsn, cb_conf);
|
||||
gtp_set_cb_recovery3(ggsn->gsn, cb_recovery3);
|
||||
|
||||
LOGPGGSN(LOGL_NOTICE, ggsn, "Successfully started\n");
|
||||
ggsn->started = true;
|
||||
@@ -1019,9 +874,7 @@ int ggsn_stop(struct ggsn_ctx *ggsn)
|
||||
|
||||
/* iterate over all APNs and stop them */
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
||||
apn_stop(apn, true);
|
||||
|
||||
osmo_timer_del(&ggsn->gtp_timer);
|
||||
apn_stop(apn);
|
||||
|
||||
osmo_fd_unregister(&ggsn->gtp_fd1u);
|
||||
osmo_fd_unregister(&ggsn->gtp_fd1c);
|
||||
@@ -1035,128 +888,3 @@ int ggsn_stop(struct ggsn_ctx *ggsn)
|
||||
ggsn->started = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
printf("Usage: osmo-ggsn [-h] [-D] [-c configfile] [-V]\n");
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf( " Some useful help...\n"
|
||||
" -h --help This help text\n"
|
||||
" -D --daemonize Fork the process into a background daemon\n"
|
||||
" -c --config-file filename The config file to use\n"
|
||||
" -V --version Print the version of OsmoGGSN\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "daemonize", 0, 0, 'D' },
|
||||
{ "config-file", 1, 0, 'c' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hdc:V", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
exit(0);
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
int rc;
|
||||
|
||||
tall_ggsn_ctx = talloc_named_const(NULL, 0, "OsmoGGSN");
|
||||
msgb_talloc_ctx_init(tall_ggsn_ctx, 0);
|
||||
g_vty_info.tall_ctx = tall_ggsn_ctx;
|
||||
|
||||
/* Handle keyboard interrupt SIGINT */
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGTERM, &signal_handler);
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging2(tall_ggsn_ctx, &log_info);
|
||||
osmo_stats_init(tall_ggsn_ctx);
|
||||
|
||||
vty_init(&g_vty_info);
|
||||
logging_vty_add_cmds(NULL);
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds(&log_info);
|
||||
ggsn_vty_init();
|
||||
ctrl_vty_init(tall_ggsn_ctx);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
rate_ctr_init(tall_ggsn_ctx);
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
rc = telnet_init_dynif(tall_ggsn_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_GGSN);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
||||
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(),
|
||||
OSMO_CTRL_PORT_GGSN, NULL);
|
||||
if (!g_ctrlh) {
|
||||
LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* qos */
|
||||
qos.l = 3;
|
||||
qos.v[2] = (args_info.qos_arg) & 0xff;
|
||||
qos.v[1] = ((args_info.qos_arg) >> 8) & 0xff;
|
||||
qos.v[0] = ((args_info.qos_arg) >> 16) & 0xff;
|
||||
#endif
|
||||
|
||||
/* Main select loop */
|
||||
while (!end) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
llist_for_each_entry(ggsn, &g_ggsn_list, list)
|
||||
ggsn_stop(ggsn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
42
ggsn/ggsn.h
42
ggsn/ggsn.h
@@ -6,6 +6,8 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
|
||||
#include "../lib/tun.h"
|
||||
#include "../lib/ippool.h"
|
||||
@@ -13,6 +15,8 @@
|
||||
#include "../lib/in46_addr.h"
|
||||
#include "../gtp/gtp.h"
|
||||
|
||||
#include "sgsn.h"
|
||||
|
||||
#define APN_TYPE_IPv4 0x01 /* v4-only */
|
||||
#define APN_TYPE_IPv6 0x02 /* v6-only */
|
||||
#define APN_TYPE_IPv4v6 0x04 /* v4v6 dual-stack */
|
||||
@@ -62,9 +66,9 @@ struct apn_ctx {
|
||||
uint32_t apn_type_mask;
|
||||
/* GTP-U via TUN device or in Linux kernel */
|
||||
enum apn_gtpu_mode gtpu_mode;
|
||||
/* administratively shut-down (true) or not (false) */
|
||||
/* administratively shut down (true) or not (false) */
|
||||
bool shutdown;
|
||||
/* transmit G-PDU sequeence numbers (true) or not (false) */
|
||||
/* transmit G-PDU sequence numbers (true) or not (false) */
|
||||
bool tx_gpdu_seq;
|
||||
} cfg;
|
||||
|
||||
@@ -88,6 +92,14 @@ struct apn_ctx {
|
||||
struct apn_ctx_ip v6;
|
||||
};
|
||||
|
||||
struct pdp_priv_t {
|
||||
struct pdp_t *lib; /* pointer to libgtp associated pdp_t instance */
|
||||
struct sgsn_peer *sgsn;
|
||||
struct apn_ctx *apn;
|
||||
struct llist_head entry; /* to be included into sgsn_peer */
|
||||
/* struct ggsn_ctx can be reached through lib->gsn->priv, or through sgsn->ggsn */
|
||||
};
|
||||
|
||||
struct ggsn_ctx {
|
||||
/* global list of GGSNs */
|
||||
struct llist_head list;
|
||||
@@ -95,6 +107,9 @@ struct ggsn_ctx {
|
||||
/* list of APNs in this GGSN */
|
||||
struct llist_head apn_list;
|
||||
|
||||
/* list of SGSN peers (struct sgsn_peer) in this GGSN. TODO: hash table with key <ip+port>? */
|
||||
struct llist_head sgsn_list;
|
||||
|
||||
bool started;
|
||||
|
||||
struct {
|
||||
@@ -111,7 +126,9 @@ struct ggsn_ctx {
|
||||
struct in46_addr gtpu_addr;
|
||||
/* directory for state file */
|
||||
char *state_dir;
|
||||
/* administratively shut-down (true) or not (false) */
|
||||
/* Time between Echo requests on each SGSN */
|
||||
unsigned int echo_interval;
|
||||
/* administratively shut down (true) or not (false) */
|
||||
bool shutdown;
|
||||
} cfg;
|
||||
|
||||
@@ -122,8 +139,6 @@ struct ggsn_ctx {
|
||||
struct osmo_fd gtp_fd0;
|
||||
struct osmo_fd gtp_fd1c;
|
||||
struct osmo_fd gtp_fd1u;
|
||||
|
||||
struct osmo_timer_list gtp_timer;
|
||||
};
|
||||
|
||||
/* ggsn_vty.c */
|
||||
@@ -135,9 +150,22 @@ struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name);
|
||||
struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name);
|
||||
struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name);
|
||||
|
||||
/* ggsn.c */
|
||||
/* ggsn_main.c */
|
||||
extern struct ctrl_handle *g_ctrlh;
|
||||
extern void *tall_ggsn_ctx;
|
||||
extern struct osmo_tdef_group ggsn_tdef_group[];
|
||||
|
||||
/* ggsn.c */
|
||||
extern int ggsn_start(struct ggsn_ctx *ggsn);
|
||||
extern int ggsn_stop(struct ggsn_ctx *ggsn);
|
||||
extern int apn_start(struct apn_ctx *apn);
|
||||
extern int apn_stop(struct apn_ctx *apn, bool force);
|
||||
extern int apn_stop(struct apn_ctx *apn);
|
||||
void ggsn_close_one_pdp(struct pdp_t *pdp);
|
||||
|
||||
#define LOGPAPN(level, apn, fmt, args...) \
|
||||
LOGP(DGGSN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)
|
||||
|
||||
#define LOGPGGSN(level, ggsn, fmt, args...) \
|
||||
LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args)
|
||||
|
||||
#define LOGPPDP(level, pdp, fmt, args...) LOGPDPX(DGGSN, level, pdp, fmt, ## args)
|
||||
|
||||
261
ggsn/ggsn_main.c
Normal file
261
ggsn/ggsn_main.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2017-2019 by Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <getopt.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
|
||||
#include "ggsn.h"
|
||||
|
||||
void *tall_ggsn_ctx;
|
||||
|
||||
static int end = 0;
|
||||
static int daemonize = 0;
|
||||
struct ctrl_handle *g_ctrlh;
|
||||
|
||||
struct ul255_t qos;
|
||||
struct ul255_t apn;
|
||||
|
||||
static char *config_file = "osmo-ggsn.cfg";
|
||||
|
||||
struct osmo_tdef_group ggsn_tdef_group[] = {
|
||||
{.name = "gtp", .tdefs = gtp_T_defs, .desc = "GTP (libgtp) timers" },
|
||||
{ }
|
||||
};
|
||||
|
||||
/* To exit gracefully. Used with GCC compilation flag -pg and gprof */
|
||||
static void signal_handler(int s)
|
||||
{
|
||||
LOGP(DGGSN, LOGL_NOTICE, "signal %d received\n", s);
|
||||
switch (s) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
LOGP(DGGSN, LOGL_NOTICE, "SIGINT received, shutting down\n");
|
||||
end = 1;
|
||||
break;
|
||||
case SIGABRT:
|
||||
/* in case of abort, we want to obtain a talloc report and
|
||||
* then run default SIGABRT handler, who will generate coredump
|
||||
* and abort the process. abort() should do this for us after we
|
||||
* return, but program wouldn't exit if an external SIGABRT is
|
||||
* received.
|
||||
*/
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_ggsn_ctx, stderr);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_ggsn_ctx, stderr);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_vty_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
printf("Usage: osmo-ggsn [-h] [-D] [-c configfile] [-V]\n");
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf( " Some useful help...\n"
|
||||
" -h --help This help text\n"
|
||||
" -D --daemonize Fork the process into a background daemon\n"
|
||||
" -c --config-file filename The config file to use\n"
|
||||
" -V --version Print the version of OsmoGGSN\n"
|
||||
);
|
||||
|
||||
printf("\nVTY reference generation:\n");
|
||||
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
|
||||
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
|
||||
}
|
||||
|
||||
static void handle_long_options(const char *prog_name, const int long_option)
|
||||
{
|
||||
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
|
||||
|
||||
switch (long_option) {
|
||||
case 1:
|
||||
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
|
||||
if (vty_ref_mode < 0) {
|
||||
fprintf(stderr, "%s: Unknown VTY reference generation "
|
||||
"mode '%s'\n", prog_name, optarg);
|
||||
exit(2);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
|
||||
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
|
||||
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
|
||||
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static int long_option = 0;
|
||||
static struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "daemonize", 0, 0, 'D' },
|
||||
{ "config-file", 1, 0, 'c' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "vty-ref-mode", 1, &long_option, 1 },
|
||||
{ "vty-ref-xml", 0, &long_option, 2 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hdc:V", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
handle_long_options(argv[0], long_option);
|
||||
break;
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
exit(0);
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
int rc;
|
||||
|
||||
tall_ggsn_ctx = talloc_named_const(NULL, 0, "OsmoGGSN");
|
||||
msgb_talloc_ctx_init(tall_ggsn_ctx, 0);
|
||||
g_vty_info.tall_ctx = tall_ggsn_ctx;
|
||||
|
||||
/* Handle keyboard interrupt SIGINT */
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGTERM, &signal_handler);
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging2(tall_ggsn_ctx, &log_info);
|
||||
osmo_stats_init(tall_ggsn_ctx);
|
||||
|
||||
vty_init(&g_vty_info);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds();
|
||||
ggsn_vty_init();
|
||||
ctrl_vty_init(tall_ggsn_ctx);
|
||||
osmo_cpu_sched_vty_init(tall_ggsn_ctx);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
rate_ctr_init(tall_ggsn_ctx);
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
rc = telnet_init_default(tall_ggsn_ctx, NULL, OSMO_VTY_PORT_GGSN);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
||||
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_GGSN, NULL);
|
||||
if (!g_ctrlh) {
|
||||
LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* qos */
|
||||
qos.l = 3;
|
||||
qos.v[2] = (args_info.qos_arg) & 0xff;
|
||||
qos.v[1] = ((args_info.qos_arg) >> 8) & 0xff;
|
||||
qos.v[0] = ((args_info.qos_arg) >> 16) & 0xff;
|
||||
#endif
|
||||
|
||||
/* Main select loop */
|
||||
while (!end) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
llist_for_each_entry(ggsn, &g_ggsn_list, list)
|
||||
ggsn_stop(ggsn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
302
ggsn/ggsn_vty.c
302
ggsn/ggsn_vty.c
@@ -26,16 +26,22 @@
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
#include <osmocom/gsm/gsm48_ie.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/tdef_vty.h>
|
||||
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
|
||||
#include "../lib/util.h"
|
||||
|
||||
#include "ggsn.h"
|
||||
#include "sgsn.h"
|
||||
|
||||
#define PREFIX_STR "Prefix (Network/Netmask)\n"
|
||||
#define IFCONFIG_STR "GGSN-based interface configuration\n"
|
||||
@@ -75,6 +81,7 @@ struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
|
||||
ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
|
||||
ggsn->cfg.shutdown = true;
|
||||
INIT_LLIST_HEAD(&ggsn->apn_list);
|
||||
INIT_LLIST_HEAD(&ggsn->sgsn_list);
|
||||
|
||||
llist_add_tail(&ggsn->list, &g_ggsn_list);
|
||||
return ggsn;
|
||||
@@ -292,7 +299,7 @@ DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
|
||||
|
||||
DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
|
||||
"shutdown ggsn",
|
||||
"Put the GGSN in administrative shut-down\n" GGSN_STR)
|
||||
"Put the GGSN in administrative shutdown\n" GGSN_STR)
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
|
||||
@@ -309,7 +316,7 @@ DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
|
||||
|
||||
DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
|
||||
"no shutdown ggsn",
|
||||
NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
|
||||
NO_STR GGSN_STR "Remove the GGSN from administrative shutdown\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
|
||||
@@ -324,6 +331,79 @@ DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void show_one_sgsn(struct vty *vty, const struct sgsn_peer *sgsn, const char* prefix)
|
||||
{
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET, &sgsn->addr, buf, sizeof(buf));
|
||||
vty_out(vty, "%s(S)GSN %s%s", prefix, buf, VTY_NEWLINE);
|
||||
vty_out(vty, "%s Restart Counter: %d%s", prefix, sgsn->remote_restart_ctr, VTY_NEWLINE);
|
||||
vty_out(vty, "%s PDP contexts: %d%s", prefix, llist_count(&sgsn->pdp_list), VTY_NEWLINE);
|
||||
vty_out(vty, "%s Echo Requests in-flight: %u%s", prefix, sgsn->tx_msgs_queued, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(cfg_ggsn_show_sgsn, cfg_ggsn_show_sgsn_cmd,
|
||||
"show sgsn",
|
||||
NO_STR GGSN_STR "Remove the GGSN from administrative shutdown\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
struct sgsn_peer *sgsn;
|
||||
|
||||
llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) {
|
||||
show_one_sgsn(vty, sgsn, "");
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Seee 3GPP TS 29.060 section 7.2.1 */
|
||||
DEFUN(cfg_ggsn_echo_interval, cfg_ggsn_echo_interval_cmd,
|
||||
"echo-interval <1-36000>",
|
||||
GGSN_STR "GGSN Number\n"
|
||||
"Send an echo request to this static GGSN every interval\n"
|
||||
"Interval between echo requests in seconds\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
int prev_interval = ggsn->cfg.echo_interval;
|
||||
struct sgsn_peer *sgsn;
|
||||
|
||||
ggsn->cfg.echo_interval = atoi(argv[0]);
|
||||
|
||||
if (ggsn->cfg.echo_interval < 60)
|
||||
vty_out(vty, "%% 3GPP TS 29.060 section states interval should " \
|
||||
"not be lower than 60 seconds, use this value for " \
|
||||
"testing purposes only!%s", VTY_NEWLINE);
|
||||
|
||||
if (prev_interval == ggsn->cfg.echo_interval)
|
||||
return CMD_SUCCESS;
|
||||
|
||||
/* Re-enable echo timer for all sgsn */
|
||||
llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
|
||||
sgsn_echo_timer_start(sgsn);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_ggsn_no_echo_interval, cfg_ggsn_no_echo_interval_cmd,
|
||||
"no echo-interval",
|
||||
GGSN_STR "GGSN Number\n"
|
||||
NO_STR "Send an echo request to this static GGSN every interval.\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
|
||||
struct sgsn_peer *sgsn;
|
||||
|
||||
if (ggsn->cfg.echo_interval == 0)
|
||||
return CMD_SUCCESS;
|
||||
|
||||
ggsn->cfg.echo_interval = 0;
|
||||
|
||||
/* Disable echo timer for all sgsn */
|
||||
llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
|
||||
sgsn_echo_timer_stop(sgsn);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* APN Node */
|
||||
|
||||
static struct cmd_node apn_node = {
|
||||
@@ -453,9 +533,11 @@ DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
|
||||
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]);
|
||||
|
||||
@@ -487,9 +569,11 @@ DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
|
||||
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;
|
||||
@@ -597,12 +681,12 @@ DEFUN(cfg_apn_no_gpdu_seq, cfg_apn_no_gpdu_seq_cmd,
|
||||
|
||||
DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
|
||||
"shutdown",
|
||||
"Put the APN in administrative shut-down\n")
|
||||
"Put the APN in administrative shutdown\n")
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
|
||||
if (!apn->cfg.shutdown) {
|
||||
if (apn_stop(apn, false)) {
|
||||
if (apn_stop(apn)) {
|
||||
vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -614,11 +698,15 @@ DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
|
||||
|
||||
DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
|
||||
"no shutdown",
|
||||
NO_STR "Remove the APN from administrative shut-down\n")
|
||||
NO_STR "Remove the APN from administrative shutdown\n")
|
||||
{
|
||||
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
|
||||
|
||||
if (apn->cfg.shutdown) {
|
||||
if (!apn->tun.cfg.dev_name) {
|
||||
vty_out(vty, "%% Failed to start APN, tun-device is not configured%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (apn_start(apn) < 0) {
|
||||
vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
@@ -652,9 +740,9 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
|
||||
vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!(apn->cfg.apn_type_mask & (1 << i)))
|
||||
if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i)))
|
||||
continue;
|
||||
vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (1 << i)),
|
||||
vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
@@ -708,10 +796,13 @@ static int config_write_ggsn(struct vty *vty)
|
||||
vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
|
||||
if (ggsn->cfg.gtpu_addr.v4.s_addr)
|
||||
vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
|
||||
osmo_tdef_vty_groups_write(vty, " ");
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
||||
config_write_apn(vty, apn);
|
||||
if (ggsn->cfg.default_apn)
|
||||
vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
|
||||
if (ggsn->cfg.echo_interval)
|
||||
vty_out(vty, " echo-interval %u%s", ggsn->cfg.echo_interval, VTY_NEWLINE);
|
||||
/* must be last */
|
||||
vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
|
||||
}
|
||||
@@ -730,12 +821,42 @@ static const char *print_gsnaddr(const struct ul16_t *in)
|
||||
return in46a_ntoa(&in46);
|
||||
}
|
||||
|
||||
static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
|
||||
/* Useful for v4v6 APNs, where we first iterate over v4 pool and then over v6
|
||||
pool. param v4only can be used to avoid printing duplicates for pdp context
|
||||
containing both IPv4 and IPv6 addresses. */
|
||||
static void show_one_pdp_v4only(struct vty *vty, struct pdp_t *pdp, bool v4only)
|
||||
{
|
||||
struct in46_addr eua46;
|
||||
struct ippoolm_t *peer4, *peer6;
|
||||
char name_buf[256];
|
||||
char *apn_name;
|
||||
int rc;
|
||||
|
||||
peer4 = pdp_get_peer_ipv(pdp, false);
|
||||
peer6 = pdp_get_peer_ipv(pdp, true);
|
||||
|
||||
if (v4only && peer6)
|
||||
return;
|
||||
|
||||
/* Attempt to decode MSISDN */
|
||||
rc = gsm48_decode_bcd_number2(name_buf, sizeof(name_buf),
|
||||
pdp->msisdn.v, pdp->msisdn.l, 0);
|
||||
|
||||
vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
|
||||
osmo_hexdump_nospc(pdp->msisdn.v, pdp->msisdn.l), VTY_NEWLINE);
|
||||
rc ? "(NONE)" : name_buf, VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " Version: %d", pdp->version);
|
||||
if (pdp->version == 1) {
|
||||
if (!pdp->secondary) {
|
||||
vty_out(vty, ", Primary, Num Secondaries: %d%s%s",
|
||||
pdp_count_secondary(pdp) - 1, /* primary included in count */
|
||||
pdp->nodata ? ", No User Plane": "",
|
||||
VTY_NEWLINE);
|
||||
} else {
|
||||
vty_out(vty, ", Secondary%s", VTY_NEWLINE);
|
||||
}
|
||||
} else {
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
|
||||
vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
|
||||
@@ -743,32 +864,61 @@ static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
|
||||
vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
|
||||
vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
|
||||
|
||||
in46a_from_eua(&pdp->eua, &eua46);
|
||||
vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), VTY_NEWLINE);
|
||||
apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
|
||||
vty_out(vty, " APN requested: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
|
||||
apn_name = osmo_apn_to_str(name_buf, pdp->apn_use.v, pdp->apn_use.l);
|
||||
vty_out(vty, " APN in use: %s%s", apn_name ? name_buf : "(NONE)", VTY_NEWLINE);
|
||||
|
||||
if (peer4)
|
||||
vty_out(vty, " End-User Address (IPv4): %s%s",
|
||||
in46a_ntop(&peer4->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
|
||||
if (peer6)
|
||||
vty_out(vty, " End-User Address (IPv6): %s%s",
|
||||
in46a_ntop(&peer6->addr, name_buf, sizeof(name_buf)), VTY_NEWLINE);
|
||||
vty_out(vty, " Transmit GTP Sequence Number for G-PDU: %s%s",
|
||||
pdp->tx_gpdu_seq ? "Yes" : "No", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
|
||||
{
|
||||
show_one_pdp_v4only(vty, pdp, false);
|
||||
}
|
||||
|
||||
DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
|
||||
"show pdp-context imsi IMSI [<0-15>]",
|
||||
"show pdp-context ggsn NAME imsi IMSI [<0-15>]",
|
||||
SHOW_STR "Display information on PDP Context\n"
|
||||
GGSN_STR "GGSN Name\n"
|
||||
"PDP contexts for given IMSI\n"
|
||||
"PDP context for given NSAPI\n")
|
||||
{
|
||||
uint64_t imsi = strtoull(argv[0], NULL, 10);
|
||||
struct ggsn_ctx *ggsn;
|
||||
uint64_t imsi;
|
||||
unsigned int nsapi;
|
||||
struct pdp_t *pdp;
|
||||
int num_found = 0;
|
||||
|
||||
if (argc > 1) {
|
||||
nsapi = atoi(argv[1]);
|
||||
if (pdp_getimsi(&pdp, imsi, nsapi)) {
|
||||
ggsn = ggsn_find(argv[0]);
|
||||
if (!ggsn) {
|
||||
vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (strlen(argv[1]) < 6 || strlen(argv[1]) > 15) {
|
||||
vty_out(vty, "%% Invalid IMSI '%s'%s", argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
imsi = gtp_imsi_str2gtp(argv[1]);
|
||||
|
||||
if (argc > 2) {
|
||||
nsapi = atoi(argv[2]);
|
||||
if (!gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi)) {
|
||||
show_one_pdp(vty, pdp);
|
||||
num_found++;
|
||||
}
|
||||
} else {
|
||||
for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
|
||||
if (pdp_getimsi(&pdp, imsi, nsapi))
|
||||
if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
|
||||
continue;
|
||||
show_one_pdp(vty, pdp);
|
||||
num_found++;
|
||||
@@ -781,8 +931,49 @@ DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_pdpctx_ip, show_pdpctx_ip_cmd,
|
||||
"show pdp-context ggsn NAME ipv4 A.B.C.D",
|
||||
SHOW_STR "Display information on PDP Context\n"
|
||||
GGSN_STR "GGSN Name\n" "IPv4 address type\n" "IP address\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
struct apn_ctx *apn;
|
||||
unsigned int i;
|
||||
|
||||
ggsn = ggsn_find(argv[0]);
|
||||
if (!ggsn) {
|
||||
vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* Iterate over all APNs of a given GGSN */
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list) {
|
||||
struct ippool_t *pool = apn->v4.pool;
|
||||
|
||||
/* In some rare cases, if GGSN fails to init TUN/TAP interfaces
|
||||
* (e.g. due to insufficient permissions), it will continue to
|
||||
* work in such broken state, and pool would be NULL. */
|
||||
if (!pool)
|
||||
continue;
|
||||
|
||||
/* Iterate over all IPv4 pool members */
|
||||
for (i = 0; i < pool->listsize; i++) {
|
||||
struct ippoolm_t *member = &pool->member[i];
|
||||
if (member->inuse == 0)
|
||||
continue;
|
||||
if (strcmp(argv[1], in46a_ntoa(&member->addr)) == 0) {
|
||||
show_one_pdp(vty, member->peer);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vty_out(vty, "%% No PDP context found for IP '%s'%s", argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* show all (active) PDP contexts within a pool */
|
||||
static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool)
|
||||
static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, bool pdp_v4only)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
@@ -793,21 +984,21 @@ static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool)
|
||||
struct ippoolm_t *member = &pool->member[i];
|
||||
if (member->inuse == 0)
|
||||
continue;
|
||||
show_one_pdp(vty, member->peer);
|
||||
show_one_pdp_v4only(vty, member->peer, pdp_v4only);
|
||||
}
|
||||
}
|
||||
|
||||
/* show all (active) PDP contexts within an APN */
|
||||
static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
|
||||
{
|
||||
ippool_show_pdp_contexts(vty, apn->v4.pool);
|
||||
ippool_show_pdp_contexts(vty, apn->v6.pool);
|
||||
ippool_show_pdp_contexts(vty, apn->v4.pool, true);
|
||||
ippool_show_pdp_contexts(vty, apn->v6.pool, false);
|
||||
}
|
||||
|
||||
DEFUN(show_pdpctx, show_pdpctx_cmd,
|
||||
"show pdp-context ggsn NAME [apn APN]",
|
||||
"show pdp-context ggsn NAME",
|
||||
SHOW_STR "Show PDP Context Information\n"
|
||||
GGSN_STR "GGSN Name\n") // FIXME
|
||||
GGSN_STR "GGSN Name\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
struct apn_ctx *apn;
|
||||
@@ -817,21 +1008,45 @@ DEFUN(show_pdpctx, show_pdpctx_cmd,
|
||||
vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (argc < 2) {
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
||||
apn_show_pdp_contexts(vty, apn);
|
||||
} else {
|
||||
apn = ggsn_find_apn(ggsn, argv[1]);
|
||||
if (!apn) {
|
||||
vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
||||
apn_show_pdp_contexts(vty, apn);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_pdpctx_apn, show_pdpctx_apn_cmd,
|
||||
"show pdp-context ggsn NAME apn APN",
|
||||
SHOW_STR "Show PDP Context Information\n"
|
||||
GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n")
|
||||
{
|
||||
struct ggsn_ctx *ggsn;
|
||||
struct apn_ctx *apn;
|
||||
|
||||
ggsn = ggsn_find(argv[0]);
|
||||
if (!ggsn) {
|
||||
vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
apn = ggsn_find_apn(ggsn, argv[1]);
|
||||
if (!apn) {
|
||||
vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
apn_show_pdp_contexts(vty, apn);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Backwards compatibility: the VTY parser is (mis)interpreting
|
||||
* "[apn APN]" as two separate elements: "[apn" and "APN]",
|
||||
* but the first part somehow turns into command "ap". */
|
||||
ALIAS_DEPRECATED(show_pdpctx_apn, show_deprecated_pdpctx_apn_cmd,
|
||||
"show pdp-context ggsn NAME ap APN",
|
||||
SHOW_STR "Show PDP Context Information\n"
|
||||
GGSN_STR "GGSN Name\n" "Filter by APN\n" "APN name\n");
|
||||
|
||||
static void show_apn(struct vty *vty, struct apn_ctx *apn)
|
||||
{
|
||||
vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
|
||||
@@ -841,12 +1056,15 @@ static void show_apn(struct vty *vty, struct apn_ctx *apn)
|
||||
static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
|
||||
{
|
||||
struct apn_ctx *apn;
|
||||
struct sgsn_peer *sgsn;
|
||||
vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
|
||||
VTY_NEWLINE);
|
||||
/* FIXME */
|
||||
|
||||
llist_for_each_entry(apn, &ggsn->apn_list, list)
|
||||
show_apn(vty, apn);
|
||||
llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry)
|
||||
show_one_sgsn(vty, sgsn, " ");
|
||||
}
|
||||
|
||||
DEFUN(show_ggsn, show_ggsn_cmd,
|
||||
@@ -871,7 +1089,10 @@ DEFUN(show_ggsn, show_ggsn_cmd,
|
||||
int ggsn_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_pdpctx_cmd);
|
||||
install_element_ve(&show_pdpctx_apn_cmd);
|
||||
install_element_ve(&show_deprecated_pdpctx_apn_cmd);
|
||||
install_element_ve(&show_pdpctx_imsi_cmd);
|
||||
install_element_ve(&show_pdpctx_ip_cmd);
|
||||
install_element_ve(&show_ggsn_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_ggsn_cmd);
|
||||
@@ -890,6 +1111,11 @@ int ggsn_vty_init(void)
|
||||
install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
|
||||
install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
|
||||
install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
|
||||
install_element(GGSN_NODE, &cfg_ggsn_show_sgsn_cmd);
|
||||
install_element(GGSN_NODE, &cfg_ggsn_echo_interval_cmd);
|
||||
install_element(GGSN_NODE, &cfg_ggsn_no_echo_interval_cmd);
|
||||
|
||||
osmo_tdef_vty_groups_init(GGSN_NODE, ggsn_tdef_group);
|
||||
|
||||
install_node(&apn_node, NULL);
|
||||
install_element(APN_NODE, &cfg_description_cmd);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
|
||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
const struct in6_addr *pdp_prefix,
|
||||
const struct in6_addr *own_ll_addr,
|
||||
const uint8_t *pack, unsigned len);
|
||||
252
ggsn/pco.c
Normal file
252
ggsn/pco.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* PCO parsing related code
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2017-2019 by Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "../lib/util.h"
|
||||
|
||||
#include "pco.h"
|
||||
#include "ggsn.h"
|
||||
|
||||
/* determine if IPCP contains given option */
|
||||
static const uint8_t *ipcp_contains_option(const struct ipcp_hdr *ipcp, size_t ipcp_len,
|
||||
enum ipcp_options opt, size_t opt_minlen)
|
||||
{
|
||||
const uint8_t *cur_opt = ipcp->options;
|
||||
|
||||
/* iterate over Options and check if protocol contained */
|
||||
while (cur_opt + sizeof(struct ipcp_option_hdr) <= (uint8_t*)ipcp + ipcp_len) {
|
||||
const struct ipcp_option_hdr *cur_opt_hdr = (const struct ipcp_option_hdr *)cur_opt;
|
||||
/* length value includes 2 bytes type/length */
|
||||
if (cur_opt_hdr->len < sizeof(struct ipcp_option_hdr))
|
||||
return NULL;
|
||||
if (cur_opt_hdr->type == opt &&
|
||||
cur_opt_hdr->len >= sizeof(struct ipcp_option_hdr) + opt_minlen)
|
||||
return cur_opt;
|
||||
cur_opt += cur_opt_hdr->len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char *pap_welcome = "Welcome to OsmoGGSN " PACKAGE_VERSION;
|
||||
|
||||
/* Handle PAP protocol according to RFC 1334 */
|
||||
static void process_pco_element_pap(const struct pco_element *pco_in, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
const struct pap_element *pap_in = (const struct pap_element *) pco_in->data;
|
||||
uint16_t pap_in_len;
|
||||
uint8_t peer_id_len;
|
||||
const uint8_t *peer_id;
|
||||
unsigned int pap_welcome_len;
|
||||
uint8_t pap_out_size;
|
||||
struct pap_element *pap_out;
|
||||
|
||||
if (pco_in->length < sizeof(struct pap_element))
|
||||
goto ret_broken;
|
||||
|
||||
pap_in_len = osmo_load16be(&pap_in->len);
|
||||
if (pco_in->length < pap_in_len)
|
||||
goto ret_broken;
|
||||
/* "pco_in->length > pap_in_len" is allowed: RFC1334 2.2 states:
|
||||
"Octets outside the range of the Length field should be treated as
|
||||
Data Link Layer padding and should be ignored on reception."
|
||||
*/
|
||||
|
||||
switch (pap_in->code) {
|
||||
case PAP_CODE_AUTH_REQ:
|
||||
if (pap_in_len < sizeof(struct pap_element) + 1)
|
||||
goto ret_broken_auth;
|
||||
peer_id_len = pap_in->data[0];
|
||||
if (pap_in_len < sizeof(struct pap_element) + 1 + peer_id_len)
|
||||
goto ret_broken_auth;
|
||||
peer_id = &pap_in->data[1];
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "PCO PAP PeerId = %s, ACKing\n",
|
||||
osmo_quote_str((const char *)peer_id, peer_id_len));
|
||||
/* Password-Length + Password following here, but we don't care */
|
||||
|
||||
/* Prepare response, we ACK all of them: */
|
||||
pap_welcome_len = strlen(pap_welcome);
|
||||
/* +1: Length field of pap_welcome Message */
|
||||
pap_out_size = sizeof(struct pap_element) + 1 + pap_welcome_len;
|
||||
pap_out = alloca(pap_out_size);
|
||||
pap_out->code = PAP_CODE_AUTH_ACK;
|
||||
pap_out->id = pap_in->id;
|
||||
pap_out->len = htons(pap_out_size);
|
||||
pap_out->data[0] = pap_welcome_len;
|
||||
memcpy(pap_out->data+1, pap_welcome, pap_welcome_len);
|
||||
msgb_t16lv_put(resp, PCO_P_PAP, pap_out_size, (uint8_t *) pap_out);
|
||||
break;
|
||||
case PAP_CODE_AUTH_ACK:
|
||||
case PAP_CODE_AUTH_NAK:
|
||||
default:
|
||||
LOGPPDP(LOGL_NOTICE, pdp, "Unsupported PAP PCO Code %u, ignoring\n", pap_in->code);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
ret_broken_auth:
|
||||
LOGPPDP(LOGL_NOTICE, pdp, "Invalid PAP AuthenticateReq: %s, ignoring\n",
|
||||
osmo_hexdump_nospc((const uint8_t *)pco_in, pco_in->length));
|
||||
return;
|
||||
|
||||
ret_broken:
|
||||
LOGPPDP(LOGL_NOTICE, pdp, "Invalid PAP PCO Length: %s, ignoring\n",
|
||||
osmo_hexdump_nospc((const uint8_t *)pco_in, pco_in->length));
|
||||
}
|
||||
|
||||
static void process_pco_element_ipcp(const struct pco_element *pco_elem, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
struct ippoolm_t *peer_v4 = pdp_get_peer_ipv(pdp, false);
|
||||
const struct in46_addr *dns1 = &apn->v4.cfg.dns[0];
|
||||
const struct in46_addr *dns2 = &apn->v4.cfg.dns[1];
|
||||
uint8_t *start = resp->tail;
|
||||
const struct ipcp_hdr *ipcp;
|
||||
uint16_t ipcp_len;
|
||||
uint8_t *len1, *len2;
|
||||
unsigned int len_appended;
|
||||
ptrdiff_t consumed;
|
||||
size_t remain;
|
||||
|
||||
if (!peer_v4) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "IPCP but no IPv4 type ?!?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ipcp = (const struct ipcp_hdr *)pco_elem->data;
|
||||
consumed = (pco_elem->data - &pdp->pco_req.v[0]);
|
||||
remain = sizeof(pdp->pco_req.v) - consumed;
|
||||
ipcp_len = osmo_load16be(&ipcp->len);
|
||||
if (remain < 0 || remain < ipcp_len) {
|
||||
LOGPPDP(LOGL_ERROR, pdp, "Malformed IPCP, ignoring\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Three byte T16L header */
|
||||
msgb_put_u16(resp, 0x8021); /* IPCP */
|
||||
len1 = msgb_put(resp, 1); /* Length of contents: delay */
|
||||
|
||||
msgb_put_u8(resp, 0x02); /* ACK */
|
||||
msgb_put_u8(resp, ipcp->id); /* ID: Needs to match request */
|
||||
msgb_put_u8(resp, 0x00); /* Length MSB */
|
||||
len2 = msgb_put(resp, 1); /* Length LSB: delay */
|
||||
|
||||
if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) {
|
||||
msgb_put_u8(resp, 0x81); /* DNS1 Tag */
|
||||
msgb_put_u8(resp, 2 + dns1->len); /* DNS1 Length, incl. TL */
|
||||
msgb_put_u32(resp, ntohl(dns1->v4.s_addr));
|
||||
}
|
||||
|
||||
if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) {
|
||||
msgb_put_u8(resp, 0x83); /* DNS2 Tag */
|
||||
msgb_put_u8(resp, 2 + dns2->len); /* DNS2 Length, incl. TL */
|
||||
msgb_put_u32(resp, ntohl(dns2->v4.s_addr));
|
||||
}
|
||||
|
||||
/* patch in length values */
|
||||
len_appended = resp->tail - start;
|
||||
*len1 = len_appended - 3;
|
||||
*len2 = len_appended - 3;
|
||||
}
|
||||
|
||||
static void process_pco_element_dns_ipv6(const struct pco_element *pco_elem, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
unsigned int i;
|
||||
const uint8_t *tail = resp->tail;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
|
||||
const struct in46_addr *i46a = &apn->v6.cfg.dns[i];
|
||||
if (i46a->len != 16)
|
||||
continue;
|
||||
msgb_t16lv_put(resp, PCO_P_DNS_IPv6_ADDR, i46a->len, i46a->v6.s6_addr);
|
||||
}
|
||||
if (resp->tail == tail)
|
||||
LOGPPDP(LOGL_NOTICE, pdp, "MS requested IPv6 DNS, but APN has none configured\n");
|
||||
}
|
||||
|
||||
static void process_pco_element_dns_ipv4(const struct pco_element *pco_elem, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
unsigned int i;
|
||||
const uint8_t *tail = resp->tail;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
|
||||
const struct in46_addr *i46a = &apn->v4.cfg.dns[i];
|
||||
if (i46a->len != 4)
|
||||
continue;
|
||||
msgb_t16lv_put(resp, PCO_P_DNS_IPv4_ADDR, i46a->len, (uint8_t *)&i46a->v4);
|
||||
}
|
||||
if (resp->tail == tail)
|
||||
LOGPPDP(LOGL_NOTICE, pdp, "MS requested IPv4 DNS, but APN has none configured\n");
|
||||
}
|
||||
|
||||
static void process_pco_element(const struct pco_element *pco_elem, struct msgb *resp,
|
||||
const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
uint16_t protocol_id = osmo_load16be(&pco_elem->protocol_id);
|
||||
|
||||
LOGPPDP(LOGL_DEBUG, pdp, "PCO Protocol 0x%04x\n", protocol_id);
|
||||
switch (protocol_id) {
|
||||
case PCO_P_PAP:
|
||||
process_pco_element_pap(pco_elem, resp, apn, pdp);
|
||||
break;
|
||||
case PCO_P_IPCP:
|
||||
process_pco_element_ipcp(pco_elem, resp, apn, pdp);
|
||||
break;
|
||||
case PCO_P_DNS_IPv6_ADDR:
|
||||
process_pco_element_dns_ipv6(pco_elem, resp, apn, pdp);
|
||||
break;
|
||||
case PCO_P_DNS_IPv4_ADDR:
|
||||
process_pco_element_dns_ipv4(pco_elem, resp, apn, pdp);
|
||||
break;
|
||||
default:
|
||||
LOGPPDP(LOGL_INFO, pdp, "Unknown/Unimplemented PCO Protocol 0x%04x: %s\n",
|
||||
protocol_id, osmo_hexdump_nospc(pco_elem->data, pco_elem->length));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* process one PCO request from a MS/UE, putting together the proper responses */
|
||||
void process_pco(const struct apn_ctx *apn, struct pdp_t *pdp)
|
||||
{
|
||||
struct msgb *resp = msgb_alloc(256, "PCO.resp");
|
||||
const struct ul255_t *pco = &pdp->pco_req;
|
||||
const struct pco_element *pco_elem;
|
||||
const uint8_t *cur;
|
||||
|
||||
/* build the header of the PCO response */
|
||||
OSMO_ASSERT(resp);
|
||||
msgb_put_u8(resp, 0x80); /* ext-bit + configuration protocol byte */
|
||||
|
||||
/* iterate over the PCO elements in the request; call process_pco_element() for each */
|
||||
for (cur = pco->v + 1, pco_elem = (const struct pco_element *) cur;
|
||||
cur + sizeof(struct pco_element) <= pco->v + pco->l;
|
||||
cur += pco_elem->length + sizeof(*pco_elem), pco_elem = (const struct pco_element *) cur) {
|
||||
process_pco_element(pco_elem, resp, apn, pdp);
|
||||
}
|
||||
|
||||
/* copy the PCO response msgb and copy its contents over to the PDP context */
|
||||
if (msgb_length(resp) > 1) {
|
||||
memcpy(pdp->pco_neg.v, msgb_data(resp), msgb_length(resp));
|
||||
pdp->pco_neg.l = msgb_length(resp);
|
||||
} else
|
||||
pdp->pco_neg.l = 0;
|
||||
msgb_free(resp);
|
||||
}
|
||||
81
ggsn/pco.h
Normal file
81
ggsn/pco.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../gtp/pdp.h"
|
||||
|
||||
/* 3GPP TS 24.008 10.5.6.3 */
|
||||
enum pco_protocols {
|
||||
PCO_P_LCP = 0xC021,
|
||||
PCO_P_PAP = 0xC023,
|
||||
PCO_P_CHAP = 0xC223,
|
||||
PCO_P_IPCP = 0x8021,
|
||||
PCO_P_PCSCF_ADDR = 0x0001,
|
||||
PCO_P_IM_CN_SS_F = 0x0002,
|
||||
PCO_P_DNS_IPv6_ADDR = 0x0003,
|
||||
PCO_P_POLICY_CTRL_REJ = 0x0004, /* only in Network->MS */
|
||||
PCO_P_MS_SUP_NETREQ_BCI = 0x0005,
|
||||
/* reserved */
|
||||
PCO_P_DSMIPv6_HA_ADDR = 0x0007,
|
||||
PCO_P_DSMIPv6_HN_PREF = 0x0008,
|
||||
PCO_P_DSMIPv6_v4_HA_ADDR= 0x0009,
|
||||
PCO_P_IP_ADDR_VIA_NAS = 0x000a, /* only MS->Network */
|
||||
PCO_P_IPv4_ADDR_VIA_DHCP= 0x000b, /* only MS->Netowrk */
|
||||
PCO_P_PCSCF_IPv4_ADDR = 0x000c,
|
||||
PCO_P_DNS_IPv4_ADDR = 0x000d,
|
||||
PCO_P_MSISDN = 0x000e,
|
||||
PCO_P_IFOM_SUPPORT = 0x000f,
|
||||
PCO_P_IPv4_LINK_MTU = 0x0010,
|
||||
PCO_P_MS_SUPP_LOC_A_TFT = 0x0011,
|
||||
PCO_P_PCSCF_RESEL_SUP = 0x0012, /* only MS->Network */
|
||||
PCO_P_NBIFOM_REQ = 0x0013,
|
||||
PCO_P_NBIFOM_MODE = 0x0014,
|
||||
PCO_P_NONIP_LINK_MTU = 0x0015,
|
||||
PCO_P_APN_RATE_CTRL_SUP = 0x0016,
|
||||
PCO_P_PS_DATA_OFF_UE = 0x0017,
|
||||
PCO_P_REL_DATA_SVC = 0x0018,
|
||||
};
|
||||
|
||||
struct pco_element {
|
||||
uint16_t protocol_id; /* network byte order */
|
||||
uint8_t length; /* length of data below */
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/* RFC 1332 */
|
||||
enum ipcp_options {
|
||||
IPCP_OPT_IPADDR = 3,
|
||||
IPCP_OPT_PRIMARY_DNS = 129,
|
||||
IPCP_OPT_SECONDARY_DNS = 131,
|
||||
};
|
||||
|
||||
struct ipcp_option_hdr {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ipcp_hdr {
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint8_t options[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* RFC 1334, section 3.2. Packet Format */
|
||||
struct pap_element {
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len; /* length including header */
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
enum pap_code {
|
||||
PAP_CODE_AUTH_REQ = 1,
|
||||
PAP_CODE_AUTH_ACK = 2,
|
||||
PAP_CODE_AUTH_NAK = 3,
|
||||
};
|
||||
|
||||
struct apn_ctx;
|
||||
void process_pco(const struct apn_ctx *apn, struct pdp_t *pdp);
|
||||
168
ggsn/sgsn.c
Normal file
168
ggsn/sgsn.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "sgsn.h"
|
||||
#include "ggsn.h"
|
||||
|
||||
|
||||
static bool sgsn_peer_attempt_free(struct sgsn_peer *sgsn)
|
||||
{
|
||||
/* We have to be careful here, since if all pdp ctx for that sgsn were
|
||||
deactivated in-between we sent the Echo Req and receivied the timeout
|
||||
indication, the sgsn (cbp) may be already gone. We need to add some
|
||||
counter reference of echo requets in flight and only free sgsn
|
||||
structures when it goes to zero decreased for all Echo Resp. We do it
|
||||
this way because currently in libgtp there's no understanding of "gsn
|
||||
peer" for which messages are grouped and hence we cannot request
|
||||
libgtp to drop all queued messages for a specific peer. */
|
||||
if (sgsn->tx_msgs_queued) {
|
||||
LOGSGSN(LOGL_INFO, sgsn, "Delaying delete, still %u echo messages queued\n",
|
||||
sgsn->tx_msgs_queued);
|
||||
return false;
|
||||
}
|
||||
llist_del(&sgsn->entry);
|
||||
LOGSGSN(LOGL_INFO, sgsn, "Deleting SGSN\n");
|
||||
talloc_free(sgsn);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sgsn_peer_echo_req(struct sgsn_peer *sgsn)
|
||||
{
|
||||
struct ggsn_ctx *ggsn = sgsn->ggsn;
|
||||
LOGSGSN(LOGL_INFO, sgsn, "Tx Echo Request\n");
|
||||
gtp_echo_req(ggsn->gsn, sgsn->gtp_version, sgsn, &sgsn->addr);
|
||||
sgsn->tx_msgs_queued++;
|
||||
}
|
||||
|
||||
void sgsn_peer_echo_resp(struct sgsn_peer *sgsn, bool timeout)
|
||||
{
|
||||
if (timeout) {
|
||||
LOGSGSN(LOGL_NOTICE, sgsn, "Rx Echo Request timed out!\n");
|
||||
sgsn_peer_drop_all_pdp(sgsn);
|
||||
} else {
|
||||
LOGSGSN(LOGL_INFO, sgsn, "Rx Echo Response\n");
|
||||
}
|
||||
|
||||
/* We decrement it here after dropping all pdps to make sure sgsn was
|
||||
not freed upon last pdp ctx deleted and is still alive now */
|
||||
sgsn->tx_msgs_queued--;
|
||||
if (llist_empty(&sgsn->pdp_list))
|
||||
sgsn_peer_attempt_free(sgsn);
|
||||
}
|
||||
|
||||
void sgsn_echo_timer_start(struct sgsn_peer *sgsn)
|
||||
{
|
||||
if (sgsn->ggsn->cfg.echo_interval == 0)
|
||||
return;
|
||||
sgsn_peer_echo_req(sgsn);
|
||||
osmo_timer_schedule(&sgsn->echo_timer, sgsn->ggsn->cfg.echo_interval, 0);
|
||||
}
|
||||
|
||||
void sgsn_echo_timer_stop(struct sgsn_peer *sgsn)
|
||||
{
|
||||
osmo_timer_del(&sgsn->echo_timer);
|
||||
}
|
||||
|
||||
static void sgsn_echo_timer_cb(void *data)
|
||||
{
|
||||
struct sgsn_peer *sgsn = (struct sgsn_peer *) data;
|
||||
sgsn_echo_timer_start(sgsn);
|
||||
}
|
||||
|
||||
struct sgsn_peer *sgsn_peer_allocate(struct ggsn_ctx *ggsn, struct in_addr *ia, unsigned int gtp_version)
|
||||
{
|
||||
struct sgsn_peer *sgsn;
|
||||
|
||||
sgsn = talloc_zero_size(ggsn, sizeof(struct sgsn_peer));
|
||||
sgsn->ggsn = ggsn;
|
||||
sgsn->addr = *ia;
|
||||
sgsn->gtp_version = gtp_version;
|
||||
sgsn->remote_restart_ctr = -1;
|
||||
INIT_LLIST_HEAD(&sgsn->pdp_list);
|
||||
INIT_LLIST_HEAD(&sgsn->entry);
|
||||
|
||||
osmo_timer_setup(&sgsn->echo_timer, sgsn_echo_timer_cb, sgsn);
|
||||
|
||||
LOGSGSN(LOGL_INFO, sgsn, "Discovered\n");
|
||||
return sgsn;
|
||||
}
|
||||
|
||||
void sgsn_peer_add_pdp_priv(struct sgsn_peer *sgsn, struct pdp_priv_t *pdp_priv)
|
||||
{
|
||||
bool was_empty = llist_empty(&sgsn->pdp_list);
|
||||
pdp_priv->sgsn = sgsn;
|
||||
llist_add(&pdp_priv->entry, &sgsn->pdp_list);
|
||||
if (was_empty)
|
||||
sgsn_echo_timer_start(sgsn);
|
||||
}
|
||||
|
||||
void sgsn_peer_remove_pdp_priv(struct pdp_priv_t* pdp_priv)
|
||||
{
|
||||
struct sgsn_peer *sgsn = pdp_priv->sgsn;
|
||||
llist_del(&pdp_priv->entry);
|
||||
if (sgsn && llist_empty(&sgsn->pdp_list)) {
|
||||
/* No PDP contexts associated to this SGSN, no need to keep it */
|
||||
sgsn_echo_timer_stop(sgsn);
|
||||
/* sgsn may not be freed if there are some messages still queued
|
||||
in libgtp which could return a pointer to it */
|
||||
sgsn_peer_attempt_free(sgsn);
|
||||
}
|
||||
|
||||
pdp_priv->sgsn = NULL;
|
||||
}
|
||||
|
||||
/* High-level function to be called in case a GGSN has disappeared or
|
||||
* otherwise lost state (recovery procedure). It will detach all related pdp ctx
|
||||
* from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can
|
||||
* be kept alive to allow handling later message which contained the Recovery IE. */
|
||||
static unsigned int sgsn_peer_drop_all_pdp_except(struct sgsn_peer *sgsn, struct pdp_priv_t *except)
|
||||
{
|
||||
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));
|
||||
|
||||
struct pdp_priv_t *pdp, *pdp2;
|
||||
llist_for_each_entry_safe(pdp, pdp2, &sgsn->pdp_list, entry) {
|
||||
if (pdp == except)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DGGSN, LOGL_INFO, "SGSN(%s) Dropped %u PDP contexts\n", buf, num);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
unsigned int sgsn_peer_drop_all_pdp(struct sgsn_peer *sgsn)
|
||||
{
|
||||
return sgsn_peer_drop_all_pdp_except(sgsn, NULL);
|
||||
}
|
||||
|
||||
int sgsn_peer_handle_recovery(struct sgsn_peer *sgsn, struct pdp_t *pdp, uint8_t recovery)
|
||||
{
|
||||
struct pdp_priv_t *pdp_priv = NULL;
|
||||
|
||||
if (sgsn->remote_restart_ctr == -1) {
|
||||
/* First received ECHO RESPONSE, note the restart ctr */
|
||||
sgsn->remote_restart_ctr = recovery;
|
||||
} else if (sgsn->remote_restart_ctr != recovery) {
|
||||
/* counter has changed (SGSN restart): release all PDP */
|
||||
LOGSGSN(LOGL_NOTICE, sgsn, "SGSN recovery (%u->%u) pdp=%p, "
|
||||
"releasing all%s PDP contexts\n",
|
||||
sgsn->remote_restart_ctr, recovery, pdp, pdp ? " other" : "");
|
||||
sgsn->remote_restart_ctr = recovery;
|
||||
if (pdp)
|
||||
pdp_priv = pdp->priv;
|
||||
sgsn_peer_drop_all_pdp_except(sgsn, pdp_priv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
46
ggsn/sgsn.h
Normal file
46
ggsn/sgsn.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "../gtp/pdp.h"
|
||||
|
||||
struct ggsn_ctx;
|
||||
struct pdp_priv_t;
|
||||
|
||||
struct sgsn_peer {
|
||||
struct llist_head entry; /* to be included into ggsn_ctx */
|
||||
struct ggsn_ctx *ggsn; /* backpointer to ggsn_ctx */
|
||||
struct in_addr addr; /* Addr of the sgsn peer */
|
||||
unsigned int gtp_version; /* GTP version */
|
||||
int remote_restart_ctr; /* Last received Restart Ctr from sgsn peer, -1 == unknown */
|
||||
/* list of pdp contexts associated with this sgsn */
|
||||
struct llist_head pdp_list;
|
||||
/* Sends echo request towards SGSN on expiration. Echo Resp is received
|
||||
through cb_recovery2(), and echo Req timeout through
|
||||
cb_conf(GTP_ECHO_REQ, EOF, NULL, cbp); */
|
||||
struct osmo_timer_list echo_timer;
|
||||
/* Number of GTP messages in libgtp transmit queue */
|
||||
unsigned int tx_msgs_queued;
|
||||
};
|
||||
|
||||
struct sgsn_peer *sgsn_peer_allocate(struct ggsn_ctx *ggsn, struct in_addr *ia, unsigned int gtp_version);
|
||||
void sgsn_peer_add_pdp_priv(struct sgsn_peer *sgsn, struct pdp_priv_t *pdp_priv);
|
||||
void sgsn_peer_remove_pdp_priv(struct pdp_priv_t *pdp_priv);
|
||||
|
||||
void sgsn_echo_timer_start(struct sgsn_peer *sgsn);
|
||||
void sgsn_echo_timer_stop(struct sgsn_peer *sgsn);
|
||||
|
||||
void sgsn_peer_echo_resp(struct sgsn_peer *sgsn, bool timeout);
|
||||
unsigned int sgsn_peer_drop_all_pdp(struct sgsn_peer *sgsn);
|
||||
int sgsn_peer_handle_recovery(struct sgsn_peer *sgsn, struct pdp_t *pdp, uint8_t recovery);
|
||||
|
||||
#define LOGSGSN(level, sgsn, fmt, args...) { \
|
||||
char _buf[INET_ADDRSTRLEN]; \
|
||||
LOGP(DGGSN, level, "SGSN(%s): " fmt, inet_ntop(AF_INET, &sgsn->addr, _buf, sizeof(_buf)), ## args); \
|
||||
} while (0)
|
||||
@@ -2,14 +2,14 @@
|
||||
# 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=4:0:0
|
||||
LIBVERSION=9:0:3
|
||||
|
||||
lib_LTLIBRARIES = libgtp.la
|
||||
|
||||
include_HEADERS = gtp.h pdp.h gtpie.h
|
||||
include_HEADERS = gtp.h gsn.h pdp.h gtpie.h
|
||||
|
||||
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
||||
|
||||
libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
|
||||
libgtp_la_SOURCES = gtp.c gtp.h gsn.c gsn.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h
|
||||
libgtp_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
|
||||
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)
|
||||
|
||||
596
gtp/gsn.c
Normal file
596
gtp/gsn.c
Normal file
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2010-2011, 2016-2017 Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2015-2017 sysmocom - s.f.m.c. GmbH
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* gtp.c: Contains all GTP functionality. Should be able to handle multiple
|
||||
* tunnels in the same program.
|
||||
*
|
||||
* TODO:
|
||||
* - Do we need to handle fragmentation?
|
||||
*/
|
||||
|
||||
#ifdef __linux__
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
|
||||
#include "../config.h"
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* #include <stdint.h> ISO C99 types */
|
||||
|
||||
#include "pdp.h"
|
||||
#include "gtp.h"
|
||||
#include "gtpie.h"
|
||||
#include "queue.h"
|
||||
|
||||
/* Error reporting functions */
|
||||
|
||||
#define LOGP_WITH_ADDR(ss, level, addr, fmt, args...) \
|
||||
LOGP(ss, level, "addr(%s:%d) " fmt, \
|
||||
inet_ntoa((addr).sin_addr), htons((addr).sin_port), \
|
||||
##args);
|
||||
|
||||
static const struct rate_ctr_desc gsn_ctr_description[] = {
|
||||
[GSN_CTR_ERR_SOCKET] = { "err:socket", "Socket error" },
|
||||
[GSN_CTR_ERR_READFROM] = { "err:readfrom", "readfrom() errors" },
|
||||
[GSN_CTR_ERR_SENDTO] = { "err:sendto", "sendto() errors" },
|
||||
[GSN_CTR_ERR_QUEUEFULL] = { "err:queuefull", "Failed to queue message because queue is full" },
|
||||
[GSN_CTR_ERR_SEQ] = { "err:seq", "Sequence number out of range" },
|
||||
[GSN_CTR_ERR_ADDRESS] = { "err:address", "GSN address conversion failed" },
|
||||
[GSN_CTR_ERR_UNKNOWN_PDP] = { "err:unknown_pdp", "Failed looking up PDP context" },
|
||||
[GSN_CTR_ERR_UNEXPECTED_CAUSE] = { "err:unexpected_cause", "Unexpected cause value received" },
|
||||
[GSN_CTR_ERR_OUT_OF_PDP] = { "err:out_of_pdp", "Out of storage for PDP contexts" },
|
||||
[GSN_CTR_PKT_EMPTY] = { "pkt:empty", "Empty packet received" },
|
||||
[GSN_CTR_PKT_UNSUP] = { "pkt:unsupported", "Unsupported GTP version received" },
|
||||
[GSN_CTR_PKT_TOOSHORT] = { "pkt:too_short", "Packet too short received" },
|
||||
[GSN_CTR_PKT_UNKNOWN] = { "pkt:unknown", "Unknown packet type received" },
|
||||
[GSN_CTR_PKT_UNEXPECT] = { "pkt:unexpected", "Unexpected packet type received" },
|
||||
[GSN_CTR_PKT_DUPLICATE] = { "pkt:duplicate", "Duplicate or unsolicited packet received" },
|
||||
[GSN_CTR_PKT_MISSING] = { "pkt:missing", "Missing IE in packet received" },
|
||||
[GSN_CTR_PKT_INCORRECT] = { "pkt:incorrect", "Incorrect IE in packet received" },
|
||||
[GSN_CTR_PKT_INVALID] = { "pkt:invalid", "Invalid format in packet received" },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc gsn_ctrg_desc = {
|
||||
"gsn",
|
||||
"GSN Statistics",
|
||||
OSMO_STATS_CLASS_PEER,
|
||||
ARRAY_SIZE(gsn_ctr_description),
|
||||
gsn_ctr_description,
|
||||
};
|
||||
static unsigned int gsn_ctr_next_idx = 0;
|
||||
|
||||
/* Global timer definitions for GTP operation, provided for convenience. To make these user configurable, it is convenient to add
|
||||
* gtp_gsn_tdefs as one of your program's osmo_tdef_group entries and call osmo_tdef_vty_init(). */
|
||||
struct osmo_tdef gtp_T_defs[] = {
|
||||
{ .T = GTP_GSN_TIMER_T3_RESPONSE, .default_val = 5, .unit = OSMO_TDEF_S,
|
||||
.desc = "Timer T3-RESPONSE holds the maximum wait time for a response of a request message"
|
||||
},
|
||||
{ .T = GTP_GSN_TIMER_N3_REQUESTS, .default_val = 3, .unit = OSMO_TDEF_CUSTOM,
|
||||
.desc = "Counter N3-REQUESTS holds the maximum number of attempts made by GTP to send a request message"
|
||||
},
|
||||
{ .T = GTP_GSN_TIMER_T3_HOLD_RESPONSE, .default_val = 5 * 3 /* (GTP_GSN_TIMER_T3_RESPONSE * GTP_GSN_TIMER_N3_REQUESTS) */, .unit = OSMO_TDEF_S,
|
||||
.desc = "Time a GTP respoonse message is kept cached to re-transmit it when a duplicate request is received. Value is generally equal to (T3-RESPONSE * N3-REQUESTS) set at the peer"
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/* API Functions */
|
||||
|
||||
/* Deprecated, use gtp_pdp_newpdp() instead */
|
||||
int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi)
|
||||
{
|
||||
int rc;
|
||||
rc = gtp_pdp_newpdp(gsn, pdp, imsi, nsapi, NULL);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp)
|
||||
{
|
||||
if (gsn->cb_delete_context)
|
||||
gsn->cb_delete_context(pdp);
|
||||
return pdp_freepdp(pdp);
|
||||
}
|
||||
|
||||
/* Free pdp and all its secondary PDP contexts. Must be called on the primary PDP context. */
|
||||
int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp)
|
||||
{
|
||||
int n;
|
||||
struct pdp_t *secondary_pdp;
|
||||
OSMO_ASSERT(!pdp->secondary);
|
||||
|
||||
for (n = 0; n < PDP_MAXNSAPI; n++) {
|
||||
if (pdp->secondary_tei[n]) {
|
||||
if (gtp_pdp_getgtp1(gsn, &secondary_pdp,
|
||||
pdp->secondary_tei[n])) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Unknown secondary PDP context\n");
|
||||
continue;
|
||||
}
|
||||
if (pdp != secondary_pdp) {
|
||||
gtp_freepdp(gsn, secondary_pdp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return gtp_freepdp(gsn, pdp);
|
||||
}
|
||||
|
||||
/* gtp_gpdu */
|
||||
|
||||
extern int gtp_fd(struct gsn_t *gsn)
|
||||
{
|
||||
return gsn->fd0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer))
|
||||
{
|
||||
gsn->cb_unsup_ind = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer))
|
||||
{
|
||||
gsn->cb_extheader_ind = cb;
|
||||
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))
|
||||
{
|
||||
gsn->cb_delete_context = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
int (*cb) (int type, int cause,
|
||||
struct pdp_t * pdp, void *cbp))
|
||||
{
|
||||
gsn->cb_conf = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer, uint8_t recovery))
|
||||
{
|
||||
gsn->cb_recovery = cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cb_recovery()
|
||||
* pdp may be NULL if Recovery IE was received from a message independent
|
||||
* of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
|
||||
* local setup. In case pdp is known, caller may want to keep that pdp alive to
|
||||
* handle subsequent msg cb as this specific pdp ctx is still valid according to
|
||||
* specs.
|
||||
*/
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery))
|
||||
{
|
||||
gsn->cb_recovery2 = cb_recovery2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cb_recovery()
|
||||
* pdp may be NULL if Recovery IE was received from a message independent
|
||||
* of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the
|
||||
* local setup. In case pdp is known, caller may want to keep that pdp alive to
|
||||
* handle subsequent msg cb as this specific pdp ctx is still valid according to
|
||||
* specs.
|
||||
*/
|
||||
int gtp_set_cb_recovery3(struct gsn_t *gsn,
|
||||
int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer,
|
||||
struct pdp_t *pdp, uint8_t recovery))
|
||||
{
|
||||
gsn->cb_recovery3 = cb_recovery3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind) (struct pdp_t * pdp,
|
||||
void *pack, unsigned len))
|
||||
{
|
||||
gsn->cb_data_ind = cb_data_ind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_timer_retrans(struct gsn_t *gsn)
|
||||
{
|
||||
/* Retransmit any outstanding packets */
|
||||
/* Remove from queue if maxretrans exceeded */
|
||||
time_t now;
|
||||
struct qmsg_t *qmsg;
|
||||
unsigned int t3_response, n3_requests;
|
||||
|
||||
now = time(NULL);
|
||||
t3_response = osmo_tdef_get(gsn->tdef, GTP_GSN_TIMER_T3_RESPONSE, OSMO_TDEF_S, -1);
|
||||
n3_requests = osmo_tdef_get(gsn->tdef, GTP_GSN_TIMER_N3_REQUESTS, OSMO_TDEF_CUSTOM, -1);
|
||||
|
||||
/* get first element in queue, as long as the timeout of that
|
||||
* element has expired */
|
||||
while ((!queue_getfirst(gsn->queue_req, &qmsg)) &&
|
||||
(qmsg->timeout <= now)) {
|
||||
if (qmsg->retrans > n3_requests) { /* Too many retrans */
|
||||
LOGP(DLGTP, LOGL_NOTICE, "Retransmit req queue timeout of seq %" PRIu16 "\n",
|
||||
qmsg->seq);
|
||||
if (gsn->cb_conf)
|
||||
gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->cbp);
|
||||
queue_freemsg(gsn->queue_req, qmsg);
|
||||
} else {
|
||||
LOGP(DLGTP, LOGL_INFO, "Retransmit (%d) of seq %" PRIu16 "\n",
|
||||
qmsg->retrans, qmsg->seq);
|
||||
if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
|
||||
(struct sockaddr *)&qmsg->peer,
|
||||
sizeof(struct sockaddr_in)) < 0) {
|
||||
rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SENDTO);
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s\n",
|
||||
gsn->fd0, (unsigned long)&qmsg->p,
|
||||
qmsg->l, strerror(errno));
|
||||
}
|
||||
queue_back(gsn->queue_req, qmsg);
|
||||
qmsg->timeout = now + t3_response;
|
||||
qmsg->retrans++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Also clean up reply timeouts */
|
||||
while ((!queue_getfirst(gsn->queue_resp, &qmsg)) &&
|
||||
(qmsg->timeout < now)) {
|
||||
LOGP(DLGTP, LOGL_DEBUG, "Retransmit resp queue seq %"
|
||||
PRIu16 " expired, removing from queue\n", qmsg->seq);
|
||||
queue_freemsg(gsn->queue_resp, qmsg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_timer_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
|
||||
{
|
||||
time_t now, later, diff;
|
||||
struct qmsg_t *qmsg;
|
||||
timeout->tv_usec = 0;
|
||||
|
||||
if (queue_getfirst(gsn->queue_req, &qmsg)) {
|
||||
timeout->tv_sec = 10;
|
||||
} else {
|
||||
now = time(NULL);
|
||||
later = qmsg->timeout;
|
||||
timeout->tv_sec = later - now;
|
||||
if (timeout->tv_sec < 0)
|
||||
timeout->tv_sec = 0; /* No negative allowed */
|
||||
if (timeout->tv_sec > 10)
|
||||
timeout->tv_sec = 10; /* Max sleep for 10 sec */
|
||||
}
|
||||
|
||||
if (queue_getfirst(gsn->queue_resp, &qmsg)) {
|
||||
/* already set by queue_req, do nothing */
|
||||
} else { /* trigger faster if earlier timeout exists in queue_resp */
|
||||
now = time(NULL);
|
||||
later = qmsg->timeout;
|
||||
diff = later - now;
|
||||
if (diff < 0)
|
||||
diff = 0;
|
||||
if (diff < timeout->tv_sec)
|
||||
timeout->tv_sec = diff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gtp_queue_timer_start(struct gsn_t *gsn)
|
||||
{
|
||||
struct timeval next;
|
||||
|
||||
/* Retrieve next retransmission as timeval */
|
||||
queue_timer_retranstimeout(gsn, &next);
|
||||
|
||||
/* re-schedule the timer */
|
||||
osmo_timer_schedule(&gsn->queue_timer, next.tv_sec, next.tv_usec/1000);
|
||||
}
|
||||
|
||||
/* timer callback for libgtp retransmission and ping */
|
||||
static void queue_timer_cb(void *data)
|
||||
{
|
||||
struct gsn_t *gsn = data;
|
||||
|
||||
/* do all the retransmissions as needed */
|
||||
queue_timer_retrans(gsn);
|
||||
|
||||
gtp_queue_timer_start(gsn);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief clear the request and response queue. Useful for debugging to reset "some" state.
|
||||
* @param gsn The GGSN instance
|
||||
*/
|
||||
void gtp_clear_queues(struct gsn_t *gsn)
|
||||
{
|
||||
struct qmsg_t *qmsg;
|
||||
|
||||
LOGP(DLGTP, LOGL_INFO, "Clearing req & resp retransmit queues\n");
|
||||
while (!queue_getfirst(gsn->queue_req, &qmsg)) {
|
||||
queue_freemsg(gsn->queue_req, qmsg);
|
||||
}
|
||||
|
||||
while (!queue_getfirst(gsn->queue_resp, &qmsg)) {
|
||||
queue_freemsg(gsn->queue_resp, qmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform restoration and recovery error handling as described in 29.060 */
|
||||
static void log_restart(struct gsn_t *gsn)
|
||||
{
|
||||
FILE *f;
|
||||
int i, rc;
|
||||
int counter = 0;
|
||||
char *filename;
|
||||
|
||||
filename = talloc_asprintf(NULL, "%s/%s", gsn->statedir, RESTART_FILE);
|
||||
OSMO_ASSERT(filename);
|
||||
|
||||
/* We try to open file. On failure we will later try to create file */
|
||||
if (!(f = fopen(filename, "r"))) {
|
||||
LOGP(DLGTP, LOGL_NOTICE,
|
||||
"State information file (%s) not found. Creating new file.\n",
|
||||
filename);
|
||||
} else {
|
||||
rc = fscanf(f, "%d", &counter);
|
||||
if (rc != 1) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fscanf failed to read counter value\n");
|
||||
goto close_file;
|
||||
}
|
||||
if (fclose(f)) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fclose failed: Error = %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
gsn->restart_counter = (unsigned char)counter;
|
||||
gsn->restart_counter++;
|
||||
|
||||
/* Keep the umask closely wrapped around our fopen() call in case the
|
||||
* log outputs cause file creation. */
|
||||
i = umask(022);
|
||||
f = fopen(filename, "w");
|
||||
umask(i);
|
||||
if (!f) {
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fopen(path=%s, mode=%s) failed: Error = %s\n", filename,
|
||||
"w", strerror(errno));
|
||||
goto free_filename;
|
||||
}
|
||||
|
||||
fprintf(f, "%d\n", gsn->restart_counter);
|
||||
close_file:
|
||||
if (fclose(f))
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"fclose failed: Error = %s\n", strerror(errno));
|
||||
free_filename:
|
||||
talloc_free(filename);
|
||||
}
|
||||
|
||||
int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
|
||||
LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started at %s\n", inet_ntoa(*listen));
|
||||
|
||||
*gsn = calloc(sizeof(struct gsn_t), 1); /* TODO */
|
||||
|
||||
(*gsn)->statedir = statedir;
|
||||
log_restart(*gsn);
|
||||
|
||||
/* Initialise sequence number */
|
||||
(*gsn)->seq_next = (*gsn)->restart_counter * 1024;
|
||||
|
||||
/* Initialize timers: */
|
||||
(*gsn)->tdef = gtp_T_defs;
|
||||
/* Small hack to properly reset tdef for old clients not using the tdef_group: */
|
||||
OSMO_ASSERT(gtp_T_defs[0].default_val != 0);
|
||||
if (gtp_T_defs[0].val == 0)
|
||||
osmo_tdefs_reset((*gsn)->tdef);
|
||||
|
||||
|
||||
/* Initialise request retransmit queue */
|
||||
queue_new(&(*gsn)->queue_req);
|
||||
queue_new(&(*gsn)->queue_resp);
|
||||
|
||||
/* Initialise pdp table */
|
||||
pdp_init(*gsn);
|
||||
|
||||
/* Initialize internal queue timer */
|
||||
osmo_timer_setup(&(*gsn)->queue_timer, queue_timer_cb, *gsn);
|
||||
|
||||
/* Initialize counter group: */
|
||||
(*gsn)->ctrg = rate_ctr_group_alloc(NULL, &gsn_ctrg_desc, gsn_ctr_next_idx++);
|
||||
|
||||
/* Initialise call back functions */
|
||||
(*gsn)->cb_create_context_ind = 0;
|
||||
(*gsn)->cb_delete_context = 0;
|
||||
(*gsn)->cb_unsup_ind = 0;
|
||||
(*gsn)->cb_conf = 0;
|
||||
(*gsn)->cb_data_ind = 0;
|
||||
|
||||
/* Store function parameters */
|
||||
(*gsn)->gsnc = *listen;
|
||||
(*gsn)->gsnu = *listen;
|
||||
(*gsn)->mode = mode;
|
||||
|
||||
/* Create GTP version 0 socket */
|
||||
if (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"GTPv0 socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
|
||||
AF_INET, SOCK_DGRAM, 0, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr = *listen; /* Same IP for user traffic and signalling */
|
||||
addr.sin_port = htons(GTP0_PORT);
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
addr.sin_len = sizeof(addr);
|
||||
#endif
|
||||
|
||||
if (bind((*gsn)->fd0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
|
||||
"bind(fd0=%d) failed: Error = %s\n",
|
||||
(*gsn)->fd0, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Create GTP version 1 control plane socket */
|
||||
if (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"GTPv1 control plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
|
||||
AF_INET, SOCK_DGRAM, 0, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr = *listen; /* Same IP for user traffic and signalling */
|
||||
addr.sin_port = htons(GTP1C_PORT);
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
addr.sin_len = sizeof(addr);
|
||||
#endif
|
||||
|
||||
if (bind((*gsn)->fd1c, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
|
||||
"bind(fd1c=%d) failed: Error = %s\n",
|
||||
(*gsn)->fd1c, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Create GTP version 1 user plane socket */
|
||||
if (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"GTPv1 user plane socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n",
|
||||
AF_INET, SOCK_DGRAM, 0, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr = *listen; /* Same IP for user traffic and signalling */
|
||||
addr.sin_port = htons(GTP1U_PORT);
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
addr.sin_len = sizeof(addr);
|
||||
#endif
|
||||
|
||||
if (bind((*gsn)->fd1u, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
rate_ctr_inc2((*gsn)->ctrg, GSN_CTR_ERR_SOCKET);
|
||||
LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr,
|
||||
"bind(fd1u=%d) failed: Error = %s\n",
|
||||
(*gsn)->fd1u, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Start internal queue timer */
|
||||
gtp_queue_timer_start(*gsn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_free(struct gsn_t *gsn)
|
||||
{
|
||||
|
||||
/* Cleanup internal queue timer */
|
||||
osmo_timer_del(&gsn->queue_timer);
|
||||
|
||||
/* Clean up retransmit queues */
|
||||
queue_free(gsn->queue_req);
|
||||
queue_free(gsn->queue_resp);
|
||||
|
||||
close(gsn->fd0);
|
||||
close(gsn->fd1c);
|
||||
close(gsn->fd1u);
|
||||
|
||||
rate_ctr_group_free(gsn->ctrg);
|
||||
|
||||
free(gsn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API: Register create context indication callback */
|
||||
int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind) (struct pdp_t *
|
||||
pdp))
|
||||
{
|
||||
gsn->cb_create_context_ind = cb_create_context_ind;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_retrans(struct gsn_t *gsn)
|
||||
{
|
||||
/* dummy API, deprecated. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout)
|
||||
{
|
||||
timeout->tv_sec = 24*60*60;
|
||||
timeout->tv_usec = 0;
|
||||
/* dummy API, deprecated. Return a huge timer to do nothing */
|
||||
return 0;
|
||||
}
|
||||
181
gtp/gsn.h
Normal file
181
gtp/gsn.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GSN_H
|
||||
#define _GSN_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#include "pdp.h"
|
||||
|
||||
#define GTP_MODE_GGSN 1
|
||||
#define GTP_MODE_SGSN 2
|
||||
|
||||
#define RESTART_FILE "gsn_restart"
|
||||
|
||||
extern struct osmo_tdef gtp_T_defs[];
|
||||
|
||||
/* ***********************************************************
|
||||
* Information storage for each gsn instance
|
||||
*
|
||||
* Normally each instance of the application corresponds to
|
||||
* one instance of a gsn.
|
||||
*
|
||||
* In order to avoid global variables in the application, and
|
||||
* also in order to allow several instances of a gsn in the same
|
||||
* application this struct is provided in order to store all
|
||||
* relevant information related to the gsn.
|
||||
*
|
||||
* Note that this does not include information storage for '
|
||||
* each pdp context. This is stored in another struct.
|
||||
*************************************************************/
|
||||
|
||||
enum gsn_rate_ctr_keys {
|
||||
GSN_CTR_ERR_SOCKET,
|
||||
GSN_CTR_ERR_READFROM, /* Number of readfrom errors */
|
||||
GSN_CTR_ERR_SENDTO, /* Number of sendto errors */
|
||||
GSN_CTR_ERR_QUEUEFULL, /* Number of times queue was full */
|
||||
GSN_CTR_ERR_SEQ, /* Number of seq out of range */
|
||||
GSN_CTR_ERR_ADDRESS, /* GSN address conversion failed */
|
||||
GSN_CTR_ERR_UNKNOWN_PDP, /* GSN address conversion failed */
|
||||
GSN_CTR_ERR_UNEXPECTED_CAUSE, /* Unexpected cause value received */
|
||||
GSN_CTR_ERR_OUT_OF_PDP, /* Out of storage for PDP contexts */
|
||||
GSN_CTR_PKT_EMPTY, /* Number of empty packets */
|
||||
GSN_CTR_PKT_UNSUP, /* Number of unsupported version 29.60 11.1.1 */
|
||||
GSN_CTR_PKT_TOOSHORT, /* Number of too short headers 29.60 11.1.2 */
|
||||
GSN_CTR_PKT_UNKNOWN, /* Number of unknown messages 29.60 11.1.3 */
|
||||
GSN_CTR_PKT_UNEXPECT, /* Number of unexpected messages 29.60 11.1.4 */
|
||||
GSN_CTR_PKT_DUPLICATE, /* Number of duplicate or unsolicited replies */
|
||||
GSN_CTR_PKT_MISSING, /* Number of missing information field messages */
|
||||
GSN_CTR_PKT_INCORRECT, /* Number of incorrect information field messages */
|
||||
GSN_CTR_PKT_INVALID, /* Number of invalid message format messages */
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.006 14.1, 14,2 */
|
||||
enum gtp_gsn_timers {
|
||||
GTP_GSN_TIMER_T3_RESPONSE = 3,
|
||||
GTP_GSN_TIMER_N3_REQUESTS = 1003,
|
||||
GTP_GSN_TIMER_T3_HOLD_RESPONSE = -3,
|
||||
};
|
||||
|
||||
struct gsn_t {
|
||||
/* Parameters related to the network interface */
|
||||
|
||||
int fd0; /* GTP0 file descriptor */
|
||||
int fd1c; /* GTP1 control plane file descriptor */
|
||||
int fd1u; /* GTP0 user plane file descriptor */
|
||||
int mode; /* Mode of operation: GGSN or SGSN */
|
||||
struct in_addr gsnc; /* IP address of this gsn for signalling */
|
||||
struct in_addr gsnu; /* IP address of this gsn for user traffic */
|
||||
|
||||
/* Parameters related to signalling messages */
|
||||
uint16_t seq_next; /* Next sequence number to use */
|
||||
int seq_first; /* First packet in queue (oldest timeout) */
|
||||
int seq_last; /* Last packet in queue (youngest timeout) */
|
||||
|
||||
unsigned char restart_counter; /* Increment on restart. Stored on disk */
|
||||
char *statedir; /* Disk location for permanent storage */
|
||||
void *priv; /* used by libgtp users to attach their own state) */
|
||||
struct queue_t *queue_req; /* Request queue */
|
||||
struct queue_t *queue_resp; /* Response queue */
|
||||
|
||||
struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
|
||||
struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
|
||||
struct osmo_timer_list queue_timer; /* internal queue_{req,resp} timer */
|
||||
|
||||
/* Call back functions */
|
||||
int (*cb_delete_context) (struct pdp_t *);
|
||||
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);
|
||||
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
|
||||
int (*cb_recovery3) (struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery);
|
||||
|
||||
/* Counters */
|
||||
struct rate_ctr_group *ctrg;
|
||||
|
||||
/* Timers: */
|
||||
struct osmo_tdef *tdef;
|
||||
};
|
||||
|
||||
/* External API functions */
|
||||
|
||||
extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode);
|
||||
|
||||
extern int gtp_free(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
|
||||
extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
extern int gtp_freepdp_teardown(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp);
|
||||
|
||||
extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind) (struct
|
||||
pdp_t *
|
||||
pdp));
|
||||
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind) (struct pdp_t * pdp,
|
||||
void *pack, unsigned len));
|
||||
extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
|
||||
int (*cb_delete_context) (struct pdp_t *
|
||||
pdp));
|
||||
/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
|
||||
int (*cb_create_context) (struct pdp_t* pdp)); */
|
||||
|
||||
extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
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));
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery3() instead, to obtain gsn handling the recovery");
|
||||
int gtp_set_cb_recovery3(struct gsn_t *gsn,
|
||||
int (*cb) (struct gsn_t * gsn, struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery));
|
||||
void gtp_clear_queues(struct gsn_t *gsn);
|
||||
extern int gtp_fd(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_retrans(struct gsn_t *gsn) OSMO_DEPRECATED("This API is a no-op, libgtp already does the job internally");
|
||||
extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) OSMO_DEPRECATED("This API is a no-op and will return a 1 day timeout");
|
||||
|
||||
/* Internal APIs: */
|
||||
void gtp_queue_timer_start(struct gsn_t *gsn);
|
||||
|
||||
#endif /* !_GSN_H */
|
||||
144
gtp/gtp.h
144
gtp/gtp.h
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 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.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GTP_H
|
||||
#define _GTP_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/defs.h>
|
||||
|
||||
#define GTP_MODE_GGSN 1
|
||||
#define GTP_MODE_SGSN 2
|
||||
#include "gtpie.h"
|
||||
#include "pdp.h"
|
||||
#include "gsn.h"
|
||||
|
||||
#define GTP0_PORT 3386
|
||||
#define GTP1C_PORT 2123
|
||||
@@ -28,12 +28,10 @@
|
||||
#define GTP1_HEADER_SIZE_SHORT 8
|
||||
#define GTP1_HEADER_SIZE_LONG 12
|
||||
|
||||
#define NAMESIZE 1024
|
||||
#define SYSLOG_PRINTSIZE 255
|
||||
#define ERRMSG_SIZE 255
|
||||
|
||||
#define RESTART_FILE "gsn_restart"
|
||||
#define NAMESIZE 1024
|
||||
|
||||
/* GTP version 1 extension header type definitions. */
|
||||
#define GTP_EXT_PDCP_PDU 0xC0 /* PDCP PDU Number */
|
||||
|
||||
@@ -82,6 +80,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 */
|
||||
@@ -142,7 +141,7 @@ struct ul66_t;
|
||||
struct ul16_t;
|
||||
struct pdp_t;
|
||||
|
||||
/* GTP 0 header.
|
||||
/* GTP 0 header.
|
||||
* Explanation to some of the fields:
|
||||
* SNDCP NPDU Number flag = 0 except for inter SGSN handover situations
|
||||
* SNDCP N-PDU LCC Number 0 = 0xff except for inter SGSN handover situations
|
||||
@@ -227,97 +226,13 @@ union gtp_packet {
|
||||
struct gtp1_packet_long gtp1l;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ***********************************************************
|
||||
* Information storage for each gsn instance
|
||||
*
|
||||
* Normally each instance of the application corresponds to
|
||||
* one instance of a gsn.
|
||||
*
|
||||
* In order to avoid global variables in the application, and
|
||||
* also in order to allow several instances of a gsn in the same
|
||||
* application this struct is provided in order to store all
|
||||
* relevant information related to the gsn.
|
||||
*
|
||||
* Note that this does not include information storage for '
|
||||
* each pdp context. This is stored in another struct.
|
||||
*************************************************************/
|
||||
|
||||
struct gsn_t {
|
||||
/* Parameters related to the network interface */
|
||||
|
||||
int fd0; /* GTP0 file descriptor */
|
||||
int fd1c; /* GTP1 control plane file descriptor */
|
||||
int fd1u; /* GTP0 user plane file descriptor */
|
||||
int mode; /* Mode of operation: GGSN or SGSN */
|
||||
struct in_addr gsnc; /* IP address of this gsn for signalling */
|
||||
struct in_addr gsnu; /* IP address of this gsn for user traffic */
|
||||
|
||||
/* Parameters related to signalling messages */
|
||||
uint16_t seq_next; /* Next sequence number to use */
|
||||
int seq_first; /* First packet in queue (oldest timeout) */
|
||||
int seq_last; /* Last packet in queue (youngest timeout) */
|
||||
|
||||
unsigned char restart_counter; /* Increment on restart. Stored on disk */
|
||||
char *statedir; /* Disk location for permanent storage */
|
||||
void *priv; /* used by libgtp users to attach their own state) */
|
||||
struct queue_t *queue_req; /* Request queue */
|
||||
struct queue_t *queue_resp; /* Response queue */
|
||||
|
||||
/* Call back functions */
|
||||
int (*cb_delete_context) (struct pdp_t *);
|
||||
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_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);
|
||||
int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery);
|
||||
|
||||
/* Counters */
|
||||
|
||||
uint64_t err_socket; /* Number of socket errors */
|
||||
uint64_t err_readfrom; /* Number of readfrom errors */
|
||||
uint64_t err_sendto; /* Number of sendto errors */
|
||||
uint64_t err_memcpy; /* Number of memcpy */
|
||||
uint64_t err_queuefull; /* Number of times queue was full */
|
||||
uint64_t err_seq; /* Number of seq out of range */
|
||||
uint64_t err_address; /* GSN address conversion failed */
|
||||
uint64_t err_unknownpdp; /* GSN address conversion failed */
|
||||
uint64_t err_unknowntid; /* Application supplied unknown imsi+nsapi */
|
||||
uint64_t err_cause; /* Unexpected cause value received */
|
||||
uint64_t err_outofpdp; /* Out of storage for PDP contexts */
|
||||
|
||||
uint64_t empty; /* Number of empty packets */
|
||||
uint64_t unsup; /* Number of unsupported version 29.60 11.1.1 */
|
||||
uint64_t tooshort; /* Number of too short headers 29.60 11.1.2 */
|
||||
uint64_t unknown; /* Number of unknown messages 29.60 11.1.3 */
|
||||
uint64_t unexpect; /* Number of unexpected messages 29.60 11.1.4 */
|
||||
uint64_t duplicate; /* Number of duplicate or unsolicited replies */
|
||||
uint64_t missing; /* Number of missing information field messages */
|
||||
uint64_t incorrect; /* Number of incorrect information field messages */
|
||||
uint64_t invalid; /* Number of invalid message format messages */
|
||||
};
|
||||
|
||||
/* External API functions */
|
||||
|
||||
extern const char *gtp_version();
|
||||
extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
|
||||
int mode);
|
||||
|
||||
extern int gtp_free(struct gsn_t *gsn);
|
||||
|
||||
extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp,
|
||||
uint64_t imsi, uint8_t nsapi);
|
||||
extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp);
|
||||
|
||||
extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *cbp);
|
||||
|
||||
extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
|
||||
int (*cb_create_context_ind) (struct
|
||||
pdp_t *
|
||||
pdp));
|
||||
|
||||
extern int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
int cause);
|
||||
|
||||
@@ -333,45 +248,15 @@ extern int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
void *pack, unsigned len);
|
||||
|
||||
extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
|
||||
int (*cb_data_ind) (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_fd(struct gsn_t *gsn);
|
||||
extern int gtp_decaps0(struct gsn_t *gsn);
|
||||
extern int gtp_decaps1c(struct gsn_t *gsn);
|
||||
extern int gtp_decaps1u(struct gsn_t *gsn);
|
||||
extern int gtp_retrans(struct gsn_t *gsn);
|
||||
extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout);
|
||||
|
||||
extern int gtp_set_cb_delete_context(struct gsn_t *gsn,
|
||||
int (*cb_delete_context) (struct pdp_t *
|
||||
pdp));
|
||||
/*extern int gtp_set_cb_create_context(struct gsn_t *gsn,
|
||||
int (*cb_create_context) (struct pdp_t* pdp)); */
|
||||
|
||||
extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer));
|
||||
|
||||
extern int gtp_set_cb_conf(struct gsn_t *gsn,
|
||||
int (*cb) (int type, int cause, struct pdp_t * pdp,
|
||||
void *cbp));
|
||||
|
||||
int gtp_set_cb_recovery(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
uint8_t recovery))
|
||||
OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery");
|
||||
int gtp_set_cb_recovery2(struct gsn_t *gsn,
|
||||
int (*cb) (struct sockaddr_in * peer,
|
||||
struct pdp_t * pdp,
|
||||
uint8_t recovery));
|
||||
|
||||
void gtp_clear_queues(struct gsn_t *gsn);
|
||||
|
||||
/* Internal functions (not part of the API */
|
||||
/* Internal functions (not part of the API) */
|
||||
|
||||
extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
|
||||
struct in_addr *inetaddrs);
|
||||
@@ -426,5 +311,6 @@ extern int eua2ipv4(struct in_addr *dst, struct ul66_t *eua);
|
||||
extern int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna);
|
||||
extern int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src);
|
||||
extern const char *imsi_gtp2str(const uint64_t *imsi);
|
||||
extern uint64_t gtp_imsi_str2gtp(const char *str);
|
||||
|
||||
#endif /* !_GTP_H */
|
||||
|
||||
10
gtp/gtpie.c
10
gtp/gtpie.c
@@ -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
|
||||
|
||||
164
gtp/pdp.c
164
gtp/pdp.c
@@ -31,14 +31,7 @@
|
||||
#include "pdp.h"
|
||||
#include "gtp.h"
|
||||
#include "lookupa.h"
|
||||
|
||||
/* ***********************************************************
|
||||
* Global variables TODO: most should be moved to gsn_t
|
||||
*************************************************************/
|
||||
|
||||
static struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
|
||||
static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
/* struct pdp_t* haship[PDP_MAX]; Hash table for IP and network interface */
|
||||
#include "queue.h"
|
||||
|
||||
/* ***********************************************************
|
||||
* Functions related to PDP storage
|
||||
@@ -112,11 +105,16 @@ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
|
||||
*
|
||||
*************************************************************/
|
||||
|
||||
int pdp_init()
|
||||
static struct gsn_t *g_gsn;
|
||||
|
||||
int pdp_init(struct gsn_t *gsn)
|
||||
{
|
||||
memset(&pdpa, 0, sizeof(pdpa));
|
||||
memset(&hashtid, 0, sizeof(hashtid));
|
||||
/* memset(&haship, 0, sizeof(haship)); */
|
||||
if (!g_gsn) {
|
||||
g_gsn = gsn;
|
||||
} else {
|
||||
LOGP(DLGTP, LOGL_FATAL, "This interface is depreacted and doesn't support multiple GGSN!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -124,6 +122,13 @@ int pdp_init()
|
||||
int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
||||
struct pdp_t *pdp_old)
|
||||
{
|
||||
return gtp_pdp_newpdp(g_gsn, pdp, imsi, nsapi, pdp_old);
|
||||
}
|
||||
|
||||
int gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
||||
struct pdp_t *pdp_old)
|
||||
{
|
||||
struct pdp_t *pdpa = gsn->pdpa;
|
||||
int n;
|
||||
for (n = 0; n < PDP_MAX; n++) { /* TODO: Need to do better than linear search */
|
||||
if (pdpa[n].inuse == 0) {
|
||||
@@ -133,6 +138,7 @@ int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
||||
else
|
||||
memset(*pdp, 0, sizeof(struct pdp_t));
|
||||
(*pdp)->inuse = 1;
|
||||
(*pdp)->gsn = gsn;
|
||||
(*pdp)->imsi = imsi;
|
||||
(*pdp)->nsapi = nsapi;
|
||||
(*pdp)->fllc = (uint16_t) n + 1;
|
||||
@@ -151,7 +157,7 @@ int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
||||
}
|
||||
/* Default: Generate G-PDU sequence numbers on Tx */
|
||||
(*pdp)->tx_gpdu_seq = true;
|
||||
|
||||
INIT_LLIST_HEAD(&(*pdp)->qmsg_list_req);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -160,6 +166,18 @@ int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
||||
|
||||
int pdp_freepdp(struct pdp_t *pdp)
|
||||
{
|
||||
struct qmsg_t *qmsg, *qmsg2;
|
||||
struct pdp_t *pdpa = pdp->gsn->pdpa;
|
||||
int rc;
|
||||
|
||||
/* Remove all enqueued messages belonging to this pdp from req tx transmit
|
||||
queue. queue_freemsg will call llist_del(). */
|
||||
llist_for_each_entry_safe(qmsg, qmsg2, &pdp->qmsg_list_req, entry) {
|
||||
if ((rc = queue_freemsg(pdp->gsn->queue_req, qmsg)))
|
||||
LOGP(DLGTP, LOGL_ERROR,
|
||||
"Failed freeing qmsg from qmsg_list_req during pdp_freepdp()! %d\n", rc);
|
||||
}
|
||||
|
||||
pdp_tiddel(pdp);
|
||||
|
||||
/* Remove any references in primary context */
|
||||
@@ -174,12 +192,20 @@ int pdp_freepdp(struct pdp_t *pdp)
|
||||
|
||||
int pdp_getpdp(struct pdp_t **pdp)
|
||||
{
|
||||
*pdp = &pdpa[0];
|
||||
*pdp = &g_gsn->pdpa[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl)
|
||||
{
|
||||
return gtp_pdp_getgtp0(g_gsn, pdp, fl);
|
||||
}
|
||||
|
||||
|
||||
int gtp_pdp_getgtp0(struct gsn_t *gsn, struct pdp_t **pdp, uint16_t fl)
|
||||
{
|
||||
struct pdp_t *pdpa = gsn->pdpa;
|
||||
|
||||
if ((fl > PDP_MAX) || (fl < 1)) {
|
||||
return EOF; /* Not found */
|
||||
} else {
|
||||
@@ -194,6 +220,13 @@ int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl)
|
||||
|
||||
int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei)
|
||||
{
|
||||
return gtp_pdp_getgtp1(g_gsn, pdp, tei);
|
||||
}
|
||||
|
||||
int gtp_pdp_getgtp1(struct gsn_t *gsn, struct pdp_t **pdp, uint32_t tei)
|
||||
{
|
||||
struct pdp_t *pdpa = gsn->pdpa;
|
||||
|
||||
if ((tei > PDP_MAX) || (tei < 1)) {
|
||||
return EOF; /* Not found */
|
||||
} else {
|
||||
@@ -209,6 +242,12 @@ int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei)
|
||||
/* get a PDP based on the *peer* address + TEI-Data. Used for matching inbound Error Ind */
|
||||
int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
|
||||
{
|
||||
return gtp_pdp_getgtp1_peer_d(g_gsn, pdp, peer, teid_gn);
|
||||
}
|
||||
|
||||
int gtp_pdp_getgtp1_peer_d(struct gsn_t *gsn, struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
|
||||
{
|
||||
struct pdp_t *pdpa = gsn->pdpa;
|
||||
unsigned int i;
|
||||
|
||||
/* this is O(n) but we don't have (nor want) another hash... */
|
||||
@@ -231,6 +270,7 @@ int pdp_tidhash(uint64_t tid)
|
||||
|
||||
int pdp_tidset(struct pdp_t *pdp, uint64_t tid)
|
||||
{
|
||||
struct pdp_t **hashtid = pdp->gsn->hashtid;
|
||||
int hash = pdp_tidhash(tid);
|
||||
struct pdp_t *pdp2;
|
||||
struct pdp_t *pdp_prev = NULL;
|
||||
@@ -249,6 +289,7 @@ int pdp_tidset(struct pdp_t *pdp, uint64_t tid)
|
||||
|
||||
int pdp_tiddel(struct pdp_t *pdp)
|
||||
{
|
||||
struct pdp_t **hashtid = pdp->gsn->hashtid;
|
||||
int hash = pdp_tidhash(pdp->tid);
|
||||
struct pdp_t *pdp2;
|
||||
struct pdp_t *pdp_prev = NULL;
|
||||
@@ -270,6 +311,12 @@ int pdp_tiddel(struct pdp_t *pdp)
|
||||
|
||||
int pdp_tidget(struct pdp_t **pdp, uint64_t tid)
|
||||
{
|
||||
return gtp_pdp_tidget(g_gsn, pdp, tid);
|
||||
}
|
||||
|
||||
int gtp_pdp_tidget(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t tid)
|
||||
{
|
||||
struct pdp_t **hashtid = gsn->hashtid;
|
||||
int hash = pdp_tidhash(tid);
|
||||
struct pdp_t *pdp2;
|
||||
DEBUGP(DLGTP, "Begin pdp_tidget tid = %"PRIx64"\n", tid);
|
||||
@@ -286,82 +333,15 @@ int pdp_tidget(struct pdp_t **pdp, uint64_t tid)
|
||||
|
||||
int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
|
||||
{
|
||||
return pdp_tidget(pdp,
|
||||
(imsi & 0x0fffffffffffffffull) +
|
||||
((uint64_t) nsapi << 60));
|
||||
return gtp_pdp_getimsi(g_gsn, pdp, imsi, nsapi);
|
||||
}
|
||||
|
||||
/*
|
||||
int pdp_iphash(void* ipif, struct ul66_t *eua) {
|
||||
/#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
|
||||
return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
|
||||
int gtp_pdp_getimsi(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
|
||||
{
|
||||
return gtp_pdp_tidget(gsn, pdp, pdp_gettid(imsi, nsapi));
|
||||
}
|
||||
|
||||
int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
|
||||
int hash;
|
||||
struct pdp_t *pdp2;
|
||||
struct pdp_t *pdp_prev = NULL;
|
||||
|
||||
if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n",
|
||||
(unsigned) ipif, eua->l,
|
||||
eua->v[2], eua->v[3],
|
||||
eua->v[4], eua->v[5]);
|
||||
|
||||
pdp->ipnext = NULL;
|
||||
pdp->ipif = ipif;
|
||||
pdp->eua.l = eua->l;
|
||||
memcpy(pdp->eua.v, eua->v, eua->l);
|
||||
|
||||
hash = pdp_iphash(pdp->ipif, &pdp->eua);
|
||||
|
||||
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
|
||||
pdp_prev = pdp2;
|
||||
if (!pdp_prev)
|
||||
haship[hash] = pdp;
|
||||
else
|
||||
pdp_prev->ipnext = pdp;
|
||||
if (PDP_DEBUG) printf("End pdp_ipset\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pdp_ipdel(struct pdp_t *pdp) {
|
||||
int hash = pdp_iphash(pdp->ipif, &pdp->eua);
|
||||
struct pdp_t *pdp2;
|
||||
struct pdp_t *pdp_prev = NULL;
|
||||
if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
|
||||
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
|
||||
if (pdp2 == pdp) {
|
||||
if (!pdp_prev)
|
||||
haship[hash] = pdp2->ipnext;
|
||||
else
|
||||
pdp_prev->ipnext = pdp2->ipnext;
|
||||
if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
|
||||
return 0;
|
||||
}
|
||||
pdp_prev = pdp2;
|
||||
}
|
||||
if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n");
|
||||
return EOF; /# End of linked list and not found #/
|
||||
}
|
||||
|
||||
int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
|
||||
int hash = pdp_iphash(ipif, eua);
|
||||
struct pdp_t *pdp2;
|
||||
/#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
|
||||
eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/
|
||||
for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
|
||||
if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
|
||||
(memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) {
|
||||
*pdp = pdp2;
|
||||
/#printf("End pdp_ipget. Found\n");#/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
|
||||
(unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
|
||||
return EOF; /# End of linked list and not found #/
|
||||
}
|
||||
*/
|
||||
/* Various conversion functions */
|
||||
|
||||
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
|
||||
@@ -374,3 +354,17 @@ void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid)
|
||||
pdp->imsi = teid & 0x0fffffffffffffffull;
|
||||
pdp->nsapi = (teid & 0xf000000000000000ull) >> 60;
|
||||
}
|
||||
|
||||
/* Count amount of secondary PDP contexts linked to this primary PDP context
|
||||
* (itself included). Must be called on a primary PDP context. */
|
||||
unsigned int pdp_count_secondary(const struct pdp_t *pdp)
|
||||
{
|
||||
unsigned int n;
|
||||
unsigned int count = 0;
|
||||
OSMO_ASSERT(!pdp->secondary);
|
||||
|
||||
for (n = 0; n < PDP_MAXNSAPI; n++)
|
||||
if (pdp->secondary_tei[n])
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
50
gtp/pdp.h
50
gtp/pdp.h
@@ -14,6 +14,10 @@
|
||||
#define _PDP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
struct gsn_t;
|
||||
|
||||
@@ -235,38 +239,42 @@ struct pdp_t {
|
||||
/* to be used by libgtp callers/users (to attach their own private state) */
|
||||
void *priv;
|
||||
|
||||
struct gsn_t *gsn;
|
||||
struct gsn_t *gsn; /* Back pointer to GSN where this pdp ctx belongs to */
|
||||
|
||||
bool tx_gpdu_seq; /* Transmit (true) or suppress G-PDU sequence numbers */
|
||||
|
||||
struct llist_head qmsg_list_req; /* list of req qmsg_t in retrans queue belonging this pdp ctx */
|
||||
};
|
||||
|
||||
/* functions related to pdp_t management */
|
||||
int pdp_init();
|
||||
int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
||||
struct pdp_t *pdp_old);
|
||||
int gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi,
|
||||
uint8_t nsapi, struct pdp_t *pdp_old);
|
||||
int pdp_freepdp(struct pdp_t *pdp);
|
||||
int pdp_getpdp(struct pdp_t **pdp);
|
||||
|
||||
int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl);
|
||||
int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei);
|
||||
int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn);
|
||||
|
||||
int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi);
|
||||
int gtp_pdp_getgtp0(struct gsn_t *gsn, struct pdp_t **pdp, uint16_t fl);
|
||||
int gtp_pdp_getgtp1(struct gsn_t *gsn, struct pdp_t **pdp, uint32_t tei);
|
||||
int gtp_pdp_getgtp1_peer_d(struct gsn_t *gsn, struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn);
|
||||
int gtp_pdp_getimsi(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi);
|
||||
int gtp_pdp_tidget(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t tid);
|
||||
|
||||
int pdp_tidhash(uint64_t tid);
|
||||
int pdp_tidset(struct pdp_t *pdp, uint64_t tid);
|
||||
int pdp_tiddel(struct pdp_t *pdp);
|
||||
int pdp_tidget(struct pdp_t **pdp, uint64_t tid);
|
||||
|
||||
void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid);
|
||||
|
||||
/*
|
||||
int pdp_iphash(void* ipif, struct ul66_t *eua);
|
||||
int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua);
|
||||
int pdp_ipdel(struct pdp_t *pdp);
|
||||
int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua);
|
||||
*/
|
||||
|
||||
uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi);
|
||||
void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid);
|
||||
|
||||
unsigned int pdp_count_secondary(const struct pdp_t *pdp);
|
||||
|
||||
/* Deprecated APIs (support for only 1 GSN per process). Must be used only after first call to gtp_new() and until it is freed. */
|
||||
int pdp_init(struct gsn_t *gsn); /* Use only allowed inside libgtp to keep compatiblity with deprecated APIs defined here. */
|
||||
int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
|
||||
struct pdp_t *pdp_old) OSMO_DEPRECATED("Use gtp_pdp_newpdp() instead");
|
||||
int pdp_getpdp(struct pdp_t **pdp) OSMO_DEPRECATED("Use gsn_t->pdpa field instead");
|
||||
int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl) OSMO_DEPRECATED("Use gtp_pdp_getgtp0() instead");
|
||||
int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei) OSMO_DEPRECATED("Use gtp_pdp_getgtp1() instead");
|
||||
int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn) OSMO_DEPRECATED("Use gtp_pdp_getgtp1_peer_d() instead");
|
||||
int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi) OSMO_DEPRECATED("Use gtp_pdp_getimsi() instead");
|
||||
int pdp_tidget(struct pdp_t **pdp, uint64_t tid) OSMO_DEPRECATED("Use gtp_pdp_tidget() instead");
|
||||
|
||||
|
||||
#endif /* !_PDP_H */
|
||||
|
||||
64
gtp/queue.c
64
gtp/queue.c
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
/*
|
||||
* OsmoGGSN - Gateway GPRS Support Node
|
||||
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
||||
* Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2016 sysmocom - s.f.m.c. GmbH
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -62,7 +62,7 @@ static int queue_seqhash(struct sockaddr_in *peer, uint16_t seq)
|
||||
return seq % QUEUE_HASH_SIZE;
|
||||
}
|
||||
|
||||
/*! \brief Insert a message with given sequence number into the hash
|
||||
/*! \brief Insert a message with given sequence number into the hash.
|
||||
*
|
||||
* This function sets the peer and the seq of the qmsg and then inserts
|
||||
* the qmsg into the queue hash. To do so, it does a hashtable lookup
|
||||
@@ -121,7 +121,10 @@ static int queue_seqdel(struct queue_t *queue, struct qmsg_t *qmsg)
|
||||
return EOF; /* End of linked list and not found */
|
||||
}
|
||||
|
||||
/*! \brief Allocates and initialises new queue structure */
|
||||
/*! Allocates and initialises new queue structure.
|
||||
* \param[out] queue pointer where to store the allocated object. Must be freed with queue_free
|
||||
* \returns zero on success, non-zero on error
|
||||
*/
|
||||
int queue_new(struct queue_t **queue)
|
||||
{
|
||||
if (QUEUE_DEBUG)
|
||||
@@ -138,7 +141,10 @@ int queue_new(struct queue_t **queue)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Deallocates queue structure */
|
||||
/*! Deallocates queue structure.
|
||||
* \param[in] queue pointer previously allocated by queue_new
|
||||
* \returns zero on success, non-zero on error.
|
||||
*/
|
||||
int queue_free(struct queue_t *queue)
|
||||
{
|
||||
if (QUEUE_DEBUG)
|
||||
@@ -149,7 +155,13 @@ int queue_free(struct queue_t *queue)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Add a new message to the queue */
|
||||
/*! Add a new message to the queue.
|
||||
* \param[in] queue pointer previously allocated by queue_new
|
||||
* \param[out] qmsg first message from the queue (if succeeds)
|
||||
* \param[in] peer who sent the message to add
|
||||
* \param[in] seq sequence number of the message to add
|
||||
* \returns zero on success, non-zero on error.
|
||||
*/
|
||||
int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
|
||||
struct sockaddr_in *peer, uint16_t seq)
|
||||
{
|
||||
@@ -160,6 +172,7 @@ int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
|
||||
} else {
|
||||
*qmsg = &queue->qmsga[queue->next];
|
||||
queue_seqset(queue, *qmsg, peer, seq);
|
||||
INIT_LLIST_HEAD(&(*qmsg)->entry);
|
||||
(*qmsg)->state = 1; /* Space taken */
|
||||
(*qmsg)->this = queue->next;
|
||||
(*qmsg)->next = -1; /* End of the queue */
|
||||
@@ -176,7 +189,11 @@ int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Simply remoev a given qmsg_t from the queue
|
||||
|
||||
/*! Remove an element from the queue.
|
||||
* \param[in] queue pointer previously allocated by queue_new
|
||||
* \param[in] qmsg message to free
|
||||
* \returns zero on success, non-zero on error.
|
||||
*
|
||||
* Internally, we first delete the entry from the queue, and then update
|
||||
* up our global queue->first / queue->last pointers. Finally,
|
||||
@@ -190,6 +207,8 @@ int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg)
|
||||
return EOF; /* Not in queue */
|
||||
}
|
||||
|
||||
llist_del(&qmsg->entry);
|
||||
|
||||
queue_seqdel(queue, qmsg);
|
||||
|
||||
if (qmsg->next == -1) /* Are we the last in queue? */
|
||||
@@ -210,7 +229,11 @@ int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Move a given qmsg_t to the end of the queue ?!? */
|
||||
/*! Move a given qmsg_t to the end of the queue.
|
||||
* \param[in] queue pointer previously allocated by queue_new
|
||||
* \param[in] qmsg message to move to the end of the queue
|
||||
* \returns zero on success, non-zero on error.
|
||||
*/
|
||||
int queue_back(struct queue_t *queue, struct qmsg_t *qmsg)
|
||||
{
|
||||
if (QUEUE_DEBUG)
|
||||
@@ -236,7 +259,11 @@ int queue_back(struct queue_t *queue, struct qmsg_t *qmsg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Get the first element in the entire queue */
|
||||
/*! Get the first element in the entire queue.
|
||||
* \param[in] queue pointer previously allocated by queue_new
|
||||
* \param[out] qmsg first message from the queue (if succeeds)
|
||||
* \returns zero on success, non-zero on error.
|
||||
*/
|
||||
int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg)
|
||||
{
|
||||
/*printf("queue_getfirst\n"); */
|
||||
@@ -250,7 +277,13 @@ int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Get a queue entry for a given peer + seq */
|
||||
/*! Get a queue entry for a given peer + seq.
|
||||
* \param[in] queue pointer previously allocated by queue_new
|
||||
* \param[out] qmsg first message from the queue (if succeeds)
|
||||
* \param[in] peer who sent the message to retrieve
|
||||
* \param[in] seq sequence number of the message to retrive
|
||||
* \returns zero on success, non-zero on error.
|
||||
*/
|
||||
int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
|
||||
struct sockaddr_in *peer, uint16_t seq)
|
||||
{
|
||||
@@ -272,7 +305,14 @@ int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
|
||||
return EOF; /* End of linked list and not found */
|
||||
}
|
||||
|
||||
/*! \brief look-up a given seq/peer, return cbp + type and free entry */
|
||||
/*! look-up a given seq/peer, return cbp + type and free entry.
|
||||
* \param[in] queue pointer previously allocated by queue_new
|
||||
* \param[in] peer who sent the message to retrieve
|
||||
* \param[in] seq sequence number of the message to retrive
|
||||
* \param[out] type GTP message type
|
||||
* \param[out] type callback pointer of the message
|
||||
* \returns zero on success, non-zero on error.
|
||||
*/
|
||||
int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
|
||||
uint16_t seq, uint8_t * type, void **cbp)
|
||||
{
|
||||
|
||||
13
gtp/queue.h
13
gtp/queue.h
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -17,9 +17,13 @@
|
||||
#ifndef _QUEUE_H
|
||||
#define _QUEUE_H
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include "gtp.h"
|
||||
|
||||
#define QUEUE_DEBUG 0 /* Print debug information */
|
||||
|
||||
#define QUEUE_SIZE 1024 /* Size of retransmission queue */
|
||||
#define QUEUE_SIZE (PDP_MAX*2) /* Size of retransmission queue */
|
||||
#define QUEUE_HASH_SIZE 65536 /* Size of hash table (2^16) */
|
||||
|
||||
struct qmsg_t { /* Holder for queued packets */
|
||||
@@ -37,6 +41,7 @@ struct qmsg_t { /* Holder for queued packets */
|
||||
int this; /* Pointer to myself */
|
||||
time_t timeout; /* When do we retransmit this packet? */
|
||||
int retrans; /* How many times did we retransmit this? */
|
||||
struct llist_head entry; /* Listed with other qmsg_t belonging to a pdp_t->qmsg_list_req */
|
||||
};
|
||||
|
||||
struct queue_t {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
noinst_LIBRARIES = libmisc.a
|
||||
|
||||
noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h
|
||||
noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h netns.h util.h icmpv6.h checksum.h
|
||||
|
||||
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
|
||||
|
||||
libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c
|
||||
libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c netns.c util.c icmpv6.c checksum.c
|
||||
|
||||
if ENABLE_GTP_KERNEL
|
||||
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
|
||||
|
||||
@@ -24,12 +24,12 @@ static const struct log_info_cat default_categories[] = {
|
||||
[DSGSN] = {
|
||||
.name = "DSGSN",
|
||||
.description = "SGSN Emulator",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DICMP6] = {
|
||||
.name = "DICMP6",
|
||||
.description = "ICMPv6",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include <libgtpnl/gtp.h>
|
||||
#include <libgtpnl/gtpnl.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
@@ -26,27 +25,32 @@
|
||||
|
||||
#include "../lib/tun.h"
|
||||
#include "../lib/syserr.h"
|
||||
#include "../lib/util.h"
|
||||
#include "../lib/ippool.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include "../gtp/gtp.h"
|
||||
|
||||
#include <libgtpnl/gtp.h>
|
||||
#include <libgtpnl/gtpnl.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
|
||||
#include "gtp-kernel.h"
|
||||
|
||||
static void pdp_debug(const char *prefix, const char *devname, struct pdp_t *pdp)
|
||||
{
|
||||
struct in46_addr ia46;
|
||||
char buf4[INET_ADDRSTRLEN], buf6[INET6_ADDRSTRLEN];
|
||||
struct ippoolm_t *peer;
|
||||
struct in_addr ia;
|
||||
|
||||
in46a_from_eua(&pdp->eua, &ia46);
|
||||
buf4[0] = '\0';
|
||||
if ((peer = pdp_get_peer_ipv(pdp, false)))
|
||||
in46a_ntop(&peer->addr, buf4, sizeof(buf4));
|
||||
buf6[0] = '\0';
|
||||
if ((peer = pdp_get_peer_ipv(pdp, true)))
|
||||
in46a_ntop(&peer->addr, buf6, sizeof(buf6));
|
||||
|
||||
gsna2in_addr(&ia, &pdp->gsnrc);
|
||||
|
||||
LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=%s SGSN=%s\n", prefix,
|
||||
LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=(%s,%s) SGSN=%s\n", prefix,
|
||||
devname, pdp->version,
|
||||
pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn,
|
||||
in46a_ntoa(&ia46), inet_ntoa(ia));
|
||||
buf4, buf6, inet_ntoa(ia));
|
||||
}
|
||||
|
||||
static struct {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Minimal ICMPv6 code for generating router advertisements as required by
|
||||
* relevant 3GPP specs for a GGSN with IPv6 PDP contexts */
|
||||
|
||||
/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* The contents of this file may be used under the terms of the GNU
|
||||
* General Public License Version 2, provided that the above copyright
|
||||
@@ -25,8 +25,9 @@
|
||||
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
#include "../lib/ippool.h"
|
||||
#include "../lib/syserr.h"
|
||||
#include "ippool.h"
|
||||
#include "syserr.h"
|
||||
#include "icmpv6.h"
|
||||
#include "config.h"
|
||||
|
||||
/* 29.061 11.2.1.3.4 IPv6 Router Configuration Variables in GGSN */
|
||||
@@ -35,78 +36,68 @@
|
||||
#define GGSN_AdvValidLifetime 0xffffffff /* infinite */
|
||||
#define GGSN_AdvPreferredLifetime 0xffffffff /* infinite */
|
||||
|
||||
struct icmpv6_hdr {
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t csum;
|
||||
} __attribute__ ((packed));
|
||||
/* RFC3307 link-local scope multicast address */
|
||||
const struct in6_addr all_router_mcast_addr = {
|
||||
.s6_addr = { 0xff,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2 }
|
||||
};
|
||||
|
||||
/* RFC4861 Section 4.2 */
|
||||
struct icmpv6_radv_hdr {
|
||||
struct icmpv6_hdr hdr;
|
||||
uint8_t cur_ho_limit;
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
uint8_t res:6,
|
||||
m:1,
|
||||
o:1;
|
||||
#elif BYTE_ORDER == BIG_ENDIAN
|
||||
uint8_t m:1,
|
||||
o:1,
|
||||
res:6;
|
||||
#else
|
||||
# error "Please fix <bits/endian.h>"
|
||||
#endif
|
||||
uint16_t router_lifetime;
|
||||
uint32_t reachable_time;
|
||||
uint32_t retrans_timer;
|
||||
uint8_t options[0];
|
||||
} __attribute__ ((packed));
|
||||
/* Prepends the ipv6 header and returns checksum content */
|
||||
uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr)
|
||||
{
|
||||
uint32_t len;
|
||||
uint16_t skb_csum;
|
||||
struct ip6_hdr *i6h;
|
||||
|
||||
/* RFC4861 Section 4.6 */
|
||||
struct icmpv6_opt_hdr {
|
||||
uint8_t type;
|
||||
/* length in units of 8 octets, including type+len! */
|
||||
uint8_t len;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
/* checksum */
|
||||
skb_csum = csum_partial(msgb_data(msg), msgb_length(msg), 0);
|
||||
len = msgb_length(msg);
|
||||
skb_csum = csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, skb_csum);
|
||||
|
||||
/* RFC4861 Section 4.6.2 */
|
||||
struct icmpv6_opt_prefix {
|
||||
struct icmpv6_opt_hdr hdr;
|
||||
uint8_t prefix_len;
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
uint8_t res:6,
|
||||
a:1,
|
||||
l:1;
|
||||
#elif BYTE_ORDER == BIG_ENDIAN
|
||||
uint8_t l:1,
|
||||
a:1,
|
||||
res:6;
|
||||
#else
|
||||
# error "Please fix <bits/endian.h>"
|
||||
#endif
|
||||
uint32_t valid_lifetime;
|
||||
uint32_t preferred_lifetime;
|
||||
uint32_t res2;
|
||||
uint8_t prefix[16];
|
||||
} __attribute__ ((packed));
|
||||
/* Push IPv6 header in front of ICMPv6 packet */
|
||||
i6h = (struct ip6_hdr *) msgb_push(msg, sizeof(*i6h));
|
||||
/* 4 bits version, 8 bits TC, 20 bits flow-ID */
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000);
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len);
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6;
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_hlim = 255;
|
||||
i6h->ip6_src = *saddr;
|
||||
i6h->ip6_dst = *daddr;
|
||||
return skb_csum;
|
||||
}
|
||||
|
||||
/*! construct a RFC4861 compliant ICMPv6 router soliciation
|
||||
* \param[in] saddr Source IPv6 address for router advertisement
|
||||
* \param[in] daddr Destination IPv6 address for router advertisement IPv6 header
|
||||
* \param[in] prefix The single prefix to be advertised (/64 implied!)
|
||||
* \returns callee-allocated message buffer containing router advertisement */
|
||||
struct msgb *icmpv6_construct_rs(const struct in6_addr *saddr)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(512,128, "IPv6 RS");
|
||||
struct icmpv6_rsol_hdr *rs;
|
||||
OSMO_ASSERT(msg);
|
||||
rs = (struct icmpv6_rsol_hdr *) msgb_put(msg, sizeof(*rs));
|
||||
rs->hdr.type = 133; /* see RFC4861 4.1 */
|
||||
rs->hdr.code = 0; /* see RFC4861 4.1 */
|
||||
rs->hdr.csum = 0; /* updated below */
|
||||
rs->reserved = 0; /* see RFC4861 4.1 */
|
||||
|
||||
rs->hdr.csum = icmpv6_prepend_ip6hdr(msg, saddr, &all_router_mcast_addr);
|
||||
|
||||
return msg;
|
||||
}
|
||||
/*! construct a 3GPP 29.061 compliant router advertisement for a given prefix
|
||||
* \param[in] saddr Source IPv6 address for router advertisement
|
||||
* \param[in] daddr Destination IPv6 address for router advertisement IPv6 header
|
||||
* \param[in] prefix The single prefix to be advertised (/64 implied!)i
|
||||
* \param[in] prefix The single prefix to be advertised (/64 implied!)
|
||||
* \returns callee-allocated message buffer containing router advertisement */
|
||||
struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
|
||||
static struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *prefix)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(512,128, "IPv6 RA");
|
||||
struct icmpv6_radv_hdr *ra;
|
||||
struct icmpv6_opt_prefix *ra_opt_pref;
|
||||
struct ip6_hdr *i6h;
|
||||
uint32_t len;
|
||||
uint16_t skb_csum;
|
||||
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
@@ -144,24 +135,12 @@ struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
|
||||
memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix));
|
||||
|
||||
/* checksum */
|
||||
skb_csum = csum_partial(msgb_data(msg), msgb_length(msg), 0);
|
||||
len = msgb_length(msg);
|
||||
ra->hdr.csum = csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, skb_csum);
|
||||
|
||||
/* Push IPv6 header in front of ICMPv6 packet */
|
||||
i6h = (struct ip6_hdr *) msgb_push(msg, sizeof(*i6h));
|
||||
/* 4 bits version, 8 bits TC, 20 bits flow-ID */
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_flow = htonl(0x60000000);
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len);
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6;
|
||||
i6h->ip6_ctlun.ip6_un1.ip6_un1_hlim = 255;
|
||||
i6h->ip6_src = *saddr;
|
||||
i6h->ip6_dst = *daddr;
|
||||
ra->hdr.csum = icmpv6_prepend_ip6hdr(msg, saddr, daddr);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* Walidate an ICMPv6 router solicitation according to RFC4861 6.1.1 */
|
||||
/* Validate an ICMPv6 router solicitation according to RFC4861 6.1.1 */
|
||||
static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
|
||||
{
|
||||
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
@@ -179,6 +158,37 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Validate an ICMPv6 router advertisement according to RFC4861 6.1.2.
|
||||
Returns pointer packet header on success, NULL otherwise. */
|
||||
struct icmpv6_radv_hdr *icmpv6_validate_router_adv(const uint8_t *pack, unsigned len)
|
||||
{
|
||||
const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
|
||||
|
||||
/* ICMP length (derived from IP length) is 16 or more octets */
|
||||
if (len < sizeof(*ip6h) + 16)
|
||||
return NULL;
|
||||
|
||||
if (ic6h->type != 134) /* router advertismenet type */
|
||||
return NULL;
|
||||
|
||||
/*Routers must use their link-local address */
|
||||
if (!IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src))
|
||||
return NULL;
|
||||
/* Hop limit field must have 255 */
|
||||
if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_hlim != 255)
|
||||
return NULL;
|
||||
/* ICMP Code is 0 */
|
||||
if (ic6h->code != 0)
|
||||
return NULL;
|
||||
/* ICMP length (derived from IP length) is 16 or more octets */
|
||||
if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen < 16)
|
||||
return NULL;
|
||||
/* FIXME: All included options have a length > 0 */
|
||||
/* FIXME: If IP source is unspecified, no source link-layer addr option */
|
||||
return (struct icmpv6_radv_hdr *)ic6h;
|
||||
}
|
||||
|
||||
/* handle incoming packets to the all-routers multicast address */
|
||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
const struct in6_addr *pdp_prefix,
|
||||
98
lib/icmpv6.h
Normal file
98
lib/icmpv6.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/endian.h>
|
||||
|
||||
#include "../gtp/gtp.h"
|
||||
#include "../gtp/pdp.h"
|
||||
|
||||
#define ICMPv6_OPT_TYPE_PREFIX_INFO 0x03
|
||||
|
||||
#define foreach_icmpv6_opt(icmpv6_pkt, icmpv6_len, opt_hdr) \
|
||||
for (opt_hdr = (struct icmpv6_opt_hdr *)(icmpv6_pkt)->options; \
|
||||
(uint8_t*)(opt_hdr) + sizeof(struct icmpv6_opt_hdr) <= (((uint8_t*)(icmpv6_pkt)) + (icmpv6_len)); \
|
||||
opt_hdr = (struct icmpv6_opt_hdr*)((uint8_t*)(opt_hdr) + (opt_hdr)->len) \
|
||||
)
|
||||
|
||||
struct icmpv6_hdr {
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t csum;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct icmpv6_echo_hdr {
|
||||
struct icmpv6_hdr hdr;
|
||||
uint16_t ident; /* Identifier */
|
||||
uint16_t seq; /* Sequence number */
|
||||
uint8_t data[0]; /* Data */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* RFC4861 Section 4.1 */
|
||||
struct icmpv6_rsol_hdr {
|
||||
struct icmpv6_hdr hdr;
|
||||
uint32_t reserved;
|
||||
uint8_t options[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* RFC4861 Section 4.2 */
|
||||
struct icmpv6_radv_hdr {
|
||||
struct icmpv6_hdr hdr;
|
||||
uint8_t cur_ho_limit;
|
||||
#if OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t res:6,
|
||||
m:1,
|
||||
o:1;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t o:1, m:1, res:6;
|
||||
#endif
|
||||
uint16_t router_lifetime;
|
||||
uint32_t reachable_time;
|
||||
uint32_t retrans_timer;
|
||||
uint8_t options[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* RFC4861 Section 4.6 */
|
||||
struct icmpv6_opt_hdr {
|
||||
uint8_t type;
|
||||
/* length in units of 8 octets, including type+len! */
|
||||
uint8_t len;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* RFC4861 Section 4.6.2 */
|
||||
struct icmpv6_opt_prefix {
|
||||
struct icmpv6_opt_hdr hdr;
|
||||
uint8_t prefix_len;
|
||||
#if OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t res:6,
|
||||
a:1,
|
||||
l:1;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t l:1, a:1, res:6;
|
||||
#endif
|
||||
uint32_t valid_lifetime;
|
||||
uint32_t preferred_lifetime;
|
||||
uint32_t res2;
|
||||
uint8_t prefix[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr);
|
||||
|
||||
struct msgb *icmpv6_construct_rs(const struct in6_addr *saddr);
|
||||
|
||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
|
||||
const struct in6_addr *pdp_prefix,
|
||||
const struct in6_addr *own_ll_addr,
|
||||
const uint8_t *pack, unsigned len);
|
||||
|
||||
struct icmpv6_radv_hdr *icmpv6_validate_router_adv(const uint8_t *pack, unsigned len);
|
||||
|
||||
|
||||
/* RFC3307 link-local scope multicast address */
|
||||
extern const struct in6_addr all_router_mcast_addr;
|
||||
@@ -60,7 +60,11 @@ int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Convenience wrapper around inet_ntop() for \ref in46_addr */
|
||||
/*! Convenience wrapper around inet_ntop() for in46_addr.
|
||||
* \param[in] in the in46_addr to print
|
||||
* \param[out] dst destination buffer where string representation of the address is stored
|
||||
* \param[out] dst_size size dst. Usually it should be at least INET6_ADDRSTRLEN.
|
||||
* \return address of dst on success, NULL on error */
|
||||
const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size)
|
||||
{
|
||||
int af;
|
||||
|
||||
@@ -31,3 +31,11 @@ unsigned int in46a_netmasklen(const struct in46_addr *netmask);
|
||||
|
||||
int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua);
|
||||
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);
|
||||
|
||||
static inline bool in46a_is_v6(const struct in46_addr *addr) {
|
||||
return addr->len == 8 || addr->len == 16;
|
||||
}
|
||||
|
||||
static inline bool in46a_is_v4(const struct in46_addr *addr) {
|
||||
return addr->len == sizeof(struct in_addr);
|
||||
}
|
||||
|
||||
79
lib/netdev.c
79
lib/netdev.c
@@ -176,7 +176,7 @@ int netdev_setaddr4(const char *devname, struct in_addr *addr,
|
||||
/* On linux the route to the interface is set automatically
|
||||
on FreeBSD we have to do this manually */
|
||||
#if defined(__FreeBSD__) || defined (__APPLE__)
|
||||
netdev_addroute(dstaddr, addr, &this->netmask);
|
||||
netdev_addroute4(dstaddr, addr, &this->netmask);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@@ -433,7 +433,7 @@ int netdev_addaddr6(const char *devname, struct in6_addr *addr,
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
|
||||
req.n.nlmsg_type = RTM_NEWADDR;
|
||||
req.i.ifa_family = AF_INET6;
|
||||
req.i.ifa_prefixlen = 64; /* 64 FOR IPv6 */
|
||||
req.i.ifa_prefixlen = prefixlen; /* 64 FOR IPv6 */
|
||||
req.i.ifa_flags = 0;
|
||||
req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */
|
||||
req.i.ifa_index = if_nametoindex(devname);
|
||||
@@ -553,7 +553,7 @@ int netdev_addaddr6(const char *devname, struct in6_addr *addr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_route(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
|
||||
static int netdev_route4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
|
||||
{
|
||||
int fd;
|
||||
#if defined(__linux__)
|
||||
@@ -643,16 +643,81 @@ static int netdev_route(struct in_addr *dst, struct in_addr *gateway, struct in_
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_addroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
|
||||
static int netdev_route6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface, int delete)
|
||||
{
|
||||
return netdev_route(dst, gateway, mask, 0);
|
||||
int fd;
|
||||
#if defined(__linux__)
|
||||
struct in6_rtmsg r;
|
||||
struct ifreq ifr;
|
||||
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.rtmsg_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */
|
||||
r.rtmsg_metric = 1;
|
||||
|
||||
/* Create a channel to the NET kernel. */
|
||||
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gw_iface) {
|
||||
strncpy(ifr.ifr_name, gw_iface, IFNAMSIZ);
|
||||
ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */
|
||||
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCGIFINDEX) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
r.rtmsg_ifindex = ifr.ifr_ifindex;
|
||||
}
|
||||
|
||||
memcpy(&r.rtmsg_dst, dst->s6_addr, sizeof(struct in6_addr));
|
||||
memcpy(&r.rtmsg_gateway, gateway->s6_addr, sizeof(struct in6_addr));
|
||||
r.rtmsg_dst_len = prefixlen;
|
||||
|
||||
if (delete) {
|
||||
if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCDELRT) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) {
|
||||
SYS_ERR(DTUN, LOGL_ERROR, errno,
|
||||
"ioctl(SIOCADDRT) failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_delroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
|
||||
int netdev_addroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
|
||||
{
|
||||
return netdev_route(dst, gateway, mask, 1);
|
||||
return netdev_route4(dst, gateway, mask, 0);
|
||||
}
|
||||
|
||||
int netdev_delroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
|
||||
{
|
||||
return netdev_route4(dst, gateway, mask, 1);
|
||||
}
|
||||
|
||||
int netdev_addroute6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface)
|
||||
{
|
||||
return netdev_route6(dst, gateway, prefixlen, gw_iface, 0);
|
||||
}
|
||||
|
||||
int netdev_delroute6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface)
|
||||
{
|
||||
return netdev_route6(dst, gateway, prefixlen, gw_iface, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include <ifaddrs.h>
|
||||
|
||||
/*! Obtain the local address of a network device
|
||||
|
||||
@@ -65,8 +65,10 @@ extern int netdev_addaddr4(const char *devname, struct in_addr *addr,
|
||||
extern int netdev_addaddr6(const char *devname, struct in6_addr *addr,
|
||||
struct in6_addr *dstaddr, int prefixlen);
|
||||
|
||||
extern int netdev_addroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int netdev_delroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int netdev_addroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int netdev_delroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask);
|
||||
extern int netdev_addroute6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface);
|
||||
extern int netdev_delroute6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface);
|
||||
|
||||
extern int netdev_ip_local_get(const char *devname, struct in46_prefix *prefix_list,
|
||||
size_t prefix_size, int flags);
|
||||
|
||||
272
lib/netns.c
Normal file
272
lib/netns.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017, Travelping GmbH <info@travelping.com>
|
||||
* Copyright (C) 2020, Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "netns.h"
|
||||
|
||||
#define NETNS_PATH "/var/run/netns"
|
||||
|
||||
/*! default namespace of the GGSN process */
|
||||
static int default_nsfd = -1;
|
||||
|
||||
/*! switch to a (non-default) namespace, store existing signal mask in oldmask.
|
||||
* \param[in] nsfd file descriptor representing the namespace to whch we shall switch
|
||||
* \param[out] oldmask caller-provided memory location to which old signal mask is stored
|
||||
* \ returns 0 on success or negative (errno) in case of error */
|
||||
int switch_ns(int nsfd, sigset_t *oldmask)
|
||||
{
|
||||
sigset_t intmask;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(default_nsfd >= 0);
|
||||
|
||||
if (sigfillset(&intmask) < 0)
|
||||
return -errno;
|
||||
if ((rc = sigprocmask(SIG_BLOCK, &intmask, oldmask)) != 0)
|
||||
return -rc;
|
||||
|
||||
if (setns(nsfd, CLONE_NEWNET) < 0) {
|
||||
/* restore old mask if we couldn't switch the netns */
|
||||
sigprocmask(SIG_SETMASK, oldmask, NULL);
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! switch back to the default namespace, restoring signal mask.
|
||||
* \param[in] oldmask signal mask to restore after returning to default namespace
|
||||
* \returns 0 on successs; negative errno value in case of error */
|
||||
int restore_ns(sigset_t *oldmask)
|
||||
{
|
||||
OSMO_ASSERT(default_nsfd >= 0);
|
||||
|
||||
int rc;
|
||||
if (setns(default_nsfd, CLONE_NEWNET) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((rc = sigprocmask(SIG_SETMASK, oldmask, NULL)) != 0)
|
||||
return -rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! open a file from within specified network namespace */
|
||||
int open_ns(int nsfd, const char *pathname, int flags)
|
||||
{
|
||||
sigset_t intmask, oldmask;
|
||||
int ret;
|
||||
int fd = -1;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(default_nsfd >= 0);
|
||||
|
||||
/* mask off all signals, store old signal mask */
|
||||
if (sigfillset(&intmask) < 0)
|
||||
return -errno;
|
||||
if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
|
||||
return -rc;
|
||||
|
||||
/* associate the calling thread with namespace file descriptor */
|
||||
if (setns(nsfd, CLONE_NEWNET) < 0) {
|
||||
ret = -errno;
|
||||
goto restore_sigmask;
|
||||
}
|
||||
/* open the requested file/path */
|
||||
if ((fd = open(pathname, flags)) < 0) {
|
||||
ret = -errno;
|
||||
goto restore_defaultns;
|
||||
}
|
||||
ret = fd;
|
||||
|
||||
restore_defaultns:
|
||||
/* return back to default namespace */
|
||||
if (setns(default_nsfd, CLONE_NEWNET) < 0) {
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
restore_sigmask:
|
||||
/* restore process mask */
|
||||
if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return -rc;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! create a socket in another namespace.
|
||||
* Switches temporarily to namespace indicated by nsfd, creates a socket in
|
||||
* that namespace and then returns to the default namespace.
|
||||
* \param[in] nsfd File descriptor of the namspace in which to create socket
|
||||
* \param[in] domain Domain of the socket (AF_INET, ...)
|
||||
* \param[in] type Type of the socket (SOCK_STREAM, ...)
|
||||
* \param[in] protocol Protocol of the socket (IPPROTO_TCP, ...)
|
||||
* \returns 0 on success; negative errno in case of error */
|
||||
int socket_ns(int nsfd, int domain, int type, int protocol)
|
||||
{
|
||||
sigset_t intmask, oldmask;
|
||||
int ret;
|
||||
int sk = -1;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(default_nsfd >= 0);
|
||||
|
||||
/* mask off all signals, store old signal mask */
|
||||
if (sigfillset(&intmask) < 0)
|
||||
return -errno;
|
||||
if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
|
||||
return -rc;
|
||||
|
||||
/* associate the calling thread with namespace file descriptor */
|
||||
if (setns(nsfd, CLONE_NEWNET) < 0) {
|
||||
ret = -errno;
|
||||
goto restore_sigmask;
|
||||
}
|
||||
|
||||
/* create socket of requested domain/type/proto */
|
||||
if ((sk = socket(domain, type, protocol)) < 0) {
|
||||
ret = -errno;
|
||||
goto restore_defaultns;
|
||||
}
|
||||
ret = sk;
|
||||
|
||||
restore_defaultns:
|
||||
/* return back to default namespace */
|
||||
if (setns(default_nsfd, CLONE_NEWNET) < 0) {
|
||||
if (sk >= 0)
|
||||
close(sk);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
restore_sigmask:
|
||||
/* restore process mask */
|
||||
if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
|
||||
if (sk >= 0)
|
||||
close(sk);
|
||||
return -rc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! initialize this network namespace helper module.
|
||||
* Must be called before using any other functions of this file.
|
||||
* \returns 0 on success; negative errno in case of error */
|
||||
int init_netns()
|
||||
{
|
||||
/* store the default namespace for later reference */
|
||||
if ((default_nsfd = open("/proc/self/ns/net", O_RDONLY)) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! create obtain file descriptor for network namespace of give name.
|
||||
* Creates /var/run/netns if it doesn't exist already.
|
||||
* \param[in] name Name of the network namespace (in /var/run/netns/)
|
||||
* \returns File descriptor of network namespace; negative errno in case of error */
|
||||
int get_nsfd(const char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
int rc;
|
||||
int fd;
|
||||
sigset_t intmask, oldmask;
|
||||
char path[MAXPATHLEN] = NETNS_PATH;
|
||||
|
||||
OSMO_ASSERT(default_nsfd >= 0);
|
||||
|
||||
/* create /var/run/netns, if it doesn't exist already */
|
||||
rc = mkdir(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
|
||||
if (rc < 0 && errno != EEXIST)
|
||||
return rc;
|
||||
|
||||
/* create /var/run/netns/[name], if it doesn't exist already */
|
||||
snprintf(path, sizeof(path), "%s/%s", NETNS_PATH, name);
|
||||
fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
|
||||
if (fd < 0) {
|
||||
if (errno == EEXIST) {
|
||||
if ((fd = open(path, O_RDONLY)) < 0)
|
||||
return -errno;
|
||||
return fd;
|
||||
}
|
||||
return -errno;
|
||||
}
|
||||
if (close(fd) < 0)
|
||||
return -errno;
|
||||
|
||||
/* mask off all signals, store old signal mask */
|
||||
if (sigfillset(&intmask) < 0)
|
||||
return -errno;
|
||||
if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
|
||||
return -rc;
|
||||
|
||||
/* create a new network namespace */
|
||||
if (unshare(CLONE_NEWNET) < 0) {
|
||||
ret = -errno;
|
||||
goto restore_sigmask;
|
||||
}
|
||||
if (mount("/proc/self/ns/net", path, "none", MS_BIND, NULL) < 0)
|
||||
ret = -errno;
|
||||
|
||||
/* switch back to default namespace */
|
||||
if (setns(default_nsfd, CLONE_NEWNET) < 0)
|
||||
return -errno;
|
||||
|
||||
restore_sigmask:
|
||||
/* restore process mask */
|
||||
if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0)
|
||||
return -rc;
|
||||
|
||||
/* might have been set above in case mount fails */
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* finally, open the created namespace file descriptor from default ns */
|
||||
if ((fd = open(path, O_RDONLY)) < 0)
|
||||
return -errno;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#endif
|
||||
35
lib/netns.h
Normal file
35
lib/netns.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017, Travelping GmbH <info@travelping.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __NETNS_H
|
||||
#define __NETNS_H
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
int init_netns(void);
|
||||
|
||||
int switch_ns(int nsfd, sigset_t *oldmask);
|
||||
int restore_ns(sigset_t *oldmask);
|
||||
|
||||
int open_ns(int nsfd, const char *pathname, int flags);
|
||||
int socket_ns(int nsfd, int domain, int type, int protocol);
|
||||
int get_nsfd(const char *name);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
11
lib/tun.c
11
lib/tun.c
@@ -276,7 +276,7 @@ int tun_free(struct tun_t *tun)
|
||||
{
|
||||
|
||||
if (tun->routes) {
|
||||
netdev_delroute(&tun->dstaddr.v4, &tun->addr.v4, &tun->netmask);
|
||||
netdev_delroute4(&tun->dstaddr.v4, &tun->addr.v4, &tun->netmask);
|
||||
}
|
||||
|
||||
if (tun->fd >= 0) {
|
||||
@@ -318,7 +318,14 @@ int tun_decaps(struct tun_t *this)
|
||||
|
||||
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)
|
||||
|
||||
@@ -59,4 +59,7 @@ extern int tun_runscript(struct tun_t *tun, char *script);
|
||||
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 */
|
||||
|
||||
35
lib/util.c
Normal file
35
lib/util.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* misc helpers
|
||||
* Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../gtp/pdp.h"
|
||||
|
||||
#include "ippool.h"
|
||||
#include "in46_addr.h"
|
||||
|
||||
/*! Get the peer of pdp based on IP version used.
|
||||
* \param[in] pdp PDP context to select the peer from.
|
||||
* \param[in] v4v6 IP version to select. Valid values are 4 and 6.
|
||||
* \returns The selected peer matching the given IP version. NULL if not present.
|
||||
*/
|
||||
struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) {
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct ippoolm_t * ippool = pdp->peer[i];
|
||||
if (!ippool)
|
||||
continue;
|
||||
if (is_ipv6 && in46a_is_v6(&ippool->addr))
|
||||
return ippool;
|
||||
else if (!is_ipv6 && in46a_is_v4(&ippool->addr))
|
||||
return ippool;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
18
lib/util.h
Normal file
18
lib/util.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
/*
|
||||
* misc helpers
|
||||
* Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct ippoolm_t;
|
||||
struct pdp_t;
|
||||
|
||||
struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6);
|
||||
@@ -1,90 +0,0 @@
|
||||
Summary: Osmocom Gateway GPRS Support Node (GGSN)
|
||||
Name: @PACKAGE@
|
||||
Version: @VERSION@
|
||||
Release: 1
|
||||
URL: https://osmocom.org/projects/openggsn
|
||||
Source0: http://prdownloads.sourceforge.net/ggsn/%{name}-%{version}.tar.gz
|
||||
License: GPL
|
||||
Group: System Environment/Daemons
|
||||
BuildRoot: %{_tmppath}/%{name}-root
|
||||
|
||||
%description
|
||||
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
|
||||
operators as the interface between the Internet and the rest of the
|
||||
mobile network infrastructure. The project also provides an SGSN
|
||||
emulator suitable for GPRS core network testing.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
|
||||
./configure --prefix=/usr --enable-static-exec
|
||||
|
||||
make
|
||||
|
||||
%install
|
||||
|
||||
make install prefix=$RPM_BUILD_ROOT/usr
|
||||
strip $RPM_BUILD_ROOT/usr/bin/osmo-ggsn
|
||||
strip $RPM_BUILD_ROOT/usr/bin/sgsnemu
|
||||
|
||||
#Copy osmo-ggsn init script in place
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
|
||||
install -m755 examples/osmo-ggsn.init \
|
||||
$RPM_BUILD_ROOT/etc/rc.d/init.d/osmo-ggsn
|
||||
|
||||
#Copy osmo-ggsn.conf in place
|
||||
install -m755 examples/osmo-ggsn.cfg \
|
||||
$RPM_BUILD_ROOT/etc/osmo-ggsn.cfg
|
||||
|
||||
#Copy gsn_restart file in place
|
||||
mkdir -p $RPM_BUILD_ROOT/var/lib/osmo-ggsn
|
||||
echo "0" > $RPM_BUILD_ROOT/var/lib/osmo-ggsn/gsn_restart
|
||||
|
||||
#Clean up unwanted library files
|
||||
rm -rf $RPM_BUILD_ROOT/usr/include/*
|
||||
rm -rf $RPM_BUILD_ROOT/usr/lib/*
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make clean
|
||||
|
||||
%post
|
||||
/sbin/chkconfig --add osmo-ggsn
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
|
||||
/usr/bin/osmo-ggsn
|
||||
/usr/bin/sgsnemu
|
||||
/etc/rc.d/init.d/osmo-ggsn
|
||||
%dir /var/lib/osmo-ggsn
|
||||
/var/lib/osmo-ggsn/gsn_restart
|
||||
|
||||
%doc AUTHORS COPYING INSTALL NEWS README.md
|
||||
%doc examples/osmo-ggsn.conf
|
||||
%doc examples/sgsnemu.conf
|
||||
%doc examples/osmo-ggsn.init
|
||||
%doc examples/firewall
|
||||
%doc /usr/man/man8/osmo-ggsn.8.gz
|
||||
%doc /usr/man/man8/sgsnemu.8.gz
|
||||
|
||||
%config /etc/osmo-ggsn.cfg
|
||||
|
||||
|
||||
#/usr/lib/libgtp.a
|
||||
#/usr/lib/libgtp.la
|
||||
#/usr/lib/libgtp.so
|
||||
#/usr/lib/libgtp.so.0
|
||||
#/usr/lib/libgtp.so.0.0.0
|
||||
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon Jun 30 2017 <laforge@gnumonks.org>
|
||||
- Update to OsmoGGSN
|
||||
|
||||
* Mon Jun 30 2003 <jj@openggsn.org>
|
||||
- Initial build.
|
||||
@@ -72,6 +72,7 @@ const char *gengetopt_args_info_help[] = {
|
||||
" --ipup=STRING Script to run after link-up",
|
||||
" --ipdown=STRING Script to run after link-down",
|
||||
" --tun-device=STRING Name of the local network interface",
|
||||
" --netns=STRING Network namespace to use",
|
||||
"\n Mode: pinghost\n generate ICMP payload inside G-PDU without setting up tun interface",
|
||||
" --pinghost=STRING Ping remote host",
|
||||
" --pingrate=INT Number of ping req per second (default=`1')",
|
||||
@@ -163,6 +164,7 @@ void clear_given(struct gengetopt_args_info *args_info)
|
||||
args_info->ipup_given = 0;
|
||||
args_info->ipdown_given = 0;
|
||||
args_info->tun_device_given = 0;
|
||||
args_info->netns_given = 0;
|
||||
args_info->pinghost_given = 0;
|
||||
args_info->pingrate_given = 0;
|
||||
args_info->pingsize_given = 0;
|
||||
@@ -244,6 +246,8 @@ void clear_args(struct gengetopt_args_info *args_info)
|
||||
args_info->ipdown_orig = NULL;
|
||||
args_info->tun_device_arg = NULL;
|
||||
args_info->tun_device_orig = NULL;
|
||||
args_info->netns_arg = NULL;
|
||||
args_info->netns_orig = NULL;
|
||||
args_info->pinghost_arg = NULL;
|
||||
args_info->pinghost_orig = NULL;
|
||||
args_info->pingrate_arg = 1;
|
||||
@@ -300,13 +304,14 @@ void init_args_info(struct gengetopt_args_info *args_info)
|
||||
args_info->ipup_help = gengetopt_args_info_help[35];
|
||||
args_info->ipdown_help = gengetopt_args_info_help[36];
|
||||
args_info->tun_device_help = gengetopt_args_info_help[37];
|
||||
args_info->pinghost_help = gengetopt_args_info_help[39];
|
||||
args_info->pingrate_help = gengetopt_args_info_help[40];
|
||||
args_info->pingsize_help = gengetopt_args_info_help[41];
|
||||
args_info->pingcount_help = gengetopt_args_info_help[42];
|
||||
args_info->pingquiet_help = gengetopt_args_info_help[43];
|
||||
args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[44];
|
||||
args_info->pdp_type_help = gengetopt_args_info_help[45];
|
||||
args_info->netns_help = gengetopt_args_info_help[38];
|
||||
args_info->pinghost_help = gengetopt_args_info_help[40];
|
||||
args_info->pingrate_help = gengetopt_args_info_help[41];
|
||||
args_info->pingsize_help = gengetopt_args_info_help[42];
|
||||
args_info->pingcount_help = gengetopt_args_info_help[43];
|
||||
args_info->pingquiet_help = gengetopt_args_info_help[44];
|
||||
args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[45];
|
||||
args_info->pdp_type_help = gengetopt_args_info_help[46];
|
||||
|
||||
}
|
||||
|
||||
@@ -363,14 +368,6 @@ void cmdline_parser_params_init(struct cmdline_parser_params *params)
|
||||
}
|
||||
}
|
||||
|
||||
struct cmdline_parser_params *cmdline_parser_params_create(void)
|
||||
{
|
||||
struct cmdline_parser_params *params = (struct cmdline_parser_params *)
|
||||
malloc(sizeof(struct cmdline_parser_params));
|
||||
cmdline_parser_params_init(params);
|
||||
return params;
|
||||
}
|
||||
|
||||
static void free_string_field(char **s)
|
||||
{
|
||||
if (*s) {
|
||||
@@ -432,6 +429,8 @@ static void cmdline_parser_release(struct gengetopt_args_info *args_info)
|
||||
free_string_field(&(args_info->ipdown_orig));
|
||||
free_string_field(&(args_info->tun_device_arg));
|
||||
free_string_field(&(args_info->tun_device_orig));
|
||||
free_string_field(&(args_info->netns_arg));
|
||||
free_string_field(&(args_info->netns_orig));
|
||||
free_string_field(&(args_info->pinghost_arg));
|
||||
free_string_field(&(args_info->pinghost_orig));
|
||||
free_string_field(&(args_info->pingrate_orig));
|
||||
@@ -545,6 +544,8 @@ int cmdline_parser_dump(FILE * outfile, struct gengetopt_args_info *args_info)
|
||||
if (args_info->tun_device_given)
|
||||
write_into_file(outfile, "tun-device",
|
||||
args_info->tun_device_orig, 0);
|
||||
if (args_info->netns_given)
|
||||
write_into_file(outfile, "netns", args_info->netns_orig, 0);
|
||||
if (args_info->pinghost_given)
|
||||
write_into_file(outfile, "pinghost", args_info->pinghost_orig,
|
||||
0);
|
||||
@@ -709,6 +710,12 @@ cmdline_parser_required2(struct gengetopt_args_info *args_info,
|
||||
prog_name, (additional_error ? additional_error : ""));
|
||||
error_occurred = 1;
|
||||
}
|
||||
if (args_info->netns_given && !args_info->createif_given) {
|
||||
fprintf(stderr,
|
||||
"%s: '--netns' option depends on option 'createif'%s\n",
|
||||
prog_name, (additional_error ? additional_error : ""));
|
||||
error_occurred = 1;
|
||||
}
|
||||
if (args_info->pingrate_given && !args_info->pinghost_given) {
|
||||
fprintf(stderr,
|
||||
"%s: '--pingrate' option depends on option 'pinghost'%s\n",
|
||||
@@ -954,6 +961,7 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
{"ipup", 1, NULL, 0},
|
||||
{"ipdown", 1, NULL, 0},
|
||||
{"tun-device", 1, NULL, 0},
|
||||
{"netns", 1, NULL, 0},
|
||||
{"pinghost", 1, NULL, 0},
|
||||
{"pingrate", 1, NULL, 0},
|
||||
{"pingsize", 1, NULL, 0},
|
||||
@@ -1493,6 +1501,22 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
additional_error))
|
||||
goto failure;
|
||||
|
||||
}
|
||||
/* Network namespace to use. */
|
||||
else if (strcmp
|
||||
(long_options[option_index].name,
|
||||
"netns") == 0) {
|
||||
args_info->createif_mode_counter += 1;
|
||||
|
||||
if (update_arg((void *)&(args_info->netns_arg),
|
||||
&(args_info->netns_orig),
|
||||
&(args_info->netns_given),
|
||||
&(local_args_info.netns_given),
|
||||
optarg, 0, 0, ARG_STRING,
|
||||
check_ambiguity, override, 0, 0,
|
||||
"netns", '-', additional_error))
|
||||
goto failure;
|
||||
|
||||
}
|
||||
/* Ping remote host. */
|
||||
else if (strcmp
|
||||
@@ -1609,11 +1633,12 @@ cmdline_parser_internal(int argc, char **argv,
|
||||
int createif_given[] =
|
||||
{ args_info->createif_given, args_info->net_given,
|
||||
args_info->defaultroute_given, args_info->ipup_given,
|
||||
args_info->ipdown_given, args_info->tun_device_given, -1
|
||||
args_info->ipdown_given, args_info->tun_device_given,
|
||||
args_info->netns_given, -1
|
||||
};
|
||||
const char *createif_desc[] =
|
||||
{ "--createif", "--net", "--defaultroute", "--ipup",
|
||||
"--ipdown", "--tun-device", 0
|
||||
"--ipdown", "--tun-device", "--netns", 0
|
||||
};
|
||||
int pinghost_given[] =
|
||||
{ args_info->pinghost_given, args_info->pingrate_given,
|
||||
|
||||
@@ -59,6 +59,7 @@ modeoption "defaultroute" - "Create default route" flag dependon="
|
||||
modeoption "ipup" - "Script to run after link-up" string dependon="createif" no mode="createif"
|
||||
modeoption "ipdown" - "Script to run after link-down" string dependon="createif" no mode="createif"
|
||||
modeoption "tun-device" - "Name of the local network interface" string dependon="createif" no mode="createif"
|
||||
modeoption "netns" - "Network namespace to use" string dependon="createif" no mode="createif"
|
||||
|
||||
modeoption "pinghost" - "Ping remote host" string no mode="pinghost"
|
||||
modeoption "pingrate" - "Number of ping req per second" int default="1" dependon="pinghost" no mode="pinghost"
|
||||
|
||||
@@ -242,6 +242,12 @@ extern "C" {
|
||||
/**< @brief Name of the local network interface original value given at command line. */
|
||||
const char *tun_device_help;
|
||||
/**< @brief Name of the local network interface help description. */
|
||||
char *netns_arg;
|
||||
/**< @brief Network namespace to use. */
|
||||
char *netns_orig;
|
||||
/**< @brief Network namespace to use original value given at command line. */
|
||||
const char *netns_help;
|
||||
/**< @brief Network namespace to use help description. */
|
||||
char *pinghost_arg;
|
||||
/**< @brief Ping remote host. */
|
||||
char *pinghost_orig;
|
||||
@@ -355,6 +361,8 @@ extern "C" {
|
||||
/**< @brief Whether ipdown was given. */
|
||||
unsigned int tun_device_given;
|
||||
/**< @brief Whether tun-device was given. */
|
||||
unsigned int netns_given;
|
||||
/**< @brief Whether netns was given. */
|
||||
unsigned int pinghost_given;
|
||||
/**< @brief Whether pinghost was given. */
|
||||
unsigned int pingrate_given;
|
||||
@@ -471,13 +479,6 @@ extern "C" {
|
||||
*/
|
||||
void cmdline_parser_params_init(struct cmdline_parser_params *params);
|
||||
|
||||
/**
|
||||
* Allocates dynamically a cmdline_parser_params structure and initializes
|
||||
* all its fields to their default values
|
||||
* @return the created and initialized cmdline_parser_params structure
|
||||
*/
|
||||
struct cmdline_parser_params *cmdline_parser_params_create(void);
|
||||
|
||||
/**
|
||||
* Initializes the passed gengetopt_args_info structure's fields
|
||||
* (also set default values for options that have a default)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,18 +2,30 @@ AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
|
||||
|
||||
EXTRA_DIST = \
|
||||
gtpie_test.ok \
|
||||
queue_test.ok \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
check_PROGRAMS = \
|
||||
gtpie_test \
|
||||
queue_test \
|
||||
$(NULL)
|
||||
|
||||
gtpie_test_SOURCES = \
|
||||
gtpie_test.c \
|
||||
$(NULL)
|
||||
|
||||
queue_test_SOURCES = \
|
||||
queue_test.c \
|
||||
$(NULL)
|
||||
|
||||
gtpie_test_LDADD = \
|
||||
$(top_builddir)/lib/debug.o \
|
||||
$(top_builddir)/gtp/libgtp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
queue_test_LDADD = \
|
||||
$(top_builddir)/lib/debug.o \
|
||||
$(top_builddir)/gtp/libgtp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -113,7 +113,7 @@ int main(int argc, char **argv)
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
|
||||
233
tests/gtp/queue_test.c
Normal file
233
tests/gtp/queue_test.c
Normal file
@@ -0,0 +1,233 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include "../../lib/syserr.h"
|
||||
#include "../../gtp/queue.h"
|
||||
|
||||
static const struct qmsg_t qmsg_zero;
|
||||
|
||||
static void queue_print(struct queue_t *queue, char* str)
|
||||
{
|
||||
int n;
|
||||
printf("=== [Queue %s] Next: %d First: %d Last: %d\n", str,
|
||||
queue->next, queue->first, queue->last);
|
||||
printf("#\tseq\tnext\tprev\ttimeout\tretrans\ttype\tcbp\n");
|
||||
for (n = 0; n < QUEUE_SIZE; n++) {
|
||||
if (queue->qmsga[n].state == 0) {
|
||||
/* Nothing there, validate everything is zeroed */
|
||||
OSMO_ASSERT(memcmp(&qmsg_zero, &queue->qmsga[n], sizeof(qmsg_zero)) == 0);
|
||||
continue;
|
||||
}
|
||||
printf("%d\t%d\t%d\t%d\t%d\t%d\t%u\t%" PRIuPTR "\n",
|
||||
n,
|
||||
queue->qmsga[n].seq,
|
||||
queue->qmsga[n].next,
|
||||
queue->qmsga[n].prev,
|
||||
(int)queue->qmsga[n].timeout,
|
||||
queue->qmsga[n].retrans,
|
||||
queue->qmsga[n].type,
|
||||
(uintptr_t)queue->qmsga[n].cbp
|
||||
);
|
||||
}
|
||||
printf("======================================================\n");
|
||||
}
|
||||
|
||||
static void test_queue_empty()
|
||||
{
|
||||
printf("***** Testing %s()\n", __func__);
|
||||
struct queue_t *queue = NULL;
|
||||
struct qmsg_t *qmsg = NULL;
|
||||
uint16_t seq = 23;
|
||||
uint8_t type = 0;
|
||||
void *cbp = NULL;
|
||||
struct sockaddr_in peer;
|
||||
int rc;
|
||||
|
||||
rc = inet_pton(AF_INET, "127.0.0.1", &(peer.sin_addr));
|
||||
OSMO_ASSERT(rc == 1);
|
||||
|
||||
rc = queue_new(&queue);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
queue_print(queue, "created");
|
||||
|
||||
rc = queue_getfirst(queue, &qmsg);
|
||||
OSMO_ASSERT(rc == EOF);
|
||||
rc = queue_seqget(queue, &qmsg, &peer, seq);
|
||||
OSMO_ASSERT(rc == EOF);
|
||||
rc = queue_freemsg_seq(queue, &peer, seq, &type, &cbp);
|
||||
OSMO_ASSERT(rc==EOF);
|
||||
|
||||
queue_print(queue, "pre-delete");
|
||||
rc = queue_free(queue);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
static void test_queue_one()
|
||||
{
|
||||
printf("***** Testing %s()\n", __func__);
|
||||
struct queue_t *queue = NULL;
|
||||
struct qmsg_t *qmsg = NULL, *qmsg2 = NULL;;
|
||||
uint16_t seq = 23;
|
||||
uint8_t type = 0;
|
||||
void *cbp = NULL;
|
||||
struct sockaddr_in peer, peer2;
|
||||
int rc;
|
||||
|
||||
rc = inet_pton(AF_INET, "127.0.0.1", &(peer.sin_addr));
|
||||
OSMO_ASSERT(rc == 1);
|
||||
rc = inet_pton(AF_INET, "127.0.0.2", &(peer2.sin_addr));
|
||||
OSMO_ASSERT(rc == 1);
|
||||
|
||||
rc = queue_new(&queue);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
queue_print(queue, "created");
|
||||
|
||||
rc = queue_newmsg(queue, &qmsg, &peer, seq);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
queue_print(queue, "first added");
|
||||
qmsg->type = GTP_ECHO_REQ;
|
||||
qmsg->cbp = (void*) 0x13243546;
|
||||
qmsg->seq = seq;
|
||||
|
||||
rc = queue_getfirst(queue, &qmsg2);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(qmsg == qmsg2);
|
||||
|
||||
rc = queue_seqget(queue, &qmsg2, &peer, seq);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(qmsg == qmsg2);
|
||||
rc = queue_seqget(queue, &qmsg, &peer2, seq);
|
||||
OSMO_ASSERT(rc == EOF);
|
||||
rc = queue_seqget(queue, &qmsg, &peer, seq + 1);
|
||||
OSMO_ASSERT(rc == EOF);
|
||||
queue_print(queue, "after-get");
|
||||
|
||||
rc = queue_back(queue, qmsg);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
queue_print(queue, "after-back");
|
||||
|
||||
rc = queue_freemsg_seq(queue, &peer2, seq, &type, &cbp);
|
||||
OSMO_ASSERT(rc == EOF);
|
||||
rc = queue_freemsg_seq(queue, &peer, seq + 1, &type, &cbp);
|
||||
OSMO_ASSERT(rc == EOF);
|
||||
queue_print(queue, "pree-freemsg");
|
||||
rc = queue_freemsg_seq(queue, &peer, seq, &type, &cbp);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(type == GTP_ECHO_REQ);
|
||||
OSMO_ASSERT(cbp == (void*)0x13243546);
|
||||
|
||||
queue_print(queue, "pre-delete");
|
||||
rc = queue_free(queue);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
#define newmsg_fill(queue, qmsg_ptr, peer_ptr, seq) \
|
||||
do { \
|
||||
int rc = queue_newmsg(queue, &(qmsg_ptr), peer_ptr, seq); \
|
||||
OSMO_ASSERT(rc == 0); \
|
||||
OSMO_ASSERT(qmsg_ptr); \
|
||||
qmsg_ptr->type = GTP_CREATE_PDP_REQ; \
|
||||
qmsg_ptr->cbp = (void*)(uintptr_t)seq; \
|
||||
} while (0);
|
||||
|
||||
#define freemsg_verify(seq, type, cbp) \
|
||||
do { \
|
||||
OSMO_ASSERT(type == GTP_CREATE_PDP_REQ); \
|
||||
OSMO_ASSERT(cbp == (void*)(uintptr_t)seq); \
|
||||
} while (0);
|
||||
|
||||
static void test_queue_full()
|
||||
{
|
||||
/* queue_newmsg until we receive EOF. Try moving back then. */
|
||||
printf("***** Testing %s()\n", __func__);
|
||||
struct queue_t *queue = NULL;
|
||||
struct qmsg_t *qmsg = NULL;
|
||||
uint8_t type = 0;
|
||||
void *cbp = NULL;
|
||||
struct sockaddr_in peer;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
rc = inet_pton(AF_INET, "127.0.0.1", &(peer.sin_addr));
|
||||
OSMO_ASSERT(rc == 1);
|
||||
|
||||
rc = queue_new(&queue);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
queue_print(queue, "created");
|
||||
|
||||
for (i = 0; i < QUEUE_SIZE - 1; i++) {
|
||||
newmsg_fill(queue, qmsg, &peer, i);
|
||||
}
|
||||
queue_print(queue, "after-fill");
|
||||
|
||||
/* There's one slot left at the end, let's use first()->back() */
|
||||
rc = queue_getfirst(queue, &qmsg);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = queue_back(queue, qmsg);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
queue_print(queue, "after-back");
|
||||
|
||||
/* Now let's fill last empty slot */
|
||||
newmsg_fill(queue, qmsg, &peer, QUEUE_SIZE - 1);
|
||||
queue_print(queue, "after-full");
|
||||
|
||||
/* queue is now full, it should fail */
|
||||
rc = queue_newmsg(queue, &qmsg, &peer, QUEUE_SIZE);
|
||||
OSMO_ASSERT(rc == EOF);
|
||||
queue_print(queue, "after-failing-full");
|
||||
|
||||
/* Remove 1before-last msg (the one moved back) and make sure we can
|
||||
re-add it at the end of the list */
|
||||
rc = queue_seqget(queue, &qmsg, &peer, 0);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = queue_freemsg(queue, qmsg);
|
||||
queue_print(queue, "after-freeing-0");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
/* Now let's fill last empty slot which should be at the end */
|
||||
newmsg_fill(queue, qmsg, &peer, 0);
|
||||
queue_print(queue, "after-refilling-0");
|
||||
|
||||
/* Now free first half seq set in increasing order */
|
||||
for (i = 0; i < QUEUE_SIZE / 2; i++) {
|
||||
rc = queue_freemsg_seq(queue, &peer, i, &type, &cbp);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
freemsg_verify(i, type, cbp);
|
||||
}
|
||||
queue_print(queue, "after-first-half-free");
|
||||
|
||||
/* Now free second half seq set in decreasing order */
|
||||
for (i = QUEUE_SIZE - 1; i >= QUEUE_SIZE / 2; i--) {
|
||||
rc = queue_freemsg_seq(queue, &peer, i, &type, &cbp);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
freemsg_verify(i, type, cbp);
|
||||
}
|
||||
queue_print(queue, "after-second-half-free");
|
||||
|
||||
rc = queue_free(queue);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
|
||||
test_queue_empty();
|
||||
test_queue_one();
|
||||
test_queue_full();
|
||||
|
||||
return 0;
|
||||
}
|
||||
13367
tests/gtp/queue_test.ok
Normal file
13367
tests/gtp/queue_test.ok
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ EXTRA_DIST = ippool_test.ok \
|
||||
in46a_test.ok \
|
||||
in46a_v6_test.ok
|
||||
|
||||
noinst_PROGRAMS = ippool_test in46a_test
|
||||
check_PROGRAMS = ippool_test in46a_test
|
||||
|
||||
ippool_test_SOURCES = \
|
||||
ippool_test.c \
|
||||
|
||||
@@ -146,7 +146,7 @@ static void test_in46a_to_eua(void)
|
||||
|
||||
static void test_in46a_from_eua(void)
|
||||
{
|
||||
struct in46_addr ia;
|
||||
struct in46_addr ia[2];
|
||||
struct ul66_t eua;
|
||||
const uint8_t v4_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4 };
|
||||
const uint8_t v4_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4, 1,2,3,4 };
|
||||
@@ -155,35 +155,35 @@ static void test_in46a_from_eua(void)
|
||||
printf("Testing in46a_from_eua() with IPv4 addresses\n");
|
||||
|
||||
/* default: v4 unspec */
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == 0);
|
||||
|
||||
/* invalid */
|
||||
eua.v[0] = 0x23;
|
||||
eua.v[1] = PDP_EUA_TYPE_v4;
|
||||
eua.l = 6;
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) < 0);
|
||||
|
||||
/* invalid */
|
||||
eua.v[0] = PDP_EUA_ORG_IETF;
|
||||
eua.v[1] = 0x23;
|
||||
eua.l = 6;
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) < 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) < 0);
|
||||
|
||||
/* unspecified V4 */
|
||||
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
|
||||
eua.l = sizeof(v4_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == 0);
|
||||
|
||||
/* specified V4 */
|
||||
memcpy(eua.v, v4_spec, sizeof(v4_spec));
|
||||
eua.l = sizeof(v4_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 4);
|
||||
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 4);
|
||||
OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
|
||||
}
|
||||
|
||||
static void test_in46a_netmasklen(void)
|
||||
@@ -318,7 +318,7 @@ static void test_in46a_to_eua_v4v6() {
|
||||
|
||||
static void test_in46a_from_eua_v6(void)
|
||||
{
|
||||
struct in46_addr ia;
|
||||
struct in46_addr ia[2];
|
||||
struct ul66_t eua;
|
||||
const uint8_t v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6 };
|
||||
const uint8_t v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6,
|
||||
@@ -331,16 +331,16 @@ static void test_in46a_from_eua_v6(void)
|
||||
/* unspecified V6 */
|
||||
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
|
||||
eua.l = sizeof(v6_unspec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 16);
|
||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 16);
|
||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[0].v6));
|
||||
|
||||
/* specified V6 */
|
||||
memcpy(eua.v, v6_spec, sizeof(v6_spec));
|
||||
eua.l = sizeof(v6_spec);
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
|
||||
OSMO_ASSERT(ia.len == 16);
|
||||
OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
|
||||
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
|
||||
OSMO_ASSERT(ia[0].len == 16);
|
||||
OSMO_ASSERT(!memcmp(&ia[0].v6, v6_spec+2, ia[0].len));
|
||||
}
|
||||
|
||||
static void test_in46a_from_eua_v4v6(void) {
|
||||
@@ -431,7 +431,7 @@ int main(int argc, char **argv)
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
|
||||
@@ -131,7 +131,9 @@ int main(int argc, char **argv)
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
log_set_print_category(osmo_stderr_target, 0);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
|
||||
@@ -32,3 +32,9 @@ AT_KEYWORDS([gtpie])
|
||||
cat $abs_srcdir/gtp/gtpie_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/gtp/gtpie_test], [], [expout], [])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([queue])
|
||||
AT_KEYWORDS([queue])
|
||||
cat $abs_srcdir/gtp/queue_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/gtp/queue_test], [], [expout], [])
|
||||
AT_CLEANUP
|
||||
|
||||
3
utils/Makefile.am
Normal file
3
utils/Makefile.am
Normal file
@@ -0,0 +1,3 @@
|
||||
bin_PROGRAMS = gtp-echo-responder
|
||||
|
||||
gtp_echo_responder_SOURCES = gtp_echo_responder.c
|
||||
470
utils/gtp_echo_responder.c
Normal file
470
utils/gtp_echo_responder.c
Normal file
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@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;
|
||||
}
|
||||
111
utils/gtp_echo_responder_test.py
Executable file
111
utils/gtp_echo_responder_test.py
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
# Author: Pau Espin Pedrol <pespin@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))
|
||||
Reference in New Issue
Block a user