Compare commits

..

8 Commits

Author SHA1 Message Date
Harald Welte
76be86b201 ggsn: don't use gtp_kernel_tunnel_{add,del}() for userspace tun
Change-Id: I00cc8eb8c4d44532f975f78783ff4e12814b3416
2018-04-25 21:13:06 +02:00
Harald Welte
0406bdde75 Move kernel GTP support from ggsn/ to lib/
This way, the IP address / route handling between TUN devices and kernel
GTP can be shared, which will provide not only a unified codebase but
also a more consistent behavior.

This also paves the road for to use kernel GTP from sgsnemu in the future.

Related: OS#3214
Change-Id: Ic53a971136edd0d8871fbd6746d7b0090ce3a188
2018-04-25 20:46:05 +02:00
Harald Welte
97e7a98e0a lib/tun: Remove tun_setaddr() API, as everyone is using tun_addaddr() now
Change-Id: I02e057d30b6773c17ea6bc31094e53587971e9e7
2018-04-25 20:46:05 +02:00
Harald Welte
f43e9de258 sgsnemu: Convert from tun_setaddr() to tun_addaddr()
This converts the last caller of tun_setaddr() outside of lib/tun.c to
use tun_addaddr().

Change-Id: Ia301d6a4ee3d02c1af1c85f2fe1041d3013268b0
2018-04-25 20:46:05 +02:00
Harald Welte
e12ab8bc89 ggsn: Don't explicitly use tun_setaddr() API anymore
tun_addaddr() internally contains a fallback to tun_setaddr() for the
first address, so we can unify the API usage a bit and use tun_addaddr()
from all call sites

Change-Id: I34de003a1a040254bd38b29e48caea34cb0c88d2
2018-04-25 20:46:05 +02:00
Harald Welte
c6109a4d40 lib/netdev.c: Cosmetic changes (coding style / cleanups)
Change-Id: I60cbca616a4f727e2374c52715f9286a0f4c5e4b
2018-04-25 20:46:05 +02:00
Harald Welte
8071128620 lib/tun: split generic network device related stuff to lib/netdev
Change-Id: Ib021e392637a43d5cf1b40e0d50621fe7e854ba5
2018-04-25 20:46:05 +02:00
Harald Welte
f7dbfeed4b lib/tun.c: Generalize tun_{set,add}addr*() functions
There's nothing really tun-specific about the adding and removing of
addresses to network devices.  Let's generalize the related code.

Change-Id: I139a950dd81a4b1199953be1608cd109a060f562
2018-04-25 20:46:05 +02:00
76 changed files with 1484 additions and 11727 deletions

16
.gitignore vendored
View File

@@ -16,6 +16,7 @@ install-sh
libtool
ltmain.sh
missing
osmo-ggsn.spec
stamp-h1
INSTALL
m4/
@@ -27,7 +28,7 @@ osmo-ggsn-*.tar*
# debian
debian/osmo-ggsn/
debian/*.debhelper
debian/libgtp*/
debian/libgtp1
debian/osmo-ggsn-dbg
debian/*.log
debian/autoreconf.*
@@ -68,16 +69,3 @@ tests/*/*_test
tests/testsuite
tests/testsuite.log
tests/package.m4
# manuals
doc/manuals/*.html
doc/manuals/*.svg
doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build
contrib/osmo-ggsn.spec

View File

@@ -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 tests
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgtp.pc
@@ -10,17 +10,6 @@ $(top_srcdir)/.version:
dist-hook:
echo $(VERSION) > $(distdir)/.tarball-version
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)
EXTRA_DIST = git-version-gen .version README.md README.FreeBSD README.MacOSX
@RELMAKE@

View File

@@ -1,15 +1,14 @@
# 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])
AC_CONFIG_HEADERS([config.h])
AM_CONFIG_HEADER([config.h])
#AC_CONFIG_HEADER([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_HOST
CFLAGS="$CFLAGS -std=gnu11"
AC_CANONICAL_SYSTEM
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -21,11 +20,6 @@ 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
@@ -44,9 +38,9 @@ AC_SUBST(EXEC_LDFLAGS)
case "${host}" in
i*86-*-linux-gnu*)
i*86-*-linux-gnu*)
EXEC_LDADD="" ;;
*solaris*)
*solaris*)
EXEC_LDADD="-lresolv -lsocket -lnsl" ;;
esac
@@ -71,7 +65,7 @@ AC_ARG_ENABLE([gtp-linux],
[enable_gtp_linux="$enableval"], [enable_gtp_linux="no"])
AS_IF([test "x$enable_gtp_linux" = "xyes"], [
PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.2.0])
PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.0.0])
])
AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_linux" = "yes"])
@@ -81,12 +75,8 @@ 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. 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 if header
AC_CHECK_HEADERS([linux/if.h net/if.h])
# Check for tun header
AC_CHECK_HEADERS([linux/if_tun.h net/if_tun.h])
@@ -133,19 +123,10 @@ 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
# AC_FUNC_MEMCMP
# AC_FUNC_MEMCMP
AC_CHECK_FUNCS([gethostbyname inet_ntoa memset select socket strdup strerror strtol])
AC_CHECK_FUNCS(inet_aton inet_addr, break)
@@ -154,9 +135,9 @@ adl_FUNC_GETOPT_LONG
AM_INIT_AUTOMAKE([foreign])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.6.4)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -188,64 +169,6 @@ then
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
# Generate manuals
AC_ARG_ENABLE(manuals,
[AS_HELP_STRING(
[--enable-manuals],
[Generate manual PDFs [default=no]],
)],
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
fallback])
if test x"$osmo_ac_build_manuals" = x"yes"
then
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
else
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
if test -n "$OSMO_GSM_MANUALS_DIR"; then
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
else
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
fi
fi
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
fi
# Find and run check-depends
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
if ! test -x "$CHECK_DEPENDS"; then
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
fi
if ! $CHECK_DEPENDS; then
AC_MSG_ERROR("missing dependencies for --enable-manuals")
fi
# Put in Makefile with absolute path
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
AC_SUBST([OSMO_GSM_MANUALS_DIR])
fi
# https://www.freedesktop.org/software/systemd/man/daemon.html
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
[with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
with_systemdsystemunitdir=no],
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
@@ -258,14 +181,11 @@ AC_CONFIG_FILES([Makefile
intl/Makefile
po/Makefile
sgsnemu/Makefile
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-ggsn.spec
tests/Makefile
tests/lib/Makefile
tests/gtp/Makefile
libgtp.pc])
libgtp.pc
osmo-ggsn.spec])
AC_OUTPUT
echo "

View File

@@ -1 +0,0 @@
SUBDIRS = systemd

View File

@@ -1,11 +1,5 @@
#!/usr/bin/env bash
# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
#
# environment variables:
# * GTP: configure GTP tunneling Linux kernel (values: "--enable-gtp-linux" or "--disable-gtp-linux")
# * WITH_MANUALS: build manual PDFs if set to "1"
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
#
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
@@ -33,13 +27,6 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
# Additional configure options and depends
CONFIG=""
if [ "$WITH_MANUALS" = "1" ]; then
CONFIG="--enable-manuals"
fi
set +x
echo
@@ -51,13 +38,8 @@ set -x
cd "$base"
autoreconf --install --force
./configure --enable-sanitize --enable-werror $GTP $CONFIG
./configure --enable-sanitize --enable-werror $GTP
$MAKE $PARALLEL_MAKE
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck
$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

View File

@@ -1,123 +0,0 @@
#
# 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.5.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
BuildRequires: pkgconfig(libosmovty) >= 1.5.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.
%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.cfg
%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
%changelog

View File

@@ -1,10 +0,0 @@
EXTRA_DIST = \
osmo-ggsn.service \
ggsn.network \
apn0.netdev \
$(NULL)
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
osmo-ggsn.service
endif

View File

@@ -1,7 +0,0 @@
[NetDev]
Name=apn0
Kind=tun
[Tun]
User=username
Group=username

View File

@@ -1,6 +0,0 @@
[Match]
Name=apn0
[Network]
Address=192.168.7.1/24
IPMasquerade=yes

396
debian/changelog vendored
View File

@@ -1,399 +1,3 @@
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 ]
* ggsn: ctrl iface: listen on IP configured by VTY
* gtp: Log type name of unexpected signalling message
* gtp: Allow recv DEL CTX REQ in sgsn and DEL CTX RSP in ggsn
* gtp: Log ignore CTX DEL REQ due to no teardown and only 1 ctx active
* gtp: Add new API to avoid freeing pdp contexts during DEL CTX REQ
* gtp: Add new replacement cb_recovery2 for cb_recovery
* Install systemd services with autotools
* Install sample cfg file to /etc/osmocom
[ Stefan Sperling ]
* fix unaligned access in build_ipcp_pco()
* fix support for multiple IPCP in PDP protocol configuration options
* check ioctl() call return value in tun_new()
* fix allocation of ippool's hash table
* replace bogus memcpy() call in ippool_newip()
* initialize local variable addr in ippool_new()
* fix format string error in ippool_printaddr()
* fix a format string directives in queue_seqset()
* properly store IPv6 addresses in struct tun_t
[ Harald Welte ]
* debian/rules: Don't overwrite .tarball-version
* osmo-ggsn.cfg: Ensure well-formed config file example
* sgsnemu: Fix printing of tun device name
* ippool.c: Use "%td" format string for ptrdiff_t
* initial version of OsmoGGSN user manual
* OsmoGGSN: Add VTY reference manual
* GGSN: Document how 'ip tuntap' is used for non-root; call netdev 'apn0'
* vty-ref: Update URI of docbook 5.0 schema
[ Alexander Couzens ]
* libgtp: implement gtp_clear_queues to clear req/resp queue
[ Neels Hofmeyr ]
* Importing history from osmo-gsm-manuals.git
* refactor Makefile build rules, don't use the FORCE
* GGSN: don't say 'NITB'
* OsmoGGSN: more info on non-root operation / tun creation
* OsmoGGSN: multiple instances: mention GTP port
* OsmoGGSN: add Routing section for IP forward and masquerading
* OsmoGGSN: typo: priveleges
* OsmoGGSN VTY ref: prep: convert newlines to unix
* OsmoGGSN vty: update VTY reference
* OsmoGGSN: fix VTY additions' node IDs
* OsmoGGSN: update vty reference
* ggsn: update vty reference
[ Max ]
* Expand OsmoGGSN manual
[ Oliver Smith ]
* build manuals moved here from osmo-gsm-manuals.git
* Fix DISTCHECK_CONFIGURE_FLAGS override
* contrib/jenkins.sh: build and publish manuals
* contrib: fix makedistcheck with disabled systemd
-- Harald Welte <laforge@gnumonks.org> Sun, 20 Jan 2019 21:34:22 +0100
osmo-ggsn (1.2.2) unstable; urgency=medium
[ Vadim Yanitskiy ]
* ggsn_vty.c: fix: use CONFIG_NODE as parent by default
[ Philipp Maier ]
* ggsn: fix misinterpreted length field in ipcp_contains_option()
* ggsn: make sure ipcp_option_hdr and and ipcp_hdr are packed
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 31 May 2018 12:44:54 +0200
osmo-ggsn (1.2.1) unstable; urgency=medium
* debian/rules: Fix debian packaging after 1.2.0 release
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 04 May 2018 12:19:58 +0200
osmo-ggsn (1.2.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* fix compiler warnings: return 0 in main(), in 3 tests
* add --enable-sanitize config option
* sanitize build: ensure uint16/32 alignment in gtpie_test and in46a_test
* configure: add --enable-werror
* jenkins.sh: use --enable-werror configure flag, not CFLAGS
[ Harald Welte ]
* sgsnemu: Don't leak FILE handle in proc_read()
* sgsnemu: Fix format string in printing tun-device name
* sgsnemu: Make sure buffer has space for terminating-NUL
* sgsnemu: Free strings in error path
* gtp: Fix buffer overflow in imsi_gtp2str()
* gtp: Explicit OSMO_ASSERT to ensure pdp variable is set
* tun: Don't copy 16byte IPv6 address to 'struct in_addr'
* ippool: Correctly compute size of static pool
* remove unused argument to alloc_ippool_blacklist()
* factor out netdev_ip_local_get() from tun_ip_local_get()
* Properly NULL-out blacklist in alloc_ippool_blacklist()
* gtp_kernel: Change gtp_kernel_init() function signature
* gtp-kernel: Re-add support for kernel GTP-U acceleration
* gtp-kernel: Get rid of hard-coded kernel GTP device name
* gtp-kernel: shut down kernel GTP device in apn_down()
* gtp-kernel: Align logging for APN start in kernel-gtp case with that of TUN
* gtp-kernel: Avoid global state variable
* gtp-kernel: Make sure repeated calls to gtp_kernel_init() are safe
* gtp-kernel: proper cleanup in error path
* gtp-kernel: Get rid of SYS_ERR where not applicable
* gtp-kernel: Add function name to pdp_debug() function calls
* gtp-kernel: Add device nime in pdp_debug() log statements
* contrib/jenkins.sh: Allow jenkins job to specify if kernel GTP is used
* ggsn.c: Fix byte order of IPCP IPv4 DNS servers
* ggsn: Ignore PCO with length 0, don't abort processing
* README.md: Remove misleading sentence on sgsnemu
* Add talloc context introspection via VTY
* fix segfault in case of kernel gtp-u
* lib/tun.c: Generalize tun_sifflags() to netdev_sifflags
* lib/tun.c: generalize tun_*route() to netdev_*route()
* lib/tun.c: Generalize tun_{set,add}addr*() functions
* lib/tun: split generic network device related stuff to lib/netdev
* lib/netdev.c: Cosmetic changes (coding style / cleanups)
* ggsn: Don't explicitly use tun_setaddr() API anymore
* sgsnemu: Convert from tun_setaddr() to tun_addaddr()
* lib/tun: Remove tun_setaddr() API, as everyone is using tun_addaddr() now
* Move kernel GTP support from ggsn/ to lib/
* ggsn: don't use gtp_kernel_tunnel_{add,del}() for userspace tun
[ Pau Espin Pedrol ]
* ggsn_vty: Stop using deprecated API vty_install_default
* contrib/jenkins.sh: Enable Werror in C(PP)FLAGS
* examples: Add secondary ipv6 google DNS to osmo-ggsn.cfg
* tun_setaddr6: Fix log typo
* cosmetic: Reorder tun_addaddr to get rid of decl of tun_setaddr4
* ggsn.c: Print version of unhandled ip packet
* Remove unused empty src/Makefile.in
* tests: Split ipv6 specific tests into a new test group
* Add support for IPv4v6 End User Addresses
* contrib: jenkins.sh: Build libgtpnl as dep when building with gtp kernel support
* cosmetic: sgsnemu.c: Fix trailing whitespace
* ggsn.c: Improve logging info on link-local ipv6 addr not found
* tun.c: tun_addaddr: Fix segfault and wrong usage of tun_nlattr
* Set tun_addaddr ipv agnostic and add support for ipv6
* ggsn: Add 'ipv6 link-local' vty cmd
* ggsn_vty.c: Print ipv6 link-local cmd when writing config to file
* gtp.c: Fix trailing whitespace
* gtp.c: Determine GTP version from header
* gtp.c: Log unsupported GTP version number
* gtp/pdp: Fix trailing whitespace
* gtp/pdp: Remove unused APIs pdp_ntoeua pdp_euaton
* gtp.c: gtp_gpdu_ind: Convert ifelse to switch statement
* gtp.c: gtp_gpdu_ind: Early return to avoid use of uninitialized var
* gtp/gtp.c: Remove unused function char2ul_t
* gtp/gtp.c: Mark non exported functions as static
* gtp/gtp.c: Use uint8_t for version param in static functions
* ggsn: encaps_tun: Avoid forwarding packet if EUA is unassigned, fix crash
* ggsn: Validate packet src addr from MS
* ggsn: Parse PCO_IPCP
* ggsn: Parse PCO_IPCP for IPv4v6 pdp ctx
* ggsn: Print all addresses on successful pdp ctx creation
* ggsn.c: cb_tun_ind: Convert ifelse to switch statement
* ggsn.c: cb_tun_ind: log dst addr of packet without pdp ctx
* ggsn.c: cb_tun_ind: Don't drop packets targeting pdp ctx ll addr
* sgsnemu: Fix bad ptr during context deallocation
* sgsnemu: listen param is a host, not an interface
* use osmo_init_logging2
[ Max ]
* Log APN and tun names for packets
* Enable sanitize for CI tests
* Fix stow-enabled jenkins build failure
* Add GTP message names
[ Viktor Tsymbalyuk ]
* sgsnemu: sgsnemu stopped after recieving "Request accepted" from ggsn
* sgsnemu: created "pinghost" and "createif" modes for mutual exclusion
* sgsnemu: fix: no outgoing GTP-U in "createif" mode
[ Martin Hauke ]
* build: Remove AC_PROG_CXX, C++ is never used
[ Stefan Sperling ]
* remove the -f option from osmo-ggsn.service
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 16:05:27 +0200
osmo-ggsn (1.1.0) unstable; urgency=medium
* libgtp: pdp.h: Addition of new tx_gpdu_seq struct member member

23
debian/control vendored
View File

@@ -1,5 +1,5 @@
Source: osmo-ggsn
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Maintainer: Harald Welte <laforge@gnumonks.org>
Section: net
Priority: optional
Build-Depends: debhelper (>= 9),
@@ -7,9 +7,7 @@ Build-Depends: debhelper (>= 9),
pkg-config,
libdpkg-perl, git,
dh-autoreconf,
libosmocore-dev (>= 1.5.0),
osmo-gsm-manuals-dev,
libgtpnl-dev (>= 1.2.0)
libosmocore-dev (>= 0.8.0)
Standards-Version: 3.9.6
Vcs-Browser: http://git.osmocom.org/osmo-ggsn/
Vcs-Git: git://git.osmocom.org/osmo-ggsn
@@ -24,7 +22,7 @@ Description: Osmocom Gateway GPRS Support Node (GGSN)
operators as the interface between the Internet and the rest of the
mobile network infrastructure.
Package: libgtp6
Package: libgtp2
Architecture: any
Multi-Arch: same
Section: libs
@@ -43,7 +41,7 @@ Architecture: any
Multi-Arch: same
Section: libdevel
Depends: ${misc:Depends},
libgtp6 (= ${binary:Version})
libgtp2 (= ${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
@@ -56,7 +54,7 @@ Package: osmo-ggsn-dbg
Section: debug
Architecture: any
Priority: extra
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp6 (= ${binary:Version}), osmo-ggsn (= ${binary:Version})
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp2 (= ${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
@@ -67,7 +65,7 @@ Package: libgtp-dbg
Section: debug
Architecture: any
Priority: extra
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp6 (= ${binary:Version})
Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp2 (= ${binary:Version})
Multi-Arch: same
Description: Debug symbols for OsmoGGSN
OsmoGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile
@@ -76,12 +74,3 @@ 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.

View File

@@ -1 +0,0 @@
usr/share/doc/osmo-ggsn-doc/*.pdf

View File

@@ -1,5 +1,3 @@
/etc/osmocom/osmo-ggsn.cfg
/lib/systemd/system/osmo-ggsn.service
/usr/bin/osmo-ggsn
/usr/bin/sgsnemu
/usr/share/man/man8/*

1
debian/osmo-ggsn.service vendored Symbolic link
View File

@@ -0,0 +1 @@
../contrib/osmo-ggsn.service

14
debian/rules vendored
View File

@@ -16,14 +16,8 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
override_dh_strip:
dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg
dh_strip -plibgtp6 --dbg-package=libgtp-dbg
dh_strip -plibgtp2 --dbg-package=libgtp-dbg
override_dh_auto_configure:
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
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf

View File

@@ -4,5 +4,4 @@ EXTRA_DIST = $(man_MANS)
SUBDIRS = \
examples \
manuals \
$(NULL)

View File

@@ -1,8 +1,3 @@
osmoconfdir = $(sysconfdir)/osmocom
osmoconf_DATA = osmo-ggsn.cfg
EXTRA_DIST = osmo-ggsn.cfg
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
dist-hook:

View File

@@ -3,32 +3,32 @@
!!
!
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
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
!
@@ -37,15 +37,15 @@ line vty
!
ggsn ggsn0
gtp state-dir /tmp
gtp bind-ip 127.0.0.2
gtp bind-ip 127.0.0.6
apn internet
gtpu-mode tun
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
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
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 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
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
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

View File

@@ -1,24 +0,0 @@
EXTRA_DIST = osmoggsn-usermanual.adoc \
osmoggsn-usermanual-docinfo.xml \
osmoggsn-vty-reference.xml \
regen_doc.sh \
chapters \
vty
if BUILD_MANUALS
ASCIIDOC = osmoggsn-usermanual.adoc
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
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

View File

@@ -1,335 +0,0 @@
== Configuring OsmoGGSN
All configuration of OsmoGGSN is performed using the VTY. For more
general information on the VTY interface, see <<vty>>.
=== Configuring a virtual GGSN instance
OsmoGGSN can run multiple GGSN instances inside one program/process.
Each GGSN instance binds to its own transport-layer GTP IP address and
has its own set of APNs and associated IP address pools + tun/gtp
devices.
In most usage cases, yo will only have a single GGSN instance inside
your configuration file, like in below example:
.Example: Single GGSN configuration section
----
ggsn ggsn0
gtp state-dir /tmp
gtp bind-ip 127.0.0.6
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
no shutdown
----
==== Creating/Editing a GGSN instance
Creating/Editing a GGSN instance can be done by the following sequence
of VTY commands:
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Create or edit the GGSN instance `ggsn0`. The name can be any ASCII
string, its significance is only to the local user.
<4> Your prompt is now in the `ggsn` config node, where you can
configure the properties of this GGSN instance.
NOTE:: After creating a new GGSN instance, it is in `shutdown` mode. See
<<unshutdown_apn>> to take it out of shutdown, but make sure to configure it fully
before taking it out of shutdown.
==== Configuring a GGSN instance
The following two mandatory configuration statements have to be given
for every GGSN instance:
----
OsmoGGSN(config-ggsn)# gtp state-dir /var/lib/ggsn/ggsn0 <1>
OsmoGGSN(config-ggsn)# gtp bind-ip 127.0.0.6 <2>
----
<1> Store the GSN restart state in the specified directory
<2> Bind the GGSN instance to the specified local IPv4 address
There are some further configuration statements that can be used at the
GGSN node, some examples are given below. For a full list, see the
_OsmoGGSN VTY reference manual_ <<vty-ref-osmoggsn>>.
----
OsmoGGSN(config-ggsn)# default-apn foobar <1>
----
<1> Configure a default APN to be used if the user-requested APN is not
found. The named APN must previously be configured
==== Deleting a GGSN instance
A GGSN instance can be removed like this
.Example: Deleting a GGSN instance
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# no ggsn ggsn0 <3>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Delete the GGSN instance
==== Taking a GGSN instance out of shutdown
.Example: Taking a GGSN instance out of shutdown
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# no shutdown ggsn <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Take the GGSN instance out of shutdown
==== Shutting a GGSN instance down
If you would like to take a GGSN instance out of service, you can
put it into shutdown mode. This will make the entire GGSN unavailable
to user traffic and permit you to e.g. reconfigure it before taking it
out of shutdown again.
.Example: Shutting down a GGSN instance
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# shutdown ggsn <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Shut down the GGSN instance
=== Configuring an Access Point Name
An Access Point Name (APN) represents a connection to an external packet
data network, such as the public Internet or private corporate networsk.
APNs are selected by terminals (MS/UE) when establishing PDP contexts.
Each OsmoGGSN GGSN instance can have any number of APNs configured.
Each APN is identified by a string name.
==== Creating/Editing an APN
.Example: Creating a new APN
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# apn internet <4>
OsmoGGSN(config-ggsn-apn)# <5>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config node of the GGSN instance `ggsn0`
<4> Create or Edit an APN called `internet`
<5> Your prompt is now in the `ggsn` config node, where you can
configure the properties of this GGSN instance.
NOTE:: The newly-create APN is created in `shutdown` mode. See <<unshutdown_apn>> to take it
out of shutdown.
==== Configuring an APN
.Example: Configuring an APN
----
OsmoGGSN(config-ggsn-apn)# gtpu-mode tun <1>
OsmoGGSN(config-ggsn-apn)# type-support v4 <2>
OsmoGGSN(config-ggsn-apn)# ip prefix dynamic 176.16.222.0/24 <3>
OsmoGGSN(config-ggsn-apn)# ip dns 0 192.168.100.1 <4>
OsmoGGSN(config-ggsn-apn)# ip dns 1 8.8.8.8 <5>
OsmoGGSN(config-ggsn-apn)# ip ifconfig 176.16.222.0/24 <6>
----
<1> Use the userspace GTP-U handling using a TUN device
<2> Support (only) IPv4 Addresses
<3> Specify the pool of dynamic IPv4 addresses to be allocated to PDP
contexts
<4> Specify the primary DNS server to be provided using IPCP/PCO
<5> Specify the secondary DNS server to be provided using IPCP/PCO
<6> Request OsmoGGSN to configure the `tun4` device network/netmask
NOTE:: If you use the optional `ip ifconfig` command to set the network
device address/mask, OsmoGGSN must run with root or `CAP_NET_ADMIN`
support. It might be better to configure related tun devices at system
startup and run OsmoGGSN as non-privileged user. See <<ggsn_no_root>> for more
details.
==== Deleting an APN
An APN configuration can be removed like this
.Example: Deleting an APN
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# no apn internet <4>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config node of the GGSN instance `ggsn0`
<4> Delete the APN `internet`
[[unshutdown_apn]]
==== Taking an APN out of shutdown
In order to bring a deactived APN in `shutdown` state into active
operation, use the `no shutdown` command at the APN node as explained in
the following example:
.Example: Taking an APN out of shutdown
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# apn internet <4>
OsmoGGSN(config-ggsn-apn)# no shutdown <5>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Enter the config ndoe of the APN `internet`
<5> Take the APN out of shutdown
==== Shutting an APN down
If you would like to take an APN instance out of service, you can
put it into shutdown mode. This will make the APN unavailable
to user traffic and permit you to e.g. reconfigure it before taking it
out of shutdown again.
.Example: Shutting down an APN
----
OsmoGGSN> enable <1>
OsmoGGSN# configure terminal <2>
OsmoGGSN(config)# ggsn ggsn0 <3>
OsmoGGSN(config-ggsn)# apn internet <4>
OsmoGGSN(config-ggsn-apn)# shutdown <5>
----
<1> Change into privileged mode
<2> Enter the interactive configuration mode
<3> Enter the config ndoe of the GGSN instance `ggsn0`
<4> Enter the config ndoe of the APN `internet`
<5> Shut down the APN
[[ggsn_no_root]]
=== Configuring for running without root privileges
It's possible to run OsmoGGSN without root privileges if the tun devices are already configured.
The interface creation + configuration must then happen before osmo-ggsn starting up. This can be
achieved by means such as
* a custom shell script run as root before starting osmo-ggsn (e.g. as init script)
* systemd .netdev and .network files, if your system is using systemd-networkd (see `networkctl status`).
==== Manual TUN device creation / configuration
If you chose to go for custom shell/init scripts, you may use the `ip` program which is the standard
tool for network interface configuration on Linux, part of the `iproute2` package. In order to
create a tun device, you must call it like this:
.Example: iproute2 command to create a tun device
----
# ip tuntap add dev apn0 mode tun user username group groupname
----
Where _username_ and _groupname_ correspond to the User and Group that will have ownership over the
device, i.e. the privileges which you intend to run osmo-ggsn under, and _apn0_ will be the
name of the network device created. After creating the interface, you can configure its addresses
using standard means like `ip addr add` or your distribution-specific utilities/tools
to match the `ip prefix dynamic` config item, and activate the link, for example:
----
# ip addr add 192.168.7.1/24 dev apn0
# ip link set apn0 up
----
==== systemd based TUN device creation+configuration
If you want to have systemd take care of creating and configuring a tun device for you,
you can use the below example config files.
.Example: device config via systemd-networkd using apn0.netdev
----
[NetDev]
Name=apn0 <1>
Kind=tun
[Tun]
User=username <2>
Group=username <3>
----
<1> The network interface name of the newly-created device
<2> The username under which you will run OsmoGGSN
<3> The group name under which you will run OsmoGGSN
.Example: network settings via systemd-networkd using ggsn.network
----
[Match]
Name=apn0 <1>
[Network]
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
<2> The local IP address configured on the device
<3> Requesting systemd to configure IP masquerading for this interface. Depending on your needs,
You may not want this if you have proper end-to-end routing set up, and want to have transparent
inbound IP access to your GPRS-attached devices.
==== Config Changes
With the tun device pre-configured in one of the ways outlined above, the main
changes in your osmo-ggsn.cfg file are:
* remove `ip ifconfig` directive,
* make sure that `no shutdown` is present in the `apn` section as well as
`no shutdown ggsn` in the `ggsn` section.
.Example: using externally configured tun device `apn0` as non-root
----
ggsn ggsn0
gtp state-dir /tmp
gtp bind-ip 127.0.0.6
apn internet
gtpu-mode tun
tun-device apn0
type-support v4
ip prefix dynamic 192.168.7.0/24
ip dns 0 192.168.100.1
ip dns 1 8.8.8.8
no shutdown
default-apn internet
no shutdown ggsn
----

View File

@@ -1,145 +0,0 @@
[[chapter_introduction]]
== Overview
[[intro_overview]]
=== About OsmoGGSN
OsmoGGSN is a Free / Open Source Software implementation of the GPRS
GGSN (Gateway GPRS support node) element in side the packet switched
core network of 2G and 3G cellular networks.
The GGSN function is the tunnel endpoint on the core network side,
from where the external (IP) packet data network
=== Software Components
==== GTP Implementation (libgtp)
The OsmoGGSN source code includes a shared library implementation of
the GTP protocol used on the GGSN-SGSN interface. This library
and associated header files are installed system-wide and are
available to other programs/applications.
In fact, libgtp is what the OsmoSGSN also uses for its use of GTP.
==== sgsnemu
In order to test OsmoGGSN without running a SGSN and other elements
of a cellular network, there is a small command-line utility called
*sgsnemu* which is able to simulate the customary operations of a SGSN
towards the GGSN, such as a PDP Context Activation.
*sgsnemu* can even be used for testing against other GGSNs, as the GTP
protocol is standardized across implementations.
==== osmo-ggsn
*osmo-ggsn* is the actual name of the OsmoGGSN executable program. It
implements the GGSN functionality. All parameters are set using the
configuration file, by default located in *./osmo-ggsn.cfg*
==== systemd service file
In *contrib/osmo-ggsn.service* you can find a sample service file for
OsmoGGSN which can be used with systemd.
==== init script
In *contrib/osmo-ggsn.init* you can find a sample init script to be used
on systems with classic init process.
=== Limitations
OsmoGGSN supports both GTP0 (GSM 09.60) and GTP1 (3GPP 29.060). In the
following tables the support of each individual message type is
detailed. The numbers before each feature indicates the relevant
section in the standard.
==== GSM 09.60 (GTPv0)
[options="header",cols="50%,15%,15%,15%,5%"]
|===
| Feature | gtplib | osmo-ggsn | sgsnemu | notes
5+<|*7.4 Path Management Messages*
|7.4.1 Echo Request |Supported |Supported |Supported |
|7.4.2 Echo Response |Supported |Supported |Supported |
|7.4.3 Version Not Supported |Supported |Supported |Supported |
5+<| *7.5 Tunnel Management Messages*
|7.5.1 Create PDP Context Request|Supported |Supported |Supported |
|7.5.2 Create PDP Context Response|Supported |Supported |Supported |
|7.5.3 Update PDP Context Request|Supported |Supported |Not |
|7.5.4 Update PDP Context Response|Supported |Supported |Not |
|7.5.5 Delete PDP Context Request|Supported |Supported |Supported |
|7.5.6 Delete PDP Context Response|Supported |Supported |Supported |
|7.5.7 Create AA PDP Context Request|Unsupported |Unsupported |Unsupported |
|7.5.8 Create AA PDP Response|Unsupported |Unsupported |Unsupported |
|7.5.9 Delete AA PDP Context Request|Unsupported |Unsupported |Unsupported |
|7.5.10 Delete AA PDP Context Response|Unsupported |Unsupported |Unsupported |
|7.5.11 Error Indication |Supported |Supported |Supported |
|7.5.12 PDU Notification Request|Unsupported |Unsupported |Unsupported |
|7.5.13 PDU Notification Response|Unsupported |Unsupported |Unsupported |
|7.5.14 PDU Notification Reject Request|Unsupported |Unsupported |Unsupported |
|7.5.15 PDU Notification Reject Response|Unsupported |Unsupported |Unsupported |
5+<| *7.6 Location Management Messages*
|7.6.1 Send Routeing Information for GPRS Request|Unsupported |Unsupported |Not applicable |
|7.6.2 Send Routeing Information for GPRS Response|Unsupported |Unsupported |Not applicable |
|7.6.3 Failure Report Request|Unsupported |Unsupported |Not applicable |
|7.6.3 Failure Report Response|Unsupported |Unsupported |Not applicable |
|7.6.5 Note MS GPRS Present Request|Unsupported |Unsupported |Not applicable|
|7.6.6 Note MS GPRS Present Response|Unsupported |Unsupported |Not applicable|
5+<| *7.5 Mobility Management Messages*
|7.5.1 Identification Request|Unsupported |Not applicable|Not applicable|
|7.5.2 Identification Response|Unsupported |Not applicable|Not applicable |
|7.5.3 SGSN Context Request|Unsupported |Not applicable|Not applicable|
|7.5.4 SGSN Context Response|Unsupported |Not applicable|Not applicable|
|7.5.5 SGSN Context Acknowledge|Unsupported |Not applicable|Not applicable|
|===
==== 3GPP 29.060 (GTPv1)
[options="header",cols="50%,15%,15%,15%,5%"]
|===
|Feature |gtplib |osmo-ggsn |sgsnemu |notes
5+<|*7.2 Path Management Messages*
|7.2.1 Echo Request |Supported |Supported |Supported |
|7.2.2 Echo Response |Supported |Supported |Supported |
|7.2.3 Version Not Supported|Supported |Supported |Supported |
|7.2.4 Extension Headers Notification|Supported |Supported |Supported |
5+<|*7.3 Tunnel Management Messages*
|7.3.1 Create PDP Context Request|Supported |Supported |Supported |1
|7.3.2 Create PDP Context Response|Supported |Supported |Supported |
|7.3.3 Update PDP Context Request|Supported |Supported |Not applicable|1
|7.3.4 Update PDP Context Response|Supported |Supported |Not applicable|
|7.3.5 Delete PDP Context Request|Supported |Supported |Supported |
|7.3.6 Delete PDP Context Response|Supported |Supported |Supported |
|7.3.7 Error Indication |Supported |Supported |Supported |
|7.3.8 PDU Notification Request|Unsupported |Unsupported |Unsupported |
|7.3.9 PDU Notification Response|Unsupported |Unsupported |Unsupported |
|7.3.10 PDU Notification Reject Request|Unsupported |Unsupported |Unsupported |
|7.3.10 PDU Notification Reject Response|Unsupported |Unsupported |Unsupported |
5+<|*7.4 Location Management Messages*
|7.4.1 Send Routeing Information for GPRS Request|Unsupported |Unsupported |Not applicable |
|7.4.2 Send Routeing Information for GPRS Response|Unsupported |Unsupported |Not applicable |
|7.4.3 Failure Report Request|Unsupported |Unsupported |Not applicable|
|7.4.3 Failure Report Response|Unsupported |Unsupported |Not applicable|
|7.4.5 Note MS GPRS Present Request|Unsupported |Unsupported |Not applicable|
|7.4.6 Note MS GPRS Present Response|Unsupported |Unsupported |Not applicable|
5+<|*7.5 Mobility Management Messages*
|7.5.1 Identification Request|Unsupported |Not applicable|Not applicable|
|7.5.2 Identification Response|Unsupported |Not applicable |Not applicable|
|7.5.3 SGSN Context Request|Unsupported |Not applicable|Not applicable|
|7.5.4 SGSN Context Response|Unsupported |Not applicable |Not applicable|
|7.5.5 SGSN Context Acknowledge|Unsupported |Not applicable|Not applicable|
|7.5.6 Forward Relocation Request|Unsupported |Not applicable|Not applicable|
|7.5.7 Forward Relocation Response|Unsupported |Not applicable|Not applicable|
|7.5.8 Forward Relocation Complete|Unsupported |Not applicable|Not applicable|
|7.5.9 Relocation Cancel Request|Unsupported |Not applicable|Not applicable|
|7.5.10 Relocation Cancel Response|Unsupported |Not applicable|Not applicable|
|7.5.11 Forward Relocation Complete |Unsupported |Not applicable |Not applicable |
|7.5.12 Forward SRNS Context Acknowledge|Unsupported |Not applicable|Not applicable|
|7.5.13 Forward SRNS Context|Unsupported |Not applicable|Not applicable|
|===
Notes
1) The "Secondary PDP Context Activation Procedure" is not supported.

View File

@@ -1,82 +0,0 @@
== Running OsmoGGSN
The OsmoGGSN executable (`osmo-ggsn`) offers the following command-line
arguments:
=== SYNOPSIS
*osmo-ggsn* [-h|-V] [-D] [-c 'CONFIGFILE']
=== OPTIONS
*-h, --help*::
Print a short help message about the supported options
*-V, --version*::
Print the compile-time version number of the program
*-D, --daemonize*::
Fork the process as a daemon into background.
*-c, --config-file 'CONFIGFILE'*::
Specify the file and path name of the configuration file to be
used. If none is specified, use `osmo-ggsn.cfg` in the current
working directory.
=== Routing
Operating the OpenGGSN tun device naturally creates a network setup with
multiple interfaces. Consider:
* Typical Linux setups prevent forwarding of packets between separate
interfaces by default. To let subscribers reach the internet uplink from the
tun device, it may be required to enable IP forwarding.
* Having a locally defined address range assigned to the tun device requires
either sensible routing for this address range, or that masquerading is
enabled to allow your single uplink IP address to "proxy" for the tun.
These are decisions to be made on a network administration level.
In a trivial case where you have a single box serving GPRS to few subscribers
on an arbitrary IP address range not known in the larger network, the easiest
way to enable GPRS uplink would be to enable IP forwarding and masquerading.
To manually enable IPv4 forwarding and masquerading ad-hoc, you can do:
----
sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
iptables -t nat -A POSTROUTING -o '*' -j MASQUERADE
----
(You may want to replace `*` with the network device name, like `-o eth0`)
There are various ways to enable these settings persistently, please refer to
your distribution's documentation -- e.g. look for @net.ipv4.ip_forward=1@ in
@/etc/sysctl.d/@, and https://wiki.debian.org/iptables for masquerading.
=== Multiple instances
Running multiple instances of `osmo-ggsn` is possible if all GGSN instances
are binding to different local IP addresse and all other interfaces (VTY,
OML) are separated using the appropriate configuration options. The IP based
interfaces are binding to local host by default. In order to separate the
processes, the user has to bind those services to specific but different
IP addresses.
The VTY and the control interface can be bound to IP addresses from the loopback
address range.
.Example: Binding VTY and control interface to a specific ip-address
----
line vty
bind 127.0.0.2
ctrl
bind 127.0.0.2
----
Also make sure to place each instance's GTP bind on a separate IP address (GTP
uses a port number that is fixed in the GTP specifications, so it will not be
possible to pick differing ports on the same IP address), like:
----
ggsn ggsn0
gtp bind-ip 127.0.0.2
----

View File

@@ -1,46 +0,0 @@
<revhistory>
<revision>
<revnumber>1</revnumber>
<date>August 2017</date>
<authorinitials>HW</authorinitials>
<revremark>
Initial version.
</revremark>
</revision>
</revhistory>
<authorgroup>
<author>
<firstname>Harald</firstname>
<surname>Welte</surname>
<email>hwelte@sysmocom.de</email>
<authorinitials>HW</authorinitials>
<affiliation>
<shortaffil>sysmocom</shortaffil>
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
<jobtitle>Managing Director</jobtitle>
</affiliation>
</author>
</authorgroup>
<copyright>
<year>2013-2017</year>
<holder>sysmocom - s.f.m.c. GmbH</holder>
</copyright>
<legalnotice>
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
</para>
<para>
The Asciidoc source code of this manual can be found at
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
http://git.osmocom.org/osmo-gsm-manuals/
</ulink>
</para>
</legalnotice>

View File

@@ -1,31 +0,0 @@
OsmoGGSN User Manual
====================
Harald Welte <hwelte@sysmocom.de>
include::./common/chapters/preface.adoc[]
include::{srcdir}/chapters/overview.adoc[]
include::{srcdir}/chapters/running.adoc[]
//include::{srcdir}/chapters/control.adoc[]
include::./common/chapters/vty.adoc[]
include::./common/chapters/logging.adoc[]
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[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
ex:ts=2:sw=42sts=2:et
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-->
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
]>
<book>
<info>
<revhistory>
<revision>
<revnumber>v1</revnumber>
<date>06th September 2017</date>
<authorinitials>hw</authorinitials>
<revremark>Initial version as of OsmoGGSN v1.0.0</revremark>
</revision>
</revhistory>
<title>OsmoGGSN VTY Reference</title>
<copyright>
<year>2017</year>
</copyright>
<legalnotice>
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
</para>
</legalnotice>
</info>
<!-- Main chapters-->
&chapter-vty;
</book>

View File

@@ -1,17 +0,0 @@
#!/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"

View File

@@ -1,30 +0,0 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
<node id='config-line'>
<child_of nodeid='config' />
<name>Telnet/VTY Configuration Node</name>
<description>
Configure parameters of the Telnet/VTY Interface, such as to which IP address it should bind/listen to.
</description>
</node>
<node id='config-ctrl'>
<child_of nodeid='config' />
<name>CTRL Configuration Node</name>
<description>
Configure parameters of the CTRL Interface, such as to which IP address it should bind/listen to.
</description>
</node>
<node id='config-ggsn'>
<child_of nodeid='config' />
<name>GGSN Instance Configuration Node</name>
<description>
Configure an Instance of a (virtual) GGSN
</description>
</node>
<node id='config-ggsn-apn'>
<child_of nodeid='config-ggsn' />
<name>APN Configuration Node</name>
<description>
Configure an Access Point Name (APN) inside a GGSN Instance
</description>
</node>
</vtydoc>

View File

@@ -12,4 +12,4 @@ osmo_ggsn_LDADD += $(LIBGTPNL_LIBS)
endif
osmo_ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
osmo_ggsn_SOURCES = ggsn_main.c ggsn_vty.c ggsn.c ggsn.h sgsn.c sgsn.h pco.c pco.h
osmo_ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h icmpv6.c icmpv6.h checksum.c checksum.h

View File

@@ -1,7 +1,7 @@
/*
* OsmoGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003, 2004 Mondru AB.
* Copyright (C) 2017-2019 by Harald Welte <laforge@gnumonks.org>
* Copyright (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
@@ -42,8 +42,21 @@
#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"
@@ -51,26 +64,31 @@
#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 "../lib/icmpv6.h"
#include "pco.h"
#include "icmpv6.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)
{
@@ -88,13 +106,14 @@ static void pool_close_all_pdp(struct ippool_t *pool)
pdp = member->peer;
if (!pdp)
continue;
ggsn_close_one_pdp(pdp);
LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n");
gtp_delete_context_req(pdp->gsn, pdp, NULL, 1);
}
}
int apn_stop(struct apn_ctx *apn)
int apn_stop(struct apn_ctx *apn, bool force)
{
LOGPAPN(LOGL_NOTICE, apn, "Stopping\n");
LOGPAPN(LOGL_NOTICE, apn, "%sStopping\n", force ? "FORCED " : "");
/* 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);
@@ -187,14 +206,14 @@ int apn_start(struct apn_ctx *apn)
switch (apn->cfg.gtpu_mode) {
case APN_GTPU_MODE_TUN:
LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name);
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, false, -1, -1)) {
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, -1, -1, false)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
return -1;
}
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, OSMO_FD_READ, ggsn_tun_fd_cb, apn, 0);
osmo_fd_setup(&apn->tun.fd, apn->tun.tun->fd, BSC_FD_READ, ggsn_tun_fd_cb, apn, 0);
osmo_fd_register(&apn->tun.fd);
/* Set TUN library callback */
@@ -204,7 +223,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);
apn_stop(apn, false);
return -1;
}
if (gsn == NULL) {
@@ -216,7 +235,7 @@ int apn_start(struct apn_ctx *apn)
return 0;
}
/* use GTP kernel module for data packet encapsulation */
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, true, gsn->fd0, gsn->fd1u)) {
if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, gsn->fd0, gsn->fd1u, true)) {
LOGPAPN(LOGL_ERROR, apn, "Failed to configure Kernel GTP device\n");
return -1;
}
@@ -238,7 +257,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);
apn_stop(apn, false);
return -1;
}
}
@@ -251,7 +270,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);
apn_stop(apn, false);
return -1;
}
}
@@ -264,7 +283,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);
apn_stop(apn, false);
return -1;
}
apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
@@ -282,7 +301,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);
apn_stop(apn, false);
return -1;
}
apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
@@ -299,7 +318,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);
apn_stop(apn, false);
return -1;
}
talloc_free(blacklist);
@@ -316,7 +335,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);
apn_stop(apn, false);
return -1;
}
talloc_free(blacklist);
@@ -346,8 +365,7 @@ 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 pdp_priv_t *pdp_priv = pdp->priv;
struct apn_ctx *apn;
struct apn_ctx *apn = pdp->priv;
struct ippoolm_t *member;
int i;
@@ -358,32 +376,212 @@ 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 (!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 (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];
};
struct ipcp_hdr {
uint8_t code;
uint8_t id;
uint16_t len;
uint8_t options[0];
};
/* determine if IPCP contains given option */
static struct ipcp_option_hdr *ipcp_contains_option(struct ipcp_hdr *ipcp, enum ipcp_options opt)
{
uint8_t *cur = ipcp->options;
/* iterate over Options and check if protocol contained */
while (cur + 2 <= ((uint8_t *)ipcp) + ipcp->len) {
struct ipcp_option_hdr *cur_opt = (struct ipcp_option_hdr *) cur;
if (cur_opt->type == opt)
return cur_opt;
cur += 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, uint16_t prot)
{
uint8_t *cur = pco->v + 1;
/* 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)
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 int 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];
struct ipcp_hdr *ipcp;
uint8_t *len1, *len2, *pco_ipcp;
uint8_t *start = msg->tail;
unsigned int len_appended;
if (!(pco_ipcp = pco_contains_proto(&pdp->pco_req, PCO_P_IPCP)))
return 0;
ipcp = (struct ipcp_hdr*) (pco_ipcp + 3); /* 2=type + 1=len */
/* 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->id); /* 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_OPT_PRIMARY_DNS)) {
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_OPT_SECONDARY_DNS)) {
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;
return 0;
}
/* 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, PCO_P_DNS_IPv6_ADDR)) {
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, PCO_P_DNS_IPv4_ADDR)) {
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)
@@ -398,36 +596,6 @@ 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];
@@ -436,19 +604,15 @@ 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 = NULL;
struct apn_ctx *apn;
int rc, num_addr, i;
char *apn_name;
struct sgsn_peer *sgsn;
struct pdp_priv_t *pdp_priv;
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)");
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);
/* First find an exact APN name match */
if (apn_name != NULL)
apn = ggsn_find_apn(ggsn, name_buf);
apn = ggsn_find_apn(ggsn, name_buf);
/* ignore if the APN has not been started */
if (apn && !apn->started)
apn = NULL;
@@ -484,15 +648,9 @@ 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 (in46a_is_v4(&addr[i])) {
if (addr[i].len == sizeof(struct in_addr)) {
/* does this APN actually have an IPv4 pool? */
if (!apn_supports_ipv4(apn))
goto err_wrong_af;
@@ -505,7 +663,7 @@ int create_context_ind(struct pdp_t *pdp)
addrv4 = member;
} else if (in46a_is_v6(&addr[i])) {
} else if (addr[i].len == sizeof(struct in6_addr)) {
/* does this APN actually have an IPv6 pool? */
if (!apn_supports_ipv6(apn))
@@ -542,14 +700,7 @@ int create_context_ind(struct pdp_t *pdp)
}
pdp->ipif = apn->tun.tun; /* TODO */
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;
pdp->priv = apn;
/* TODO: change trap to send 2 IPs */
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
@@ -613,7 +764,7 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
pool = apn->v6.pool;
break;
default:
LOGTUN(LOGL_NOTICE, tun, "non-IPv%u packet received\n", iph->version);
LOGP(DTUN, LOGL_NOTICE, "non-IPv%u packet received from tun\n", iph->version);
return -1;
}
@@ -621,21 +772,26 @@ 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)) {
LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s) with no PDP contex! (%s)\n",
apn->cfg.name,
iph->version == 4 ?
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)));
return 0;
}
LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s)\n", apn->cfg.name);
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);
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)
{
@@ -699,12 +855,14 @@ 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 & OSMO_FD_READ);
OSMO_ASSERT(what & BSC_FD_READ);
return tun_decaps(apn->tun.tun);
}
@@ -715,7 +873,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 & OSMO_FD_READ);
OSMO_ASSERT(what & BSC_FD_READ);
switch (fd->priv_nr) {
case 0:
@@ -734,54 +892,52 @@ static int ggsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
return rc;
}
/* libgtp callback for confirmations */
static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
static void ggsn_gtp_tmr_start(struct ggsn_ctx *ggsn)
{
struct sgsn_peer *sgsn;
int rc = 0;
struct timeval next;
if (cause == EOF)
LOGP(DGGSN, LOGL_NOTICE, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n",
type, pdp, cbp);
/* Retrieve next retransmission as timeval */
gtp_retranstimeout(ggsn->gsn, &next);
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 GTP_ECHO_REQ:
sgsn = (struct sgsn_peer *)cbp;
sgsn_peer_echo_resp(sgsn, cause == EOF);
break;
}
return rc;
/* re-schedule the timer */
osmo_timer_schedule(&ggsn->gtp_timer, next.tv_sec, next.tv_usec/1000);
}
static int cb_recovery3(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery)
/* timer callback for libgtp retransmission and ping */
static void ggsn_gtp_tmr_cb(void *data)
{
struct ggsn_ctx *ggsn = (struct ggsn_ctx *)gsn->priv;
struct sgsn_peer *sgsn;
struct ggsn_ctx *ggsn = data;
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;
}
/* do all the retransmissions as needed */
gtp_retrans(ggsn->gsn);
return sgsn_peer_handle_recovery(sgsn, pdp, recovery);
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;
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:
break;
}
}
/* Start a given GGSN */
int ggsn_start(struct ggsn_ctx *ggsn)
{
@@ -809,23 +965,24 @@ 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, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_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, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 1);
osmo_fd_setup(&ggsn->gtp_fd1c, ggsn->gsn->fd1c, BSC_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, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 2);
osmo_fd_setup(&ggsn->gtp_fd1u, ggsn->gsn->fd1u, BSC_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;
@@ -846,7 +1003,9 @@ 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);
apn_stop(apn, true);
osmo_timer_del(&ggsn->gtp_timer);
osmo_fd_unregister(&ggsn->gtp_fd1u);
osmo_fd_unregister(&ggsn->gtp_fd1c);
@@ -860,3 +1019,127 @@ 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(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 1;
}

View File

@@ -6,7 +6,6 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/ctrl/control_if.h>
#include "../lib/tun.h"
#include "../lib/ippool.h"
@@ -14,8 +13,6 @@
#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 */
@@ -91,14 +88,6 @@ 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;
@@ -106,9 +95,6 @@ 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 {
@@ -125,8 +111,6 @@ struct ggsn_ctx {
struct in46_addr gtpu_addr;
/* directory for state file */
char *state_dir;
/* Time between Echo requests on each SGSN */
unsigned int echo_interval;
/* administratively shut-down (true) or not (false) */
bool shutdown;
} cfg;
@@ -138,6 +122,8 @@ 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 */
@@ -149,24 +135,9 @@ 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_main.c */
extern struct ctrl_handle *g_ctrlh;
extern void *tall_ggsn_ctx;
/* ggsn.c */
extern void *tall_ggsn_ctx;
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);
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)
#define LOGTUN(level, tun, fmt, args...) \
LOGP(DTUN, level, "TUN(%s): " fmt, (tun)->devname, ## args)
extern int apn_stop(struct apn_ctx *apn, bool force);

View File

@@ -1,256 +0,0 @@
/*
* 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/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";
/* 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_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 0;
}

View File

@@ -26,8 +26,6 @@
#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>
@@ -37,10 +35,7 @@
#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"
@@ -80,7 +75,6 @@ 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;
@@ -330,80 +324,6 @@ 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 shut-down\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;
int prev_interval = ggsn->cfg.echo_interval;
struct sgsn_peer *sgsn;
if (prev_interval == ggsn->cfg.echo_interval)
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 = {
@@ -682,7 +602,7 @@ DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
struct apn_ctx *apn = (struct apn_ctx *) vty->index;
if (!apn->cfg.shutdown) {
if (apn_stop(apn)) {
if (apn_stop(apn, false)) {
vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -699,10 +619,6 @@ DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
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;
@@ -736,9 +652,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 & (UINT32_C(1) << i)))
if (!(apn->cfg.apn_type_mask & (1 << i)))
continue;
vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (1 << i)),
VTY_NEWLINE);
}
@@ -796,8 +712,6 @@ static int config_write_ggsn(struct vty *vty)
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);
}
@@ -816,42 +730,12 @@ static const char *print_gsnaddr(const struct ul16_t *in)
return in46a_ntoa(&in46);
}
/* 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)
static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
{
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);
struct in46_addr eua46;
vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
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);
}
osmo_hexdump_nospc(pdp->msisdn.v, pdp->msisdn.l), 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);
@@ -859,61 +743,32 @@ static void show_one_pdp_v4only(struct vty *vty, struct pdp_t *pdp, bool v4only)
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);
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);
in46a_from_eua(&pdp->eua, &eua46);
vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), 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 ggsn NAME imsi IMSI [<0-15>]",
"show pdp-context 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")
{
struct ggsn_ctx *ggsn;
uint64_t imsi;
uint64_t imsi = strtoull(argv[0], NULL, 10);
unsigned int nsapi;
struct pdp_t *pdp;
int num_found = 0;
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)) {
if (argc > 1) {
nsapi = atoi(argv[1]);
if (pdp_getimsi(&pdp, imsi, nsapi)) {
show_one_pdp(vty, pdp);
num_found++;
}
} else {
for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
if (gtp_pdp_getimsi(ggsn->gsn, &pdp, imsi, nsapi))
if (pdp_getimsi(&pdp, imsi, nsapi))
continue;
show_one_pdp(vty, pdp);
num_found++;
@@ -926,49 +781,8 @@ 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, bool pdp_v4only)
static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool)
{
unsigned int i;
@@ -979,21 +793,21 @@ static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool, boo
struct ippoolm_t *member = &pool->member[i];
if (member->inuse == 0)
continue;
show_one_pdp_v4only(vty, member->peer, pdp_v4only);
show_one_pdp(vty, member->peer);
}
}
/* 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, true);
ippool_show_pdp_contexts(vty, apn->v6.pool, false);
ippool_show_pdp_contexts(vty, apn->v4.pool);
ippool_show_pdp_contexts(vty, apn->v6.pool);
}
DEFUN(show_pdpctx, show_pdpctx_cmd,
"show pdp-context ggsn NAME",
"show pdp-context ggsn NAME [apn APN]",
SHOW_STR "Show PDP Context Information\n"
GGSN_STR "GGSN Name\n")
GGSN_STR "GGSN Name\n") // FIXME
{
struct ggsn_ctx *ggsn;
struct apn_ctx *apn;
@@ -1003,45 +817,21 @@ DEFUN(show_pdpctx, show_pdpctx_cmd,
vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
llist_for_each_entry(apn, &ggsn->apn_list, list)
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;
}
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);
@@ -1051,15 +841,12 @@ 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,
@@ -1084,10 +871,7 @@ 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);
@@ -1106,9 +890,6 @@ 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);
install_node(&apn_node, NULL);
install_element(APN_NODE, &cfg_description_cmd);
@@ -1167,10 +948,6 @@ static int ggsn_vty_go_parent(struct vty *vty)
vty->index_sub = &apn->ggsn->cfg.description;
}
break;
default:
vty->node = CONFIG_NODE;
vty->index = NULL;
vty->index_sub = NULL;
}
return vty->node;

View File

@@ -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,9 +25,8 @@
#include "../gtp/gtp.h"
#include "../gtp/pdp.h"
#include "ippool.h"
#include "syserr.h"
#include "icmpv6.h"
#include "../lib/ippool.h"
#include "../lib/syserr.h"
#include "config.h"
/* 29.061 11.2.1.3.4 IPv6 Router Configuration Variables in GGSN */
@@ -36,68 +35,78 @@
#define GGSN_AdvValidLifetime 0xffffffff /* infinite */
#define GGSN_AdvPreferredLifetime 0xffffffff /* infinite */
/* 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 }
};
struct icmpv6_hdr {
uint8_t type;
uint8_t code;
uint16_t csum;
} __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.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));
/* 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 */
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));
/* 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;
}
/* 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));
/*! 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!)
* \param[in] prefix The single prefix to be advertised (/64 implied!)i
* \returns callee-allocated message buffer containing router advertisement */
static struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
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);
@@ -135,12 +144,24 @@ static struct msgb *icmpv6_construct_ra(const struct in6_addr *saddr,
memcpy(ra_opt_pref->prefix, prefix, sizeof(ra_opt_pref->prefix));
/* checksum */
ra->hdr.csum = icmpv6_prepend_ip6hdr(msg, saddr, daddr);
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;
return msg;
}
/* Validate an ICMPv6 router solicitation according to RFC4861 6.1.1 */
/* Walidate 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;
@@ -158,37 +179,6 @@ 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,

9
ggsn/icmpv6.h Normal file
View File

@@ -0,0 +1,9 @@
#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);

View File

@@ -1,252 +0,0 @@
/*
* 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);
}

View File

@@ -1,81 +0,0 @@
#pragma once
#include <stdint.h>
#include "../gtp/pdp.h"
/* 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,
};
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);

View File

@@ -1,160 +0,0 @@
#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];
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++;
}
/* Note: if except is NULL, all pdp contexts are freed and sgsn is
already freed at this point */
LOGP(DGGSN, LOGL_INFO, "SGSN(%s) Dropped %u PDP contexts\n", buf, num);
return num;
}
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;
}

View File

@@ -1,46 +0,0 @@
#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)

View File

@@ -1,9 +1,7 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
# If major=current-age is increased, remember to update the dh_strip line in debian/rules!
LIBVERSION=7:0:1
LIBVERSION=2:0:0
lib_LTLIBRARIES = libgtp.la
include_HEADERS = gtp.h pdp.h gtpie.h
@@ -13,3 +11,7 @@ AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_
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_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS)

856
gtp/gtp.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,18 @@
/*
/*
* 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>
#include <osmocom/core/timer.h>
#include "pdp.h"
#define GTP_MODE_GGSN 1
#define GTP_MODE_SGSN 2
@@ -145,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
@@ -234,13 +230,13 @@ union gtp_packet {
* Information storage for each gsn instance
*
* Normally each instance of the application corresponds to
* one instance of a gsn.
*
* 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.
*************************************************************/
@@ -266,11 +262,6 @@ struct gsn_t {
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 *);
@@ -279,8 +270,6 @@ struct gsn_t {
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 */
@@ -316,9 +305,8 @@ extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
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");
uint64_t imsi, uint8_t nsapi);
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);
@@ -335,10 +323,7 @@ extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp, struct in_addr *inetaddr);
extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp, int teardown)
OSMO_DEPRECATED("Use gtp_delete_context_req2() instead, to avoid freeing pdp ctx before reply");
extern int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp,
void *cbp, int teardown);
void *cbp, int teardown);
extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp,
void *pack, unsigned len);
@@ -351,8 +336,8 @@ 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) 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");
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 *
@@ -372,20 +357,8 @@ extern int gtp_set_cb_conf(struct gsn_t *gsn,
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);
/* Internal functions (not part of the API */
extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
@@ -441,6 +414,5 @@ 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 */

164
gtp/pdp.c
View File

@@ -31,7 +31,14 @@
#include "pdp.h"
#include "gtp.h"
#include "lookupa.h"
#include "queue.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 */
/* ***********************************************************
* Functions related to PDP storage
@@ -105,16 +112,11 @@
*
*************************************************************/
static struct gsn_t *g_gsn;
int pdp_init(struct gsn_t *gsn)
int pdp_init()
{
if (!g_gsn) {
g_gsn = gsn;
} else {
LOGP(DLGTP, LOGL_FATAL, "This interface is depreacted and doesn't support multiple GGSN!");
return -1;
}
memset(&pdpa, 0, sizeof(pdpa));
memset(&hashtid, 0, sizeof(hashtid));
/* memset(&haship, 0, sizeof(haship)); */
return 0;
}
@@ -122,13 +124,6 @@ int pdp_init(struct gsn_t *gsn)
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) {
@@ -138,7 +133,6 @@ int gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t
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;
@@ -157,7 +151,7 @@ int gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t
}
/* Default: Generate G-PDU sequence numbers on Tx */
(*pdp)->tx_gpdu_seq = true;
INIT_LLIST_HEAD(&(*pdp)->qmsg_list_req);
return 0;
}
}
@@ -166,18 +160,6 @@ int gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t
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 */
@@ -192,20 +174,12 @@ int pdp_freepdp(struct pdp_t *pdp)
int pdp_getpdp(struct pdp_t **pdp)
{
*pdp = &g_gsn->pdpa[0];
*pdp = &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 {
@@ -220,13 +194,6 @@ int gtp_pdp_getgtp0(struct gsn_t *gsn, 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 {
@@ -242,12 +209,6 @@ int gtp_pdp_getgtp1(struct gsn_t *gsn, 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... */
@@ -270,7 +231,6 @@ 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;
@@ -289,7 +249,6 @@ 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;
@@ -311,12 +270,6 @@ 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);
@@ -333,15 +286,82 @@ int gtp_pdp_tidget(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t tid)
int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
{
return gtp_pdp_getimsi(g_gsn, pdp, imsi, nsapi);
return pdp_tidget(pdp,
(imsi & 0x0fffffffffffffffull) +
((uint64_t) nsapi << 60));
}
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_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 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)
@@ -354,17 +374,3 @@ 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;
}

View File

@@ -14,10 +14,6 @@
#define _PDP_H
#include <stdbool.h>
#include <netinet/in.h>
#include <osmocom/core/defs.h>
#include <osmocom/core/linuxlist.h>
struct gsn_t;
@@ -239,42 +235,38 @@ struct pdp_t {
/* to be used by libgtp callers/users (to attach their own private state) */
void *priv;
struct gsn_t *gsn; /* Back pointer to GSN where this pdp ctx belongs to */
struct gsn_t *gsn;
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 gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi,
uint8_t nsapi, struct pdp_t *pdp_old);
int pdp_init();
int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
struct pdp_t *pdp_old);
int pdp_freepdp(struct pdp_t *pdp);
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_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 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);
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");
/*
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);
#endif /* !_PDP_H */

View File

@@ -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
@@ -79,7 +79,7 @@ static int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg,
if (QUEUE_DEBUG)
printf("Begin queue_seqset seq = %d\n", (int)seq);
if (QUEUE_DEBUG)
printf("SIZEOF PEER %zu, *PEER %zu\n", sizeof(peer),
printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer),
sizeof(*peer));
qmsg->seq = seq;
@@ -121,10 +121,7 @@ static int queue_seqdel(struct queue_t *queue, struct qmsg_t *qmsg)
return EOF; /* End of linked list and not found */
}
/*! 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
*/
/*! \brief Allocates and initialises new queue structure */
int queue_new(struct queue_t **queue)
{
if (QUEUE_DEBUG)
@@ -141,10 +138,7 @@ int queue_new(struct queue_t **queue)
return 0;
}
/*! Deallocates queue structure.
* \param[in] queue pointer previously allocated by queue_new
* \returns zero on success, non-zero on error.
*/
/*! \brief Deallocates queue structure */
int queue_free(struct queue_t *queue)
{
if (QUEUE_DEBUG)
@@ -155,13 +149,7 @@ int queue_free(struct queue_t *queue)
return 0;
}
/*! 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.
*/
/*! \brief Add a new message to the queue */
int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
struct sockaddr_in *peer, uint16_t seq)
{
@@ -172,7 +160,6 @@ 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 */
@@ -189,11 +176,7 @@ int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
}
}
/*! 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.
/*! \brief Simply remoev a given qmsg_t from the queue
*
* Internally, we first delete the entry from the queue, and then update
* up our global queue->first / queue->last pointers. Finally,
@@ -207,8 +190,6 @@ 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? */
@@ -229,11 +210,7 @@ int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg)
return 0;
}
/*! 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.
*/
/*! \brief Move a given qmsg_t to the end of the queue ?!? */
int queue_back(struct queue_t *queue, struct qmsg_t *qmsg)
{
if (QUEUE_DEBUG)
@@ -259,11 +236,7 @@ int queue_back(struct queue_t *queue, struct qmsg_t *qmsg)
return 0;
}
/*! 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.
*/
/*! \brief Get the first element in the entire queue */
int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg)
{
/*printf("queue_getfirst\n"); */
@@ -277,13 +250,7 @@ int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg)
return 0;
}
/*! 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.
*/
/*! \brief Get a queue entry for a given peer + seq */
int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
struct sockaddr_in *peer, uint16_t seq)
{
@@ -305,14 +272,7 @@ int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
return EOF; /* End of linked list and not found */
}
/*! 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.
*/
/*! \brief look-up a given seq/peer, return cbp + type and free entry */
int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
uint16_t seq, uint8_t * type, void **cbp)
{

View File

@@ -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,10 +17,6 @@
#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 */
@@ -41,7 +37,6 @@ 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 {

View File

@@ -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 netns.h util.h icmpv6.h checksum.h
noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.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 netns.c util.c icmpv6.c checksum.c
libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c
if ENABLE_GTP_KERNEL
AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)

View File

@@ -24,12 +24,12 @@ static const struct log_info_cat default_categories[] = {
[DSGSN] = {
.name = "DSGSN",
.description = "SGSN Emulator",
.enabled = 1, .loglevel = LOGL_INFO,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DICMP6] = {
.name = "DICMP6",
.description = "ICMPv6",
.enabled = 1, .loglevel = LOGL_NOTICE,
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};

View File

@@ -18,6 +18,7 @@
#include <libgtpnl/gtp.h>
#include <libgtpnl/gtpnl.h>
#include <libmnl/libmnl.h>
#include <errno.h>
@@ -25,32 +26,27 @@
#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)
{
char buf4[INET_ADDRSTRLEN], buf6[INET6_ADDRSTRLEN];
struct ippoolm_t *peer;
struct in46_addr ia46;
struct in_addr ia;
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));
in46a_from_eua(&pdp->eua, &ia46);
gsna2in_addr(&ia, &pdp->gsnrc);
LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=(%s,%s) SGSN=%s\n", prefix,
LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=%s SGSN=%s\n", prefix,
devname, pdp->version,
pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn,
buf4, buf6, inet_ntoa(ia));
in46a_ntoa(&ia46), inet_ntoa(ia));
}
static struct {

View File

@@ -1,100 +0,0 @@
#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;
#else
uint8_t m:1,
o: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;
#else
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;

View File

@@ -60,11 +60,7 @@ int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
return 0;
}
/*! 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 */
/*! Convenience wrapper around inet_ntop() for \ref in46_addr */
const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size)
{
int af;

View File

@@ -31,11 +31,3 @@ 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);
}

View File

@@ -26,16 +26,16 @@ int ippool_printaddr(struct ippool_t *this)
{
unsigned int n;
printf("ippool_printaddr\n");
printf("Firstdyn %td\n", this->firstdyn - this->member);
printf("Lastdyn %td\n", this->lastdyn - this->member);
printf("Firststat %td\n", this->firststat - this->member);
printf("Laststat %td\n", this->laststat - this->member);
printf("Listsize %u\n", this->listsize);
printf("Firstdyn %d\n", this->firstdyn - this->member);
printf("Lastdyn %d\n", this->lastdyn - this->member);
printf("Firststat %d\n", this->firststat - this->member);
printf("Laststat %d\n", this->laststat - this->member);
printf("Listsize %d\n", this->listsize);
for (n = 0; n < this->listsize; n++) {
char s[256];
in46a_ntop(&this->member[n].addr, s, sizeof(s));
printf("Unit %d inuse %d prev %td next %td addr %s\n",
printf("Unit %d inuse %d prev %d next %d addr %s\n",
n,
this->member[n].inuse,
this->member[n].prev - this->member,
@@ -202,7 +202,7 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
/* Parse only first instance of pool for now */
int i;
struct in46_addr addr = { 0 };
struct in46_addr addr;
size_t addrprefixlen;
struct in46_addr stataddr;
size_t stataddrprefixlen;
@@ -276,8 +276,9 @@ int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const stru
(*this)->hashmask = (*this)->hashsize - 1;
/* Allocate hash table */
(*this)->hash = calloc((*this)->hashsize, sizeof(struct ippoolm_t *));
if (!(*this)->hash) {
if (!
((*this)->hash =
calloc(sizeof(struct ippoolm_t), (*this)->hashsize))) {
SYS_ERR(DIP, LOGL_ERROR, 0,
"Failed to allocate memory for hash members in ippool");
return -1;
@@ -512,15 +513,7 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
p2->next = NULL;
p2->prev = NULL;
p2->inuse = 2; /* Static address in use */
/* p2->addr.len and addr->len already match (see above). */
if (p2->addr.len == sizeof(struct in_addr))
p2->addr.v4 = addr->v4;
else if (p2->addr.len == sizeof(struct in6_addr))
p2->addr.v6 = addr->v6;
else {
SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
return -GTPCAUSE_UNKNOWN_PDP;
}
memcpy(&p2->addr, addr, sizeof(addr));
*member = p2;
(void)ippool_hashadd(this, *member);
if (0)

View File

@@ -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_addroute4(dstaddr, addr, &this->netmask);
netdev_addroute(dstaddr, addr, &this->netmask);
#endif
return 0;
@@ -224,6 +224,7 @@ int netdev_setaddr6(const char *devname, struct in6_addr *addr, struct in6_addr
#if 0
/* FIXME: looks like this is not possible/necessary for IPv6? */
if (dstaddr) {
memcpy(&dstaddr, dstaddr, sizeof(*dstaddr));
memcpy(&ifr.ifr6_addr, dstaddr, sizeof(*dstaddr));
if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t *) &ifr) < 0) {
SYS_ERR(DTUN, LOGL_ERROR, "ioctl(SIOCSIFDSTADDR) failed");
@@ -433,7 +434,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 = prefixlen; /* 64 FOR IPv6 */
req.i.ifa_prefixlen = 64; /* 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 +554,7 @@ int netdev_addaddr6(const char *devname, struct in6_addr *addr,
return 0;
}
static int netdev_route4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
static int netdev_route(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete)
{
int fd;
#if defined(__linux__)
@@ -643,81 +644,16 @@ static int netdev_route4(struct in_addr *dst, struct in_addr *gateway, struct in
return 0;
}
static int netdev_route6(struct in6_addr *dst, struct in6_addr *gateway, int prefixlen, const char *gw_iface, int delete)
int netdev_addroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
{
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;
return netdev_route(dst, gateway, mask, 0);
}
int netdev_addroute4(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
int netdev_delroute(struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask)
{
return netdev_route4(dst, gateway, mask, 0);
return netdev_route(dst, gateway, mask, 1);
}
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

View File

@@ -65,10 +65,8 @@ 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_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_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_ip_local_get(const char *devname, struct in46_prefix *prefix_list,
size_t prefix_size, int flags);

View File

@@ -1,272 +0,0 @@
/*
* 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

View File

@@ -1,35 +0,0 @@
/*
* 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

View File

@@ -68,14 +68,10 @@ static int tun_setaddr4(struct tun_t *this, struct in_addr *addr,
if (rc < 0)
return rc;
if (addr) {
this->addr.len = sizeof(struct in_addr);
this->addr.v4.s_addr = addr->s_addr;
}
if (dstaddr) {
this->dstaddr.len = sizeof(struct in_addr);
this->dstaddr.v4.s_addr = dstaddr->s_addr;
}
if (addr)
this->addr.s_addr = addr->s_addr;
if (dstaddr)
this->dstaddr.s_addr = dstaddr->s_addr;
if (netmask)
this->netmask.s_addr = netmask->s_addr;
this->addrs++;
@@ -93,10 +89,6 @@ static int tun_setaddr6(struct tun_t *this, struct in6_addr *addr, struct in6_ad
rc = netdev_setaddr6(this->devname, addr, dstaddr, prefixlen);
if (rc < 0)
return rc;
if (dstaddr) {
this->dstaddr.len = sizeof(*dstaddr);
memcpy(&this->dstaddr.v6, dstaddr, sizeof(*dstaddr));
}
this->addrs++;
#if defined(__FreeBSD__) || defined (__APPLE__)
this->routes = 1;
@@ -155,7 +147,7 @@ int tun_addaddr(struct tun_t *this, struct in46_addr *addr, struct in46_addr *ds
}
}
int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u)
int tun_new(struct tun_t **tun, const char *dev_name, int fd0, int fd1u, bool use_kernel)
{
#if defined(__linux__)
@@ -199,10 +191,7 @@ int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0,
strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
(*tun)->devname[IFNAMSIZ - 1] = 0;
/* Disable checksums */
if (ioctl((*tun)->fd, TUNSETNOCSUM, 1) < 0) {
SYS_ERR(DTUN, LOGL_NOTICE, errno, "could not disable checksum on %s", (*tun)->devname);
}
ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
return 0;
} else {
strncpy((*tun)->devname, dev_name, IFNAMSIZ);
@@ -276,7 +265,7 @@ int tun_free(struct tun_t *tun)
{
if (tun->routes) {
netdev_delroute4(&tun->dstaddr.v4, &tun->addr.v4, &tun->netmask);
netdev_delroute(&tun->dstaddr, &tun->addr, &tun->netmask);
}
if (tun->fd >= 0) {
@@ -329,7 +318,7 @@ int tun_runscript(struct tun_t *tun, char *script)
char smask[TUN_ADDRSIZE];
int rc;
strncpy(snet, inet_ntoa(tun->addr.v4), sizeof(snet));
strncpy(snet, inet_ntoa(tun->addr), sizeof(snet));
snet[sizeof(snet) - 1] = 0;
strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask));
smask[sizeof(smask) - 1] = 0;

View File

@@ -31,8 +31,8 @@
struct tun_t {
int fd; /* File descriptor to tun interface */
struct in46_addr addr;
struct in46_addr dstaddr;
struct in_addr addr;
struct in_addr dstaddr;
struct in_addr netmask;
int addrs; /* Number of allocated IP addresses */
int routes; /* One if we allocated an automatic route */
@@ -42,7 +42,7 @@ struct tun_t {
void *priv;
};
extern int tun_new(struct tun_t **tun, const char *dev_name, bool use_kernel, int fd0, int fd1u);
extern int tun_new(struct tun_t **tun, const char *dev_name, int fd0, int fd1u, bool use_kernel);
extern int tun_free(struct tun_t *tun);
extern int tun_decaps(struct tun_t *this);
extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len);

View File

@@ -1,35 +0,0 @@
/*
* 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;
}

View File

@@ -1,18 +0,0 @@
#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);

90
osmo-ggsn.spec.in Normal file
View File

@@ -0,0 +1,90 @@
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.

View File

@@ -72,7 +72,6 @@ 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')",
@@ -164,7 +163,6 @@ 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;
@@ -246,8 +244,6 @@ 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;
@@ -304,14 +300,13 @@ 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->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];
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];
}
@@ -368,6 +363,14 @@ 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) {
@@ -429,8 +432,6 @@ 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));
@@ -544,8 +545,6 @@ 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);
@@ -710,12 +709,6 @@ 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",
@@ -961,7 +954,6 @@ 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},
@@ -1501,22 +1493,6 @@ 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
@@ -1633,12 +1609,11 @@ 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,
args_info->netns_given, -1
args_info->ipdown_given, args_info->tun_device_given, -1
};
const char *createif_desc[] =
{ "--createif", "--net", "--defaultroute", "--ipup",
"--ipdown", "--tun-device", "--netns", 0
"--ipdown", "--tun-device", 0
};
int pinghost_given[] =
{ args_info->pinghost_given, args_info->pingrate_given,

View File

@@ -59,7 +59,6 @@ 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"

View File

@@ -242,12 +242,6 @@ 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;
@@ -361,8 +355,6 @@ 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;
@@ -479,6 +471,13 @@ 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

View File

@@ -2,30 +2,18 @@ AM_CFLAGS = -Wall -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -g
EXTRA_DIST = \
gtpie_test.ok \
queue_test.ok \
$(NULL)
noinst_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)

View File

@@ -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_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));

View File

@@ -1,233 +0,0 @@
#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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));

View File

@@ -131,9 +131,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_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_category(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
srand(time(NULL));

View File

@@ -32,9 +32,3 @@ 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